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:
parent
b1b4350017
commit
e6efbe62b8
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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. '
|
||||
|
|
|
@ -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)
|
|
@ -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(),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 =
|
||||
|
|
Loading…
Reference in New Issue