2016-06-02 03:12:26 -05:00
|
|
|
#
|
|
|
|
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
|
|
|
#
|
|
|
|
|
2016-07-26 06:35:22 -05:00
|
|
|
import errno
|
|
|
|
import json
|
2016-08-22 06:34:30 -05:00
|
|
|
import locale
|
2017-05-25 05:42:54 -05:00
|
|
|
import logging
|
2016-07-26 06:35:22 -05:00
|
|
|
import os
|
2016-08-22 06:34:30 -05:00
|
|
|
import time
|
2016-07-26 06:35:22 -05:00
|
|
|
|
2018-07-04 03:07:49 -05:00
|
|
|
import six
|
|
|
|
|
2016-06-30 08:51:29 -05:00
|
|
|
from . import compat
|
2016-06-02 03:12:26 -05:00
|
|
|
from . import schema
|
2016-06-30 08:51:29 -05:00
|
|
|
from ipaclient.plugins.rpcclient import rpcclient
|
2017-02-17 04:25:17 -06:00
|
|
|
from ipalib.constants import USER_CACHE_PATH
|
2016-07-26 06:35:22 -05:00
|
|
|
from ipapython.dnsutil import DNSName
|
|
|
|
|
2018-07-04 03:07:49 -05:00
|
|
|
# pylint: disable=no-name-in-module, import-error
|
|
|
|
if six.PY3:
|
|
|
|
from collections.abc import MutableMapping
|
|
|
|
else:
|
|
|
|
from collections import MutableMapping
|
|
|
|
# pylint: enable=no-name-in-module, import-error
|
|
|
|
|
2017-05-25 05:42:54 -05:00
|
|
|
logger = logging.getLogger(__name__)
|
2016-07-26 06:35:22 -05:00
|
|
|
|
|
|
|
|
2018-07-04 03:07:49 -05:00
|
|
|
class ServerInfo(MutableMapping):
|
2017-02-17 04:25:17 -06:00
|
|
|
_DIR = os.path.join(USER_CACHE_PATH, 'ipa', 'servers')
|
2016-07-26 06:35:22 -05:00
|
|
|
|
|
|
|
def __init__(self, api):
|
|
|
|
hostname = DNSName(api.env.server).ToASCII()
|
|
|
|
self._path = os.path.join(self._DIR, hostname)
|
2016-08-22 06:34:30 -05:00
|
|
|
self._force_check = api.env.force_schema_check
|
2016-07-26 06:35:22 -05:00
|
|
|
self._dict = {}
|
|
|
|
|
2016-08-22 06:34:30 -05:00
|
|
|
# copy-paste from ipalib/rpc.py
|
|
|
|
try:
|
2018-01-23 07:41:25 -06:00
|
|
|
self._language = locale.setlocale(
|
|
|
|
locale.LC_MESSAGES, ''
|
|
|
|
).split('.')[0].lower()
|
2016-08-22 06:34:30 -05:00
|
|
|
except locale.Error:
|
|
|
|
self._language = 'en_us'
|
2016-08-09 10:05:17 -05:00
|
|
|
|
2016-08-22 06:34:30 -05:00
|
|
|
self._read()
|
2016-07-26 06:35:22 -05:00
|
|
|
|
|
|
|
def _read(self):
|
|
|
|
try:
|
|
|
|
with open(self._path, 'r') as sc:
|
|
|
|
self._dict = json.load(sc)
|
2017-01-03 01:57:21 -06:00
|
|
|
except Exception as e:
|
|
|
|
if (isinstance(e, EnvironmentError) and
|
|
|
|
e.errno == errno.ENOENT): # pylint: disable=no-member
|
|
|
|
# ignore non-existent file, this happens when the cache was
|
|
|
|
# erased or the server is contacted for the first time
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
# warn that the file is unreadable, probably corrupted
|
2017-05-25 05:42:54 -05:00
|
|
|
logger.warning('Failed to read server info: %s', e)
|
2016-07-26 06:35:22 -05:00
|
|
|
|
|
|
|
def _write(self):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
os.makedirs(self._DIR)
|
|
|
|
except EnvironmentError as e:
|
|
|
|
if e.errno != errno.EEXIST:
|
|
|
|
raise
|
|
|
|
with open(self._path, 'w') as sc:
|
|
|
|
json.dump(self._dict, sc)
|
|
|
|
except EnvironmentError as e:
|
2017-05-25 05:42:54 -05:00
|
|
|
logger.warning('Failed to write server info: %s', e)
|
2016-07-26 06:35:22 -05:00
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
return self._dict[key]
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
self._dict[key] = value
|
|
|
|
|
|
|
|
def __delitem__(self, key):
|
|
|
|
del self._dict[key]
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return iter(self._dict)
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self._dict)
|
2016-06-02 03:12:26 -05:00
|
|
|
|
2016-08-22 06:34:30 -05:00
|
|
|
def update_validity(self, ttl=None):
|
|
|
|
if ttl is None:
|
|
|
|
ttl = 3600
|
|
|
|
self['expiration'] = time.time() + ttl
|
|
|
|
self['language'] = self._language
|
|
|
|
self._write()
|
|
|
|
|
|
|
|
def is_valid(self):
|
|
|
|
if self._force_check:
|
|
|
|
return False
|
|
|
|
|
|
|
|
try:
|
|
|
|
expiration = self._dict['expiration']
|
|
|
|
language = self._dict['language']
|
|
|
|
except KeyError:
|
|
|
|
# if any of these is missing consider the entry expired
|
|
|
|
return False
|
|
|
|
|
|
|
|
if expiration < time.time():
|
|
|
|
# validity passed
|
|
|
|
return False
|
|
|
|
|
|
|
|
if language != self._language:
|
|
|
|
# language changed since last check
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2016-06-02 03:12:26 -05:00
|
|
|
|
|
|
|
def get_package(api):
|
|
|
|
if api.env.in_tree:
|
2017-02-14 02:58:44 -06:00
|
|
|
# pylint: disable=import-error,ipa-forbidden-import
|
|
|
|
from ipaserver import plugins
|
|
|
|
# pylint: enable=import-error,ipa-forbidden-import
|
2016-06-02 03:12:26 -05:00
|
|
|
else:
|
2016-08-09 10:05:17 -05:00
|
|
|
try:
|
2016-08-22 06:34:30 -05:00
|
|
|
plugins = api._remote_plugins
|
2016-08-09 10:05:17 -05:00
|
|
|
except AttributeError:
|
2016-08-22 06:34:30 -05:00
|
|
|
server_info = ServerInfo(api)
|
2016-08-09 10:05:17 -05:00
|
|
|
|
2016-08-22 06:34:30 -05:00
|
|
|
client = rpcclient(api)
|
|
|
|
client.finalize()
|
|
|
|
|
|
|
|
try:
|
|
|
|
plugins = schema.get_package(server_info, client)
|
|
|
|
except schema.NotAvailable:
|
|
|
|
plugins = compat.get_package(server_info, client)
|
|
|
|
finally:
|
|
|
|
if client.isconnected():
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
object.__setattr__(api, '_remote_plugins', plugins)
|
2016-06-02 03:12:26 -05:00
|
|
|
|
|
|
|
return plugins
|