Remove LDAP Resource and LDAP Assignment backends

LDAP Resource and LDAP Assignment backends have been slated for removal
in the Mitaka release. This patchset removes support for the deprecated
LDAP backends.

Change-Id: I848bf41022224fec65cd9555a6e82790b296dcbe
bp: removed-as-of-mitaka
This commit is contained in:
Morgan Fainberg 2015-10-07 00:27:43 -07:00 committed by Steve Martinelli
parent b1b4350017
commit e6efbe62b8
10 changed files with 76 additions and 1227 deletions

View File

@ -1,464 +0,0 @@
# Copyright 2012-2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import ldap.filter
from oslo_config import cfg
from oslo_log import versionutils
from keystone import assignment
from keystone.assignment.role_backends import ldap as ldap_role
from keystone.common import ldap as common_ldap
from keystone.common import models
from keystone import exception
from keystone.i18n import _
from keystone.identity.backends import ldap as ldap_identity
CONF = cfg.CONF
class Assignment(assignment.AssignmentDriverV9):
@versionutils.deprecated(
versionutils.deprecated.KILO,
remove_in=+2,
what='ldap assignment')
def __init__(self):
super(Assignment, self).__init__()
self.LDAP_URL = CONF.ldap.url
self.LDAP_USER = CONF.ldap.user
self.LDAP_PASSWORD = CONF.ldap.password
self.suffix = CONF.ldap.suffix
# This is the only deep dependency from assignment back to identity.
# This is safe to do since if you are using LDAP for assignment, it is
# required that you are using it for identity as well.
self.user = ldap_identity.UserApi(CONF)
self.group = ldap_identity.GroupApi(CONF)
self.project = ProjectApi(CONF)
self.role = RoleApi(CONF, self.user)
def default_role_driver(self):
return 'ldap'
def default_resource_driver(self):
return 'ldap'
def list_role_ids_for_groups_on_project(
self, groups, project_id, project_domain_id, project_parents):
group_dns = [self.group._id_to_dn(group_id) for group_id in groups]
role_list = [self.role._dn_to_id(role_assignment.role_dn)
for role_assignment in self.role.get_role_assignments
(self.project._id_to_dn(project_id))
if role_assignment.user_dn.upper() in group_dns]
# NOTE(morganfainberg): Does not support OS-INHERIT as domain
# metadata/roles are not supported by LDAP backend. Skip OS-INHERIT
# logic.
return role_list
def list_role_ids_for_groups_on_domain(self, group_ids, domain_id):
raise exception.NotImplemented()
def list_project_ids_for_groups(self, group_ids, hints,
inherited=False):
raise exception.NotImplemented()
def list_domain_ids_for_groups(self, group_ids, inherited=False):
raise exception.NotImplemented()
def _subrole_id_to_dn(self, role_id, tenant_id):
if tenant_id is None:
return self.role._id_to_dn(role_id)
else:
return '%s=%s,%s' % (self.role.id_attr,
ldap.dn.escape_dn_chars(role_id),
self.project._id_to_dn(tenant_id))
def add_role_to_user_and_project(self, user_id, tenant_id, role_id):
user_dn = self.user._id_to_dn(user_id)
role_dn = self._subrole_id_to_dn(role_id, tenant_id)
self.role.add_user(role_id, role_dn, user_dn, user_id, tenant_id)
tenant_dn = self.project._id_to_dn(tenant_id)
return UserRoleAssociation(role_dn=role_dn,
user_dn=user_dn,
tenant_dn=tenant_dn)
def _add_role_to_group_and_project(self, group_id, tenant_id, role_id):
group_dn = self.group._id_to_dn(group_id)
role_dn = self._subrole_id_to_dn(role_id, tenant_id)
self.role.add_user(role_id, role_dn, group_dn, group_id, tenant_id)
tenant_dn = self.project._id_to_dn(tenant_id)
return GroupRoleAssociation(group_dn=group_dn,
role_dn=role_dn,
tenant_dn=tenant_dn)
def remove_role_from_user_and_project(self, user_id, tenant_id, role_id):
role_dn = self._subrole_id_to_dn(role_id, tenant_id)
return self.role.delete_user(role_dn,
self.user._id_to_dn(user_id), role_id)
def _remove_role_from_group_and_project(self, group_id, tenant_id,
role_id):
role_dn = self._subrole_id_to_dn(role_id, tenant_id)
return self.role.delete_user(role_dn,
self.group._id_to_dn(group_id), role_id)
# Bulk actions on User From identity
def delete_user_assignments(self, user_id):
user_dn = self.user._id_to_dn(user_id)
for ref in self.role.list_global_roles_for_user(user_dn):
self.role.delete_user(ref.role_dn, ref.user_dn,
self.role._dn_to_id(ref.role_dn))
for ref in self.role.list_project_roles_for_user(user_dn,
self.project.tree_dn):
self.role.delete_user(ref.role_dn, ref.user_dn,
self.role._dn_to_id(ref.role_dn))
def delete_group_assignments(self, group_id):
"""Called when the group was deleted.
Any role assignments for the group should be cleaned up.
"""
group_dn = self.group._id_to_dn(group_id)
group_role_assignments = self.role.list_project_roles_for_group(
group_dn, self.project.tree_dn)
for ref in group_role_assignments:
self.role.delete_user(ref.role_dn, ref.group_dn,
self.role._dn_to_id(ref.role_dn))
def _assert_not_domain_grant(self, domain_id):
if domain_id is not None:
msg = _('Domain grants not supported by LDAP')
raise exception.NotImplemented(message=msg)
def create_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
self._assert_not_domain_grant(domain_id)
if user_id is None:
self._add_role_to_group_and_project(
group_id, project_id, role_id)
else:
self.add_role_to_user_and_project(
user_id, project_id, role_id)
def check_grant_role_id(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
self._assert_not_domain_grant(domain_id)
if not self.list_role_assignments(
role_id=role_id, user_id=user_id,
group_ids=[group_id] if group_id else [],
project_ids=[project_id] if project_id else []):
actor_id = user_id or group_id
target_id = domain_id or project_id
raise exception.RoleAssignmentNotFound(role_id=role_id,
actor_id=actor_id,
target_id=target_id)
def delete_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
self.check_grant_role_id(
role_id, user_id=user_id, group_id=group_id,
domain_id=domain_id, project_id=project_id,
inherited_to_projects=inherited_to_projects)
try:
if user_id is None:
self._remove_role_from_group_and_project(
group_id, project_id, role_id)
else:
self.remove_role_from_user_and_project(
user_id, project_id, role_id)
except (exception.RoleNotFound, KeyError):
actor_id = user_id or group_id
target_id = domain_id or project_id
raise exception.RoleAssignmentNotFound(role_id=role_id,
actor_id=actor_id,
target_id=target_id)
def list_grant_role_ids(self, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
self._assert_not_domain_grant(domain_id)
assignment_list = self.list_role_assignments(
user_id=user_id, group_ids=[group_id] if group_id else [],
project_ids=[project_id] if project_id else [])
# Use set() to process the list to remove any duplicates
return list(set([x['role_id'] for x in assignment_list]))
def list_role_assignments(self, role_id=None,
user_id=None, group_ids=None,
domain_id=None, project_ids=None,
inherited_to_projects=None):
role_assignments = []
# Since the LDAP backend does not support assignments to domains, if
# the request is to filter by domain, then the answer is guaranteed
# to be an empty list.
if not domain_id:
for a in self.role.list_role_assignments(self.project.tree_dn):
if isinstance(a, UserRoleAssociation):
assignment = {
'role_id': self.role._dn_to_id(a.role_dn),
'user_id': self.user._dn_to_id(a.user_dn),
'project_id': self.project._dn_to_id(a.project_dn)}
else:
assignment = {
'role_id': self.role._dn_to_id(a.role_dn),
'group_id': self.group._dn_to_id(a.group_dn),
'project_id': self.project._dn_to_id(a.project_dn)}
if role_id and assignment['role_id'] != role_id:
continue
if user_id and assignment.get('user_id') != user_id:
continue
if group_ids and assignment.get('group_id') not in group_ids:
continue
if project_ids and assignment['project_id'] not in project_ids:
continue
role_assignments.append(assignment)
return role_assignments
def delete_project_assignments(self, project_id):
tenant_dn = self.project._id_to_dn(project_id)
self.role.roles_delete_subtree_by_project(tenant_dn)
def delete_role_assignments(self, role_id):
self.role.roles_delete_subtree_by_role(role_id, self.project.tree_dn)
# TODO(termie): turn this into a data object and move logic to driver
class ProjectApi(common_ldap.ProjectLdapStructureMixin,
common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):
model = models.Project
def __init__(self, conf):
super(ProjectApi, self).__init__(conf)
self.member_attribute = (conf.ldap.project_member_attribute
or self.DEFAULT_MEMBER_ATTRIBUTE)
def get_user_dns(self, tenant_id, rolegrants, role_dn=None):
tenant = self._ldap_get(tenant_id)
res = set()
if not role_dn:
# Get users who have default tenant mapping
for user_dn in tenant[1].get(self.member_attribute, []):
if self._is_dumb_member(user_dn):
continue
res.add(user_dn)
# Get users who are explicitly mapped via a tenant
for rolegrant in rolegrants:
if role_dn is None or rolegrant.role_dn == role_dn:
res.add(rolegrant.user_dn)
return list(res)
class UserRoleAssociation(object):
"""Role Grant model."""
def __init__(self, user_dn=None, role_dn=None, tenant_dn=None,
*args, **kw):
self.user_dn = user_dn
self.role_dn = role_dn
self.project_dn = tenant_dn
class GroupRoleAssociation(object):
"""Role Grant model."""
def __init__(self, group_dn=None, role_dn=None, tenant_dn=None,
*args, **kw):
self.group_dn = group_dn
self.role_dn = role_dn
self.project_dn = tenant_dn
# TODO(termie): turn this into a data object and move logic to driver
# NOTE(heny-nash): The RoleLdapStructureMixin class enables the sharing of the
# LDAP structure between here and the role backend LDAP, no methods are shared.
class RoleApi(ldap_role.RoleLdapStructureMixin, common_ldap.BaseLdap):
def __init__(self, conf, user_api):
super(RoleApi, self).__init__(conf)
self.member_attribute = (conf.ldap.role_member_attribute
or self.DEFAULT_MEMBER_ATTRIBUTE)
self._user_api = user_api
def add_user(self, role_id, role_dn, user_dn, user_id, tenant_id=None):
try:
super(RoleApi, self).add_member(user_dn, role_dn)
except exception.Conflict:
msg = (_('User %(user_id)s already has role %(role_id)s in '
'tenant %(tenant_id)s') %
dict(user_id=user_id, role_id=role_id, tenant_id=tenant_id))
raise exception.Conflict(type='role grant', details=msg)
except self.NotFound:
if tenant_id is None or self.get(role_id) is None:
raise Exception(_("Role %s not found") % (role_id,))
attrs = [('objectClass', [self.object_class]),
(self.member_attribute, [user_dn]),
(self.id_attr, [role_id])]
if self.use_dumb_member:
attrs[1][1].append(self.dumb_member)
with self.get_connection() as conn:
conn.add_s(role_dn, attrs)
def delete_user(self, role_dn, user_dn, role_id):
try:
super(RoleApi, self).remove_member(user_dn, role_dn)
except (self.NotFound, ldap.NO_SUCH_ATTRIBUTE):
raise exception.RoleNotFound(message=_(
'Cannot remove role that has not been granted, %s') %
role_id)
def get_role_assignments(self, tenant_dn):
try:
roles = self._ldap_get_list(tenant_dn, ldap.SCOPE_ONELEVEL,
attrlist=[self.member_attribute])
except ldap.NO_SUCH_OBJECT:
roles = []
res = []
for role_dn, attrs in roles:
try:
user_dns = attrs[self.member_attribute]
except KeyError:
continue
for user_dn in user_dns:
if self._is_dumb_member(user_dn):
continue
res.append(UserRoleAssociation(
user_dn=user_dn,
role_dn=role_dn,
tenant_dn=tenant_dn))
return res
def list_global_roles_for_user(self, user_dn):
user_dn_esc = ldap.filter.escape_filter_chars(user_dn)
roles = self.get_all('(%s=%s)' % (self.member_attribute, user_dn_esc))
return [UserRoleAssociation(
role_dn=role.dn,
user_dn=user_dn) for role in roles]
def list_project_roles_for_user(self, user_dn, project_subtree):
try:
roles = self._ldap_get_list(project_subtree, ldap.SCOPE_SUBTREE,
query_params={
self.member_attribute: user_dn},
attrlist=common_ldap.DN_ONLY)
except ldap.NO_SUCH_OBJECT:
roles = []
res = []
for role_dn, _role_attrs in roles:
# ldap.dn.dn2str returns an array, where the first
# element is the first segment.
# For a role assignment, this contains the role ID,
# The remainder is the DN of the tenant.
# role_dn is already utf8 encoded since it came from LDAP.
tenant = ldap.dn.str2dn(role_dn)
tenant.pop(0)
tenant_dn = ldap.dn.dn2str(tenant)
res.append(UserRoleAssociation(
user_dn=user_dn,
role_dn=role_dn,
tenant_dn=tenant_dn))
return res
def list_project_roles_for_group(self, group_dn, project_subtree):
group_dn_esc = ldap.filter.escape_filter_chars(group_dn)
query = '(&(objectClass=%s)(%s=%s))' % (self.object_class,
self.member_attribute,
group_dn_esc)
with self.get_connection() as conn:
try:
roles = conn.search_s(project_subtree,
ldap.SCOPE_SUBTREE,
query,
attrlist=common_ldap.DN_ONLY)
except ldap.NO_SUCH_OBJECT:
# Return no roles rather than raise an exception if the project
# subtree entry doesn't exist because an empty subtree is not
# an error.
return []
res = []
for role_dn, _role_attrs in roles:
# ldap.dn.str2dn returns a list, where the first
# element is the first RDN.
# For a role assignment, this contains the role ID,
# the remainder is the DN of the project.
# role_dn is already utf8 encoded since it came from LDAP.
project = ldap.dn.str2dn(role_dn)
project.pop(0)
project_dn = ldap.dn.dn2str(project)
res.append(GroupRoleAssociation(
group_dn=group_dn,
role_dn=role_dn,
tenant_dn=project_dn))
return res
def roles_delete_subtree_by_project(self, tenant_dn):
self._delete_tree_nodes(tenant_dn, ldap.SCOPE_ONELEVEL)
def roles_delete_subtree_by_role(self, role_id, tree_dn):
self._delete_tree_nodes(tree_dn, ldap.SCOPE_SUBTREE, query_params={
self.id_attr: role_id})
def list_role_assignments(self, project_tree_dn):
"""List the role assignments linked to project_tree_dn attribute."""
try:
roles = self._ldap_get_list(project_tree_dn, ldap.SCOPE_SUBTREE,
attrlist=[self.member_attribute])
except ldap.NO_SUCH_OBJECT:
roles = []
res = []
for role_dn, role in roles:
# role_dn is already utf8 encoded since it came from LDAP.
tenant = ldap.dn.str2dn(role_dn)
tenant.pop(0)
# It obtains the tenant DN to construct the UserRoleAssociation
# object.
tenant_dn = ldap.dn.dn2str(tenant)
for occupant_dn in role[self.member_attribute]:
if self._is_dumb_member(occupant_dn):
continue
if self._user_api.is_user(occupant_dn):
association = UserRoleAssociation(
user_dn=occupant_dn,
role_dn=role_dn,
tenant_dn=tenant_dn)
else:
# occupant_dn is a group.
association = GroupRoleAssociation(
group_dn=occupant_dn,
role_dn=role_dn,
tenant_dn=tenant_dn)
res.append(association)
return res

View File

@ -56,15 +56,6 @@ class Manager(manager.Manager):
def __init__(self):
assignment_driver = CONF.assignment.driver
# If there is no explicit assignment driver specified, we let the
# identity driver tell us what to use. This is for backward
# compatibility reasons from the time when identity, resource and
# assignment were all part of identity.
if assignment_driver is None:
identity_driver = dependency.get_provider('identity_api').driver
assignment_driver = identity_driver.default_assignment_driver()
super(Manager, self).__init__(assignment_driver)
# Make sure it is a driver version we support, and if it is a legacy

View File

@ -371,16 +371,15 @@ FILE_OPTIONS = {
'assignment': [
cfg.StrOpt('driver',
help='Entrypoint for the assignment backend driver in the '
'keystone.assignment namespace. Supplied drivers are '
'ldap and sql. If an assignment driver is not '
'specified, the identity driver will choose the '
'assignment driver.'),
'keystone.assignment namespace. Only an SQL driver is '
'supplied.',
default='sql'),
],
'resource': [
cfg.StrOpt('driver',
help='Entrypoint for the resource backend driver in the '
'keystone.resource namespace. Supplied drivers are '
'ldap and sql. If a resource driver is not specified, '
'keystone.resource namespace. Only an SQL driver is '
'supplied. If a resource driver is not specified, '
'the assignment driver will choose the resource '
'driver.'),
cfg.BoolOpt('caching', default=True,
@ -648,105 +647,6 @@ FILE_OPTIONS = {
'ldap_attr is the attribute in the LDAP entry and '
'user_attr is the Identity API attribute.'),
cfg.StrOpt('project_tree_dn',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_tree_dn', group='ldap')],
deprecated_for_removal=True,
help='Search base for projects. '
'Defaults to the suffix value.'),
cfg.StrOpt('project_filter',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_filter', group='ldap')],
deprecated_for_removal=True,
help='LDAP search filter for projects.'),
cfg.StrOpt('project_objectclass', default='groupOfNames',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_objectclass', group='ldap')],
deprecated_for_removal=True,
help='LDAP objectclass for projects.'),
cfg.StrOpt('project_id_attribute', default='cn',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_id_attribute', group='ldap')],
deprecated_for_removal=True,
help='LDAP attribute mapped to project id.'),
cfg.StrOpt('project_member_attribute', default='member',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_member_attribute', group='ldap')],
deprecated_for_removal=True,
help='LDAP attribute mapped to project membership for '
'user.'),
cfg.StrOpt('project_name_attribute', default='ou',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_name_attribute', group='ldap')],
deprecated_for_removal=True,
help='LDAP attribute mapped to project name.'),
cfg.StrOpt('project_desc_attribute', default='description',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_desc_attribute', group='ldap')],
deprecated_for_removal=True,
help='LDAP attribute mapped to project description.'),
cfg.StrOpt('project_enabled_attribute', default='enabled',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_enabled_attribute', group='ldap')],
deprecated_for_removal=True,
help='LDAP attribute mapped to project enabled.'),
cfg.StrOpt('project_domain_id_attribute',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_domain_id_attribute', group='ldap')],
deprecated_for_removal=True,
default='businessCategory',
help='LDAP attribute mapped to project domain_id.'),
cfg.ListOpt('project_attribute_ignore', default=[],
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_attribute_ignore', group='ldap')],
deprecated_for_removal=True,
help='List of attributes stripped off the project on '
'update.'),
cfg.BoolOpt('project_allow_create', default=True,
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_allow_create', group='ldap')],
deprecated_for_removal=True,
help='Allow project creation in LDAP backend.'),
cfg.BoolOpt('project_allow_update', default=True,
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_allow_update', group='ldap')],
deprecated_for_removal=True,
help='Allow project update in LDAP backend.'),
cfg.BoolOpt('project_allow_delete', default=True,
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_allow_delete', group='ldap')],
deprecated_for_removal=True,
help='Allow project deletion in LDAP backend.'),
cfg.BoolOpt('project_enabled_emulation', default=False,
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_enabled_emulation', group='ldap')],
deprecated_for_removal=True,
help='If true, Keystone uses an alternative method to '
'determine if a project is enabled or not by '
'checking if they are a member of the '
'"project_enabled_emulation_dn" group.'),
cfg.StrOpt('project_enabled_emulation_dn',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_enabled_emulation_dn', group='ldap')],
deprecated_for_removal=True,
help='DN of the group entry to hold enabled projects when '
'using enabled emulation.'),
cfg.BoolOpt('project_enabled_emulation_use_group_config',
default=False,
help='Use the "group_member_attribute" and '
'"group_objectclass" settings to determine '
'membership in the emulated enabled group.'),
cfg.ListOpt('project_additional_attribute_mapping',
deprecated_opts=[cfg.DeprecatedOpt(
'tenant_additional_attribute_mapping', group='ldap')],
deprecated_for_removal=True,
default=[],
help='Additional attribute mappings for projects. '
'Attribute mapping format is '
'<ldap_attr>:<user_attr>, where ldap_attr is the '
'attribute in the LDAP entry and user_attr is the '
'Identity API attribute.'),
cfg.StrOpt('role_tree_dn',
deprecated_for_removal=True,
help='Search base for roles. '

View File

@ -1,215 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import uuid
from oslo_config import cfg
from oslo_log import versionutils
from keystone.common import clean
from keystone.common import driver_hints
from keystone.common import ldap as common_ldap
from keystone.common import models
from keystone import exception
from keystone.i18n import _
from keystone.identity.backends import ldap as ldap_identity
from keystone import resource
CONF = cfg.CONF
class Resource(resource.ResourceDriverV9):
@versionutils.deprecated(
versionutils.deprecated.LIBERTY,
remove_in=+1,
what='ldap resource')
def __init__(self):
super(Resource, self).__init__()
self.LDAP_URL = CONF.ldap.url
self.LDAP_USER = CONF.ldap.user
self.LDAP_PASSWORD = CONF.ldap.password
self.suffix = CONF.ldap.suffix
# This is the only deep dependency from resource back to identity.
# This is safe to do since if you are using LDAP for resource, it is
# required that you are using it for identity as well.
self.user = ldap_identity.UserApi(CONF)
self.project = ProjectApi(CONF)
def default_assignment_driver(self):
return 'ldap'
def _set_default_parent_project(self, ref):
"""If the parent project ID has not been set, set it to None."""
if isinstance(ref, dict):
if 'parent_id' not in ref:
ref = dict(ref, parent_id=None)
return ref
elif isinstance(ref, list):
return [self._set_default_parent_project(x) for x in ref]
else:
raise ValueError(_('Expected dict or list: %s') % type(ref))
def _set_default_is_domain_project(self, ref):
if isinstance(ref, dict):
return dict(ref, is_domain=False)
elif isinstance(ref, list):
return [self._set_default_is_domain_project(x) for x in ref]
else:
raise ValueError(_('Expected dict or list: %s') % type(ref))
def _validate_parent_project_is_none(self, ref):
"""If a parent_id different from None was given, raises
InvalidProjectException.
"""
parent_id = ref.get('parent_id')
if parent_id is not None:
raise exception.InvalidParentProject(parent_id)
def _validate_is_domain_field_is_false(self, ref):
is_domain = ref.pop('is_domain', None)
if is_domain:
raise exception.ValidationError(_('LDAP does not support projects '
'with is_domain flag enabled'))
def _set_default_attributes(self, project_ref):
project_ref = self._set_default_domain(project_ref)
project_ref = self._set_default_is_domain_project(project_ref)
return self._set_default_parent_project(project_ref)
def get_project(self, tenant_id):
return self._set_default_attributes(
self.project.get(tenant_id))
def list_projects(self, hints):
return self._set_default_attributes(
self.project.get_all_filtered(hints))
def list_projects_in_domain(self, domain_id):
# We don't support multiple domains within this driver, so ignore
# any domain specified
return self.list_projects(driver_hints.Hints())
def list_projects_in_subtree(self, project_id):
# We don't support projects hierarchy within this driver, so a
# project will never have children
return []
def list_project_parents(self, project_id):
# We don't support projects hierarchy within this driver, so a
# project will never have parents
return []
def is_leaf_project(self, project_id):
# We don't support projects hierarchy within this driver, so a
# project will always be a root and a leaf at the same time
return True
def list_projects_from_ids(self, ids):
return [self.get_project(id) for id in ids]
def list_project_ids_from_domain_ids(self, domain_ids):
# We don't support multiple domains within this driver, so ignore
# any domain specified
return [x.id for x in self.list_projects(driver_hints.Hints())]
def get_project_by_name(self, tenant_name, domain_id):
self._validate_default_domain_id(domain_id)
return self._set_default_attributes(
self.project.get_by_name(tenant_name))
def create_project(self, tenant_id, tenant):
self.project.check_allow_create()
self._validate_parent_project_is_none(tenant)
self._validate_is_domain_field_is_false(tenant)
tenant['name'] = clean.project_name(tenant['name'])
data = tenant.copy()
if 'id' not in data or data['id'] is None:
data['id'] = str(uuid.uuid4().hex)
if 'description' in data and data['description'] in ['', None]:
data.pop('description')
return self._set_default_attributes(
self.project.create(data))
def update_project(self, tenant_id, tenant):
self.project.check_allow_update()
tenant = self._validate_default_domain(tenant)
self._validate_is_domain_field_is_false(tenant)
if 'name' in tenant:
tenant['name'] = clean.project_name(tenant['name'])
return self._set_default_attributes(
self.project.update(tenant_id, tenant))
def delete_project(self, tenant_id):
self.project.check_allow_delete()
if self.project.subtree_delete_enabled:
self.project.deleteTree(tenant_id)
else:
# The manager layer will call assignments to delete the
# role assignments, so we just have to delete the project itself.
self.project.delete(tenant_id)
def create_domain(self, domain_id, domain):
if domain_id == CONF.identity.default_domain_id:
msg = _('Duplicate ID, %s.') % domain_id
raise exception.Conflict(type='domain', details=msg)
raise exception.Forbidden(_('Domains are read-only against LDAP'))
def get_domain(self, domain_id):
self._validate_default_domain_id(domain_id)
return resource.calc_default_domain()
def update_domain(self, domain_id, domain):
self._validate_default_domain_id(domain_id)
raise exception.Forbidden(_('Domains are read-only against LDAP'))
def delete_domain(self, domain_id):
self._validate_default_domain_id(domain_id)
raise exception.Forbidden(_('Domains are read-only against LDAP'))
def list_domains(self, hints):
return [resource.calc_default_domain()]
def list_domains_from_ids(self, ids):
return [resource.calc_default_domain()]
def get_domain_by_name(self, domain_name):
default_domain = resource.calc_default_domain()
if domain_name != default_domain['name']:
raise exception.DomainNotFound(domain_id=domain_name)
return default_domain
# TODO(termie): turn this into a data object and move logic to driver
class ProjectApi(common_ldap.ProjectLdapStructureMixin,
common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):
model = models.Project
def create(self, values):
data = values.copy()
if data.get('id') is None:
data['id'] = uuid.uuid4().hex
return super(ProjectApi, self).create(data)
def update(self, project_id, values):
old_obj = self.get(project_id)
return super(ProjectApi, self).update(project_id, values, old_obj)
def get_all_filtered(self, hints):
query = self.filter_query(hints)
return super(ProjectApi, self).get_all(query)

View File

@ -32,11 +32,8 @@ def load_backends():
cache.configure_cache()
cache.configure_cache(region=catalog.COMPUTED_CATALOG_REGION)
# Ensure that the identity driver is created before the assignment manager
# and that the assignment driver is created before the resource manager.
# The default resource driver depends on assignment, which in turn
# depends on identity - hence we need to ensure the chain is available.
_IDENTITY_API = identity.Manager()
# Ensure that the assignment driver is created before the resource manager.
# The default resource driver depends on assignment.
_ASSIGNMENT_API = assignment.Manager()
DRIVERS = dict(
@ -48,7 +45,7 @@ def load_backends():
federation_api=federation.Manager(),
id_generator_api=identity.generator.Manager(),
id_mapping_api=identity.MappingManager(),
identity_api=_IDENTITY_API,
identity_api=identity.Manager(),
oauth_api=oauth1.Manager(),
policy_api=policy.Manager(),
resource_api=resource.Manager(),

View File

@ -11,15 +11,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from oslo_config import cfg
from keystone import exception
from keystone.tests import unit
from keystone.tests.unit.backend import core_ldap
from keystone.tests.unit.backend.role import core as core_role
from keystone.tests.unit import default_fixtures
CONF = cfg.CONF
@ -45,108 +41,19 @@ class LdapRole(LdapRoleCommon, core_ldap.BaseBackendLdap, unit.TestCase):
"""
def test_configurable_allowed_role_actions(self):
role = unit.new_role_ref(id=u'fäké1', name=u'fäké1')
self.role_api.create_role(u'fäké1', role)
role_ref = self.role_api.get_role(u'fäké1')
self.assertEqual(u'fäké1', role_ref['id'])
role['name'] = u'fäké2'
self.role_api.update_role(u'fäké1', role)
self.role_api.delete_role(u'fäké1')
self.assertRaises(exception.RoleNotFound,
self.role_api.get_role,
u'fäké1')
self.skipTest("An all-LDAP configuration is no longer supported")
def test_configurable_forbidden_role_actions(self):
self.config_fixture.config(
group='ldap', role_allow_create=False, role_allow_update=False,
role_allow_delete=False)
self.load_backends()
role = unit.new_role_ref()
self.assertRaises(exception.ForbiddenAction,
self.role_api.create_role,
role['id'],
role)
self.role_member['name'] = uuid.uuid4().hex
self.assertRaises(exception.ForbiddenAction,
self.role_api.update_role,
self.role_member['id'],
self.role_member)
self.assertRaises(exception.ForbiddenAction,
self.role_api.delete_role,
self.role_member['id'])
self.skipTest("An all-LDAP configuration is no longer supported")
def test_role_filter(self):
role_ref = self.role_api.get_role(self.role_member['id'])
self.assertDictEqual(self.role_member, role_ref)
self.config_fixture.config(group='ldap',
role_filter='(CN=DOES_NOT_MATCH)')
self.load_backends()
# NOTE(morganfainberg): CONF.ldap.role_filter will not be
# dynamically changed at runtime. This invalidate is a work-around for
# the expectation that it is safe to change config values in tests that
# could affect what the drivers would return up to the manager. This
# solves this assumption when working with aggressive (on-create)
# cache population.
self.role_api.get_role.invalidate(self.role_api,
self.role_member['id'])
self.assertRaises(exception.RoleNotFound,
self.role_api.get_role,
self.role_member['id'])
self.skipTest("An all-LDAP configuration is no longer supported")
def test_role_attribute_mapping(self):
self.config_fixture.config(group='ldap', role_name_attribute='ou')
self.clear_database()
self.load_backends()
self.load_fixtures(default_fixtures)
# NOTE(morganfainberg): CONF.ldap.role_name_attribute will not be
# dynamically changed at runtime. This invalidate is a work-around for
# the expectation that it is safe to change config values in tests that
# could affect what the drivers would return up to the manager. This
# solves this assumption when working with aggressive (on-create)
# cache population.
self.role_api.get_role.invalidate(self.role_api,
self.role_member['id'])
role_ref = self.role_api.get_role(self.role_member['id'])
self.assertEqual(self.role_member['id'], role_ref['id'])
self.assertEqual(self.role_member['name'], role_ref['name'])
self.config_fixture.config(group='ldap', role_name_attribute='sn')
self.load_backends()
# NOTE(morganfainberg): CONF.ldap.role_name_attribute will not be
# dynamically changed at runtime. This invalidate is a work-around for
# the expectation that it is safe to change config values in tests that
# could affect what the drivers would return up to the manager. This
# solves this assumption when working with aggressive (on-create)
# cache population.
self.role_api.get_role.invalidate(self.role_api,
self.role_member['id'])
role_ref = self.role_api.get_role(self.role_member['id'])
self.assertEqual(self.role_member['id'], role_ref['id'])
self.assertNotIn('name', role_ref)
self.skipTest("An all-LDAP configuration is no longer supported")
def test_role_attribute_ignore(self):
self.config_fixture.config(group='ldap',
role_attribute_ignore=['name'])
self.clear_database()
self.load_backends()
self.load_fixtures(default_fixtures)
# NOTE(morganfainberg): CONF.ldap.role_attribute_ignore will not be
# dynamically changed at runtime. This invalidate is a work-around for
# the expectation that it is safe to change config values in tests that
# could affect what the drivers would return up to the manager. This
# solves this assumption when working with aggressive (on-create)
# cache population.
self.role_api.get_role.invalidate(self.role_api,
self.role_member['id'])
role_ref = self.role_api.get_role(self.role_member['id'])
self.assertEqual(self.role_member['id'], role_ref['id'])
self.assertNotIn('name', role_ref)
self.skipTest("An all-LDAP configuration is no longer supported")
class LdapIdentitySqlEverythingElseRole(

View File

@ -27,6 +27,7 @@ from keystone.common.ldap import core as common_ldap_core
from keystone.tests import unit
from keystone.tests.unit import default_fixtures
from keystone.tests.unit import fakeldap
from keystone.tests.unit.ksfixtures import database
CONF = cfg.CONF
@ -207,6 +208,8 @@ class LDAPDeleteTreeTest(unit.TestCase):
ks_ldap.register_handler('fake://',
fakeldap.FakeLdapNoSubtreeDelete)
self.useFixture(database.Database(self.sql_driver_version_overrides))
self.load_backends()
self.load_fixtures(default_fixtures)
@ -359,6 +362,7 @@ class LDAPPagedResultsTest(unit.TestCase):
ks_ldap.register_handler('fake://', fakeldap.FakeLdap)
self.addCleanup(common_ldap_core._HANDLERS.clear)
self.useFixture(database.Database(self.sql_driver_version_overrides))
self.load_backends()
self.load_fixtures(default_fixtures)

View File

@ -514,11 +514,7 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
self.assertEqual(existing_assignments + 2, after_assignments)
def test_list_role_assignments_filtered_by_role(self):
# Domain roles are not supported by the LDAP Assignment backend
self.assertRaises(
exception.NotImplemented,
super(BaseLDAPIdentity, self).
test_list_role_assignments_filtered_by_role)
self.skipTest('Resource LDAP has been removed')
def test_list_role_assignments_dumb_member(self):
self.config_fixture.config(group='ldap', use_dumb_member=True)
@ -634,22 +630,20 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
self.assertNotIn(dumb_id, user_ids)
def test_list_domains(self):
# We have more domains here than the parent class, check for the
# correct number of domains for the multildap backend configs
domain1 = unit.new_domain_ref()
domain2 = unit.new_domain_ref()
self.resource_api.create_domain(domain1['id'], domain1)
self.resource_api.create_domain(domain2['id'], domain2)
domains = self.resource_api.list_domains()
self.assertEqual(
[resource.calc_default_domain()],
domains)
def test_list_domains_non_default_domain_id(self):
# If change the default_domain_id, the ID of the default domain
# returned by list_domains changes is the new default_domain_id.
new_domain_id = uuid.uuid4().hex
self.config_fixture.config(group='identity',
default_domain_id=new_domain_id)
domains = self.resource_api.list_domains()
self.assertEqual(new_domain_id, domains[0]['id'])
self.assertEqual(7, len(domains))
domain_ids = []
for domain in domains:
domain_ids.append(domain.get('id'))
self.assertIn(test_backend.DEFAULT_DOMAIN_ID, domain_ids)
self.assertIn(domain1['id'], domain_ids)
self.assertIn(domain2['id'], domain_ids)
def test_authenticate_requires_simple_bind(self):
user = self.new_user_ref(domain_id=test_backend.DEFAULT_DOMAIN_ID)
@ -906,25 +900,7 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
self.assertNotIn('enabled', group_info)
def test_project_enabled_ignored_disable_error(self):
# When the server is configured so that the enabled attribute is
# ignored for projects, projects cannot be disabled.
self.config_fixture.config(group='ldap',
project_attribute_ignore=['enabled'])
# Need to re-load backends for the config change to take effect.
self.load_backends()
# Attempt to disable the project.
self.assertRaises(exception.ForbiddenAction,
self.resource_api.update_project,
self.tenant_baz['id'], {'enabled': False})
project_info = self.resource_api.get_project(self.tenant_baz['id'])
# Unlike other entities, if 'enabled' is ignored then 'enabled' is
# returned as part of the ref.
self.assertIs(True, project_info['enabled'])
self.skipTest('Resource LDAP has been removed')
def test_list_role_assignment_by_domain(self):
"""Multiple domain assignments are not supported."""
@ -948,16 +924,16 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase):
# credentials) that require a database.
self.useFixture(database.Database())
super(LDAPIdentity, self).setUp()
_assert_backends(self,
assignment='ldap',
identity='ldap',
resource='ldap')
_assert_backends(self, identity='ldap')
def load_fixtures(self, fixtures):
# Override super impl since need to create group container.
create_group_container(self.identity_api)
super(LDAPIdentity, self).load_fixtures(fixtures)
def test_list_domains(self):
self.skipTest('Basic Ldap Identity is not multi-domain-aware.')
def test_configurable_allowed_project_actions(self):
domain = self._get_domain_fixture()
project = unit.new_project_ref(domain_id=domain['id'])
@ -1005,48 +981,10 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase):
self.assertEqual(0, len(list))
def test_configurable_forbidden_project_actions(self):
self.config_fixture.config(
group='ldap', project_allow_create=False,
project_allow_update=False, project_allow_delete=False)
self.load_backends()
domain = self._get_domain_fixture()
project = unit.new_project_ref(domain_id=domain['id'])
self.assertRaises(exception.ForbiddenAction,
self.resource_api.create_project,
project['id'],
project)
self.tenant_bar['enabled'] = False
self.assertRaises(exception.ForbiddenAction,
self.resource_api.update_project,
self.tenant_bar['id'],
self.tenant_bar)
self.assertRaises(exception.ForbiddenAction,
self.resource_api.delete_project,
self.tenant_bar['id'])
self.skipTest('Resource LDAP has been removed')
def test_project_filter(self):
tenant_ref = self.resource_api.get_project(self.tenant_bar['id'])
self.assertDictEqual(self.tenant_bar, tenant_ref)
self.config_fixture.config(group='ldap',
project_filter='(CN=DOES_NOT_MATCH)')
self.load_backends()
# NOTE(morganfainberg): CONF.ldap.project_filter will not be
# dynamically changed at runtime. This invalidate is a work-around for
# the expectation that it is safe to change config values in tests that
# could affect what the drivers would return up to the manager. This
# solves this assumption when working with aggressive (on-create)
# cache population.
self.role_api.get_role.invalidate(self.role_api,
self.role_member['id'])
self.role_api.get_role(self.role_member['id'])
self.resource_api.get_project.invalidate(self.resource_api,
self.tenant_bar['id'])
self.assertRaises(exception.ProjectNotFound,
self.resource_api.get_project,
self.tenant_bar['id'])
self.skipTest('Resource LDAP has been removed')
def test_dumb_member(self):
self.config_fixture.config(group='ldap', use_dumb_member=True)
@ -1059,71 +997,10 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase):
dumb_id)
def test_project_attribute_mapping(self):
self.config_fixture.config(
group='ldap', project_name_attribute='ou',
project_desc_attribute='description',
project_enabled_attribute='enabled')
self.ldapdb.clear()
self.load_backends()
self.load_fixtures(default_fixtures)
# NOTE(morganfainberg): CONF.ldap.project_name_attribute,
# CONF.ldap.project_desc_attribute, and
# CONF.ldap.project_enabled_attribute will not be
# dynamically changed at runtime. This invalidate is a work-around for
# the expectation that it is safe to change config values in tests that
# could affect what the drivers would return up to the manager. This
# solves this assumption when working with aggressive (on-create)
# cache population.
self.resource_api.get_project.invalidate(self.resource_api,
self.tenant_baz['id'])
tenant_ref = self.resource_api.get_project(self.tenant_baz['id'])
self.assertEqual(self.tenant_baz['id'], tenant_ref['id'])
self.assertEqual(self.tenant_baz['name'], tenant_ref['name'])
self.assertEqual(
self.tenant_baz['description'],
tenant_ref['description'])
self.assertEqual(self.tenant_baz['enabled'], tenant_ref['enabled'])
self.config_fixture.config(group='ldap',
project_name_attribute='description',
project_desc_attribute='ou')
self.load_backends()
# NOTE(morganfainberg): CONF.ldap.project_name_attribute,
# CONF.ldap.project_desc_attribute, and
# CONF.ldap.project_enabled_attribute will not be
# dynamically changed at runtime. This invalidate is a work-around for
# the expectation that it is safe to change config values in tests that
# could affect what the drivers would return up to the manager. This
# solves this assumption when working with aggressive (on-create)
# cache population.
self.resource_api.get_project.invalidate(self.resource_api,
self.tenant_baz['id'])
tenant_ref = self.resource_api.get_project(self.tenant_baz['id'])
self.assertEqual(self.tenant_baz['id'], tenant_ref['id'])
self.assertEqual(self.tenant_baz['description'], tenant_ref['name'])
self.assertEqual(self.tenant_baz['name'], tenant_ref['description'])
self.assertEqual(self.tenant_baz['enabled'], tenant_ref['enabled'])
self.skipTest('Resource LDAP has been removed')
def test_project_attribute_ignore(self):
self.config_fixture.config(
group='ldap',
project_attribute_ignore=['name', 'description', 'enabled'])
self.ldapdb.clear()
self.load_backends()
self.load_fixtures(default_fixtures)
# NOTE(morganfainberg): CONF.ldap.project_attribute_ignore will not be
# dynamically changed at runtime. This invalidate is a work-around for
# the expectation that it is safe to change configs values in tests
# that could affect what the drivers would return up to the manager.
# This solves this assumption when working with aggressive (on-create)
# cache population.
self.resource_api.get_project.invalidate(self.resource_api,
self.tenant_baz['id'])
tenant_ref = self.resource_api.get_project(self.tenant_baz['id'])
self.assertEqual(self.tenant_baz['id'], tenant_ref['id'])
self.assertNotIn('name', tenant_ref)
self.assertNotIn('description', tenant_ref)
self.assertNotIn('enabled', tenant_ref)
self.skipTest('Resource LDAP has been removed')
def test_user_enable_attribute_mask(self):
self.config_fixture.config(group='ldap', user_enabled_mask=2,
@ -1486,40 +1363,7 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase):
self.assertDictEqual(expected_dict, mapping)
def test_domain_crud(self):
domain = unit.new_domain_ref()
self.assertRaises(exception.Forbidden,
self.resource_api.create_domain,
domain['id'],
domain)
self.assertRaises(exception.Conflict,
self.resource_api.create_domain,
CONF.identity.default_domain_id,
domain)
self.assertRaises(exception.DomainNotFound,
self.resource_api.get_domain,
domain['id'])
domain['description'] = uuid.uuid4().hex
self.assertRaises(exception.DomainNotFound,
self.resource_api.update_domain,
domain['id'],
domain)
self.assertRaises(exception.Forbidden,
self.resource_api.update_domain,
CONF.identity.default_domain_id,
domain)
self.assertRaises(exception.DomainNotFound,
self.resource_api.get_domain,
domain['id'])
self.assertRaises(exception.DomainNotFound,
self.resource_api.delete_domain,
domain['id'])
self.assertRaises(exception.Forbidden,
self.resource_api.delete_domain,
CONF.identity.default_domain_id)
self.assertRaises(exception.DomainNotFound,
self.resource_api.get_domain,
domain['id'])
self.skipTest('Resource LDAP has been removed')
@unit.skip_if_no_multiple_domains_support
def test_create_domain_case_sensitivity(self):
@ -1623,44 +1467,6 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase):
self.resource_api.get_project,
project_id)
def _assert_create_hierarchy_not_allowed(self):
domain = self._get_domain_fixture()
project1 = unit.new_project_ref(domain_id=domain['id'])
self.resource_api.create_project(project1['id'], project1)
# Creating project2 under project1. LDAP will not allow
# the creation of a project with parent_id being set
project2 = unit.new_project_ref(domain_id=domain['id'],
parent_id=project1['id'])
self.assertRaises(exception.InvalidParentProject,
self.resource_api.create_project,
project2['id'],
project2)
# Now, we'll create project 2 with no parent
project2['parent_id'] = None
self.resource_api.create_project(project2['id'], project2)
# Returning projects to be used across the tests
return [project1, project2]
def _assert_create_is_domain_project_not_allowed(self):
"""Tests that we can't create more than one project acting as domain.
This method will be used at any test that require the creation of a
project that act as a domain. LDAP does not support multiple domains
and the only domain it has (default) is immutable.
"""
domain = self._get_domain_fixture()
project = unit.new_project_ref(domain_id=domain['id'],
is_domain=True)
self.assertRaises(exception.ValidationError,
self.resource_api.create_project,
project['id'], project)
def test_update_is_domain_field(self):
domain = self._get_domain_fixture()
project = unit.new_project_ref(domain_id=domain['id'])
@ -1673,112 +1479,64 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase):
project['id'], project)
def test_delete_is_domain_project(self):
self._assert_create_is_domain_project_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_create_domain_under_regular_project_hierarchy_fails(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_create_not_is_domain_project_under_is_domain_hierarchy(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_create_project_passing_is_domain_flag_true(self):
# Creating a project passing is_domain=True is not supported by
# the LDAP backend
self._assert_create_is_domain_project_not_allowed()
def test_set_default_is_domain_project(self):
# Tests the internal method _set_default_is_domain_project, which
# allows either a project ref or a list of project refs
new_project_ref = unit.new_project_ref()
# Calling it with a dict is valid
updated_project_ref = (self.resource_api.driver.
_set_default_is_domain_project(new_project_ref))
self.assertFalse(updated_project_ref['is_domain'])
# So it is with a list of refs
another_new_project_ref = unit.new_project_ref()
refs_list = [new_project_ref, another_new_project_ref]
new_refs_list = (self.resource_api.driver.
_set_default_is_domain_project(refs_list))
for ref in new_refs_list:
self.assertFalse(ref['is_domain'])
# Passing another type is not allowed
self.assertRaises(ValueError,
self.resource_api.driver.
_set_default_is_domain_project,
'foo_bar')
# A list with another type is not allowed either
wrong_list = [new_project_ref, 'foo_bar']
self.assertRaises(ValueError,
self.resource_api.driver.
_set_default_is_domain_project,
wrong_list)
self.skipTest('Resource LDAP has been removed')
def test_create_project_with_parent_id_and_without_domain_id(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_check_leaf_projects(self):
projects = self._assert_create_hierarchy_not_allowed()
for project in projects:
self.assertTrue(self.resource_api.is_leaf_project(project))
self.skipTest('Resource LDAP has been removed')
def test_list_projects_in_subtree(self):
projects = self._assert_create_hierarchy_not_allowed()
for project in projects:
subtree_list = self.resource_api.list_projects_in_subtree(
project['id'])
self.assertEqual(0, len(subtree_list))
self.skipTest('Resource LDAP has been removed')
def test_list_projects_in_subtree_with_circular_reference(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_list_project_parents(self):
projects = self._assert_create_hierarchy_not_allowed()
for project in projects:
parents_list = self.resource_api.list_project_parents(
project['id'])
self.assertEqual(0, len(parents_list))
self.skipTest('Resource LDAP has been removed')
def test_hierarchical_projects_crud(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_create_project_under_disabled_one(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_create_project_with_invalid_parent(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_create_leaf_project_with_invalid_domain(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_update_project_parent(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_enable_project_with_disabled_parent(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_disable_hierarchical_leaf_project(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_disable_hierarchical_not_leaf_project(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_delete_hierarchical_leaf_project(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_delete_hierarchical_not_leaf_project(self):
self._assert_create_hierarchy_not_allowed()
self.skipTest('Resource LDAP has been removed')
def test_check_hierarchy_depth(self):
projects = self._assert_create_hierarchy_not_allowed()
for project in projects:
depth = self._get_hierarchy_depth(project['id'])
self.assertEqual(1, depth)
self.skipTest('Resource LDAP has been removed')
def test_multi_role_grant_by_user_group_on_project_domain(self):
# This is a partial implementation of the standard test that
@ -2018,15 +1776,11 @@ class LDAPLimitTests(unit.TestCase, test_backend.LimitTests):
self.useFixture(ldapdb.LDAPDatabase())
self.useFixture(database.Database())
self.useFixture(database.Database(self.sql_driver_version_overrides))
self.load_backends()
test_backend.LimitTests.setUp(self)
self.load_fixtures(default_fixtures)
_assert_backends(self,
assignment='ldap',
identity='ldap',
resource='ldap')
test_backend.LimitTests.setUp(self)
_assert_backends(self, identity='ldap')
def config_overrides(self):
super(LDAPLimitTests, self).config_overrides()
@ -2052,10 +1806,7 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity):
for obj in [self.tenant_bar, self.tenant_baz, self.user_foo,
self.user_two, self.user_badguy]:
obj.setdefault('enabled', True)
_assert_backends(self,
assignment='ldap',
identity='ldap',
resource='ldap')
_assert_backends(self, identity='ldap')
def load_fixtures(self, fixtures):
# Override super impl since need to create group container.
@ -2070,8 +1821,7 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity):
def config_overrides(self):
super(LDAPIdentityEnabledEmulation, self).config_overrides()
self.config_fixture.config(group='ldap',
user_enabled_emulation=True,
project_enabled_emulation=True)
user_enabled_emulation=True)
def test_project_crud(self):
# NOTE(topol): LDAPIdentityEnabledEmulation will create an
@ -2283,10 +2033,7 @@ class LdapIdentitySqlAssignment(BaseLDAPIdentity, unit.SQLDriverOverrides,
self.load_fixtures(default_fixtures)
# defaulted by the data load
self.user_foo['enabled'] = True
_assert_backends(self,
assignment='sql',
identity='ldap',
resource='sql')
_assert_backends(self, identity='ldap')
def config_overrides(self):
super(LdapIdentitySqlAssignment, self).config_overrides()
@ -2301,22 +2048,6 @@ class LdapIdentitySqlAssignment(BaseLDAPIdentity, unit.SQLDriverOverrides,
domains = self.resource_api.list_domains()
self.assertEqual([resource.calc_default_domain()], domains)
def test_list_domains_non_default_domain_id(self):
# If change the default_domain_id, the ID of the default domain
# returned by list_domains doesn't change because the SQL identity
# backend reads it from the database, which doesn't get updated by
# config change.
orig_default_domain_id = CONF.identity.default_domain_id
new_domain_id = uuid.uuid4().hex
self.config_fixture.config(group='identity',
default_domain_id=new_domain_id)
domains = self.resource_api.list_domains()
self.assertEqual(orig_default_domain_id, domains[0]['id'])
def test_create_domain(self):
domain = unit.new_domain_ref()
self.assertRaises(exception.Forbidden,
@ -2610,14 +2341,6 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, unit.SQLDriverOverrides,
# if no specific config defined for this domain
return self.identity_api.domain_configs.get_domain_conf(domain_id)
def test_list_domains(self):
self.skipTest(
'N/A: Not relevant for multi ldap testing')
def test_list_domains_non_default_domain_id(self):
self.skipTest(
'N/A: Not relevant for multi ldap testing')
def test_list_users(self):
# Override the standard list users, since we have added an extra user
# to the default domain, so the number of expected users is one more
@ -3299,7 +3022,7 @@ class LdapFilterTests(test_backend.FilterTests, unit.TestCase):
self.load_backends()
self.load_fixtures(default_fixtures)
sqldb.recreate()
_assert_backends(self, assignment='ldap', identity='ldap')
_assert_backends(self, identity='ldap')
def config_overrides(self):
super(LdapFilterTests, self).config_overrides()

View File

@ -26,3 +26,11 @@ other:
[`blueprint removed-as-of-mitaka <https://blueprints.launchpad.net/keystone/+spec/removed-as-of-mitaka>`_]
Removed Catalog KVS backend (``keystone.catalog.backends.sql.Catalog``).
This was deprecated in the Icehouse release.
- >
[`blueprint removed-as-of-mitaka <https://blueprints.launchpad.net/keystone/+spec/removed-as-of-mitaka>`_]
The LDAP backend for Assignment has been removed. This was deprecated in
the Kilo release.
- >
[`blueprint removed-as-of-mitaka <https://blueprints.launchpad.net/keystone/+spec/removed-as-of-mitaka>`_]
The LDAP backend for Resource has been removed. This was deprecated in
the Kilo release.

View File

@ -76,7 +76,6 @@ wsgi_scripts =
keystone-wsgi-public = keystone.server.wsgi:initialize_public_application
keystone.assignment =
ldap = keystone.assignment.backends.ldap:Assignment
sql = keystone.assignment.backends.sql:Assignment
keystone.auth.external =
@ -128,7 +127,6 @@ keystone.policy =
sql = keystone.policy.backends.sql:Policy
keystone.resource =
ldap = keystone.resource.backends.ldap:Resource
sql = keystone.resource.backends.sql:Resource
keystone.resource.domain_config =