diff options
author | asteroide <thomas.duval@orange.com> | 2015-09-01 16:03:26 +0200 |
---|---|---|
committer | asteroide <thomas.duval@orange.com> | 2015-09-01 16:04:53 +0200 |
commit | 92fd2dbfb672d7b2b1cdfd5dd5cf89f7716b3e12 (patch) | |
tree | 7ba22297042019e7363fa1d4ad26d1c32c5908c6 /keystone-moon/keystone/resource/core.py | |
parent | 26e753254f3e43399cc76e62892908b7742415e8 (diff) |
Update Keystone code from official Github repository with branch Master on 09/01/2015.
Change-Id: I0ff6099e6e2580f87f502002a998bbfe12673498
Diffstat (limited to 'keystone-moon/keystone/resource/core.py')
-rw-r--r-- | keystone-moon/keystone/resource/core.py | 107 |
1 files changed, 72 insertions, 35 deletions
diff --git a/keystone-moon/keystone/resource/core.py b/keystone-moon/keystone/resource/core.py index 017eb4e7..ca69b729 100644 --- a/keystone-moon/keystone/resource/core.py +++ b/keystone-moon/keystone/resource/core.py @@ -10,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. -"""Main entry point into the resource service.""" +"""Main entry point into the Resource service.""" import abc @@ -18,12 +18,11 @@ from oslo_config import cfg from oslo_log import log import six -from keystone import clean from keystone.common import cache +from keystone.common import clean from keystone.common import dependency from keystone.common import driver_hints from keystone.common import manager -from keystone.contrib import federation from keystone import exception from keystone.i18n import _, _LE, _LW from keystone import notifications @@ -47,12 +46,15 @@ def calc_default_domain(): @dependency.requires('assignment_api', 'credential_api', 'domain_config_api', 'identity_api', 'revoke_api') class Manager(manager.Manager): - """Default pivot point for the resource backend. + """Default pivot point for the Resource backend. See :mod:`keystone.common.manager.Manager` for more details on how this dynamically calls the backend. """ + + driver_namespace = 'keystone.resource' + _DOMAIN = 'domain' _PROJECT = 'project' @@ -62,9 +64,8 @@ class Manager(manager.Manager): resource_driver = CONF.resource.driver if resource_driver is None: - assignment_driver = ( - dependency.get_provider('assignment_api').driver) - resource_driver = assignment_driver.default_resource_driver() + assignment_manager = dependency.get_provider('assignment_api') + resource_driver = assignment_manager.default_resource_driver() super(Manager, self).__init__(resource_driver) @@ -86,21 +87,23 @@ class Manager(manager.Manager): tenant['enabled'] = clean.project_enabled(tenant['enabled']) tenant.setdefault('description', '') tenant.setdefault('parent_id', None) + tenant.setdefault('is_domain', False) + self.get_domain(tenant.get('domain_id')) if tenant.get('parent_id') is not None: parent_ref = self.get_project(tenant.get('parent_id')) parents_list = self.list_project_parents(parent_ref['id']) parents_list.append(parent_ref) for ref in parents_list: if ref.get('domain_id') != tenant.get('domain_id'): - raise exception.ForbiddenAction( - action=_('cannot create a project within a different ' - 'domain than its parents.')) + raise exception.ValidationError( + message=_('cannot create a project within a different ' + 'domain than its parents.')) if not ref.get('enabled', True): - raise exception.ForbiddenAction( - action=_('cannot create a project in a ' - 'branch containing a disabled ' - 'project: %s') % ref['id']) + raise exception.ValidationError( + message=_('cannot create a project in a ' + 'branch containing a disabled ' + 'project: %s') % ref['id']) self._assert_max_hierarchy_depth(tenant.get('parent_id'), parents_list) @@ -135,14 +138,13 @@ class Manager(manager.Manager): """ # NOTE(marek-denis): We cannot create this attribute in the __init__ as # config values are always initialized to default value. - federated_domain = (CONF.federation.federated_domain_name or - federation.FEDERATED_DOMAIN_KEYWORD).lower() + federated_domain = CONF.federation.federated_domain_name.lower() if (domain.get('name') and domain['name'].lower() == federated_domain): raise AssertionError(_('Domain cannot be named %s') - % federated_domain) + % domain['name']) if (domain_id.lower() == federated_domain): raise AssertionError(_('Domain cannot have ID %s') - % federated_domain) + % domain_id) def assert_project_enabled(self, project_id, project=None): """Assert the project is enabled and its associated domain is enabled. @@ -177,7 +179,7 @@ class Manager(manager.Manager): 'disabled parents') % project_id) def _assert_whole_subtree_is_disabled(self, project_id): - subtree_list = self.driver.list_projects_in_subtree(project_id) + subtree_list = self.list_projects_in_subtree(project_id) for ref in subtree_list: if ref.get('enabled', True): raise exception.ForbiddenAction( @@ -194,6 +196,11 @@ class Manager(manager.Manager): raise exception.ForbiddenAction( action=_('Update of `parent_id` is not allowed.')) + if ('is_domain' in tenant and + tenant['is_domain'] != original_tenant['is_domain']): + raise exception.ValidationError( + message=_('Update of `is_domain` is not allowed.')) + if 'enabled' in tenant: tenant['enabled'] = clean.project_enabled(tenant['enabled']) @@ -241,15 +248,23 @@ class Manager(manager.Manager): user_projects = self.assignment_api.list_projects_for_user(user_id) user_projects_ids = set([proj['id'] for proj in user_projects]) # Keep only the projects present in user_projects - projects_list = [proj for proj in projects_list - if proj['id'] in user_projects_ids] + return [proj for proj in projects_list + if proj['id'] in user_projects_ids] + + def _assert_valid_project_id(self, project_id): + if project_id is None: + msg = _('Project field is required and cannot be empty.') + raise exception.ValidationError(message=msg) + # Check if project_id exists + self.get_project(project_id) def list_project_parents(self, project_id, user_id=None): + self._assert_valid_project_id(project_id) parents = self.driver.list_project_parents(project_id) # If a user_id was provided, the returned list should be filtered # against the projects this user has access to. if user_id: - self._filter_projects_list(parents, user_id) + parents = self._filter_projects_list(parents, user_id) return parents def _build_parents_as_ids_dict(self, project, parents_by_id): @@ -296,11 +311,12 @@ class Manager(manager.Manager): return parents_as_ids def list_projects_in_subtree(self, project_id, user_id=None): + self._assert_valid_project_id(project_id) subtree = self.driver.list_projects_in_subtree(project_id) # If a user_id was provided, the returned list should be filtered # against the projects this user has access to. if user_id: - self._filter_projects_list(subtree, user_id) + subtree = self._filter_projects_list(subtree, user_id) return subtree def _build_subtree_as_ids_dict(self, project_id, subtree_by_parent): @@ -780,6 +796,9 @@ class Driver(object): raise exception.DomainNotFound(domain_id=domain_id) +MEMOIZE_CONFIG = cache.get_memoization_decorator(section='domain_config') + + @dependency.provider('domain_config_api') class DomainConfigManager(manager.Manager): """Default pivot point for the Domain Config backend.""" @@ -793,6 +812,8 @@ class DomainConfigManager(manager.Manager): # Only those options that affect the domain-specific driver support in # the identity manager are supported. + driver_namespace = 'keystone.resource.domain_config' + whitelisted_options = { 'identity': ['driver'], 'ldap': [ @@ -975,6 +996,10 @@ class DomainConfigManager(manager.Manager): self.create_config_option( domain_id, option['group'], option['option'], option['value'], sensitive=True) + # Since we are caching on the full substituted config, we just + # invalidate here, rather than try and create the right result to + # cache. + self.get_config_with_sensitive_info.invalidate(self, domain_id) return self._list_to_config(whitelisted) def get_config(self, domain_id, group=None, option=None): @@ -999,7 +1024,7 @@ class DomainConfigManager(manager.Manager): 'url': 'myurl' 'user_tree_dn': 'OU=myou'}, 'identity': { - 'driver': 'keystone.identity.backends.ldap.Identity'} + 'driver': 'ldap'} } @@ -1077,22 +1102,22 @@ class DomainConfigManager(manager.Manager): 'provided contains group %(group_other)s ' 'instead') % { 'group': group, - 'group_other': config.keys()[0]} + 'group_other': list(config.keys())[0]} raise exception.InvalidDomainConfig(reason=msg) if option and option not in config[group]: msg = _('Trying to update option %(option)s in group ' '%(group)s, but config provided contains option ' '%(option_other)s instead') % { 'group': group, 'option': option, - 'option_other': config[group].keys()[0]} + 'option_other': list(config[group].keys())[0]} raise exception.InvalidDomainConfig(reason=msg) # Finally, we need to check if the group/option specified # already exists in the original config - since if not, to keep # with the semantics of an update, we need to fail with # a DomainConfigNotFound - if not self.get_config_with_sensitive_info(domain_id, - group, option): + if not self._get_config_with_sensitive_info(domain_id, + group, option): if option: msg = _('option %(option)s in group %(group)s') % { 'group': group, 'option': option} @@ -1131,6 +1156,7 @@ class DomainConfigManager(manager.Manager): for new_option in sensitive: _update_or_create(domain_id, new_option, sensitive=True) + self.get_config_with_sensitive_info.invalidate(self, domain_id) return self.get_config(domain_id) def delete_config(self, domain_id, group=None, option=None): @@ -1154,7 +1180,7 @@ class DomainConfigManager(manager.Manager): if group: # As this is a partial delete, then make sure the items requested # are valid and exist in the current config - current_config = self.get_config_with_sensitive_info(domain_id) + current_config = self._get_config_with_sensitive_info(domain_id) # Raise an exception if the group/options specified don't exist in # the current config so that the delete method provides the # correct error semantics. @@ -1171,14 +1197,14 @@ class DomainConfigManager(manager.Manager): self.delete_config_options(domain_id, group, option) self.delete_config_options(domain_id, group, option, sensitive=True) + self.get_config_with_sensitive_info.invalidate(self, domain_id) - def get_config_with_sensitive_info(self, domain_id, group=None, - option=None): - """Get config for a domain with sensitive info included. + def _get_config_with_sensitive_info(self, domain_id, group=None, + option=None): + """Get config for a domain/group/option with sensitive info included. - This method is not exposed via the public API, but is used by the - identity manager to initialize a domain with the fully formed config - options. + This is only used by the methods within this class, which may need to + check individual groups or options. """ whitelisted = self.list_config_options(domain_id, group, option) @@ -1233,6 +1259,17 @@ class DomainConfigManager(manager.Manager): return self._list_to_config(whitelisted, sensitive) + @MEMOIZE_CONFIG + def get_config_with_sensitive_info(self, domain_id): + """Get config for a domain with sensitive info included. + + This method is not exposed via the public API, but is used by the + identity manager to initialize a domain with the fully formed config + options. + + """ + return self._get_config_with_sensitive_info(domain_id) + @six.add_metaclass(abc.ABCMeta) class DomainConfigDriver(object): |