Removing LDAP API Shim

The LDAP identity code had many circular dependecies between
data objects due to the need to look up DNs from ID.   This change
pulls the lookups into the driver layer, and modifies most of the
data objects to take DNs in as their parameters instead. Only objects
that know how to look up their own DNs from thei IDs will continue to
take IDs in, to support the "get" methods.

Change-Id: I0bac360650ccbf72c7ca8317997031420f66e4f3
This commit is contained in:
Adam Young
2013-06-05 16:28:16 -04:00
parent 7cd0bb0682
commit 7ccfac73fb
2 changed files with 185 additions and 258 deletions

View File

@@ -167,37 +167,86 @@ class Identity(identity.Driver):
def get_projects_for_user(self, user_id): def get_projects_for_user(self, user_id):
self.get_user(user_id) self.get_user(user_id)
return [p['id'] for p in self.project.get_user_projects(user_id)] user_dn = self.user._id_to_dn(user_id)
associations = (self.role.list_project_roles_for_user
(user_dn, self.project.tree_dn))
return [p['id'] for p in
self.project.get_user_projects(user_dn, associations)]
def get_project_users(self, tenant_id): def get_project_users(self, tenant_id):
self.get_project(tenant_id) self.get_project(tenant_id)
user_refs = self.project.get_users(tenant_id) tenant_dn = self.project._id_to_dn(tenant_id)
users = [] rolegrants = self.role.get_role_assignments(tenant_dn)
for user_ref in user_refs: users = [self.user.get_filtered(self.user._dn_to_id(user_id))
users.append(identity.filter_user(user_ref)) for user_id in
self.project.get_user_dns(tenant_id, rolegrants)]
return self._set_default_domain(users) return self._set_default_domain(users)
def get_roles_for_user_and_project(self, user_id, tenant_id): def get_roles_for_user_and_project(self, user_id, tenant_id):
self.get_user(user_id) self.get_user(user_id)
self.get_project(tenant_id) self.get_project(tenant_id)
return [a.role_id for a in self.role.get_role_assignments(tenant_id) user_dn = self.user._id_to_dn(user_id)
if a.user_id == user_id] return [self.role._dn_to_id(a.role_dn)
for a in self.role.get_role_assignments
(self.project._id_to_dn(tenant_id))
if a.user_dn == user_dn]
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): def add_role_to_user_and_project(self, user_id, tenant_id, role_id):
self.get_user(user_id) self.get_user(user_id)
self.get_project(tenant_id) self.get_project(tenant_id)
self.get_role(role_id) self.get_role(role_id)
self.role.add_user(role_id, user_id, tenant_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)
# CRUD # CRUD
def create_user(self, user_id, user): def create_user(self, user_id, user):
user = self._validate_domain(user) user = self._validate_domain(user)
user_ref = self.user.create(user) user_ref = self.user.create(user)
tenant_id = user.get('tenant_id')
user_dn = self.user._id_to_dn(user['id'])
if tenant_id is not None:
self.project.add_user(tenant_id, user_dn)
return self._set_default_domain(identity.filter_user(user_ref)) return self._set_default_domain(identity.filter_user(user_ref))
def update_user(self, user_id, user): def update_user(self, user_id, user):
user = self._validate_domain(user) user = self._validate_domain(user)
return self._set_default_domain(self.user.update(user_id, user)) if 'id' in user and user['id'] != user_id:
raise exception.ValidationError('Cannot change user ID')
old_obj = self.user.get(user_id)
if 'name' in user and old_obj.get('name') != user['name']:
raise exception.Conflict('Cannot change user name')
if 'tenant_id' in user and \
old_obj.get('tenant_id') != user['tenant_id']:
if old_obj['tenant_id']:
self.project.remove_user(old_obj['tenant_id'],
self.user._id_to_dn(user_id),
user_id)
if user['tenant_id']:
self.project.add_user(user['tenant_id'],
self.user._id_to_dn(user_id),
user_id)
user = utils.hash_ldap_user_password(user)
if self.user.enabled_mask:
user['enabled_nomask'] = old_obj['enabled_nomask']
self.user.mask_enabled_attribute(user)
self.user.update(user_id, user, old_obj)
return self._set_default_domain(self.user.get_filtered(user_id))
def create_project(self, tenant_id, tenant): def create_project(self, tenant_id, tenant):
tenant = self._validate_domain(tenant) tenant = self._validate_domain(tenant)
@@ -238,16 +287,42 @@ class Identity(identity.Driver):
return self.role.create(role) return self.role.create(role)
def delete_role(self, role_id): def delete_role(self, role_id):
return self.role.delete(role_id) return self.role.delete(role_id, self.project.tree_dn)
def delete_project(self, tenant_id): def delete_project(self, tenant_id):
return self.project.delete(tenant_id) if self.project.subtree_delete_enabled:
self.project.deleteTree(id)
else:
tenant_dn = self.project._id_to_dn(tenant_id)
self.role.roles_delete_subtree_by_project(tenant_dn)
self.project.delete(tenant_id)
def delete_user(self, user_id): def delete_user(self, user_id):
return self.user.delete(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, ref.project_dn,
user_id, 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, ref.project_dn,
user_id, self.role._dn_to_id(ref.role_dn))
groups = self.group.list_user_groups(user_dn)
for group in groups:
self.group.remove_user(user_dn, group['id'], user_id)
user = self.user.get(user_id)
if hasattr(user, 'tenant_id'):
self.project.remove_user(user.tenant_id,
self.user._id_to_dn(user_id))
self.user.delete(user_id)
def remove_role_from_user_and_project(self, user_id, tenant_id, role_id): def remove_role_from_user_and_project(self, user_id, tenant_id, role_id):
return self.role.delete_user(role_id, user_id, tenant_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),
self.project._id_to_dn(tenant_id),
user_id, role_id)
def update_role(self, role_id, role): def update_role(self, role_id, role):
self.get_role(role_id) self.get_role(role_id)
@@ -273,23 +348,30 @@ class Identity(identity.Driver):
def add_user_to_group(self, user_id, group_id): def add_user_to_group(self, user_id, group_id):
self.get_user(user_id) self.get_user(user_id)
self.get_group(group_id) self.get_group(group_id)
self.group.add_user(user_id, group_id) user_dn = self.user._id_to_dn(user_id)
self.group.add_user(user_dn, group_id, user_id)
def remove_user_from_group(self, user_id, group_id): def remove_user_from_group(self, user_id, group_id):
self.get_user(user_id) self.get_user(user_id)
self.get_group(group_id) self.get_group(group_id)
self.group.remove_user(user_id, group_id) user_dn = self.user._id_to_dn(user_id)
self.group.remove_user(user_dn, group_id, user_id)
def list_groups_for_user(self, user_id): def list_groups_for_user(self, user_id):
self.get_user(user_id) self.get_user(user_id)
return self._set_default_domain(self.group.list_user_groups(user_id)) user_dn = self.user._id_to_dn(user_id)
return self._set_default_domain(self.group.list_user_groups(user_dn))
def list_groups(self): def list_groups(self):
return self._set_default_domain(self.group.get_all()) return self._set_default_domain(self.group.get_all())
def list_users_in_group(self, group_id): def list_users_in_group(self, group_id):
self.get_group(group_id) self.get_group(group_id)
return self._set_default_domain(self.group.list_group_users(group_id)) users = []
for user_dn in self.group.list_group_users(group_id):
user_id = self.user._dn_to_id(user_dn)
users.append(self.user.get(user_id))
return self._set_default_domain(users)
def check_user_in_group(self, user_id, group_id): def check_user_in_group(self, user_id, group_id):
self.get_user(user_id) self.get_user(user_id)
@@ -324,75 +406,8 @@ class Identity(identity.Driver):
return [DEFAULT_DOMAIN] return [DEFAULT_DOMAIN]
# TODO(termie): remove this and move cross-api calls into driver
class ApiShim(object):
"""Quick singleton-y shim to get around recursive dependencies.
NOTE(termie): this should be removed and the cross-api code
should be moved into the driver itself.
"""
_role = None
_project = None
_user = None
_group = None
_domain = None
def __init__(self, conf):
self.conf = conf
@property
def role(self):
if not self._role:
self._role = RoleApi(self.conf)
return self._role
@property
def project(self):
if not self._project:
self._project = ProjectApi(self.conf)
return self._project
@property
def user(self):
if not self._user:
self._user = UserApi(self.conf)
return self._user
@property
def group(self):
if not self._group:
self._group = GroupApi(self.conf)
return self._group
# TODO(termie): remove this and move cross-api calls into driver
class ApiShimMixin(object):
"""Mixin to share some ApiShim code. Remove me."""
@property
def role_api(self):
return self.api.role
@property
def project_api(self):
return self.api.project
@property
def user_api(self):
return self.api.user
@property
def group_api(self):
return self.api.group
@property
def domain_api(self):
return self.api.domain
# TODO(termie): turn this into a data object and move logic to driver # TODO(termie): turn this into a data object and move logic to driver
class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap, ApiShimMixin): class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):
DEFAULT_OU = 'ou=Users' DEFAULT_OU = 'ou=Users'
DEFAULT_STRUCTURAL_CLASSES = ['person'] DEFAULT_STRUCTURAL_CLASSES = ['person']
DEFAULT_ID_ATTR = 'cn' DEFAULT_ID_ATTR = 'cn'
@@ -420,7 +435,6 @@ class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap, ApiShimMixin):
self.enabled_default = conf.ldap.user_enabled_default self.enabled_default = conf.ldap.user_enabled_default
self.attribute_ignore = (getattr(conf.ldap, 'user_attribute_ignore') self.attribute_ignore = (getattr(conf.ldap, 'user_attribute_ignore')
or self.DEFAULT_ATTRIBUTE_IGNORE) or self.DEFAULT_ATTRIBUTE_IGNORE)
self.api = ApiShim(conf)
def _ldap_res_to_model(self, res): def _ldap_res_to_model(self, res):
obj = super(UserApi, self)._ldap_res_to_model(res) obj = super(UserApi, self)._ldap_res_to_model(res)
@@ -445,53 +459,19 @@ class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap, ApiShimMixin):
if self.enabled_mask: if self.enabled_mask:
self.mask_enabled_attribute(values) self.mask_enabled_attribute(values)
values = super(UserApi, self).create(values) values = super(UserApi, self).create(values)
tenant_id = values.get('tenant_id')
if tenant_id is not None:
self.project_api.add_user(values['tenant_id'], values['id'])
return values return values
def update(self, id, values):
if 'id' in values and values['id'] != id:
raise exception.ValidationError('Cannot change user ID')
old_obj = self.get(id)
if 'name' in values and old_obj.get('name') != values['name']:
raise exception.Conflict('Cannot change user name')
if 'tenant_id' in values and \
old_obj.get('tenant_id') != values['tenant_id']:
if old_obj['tenant_id']:
self.project_api.remove_user(old_obj['tenant_id'], id)
if values['tenant_id']:
self.project_api.add_user(values['tenant_id'], id)
values = utils.hash_ldap_user_password(values)
if self.enabled_mask:
values['enabled_nomask'] = old_obj['enabled_nomask']
self.mask_enabled_attribute(values)
super(UserApi, self).update(id, values, old_obj)
return self.get(id)
def delete(self, id):
user = self.get(id)
if hasattr(user, 'tenant_id'):
self.project_api.remove_user(user.tenant_id, id)
super(UserApi, self).delete(id)
for ref in self.role_api.list_global_roles_for_user(id):
self.role_api.delete_user(ref.role_id, ref.user_id, ref.project_id)
for ref in self.role_api.list_project_roles_for_user(id):
self.role_api.delete_user(ref.role_id, ref.user_id, ref.project_id)
def check_password(self, user_id, password): def check_password(self, user_id, password):
user = self.get(user_id) user = self.get(user_id)
return utils.check_password(password, user.password) return utils.check_password(password, user.password)
def get_filtered(self, user_id):
user = self.get(user_id)
return identity.filter_user(user)
# TODO(termie): turn this into a data object and move logic to driver # TODO(termie): turn this into a data object and move logic to driver
class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap, class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):
ApiShimMixin):
DEFAULT_OU = 'ou=Projects' DEFAULT_OU = 'ou=Projects'
DEFAULT_STRUCTURAL_CLASSES = [] DEFAULT_STRUCTURAL_CLASSES = []
DEFAULT_OBJECTCLASS = 'groupOfNames' DEFAULT_OBJECTCLASS = 'groupOfNames'
@@ -510,7 +490,6 @@ class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap,
def __init__(self, conf): def __init__(self, conf):
super(ProjectApi, self).__init__(conf) super(ProjectApi, self).__init__(conf)
self.api = ApiShim(conf)
self.attribute_mapping['name'] = conf.ldap.tenant_name_attribute self.attribute_mapping['name'] = conf.ldap.tenant_name_attribute
self.attribute_mapping['description'] = conf.ldap.tenant_desc_attribute self.attribute_mapping['description'] = conf.ldap.tenant_desc_attribute
self.attribute_mapping['enabled'] = conf.ldap.tenant_enabled_attribute self.attribute_mapping['enabled'] = conf.ldap.tenant_enabled_attribute
@@ -528,13 +507,13 @@ class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap,
data['id'] = uuid.uuid4().hex data['id'] = uuid.uuid4().hex
return super(ProjectApi, self).create(data) return super(ProjectApi, self).create(data)
def get_user_projects(self, user_id): def get_user_projects(self, user_dn, associations):
"""Returns list of tenants a user has access to """Returns list of tenants a user has access to
""" """
associations = self.role_api.list_project_roles_for_user(user_id)
project_ids = set() project_ids = set()
for assoc in associations: for assoc in associations:
project_ids.add(assoc.project_id) project_ids.add(self._dn_to_id(assoc.project_dn))
projects = [] projects = []
for project_id in project_ids: for project_id in project_ids:
#slower to get them one at a time, but a huge list could blow out #slower to get them one at a time, but a huge list could blow out
@@ -542,57 +521,46 @@ class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap,
projects.append(self.get(project_id)) projects.append(self.get(project_id))
return projects return projects
def get_role_assignments(self, tenant_id): def add_user(self, tenant_id, user_dn):
return self.role_api.get_role_assignments(tenant_id)
def add_user(self, tenant_id, user_id):
conn = self.get_connection() conn = self.get_connection()
try: try:
conn.modify_s( conn.modify_s(
self._id_to_dn(tenant_id), self._id_to_dn(tenant_id),
[(ldap.MOD_ADD, [(ldap.MOD_ADD,
self.member_attribute, self.member_attribute,
self.user_api._id_to_dn(user_id))]) user_dn)])
except ldap.TYPE_OR_VALUE_EXISTS: except ldap.TYPE_OR_VALUE_EXISTS:
# As adding a user to a tenant is done implicitly in several # As adding a user to a tenant is done implicitly in several
# places, and is not part of the exposed API, it's easier for us to # places, and is not part of the exposed API, it's easier for us to
# just ignore this instead of raising exception.Conflict. # just ignore this instead of raising exception.Conflict.
pass pass
def remove_user(self, tenant_id, user_id): def remove_user(self, tenant_id, user_dn, user_id):
conn = self.get_connection() conn = self.get_connection()
try: try:
conn.modify_s(self._id_to_dn(tenant_id), conn.modify_s(self._id_to_dn(tenant_id),
[(ldap.MOD_DELETE, [(ldap.MOD_DELETE,
self.member_attribute, self.member_attribute,
self.user_api._id_to_dn(user_id))]) user_dn)])
except ldap.NO_SUCH_ATTRIBUTE: except ldap.NO_SUCH_ATTRIBUTE:
raise exception.NotFound(user_id) raise exception.NotFound(user_id)
def get_users(self, tenant_id, role_id=None): def get_user_dns(self, tenant_id, rolegrants, role_dn=None):
tenant = self._ldap_get(tenant_id) tenant = self._ldap_get(tenant_id)
res = set() res = set()
if not role_id: if not role_dn:
# Get users who have default tenant mapping # Get users who have default tenant mapping
for user_dn in tenant[1].get(self.member_attribute, []): for user_dn in tenant[1].get(self.member_attribute, []):
if self.use_dumb_member and user_dn == self.dumb_member: if self.use_dumb_member and user_dn == self.dumb_member:
continue continue
res.add(self.user_api.get(self.user_api._dn_to_id(user_dn))) res.add(user_dn)
# Get users who are explicitly mapped via a tenant # Get users who are explicitly mapped via a tenant
rolegrants = self.role_api.get_role_assignments(tenant_id)
for rolegrant in rolegrants: for rolegrant in rolegrants:
if role_id is None or rolegrant.role_id == role_id: if role_dn is None or rolegrant.role_dn == role_dn:
res.add(self.user_api.get(rolegrant.user_id)) res.add(rolegrant.user_dn)
return list(res) return list(res)
def delete(self, id):
if self.subtree_delete_enabled:
super(ProjectApi, self).deleteTree(id)
else:
self.role_api.roles_delete_subtree_by_project(id)
super(ProjectApi, self).delete(id)
def update(self, id, values): def update(self, id, values):
old_obj = self.get(id) old_obj = self.get(id)
if old_obj['name'] != values['name']: if old_obj['name'] != values['name']:
@@ -604,25 +572,25 @@ class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap,
class UserRoleAssociation(object): class UserRoleAssociation(object):
"""Role Grant model.""" """Role Grant model."""
def __init__(self, user_id=None, role_id=None, tenant_id=None, def __init__(self, user_dn=None, role_dn=None, tenant_dn=None,
*args, **kw): *args, **kw):
self.user_id = str(user_id) self.user_dn = user_dn
self.role_id = role_id self.role_dn = role_dn
self.project_id = str(tenant_id) self.project_dn = tenant_dn
class GroupRoleAssociation(object): class GroupRoleAssociation(object):
"""Role Grant model.""" """Role Grant model."""
def __init__(self, group_id=None, role_id=None, tenant_id=None, def __init__(self, group_dn=None, role_dn=None, tenant_dn=None,
*args, **kw): *args, **kw):
self.group_id = str(group_id) self.group_dn = group_dn
self.role_id = role_id self.role_dn = role_dn
self.project_id = str(tenant_id) self.project_dn = tenant_dn
# TODO(termie): turn this into a data object and move logic to driver # TODO(termie): turn this into a data object and move logic to driver
class RoleApi(common_ldap.BaseLdap, ApiShimMixin): class RoleApi(common_ldap.BaseLdap):
DEFAULT_OU = 'ou=Roles' DEFAULT_OU = 'ou=Roles'
DEFAULT_STRUCTURAL_CLASSES = [] DEFAULT_STRUCTURAL_CLASSES = []
DEFAULT_OBJECTCLASS = 'organizationalRole' DEFAULT_OBJECTCLASS = 'organizationalRole'
@@ -637,21 +605,12 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
def __init__(self, conf): def __init__(self, conf):
super(RoleApi, self).__init__(conf) super(RoleApi, self).__init__(conf)
self.api = ApiShim(conf)
self.attribute_mapping['name'] = conf.ldap.role_name_attribute self.attribute_mapping['name'] = conf.ldap.role_name_attribute
self.member_attribute = (getattr(conf.ldap, 'role_member_attribute') self.member_attribute = (getattr(conf.ldap, 'role_member_attribute')
or self.DEFAULT_MEMBER_ATTRIBUTE) or self.DEFAULT_MEMBER_ATTRIBUTE)
self.attribute_ignore = (getattr(conf.ldap, 'role_attribute_ignore') self.attribute_ignore = (getattr(conf.ldap, 'role_attribute_ignore')
or self.DEFAULT_ATTRIBUTE_IGNORE) or self.DEFAULT_ATTRIBUTE_IGNORE)
def _subrole_id_to_dn(self, role_id, tenant_id):
if tenant_id is None:
return self._id_to_dn(role_id)
else:
return '%s=%s,%s' % (self.id_attr,
ldap.dn.escape_dn_chars(role_id),
self.project_api._id_to_dn(tenant_id))
def get(self, id, filter=None): def get(self, id, filter=None):
model = super(RoleApi, self).get(id, filter) model = super(RoleApi, self).get(id, filter)
return model return model
@@ -659,10 +618,8 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
def create(self, values): def create(self, values):
return super(RoleApi, self).create(values) return super(RoleApi, self).create(values)
def add_user(self, role_id, user_id, tenant_id=None): def add_user(self, role_id, role_dn, user_dn, user_id, tenant_id=None):
role_dn = self._subrole_id_to_dn(role_id, tenant_id)
conn = self.get_connection() conn = self.get_connection()
user_dn = self.user_api._id_to_dn(user_id)
try: try:
conn.modify_s(role_dn, [(ldap.MOD_ADD, conn.modify_s(role_dn, [(ldap.MOD_ADD,
self.member_attribute, user_dn)]) self.member_attribute, user_dn)])
@@ -684,20 +641,14 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
except Exception as inst: except Exception as inst:
raise inst raise inst
return UserRoleAssociation( def delete_user(self, role_dn, user_dn, tenant_dn,
role_id=role_id, user_id, role_id):
user_id=user_id,
tenant_id=tenant_id)
def delete_user(self, role_id, user_id, tenant_id):
role_dn = self._subrole_id_to_dn(role_id, tenant_id)
conn = self.get_connection() conn = self.get_connection()
user_dn = self.user_api._id_to_dn(user_id)
try: try:
conn.modify_s(role_dn, [(ldap.MOD_DELETE, conn.modify_s(role_dn, [(ldap.MOD_DELETE,
self.member_attribute, user_dn)]) self.member_attribute, user_dn)])
except ldap.NO_SUCH_OBJECT: except ldap.NO_SUCH_OBJECT:
if tenant_id is None or self.get(role_id) is None: if tenant_dn is None:
raise exception.RoleNotFound(role_id=role_id) raise exception.RoleNotFound(role_id=role_id)
attrs = [('objectClass', [self.object_class]), attrs = [('objectClass', [self.object_class]),
(self.member_attribute, [user_dn])] (self.member_attribute, [user_dn])]
@@ -708,14 +659,12 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
conn.add_s(role_dn, attrs) conn.add_s(role_dn, attrs)
except Exception as inst: except Exception as inst:
raise inst raise inst
except ldap.NO_SUCH_ATTRIBUTE: except ldap.NO_SUCH_ATTRIBUTE:
raise exception.UserNotFound(user_id=user_id) raise exception.UserNotFound(user_id=user_id)
def get_role_assignments(self, tenant_id): def get_role_assignments(self, tenant_dn):
conn = self.get_connection() conn = self.get_connection()
query = '(objectClass=%s)' % self.object_class query = '(objectClass=%s)' % self.object_class
tenant_dn = self.project_api._id_to_dn(tenant_id)
try: try:
roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query)
@@ -731,64 +680,49 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
for user_dn in user_dns: for user_dn in user_dns:
if self.use_dumb_member and user_dn == self.dumb_member: if self.use_dumb_member and user_dn == self.dumb_member:
continue continue
user_id = self.user_api._dn_to_id(user_dn)
role_id = self._dn_to_id(role_dn)
res.append(UserRoleAssociation( res.append(UserRoleAssociation(
user_id=user_id, user_dn=user_dn,
role_id=role_id, role_dn=role_dn,
tenant_id=tenant_id)) tenant_dn=tenant_dn))
return res return res
def list_global_roles_for_user(self, user_id): def list_global_roles_for_user(self, user_dn):
user_dn = self.user_api._id_to_dn(user_id)
roles = self.get_all('(%s=%s)' % (self.member_attribute, user_dn)) roles = self.get_all('(%s=%s)' % (self.member_attribute, user_dn))
return [UserRoleAssociation( return [UserRoleAssociation(
role_id=role.id, role_dn=role.dn,
user_id=user_id) for role in roles] user_dn=user_dn) for role in roles]
def list_project_roles_for_user(self, user_id, tenant_id=None): def list_project_roles_for_user(self, user_dn, project_subtree):
conn = self.get_connection() conn = self.get_connection()
user_dn = self.user_api._id_to_dn(user_id)
query = '(&(objectClass=%s)(%s=%s))' % (self.object_class, query = '(&(objectClass=%s)(%s=%s))' % (self.object_class,
self.member_attribute, self.member_attribute,
user_dn) user_dn)
if tenant_id is not None: try:
tenant_dn = self.project_api._id_to_dn(tenant_id) roles = conn.search_s(project_subtree,
try: ldap.SCOPE_SUBTREE,
roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) query)
except ldap.NO_SUCH_OBJECT: except ldap.NO_SUCH_OBJECT:
return [] return []
res = [] res = []
for role_dn, _ in roles: for role_dn, _ in roles:
role_id = self._dn_to_id(role_dn) #ldap.dn.dn2str returns an array, where the first
res.append(UserRoleAssociation( #element is the first segment.
user_id=user_id, #For a role assignment, this contains the role ID,
role_id=role_id, #The remainder is the DN of the tenant.
tenant_id=tenant_id)) tenant = ldap.dn.str2dn(role_dn)
else: tenant.pop(0)
try: tenant_dn = ldap.dn.dn2str(tenant)
roles = conn.search_s(self.project_api.tree_dn, res.append(UserRoleAssociation(
ldap.SCOPE_SUBTREE, user_dn=user_dn,
query) role_dn=role_dn,
except ldap.NO_SUCH_OBJECT: tenant_dn=tenant_dn))
return []
res = []
for role_dn, _ in roles:
role_id = self._dn_to_id(role_dn)
tenant_id = ldap.dn.str2dn(role_dn)[1][0][1]
res.append(UserRoleAssociation(
user_id=user_id,
role_id=role_id,
tenant_id=tenant_id))
return res return res
def roles_delete_subtree_by_project(self, tenant_id): def roles_delete_subtree_by_project(self, tenant_dn):
conn = self.get_connection() conn = self.get_connection()
query = '(objectClass=%s)' % self.object_class query = '(objectClass=%s)' % self.object_class
tenant_dn = self.project_api._id_to_dn(tenant_id)
try: try:
roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query)
for role_dn, _ in roles: for role_dn, _ in roles:
@@ -809,11 +743,10 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
pass pass
return super(RoleApi, self).update(role_id, role) return super(RoleApi, self).update(role_id, role)
def delete(self, id): def delete(self, id, tenant_dn):
conn = self.get_connection() conn = self.get_connection()
query = '(&(objectClass=%s)(%s=%s))' % (self.object_class, query = '(&(objectClass=%s)(%s=%s))' % (self.object_class,
self.id_attr, id) self.id_attr, id)
tenant_dn = self.project_api.tree_dn
try: try:
for role_dn, _ in conn.search_s(tenant_dn, for role_dn, _ in conn.search_s(tenant_dn,
ldap.SCOPE_SUBTREE, ldap.SCOPE_SUBTREE,
@@ -823,27 +756,8 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
pass pass
super(RoleApi, self).delete(id) super(RoleApi, self).delete(id)
# TODO(spzala): this is only placeholder for group and domain role support
# which will be added under bug 1101287
def roles_delete_subtree_by_type(self, id, type):
conn = self.get_connection()
query = '(objectClass=%s)' % self.object_class
dn = None
if type == 'Group':
dn = self.group_api._id_to_dn(id)
if type == 'Domain':
dn = self.domain_api._id_to_dn(id)
if dn:
try:
roles = conn.search_s(dn, ldap.SCOPE_ONELEVEL,
query, ['%s' % '1.1'])
for role_dn, _ in roles:
conn.delete_s(role_dn)
except ldap.NO_SUCH_OBJECT:
pass
class GroupApi(common_ldap.BaseLdap):
class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
DEFAULT_OU = 'ou=UserGroups' DEFAULT_OU = 'ou=UserGroups'
DEFAULT_STRUCTURAL_CLASSES = [] DEFAULT_STRUCTURAL_CLASSES = []
DEFAULT_OBJECTCLASS = 'groupOfNames' DEFAULT_OBJECTCLASS = 'groupOfNames'
@@ -860,7 +774,6 @@ class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
def __init__(self, conf): def __init__(self, conf):
super(GroupApi, self).__init__(conf) super(GroupApi, self).__init__(conf)
self.api = ApiShim(conf)
self.attribute_mapping['name'] = conf.ldap.group_name_attribute self.attribute_mapping['name'] = conf.ldap.group_name_attribute
self.attribute_mapping['description'] = conf.ldap.group_desc_attribute self.attribute_mapping['description'] = conf.ldap.group_desc_attribute
self.attribute_mapping['domain_id'] = ( self.attribute_mapping['domain_id'] = (
@@ -883,7 +796,22 @@ class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
if self.subtree_delete_enabled: if self.subtree_delete_enabled:
super(GroupApi, self).deleteTree(id) super(GroupApi, self).deleteTree(id)
else: else:
self.role_api.roles_delete_subtree_by_type(id, 'Group') # TODO(spzala): this is only placeholder for group and domain
# role support which will be added under bug 1101287
conn = self.get_connection()
query = '(objectClass=%s)' % self.object_class
dn = None
dn = self._id_to_dn(id)
if dn:
try:
roles = conn.search_s(dn, ldap.SCOPE_ONELEVEL,
query, ['%s' % '1.1'])
for role_dn, _ in roles:
conn.delete_s(role_dn)
except ldap.NO_SUCH_OBJECT:
pass
super(GroupApi, self).delete(id) super(GroupApi, self).delete(id)
def update(self, id, values): def update(self, id, values):
@@ -893,39 +821,39 @@ class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
raise exception.NotImplemented(message=msg) raise exception.NotImplemented(message=msg)
return super(GroupApi, self).update(id, values, old_obj) return super(GroupApi, self).update(id, values, old_obj)
def add_user(self, user_id, group_id): def add_user(self, user_dn, group_id, user_id):
conn = self.get_connection() conn = self.get_connection()
try: try:
conn.modify_s( conn.modify_s(
self._id_to_dn(group_id), self._id_to_dn(group_id),
[(ldap.MOD_ADD, [(ldap.MOD_ADD,
self.member_attribute, self.member_attribute,
self.user_api._id_to_dn(user_id))]) user_dn)])
except ldap.TYPE_OR_VALUE_EXISTS: except ldap.TYPE_OR_VALUE_EXISTS:
raise exception.Conflict(_( raise exception.Conflict(_(
'User %(user_id)s is already a member of group %(group_id)s') % 'User %(user_id)s is already a member of group %(group_id)s') %
{'user_id': user_id, 'group_id': group_id}) {'user_id': user_id, 'group_id': group_id})
def remove_user(self, user_id, group_id): def remove_user(self, user_dn, group_id, user_id):
conn = self.get_connection() conn = self.get_connection()
try: try:
conn.modify_s( conn.modify_s(
self._id_to_dn(group_id), self._id_to_dn(group_id),
[(ldap.MOD_DELETE, [(ldap.MOD_DELETE,
self.member_attribute, self.member_attribute,
self.user_api._id_to_dn(user_id))]) user_dn)])
except ldap.NO_SUCH_ATTRIBUTE: except ldap.NO_SUCH_ATTRIBUTE:
raise exception.UserNotFound(user_id=user_id) raise exception.UserNotFound(user_id=user_id)
def list_user_groups(self, user_id): def list_user_groups(self, user_dn):
"""Return a list of groups for which the user is a member.""" """Return a list of groups for which the user is a member."""
user_dn = self.user_api._id_to_dn(user_id)
query = '(%s=%s)' % (self.member_attribute, user_dn) query = '(%s=%s)' % (self.member_attribute, user_dn)
memberships = self.get_all(query) memberships = self.get_all(query)
return memberships return memberships
def list_group_users(self, group_id): def list_group_users(self, group_id):
"""Return a list of users which are members of a group.""" """Return a list of user dns which are members of a group."""
query = '(objectClass=%s)' % self.object_class query = '(objectClass=%s)' % self.object_class
conn = self.get_connection() conn = self.get_connection()
group_dn = self._id_to_dn(group_id) group_dn = self._id_to_dn(group_id)
@@ -942,8 +870,7 @@ class GroupApi(common_ldap.BaseLdap, ApiShimMixin):
if self.use_dumb_member and user_dn == self.dumb_member: if self.use_dumb_member and user_dn == self.dumb_member:
continue continue
try: try:
user_id = self.user_api._dn_to_id(user_dn) users.append(user_dn)
users.append(self.user_api.get(user_id))
except exception.UserNotFound: except exception.UserNotFound:
LOG.debug(_("Group member '%(user_dn)s' not found in" LOG.debug(_("Group member '%(user_dn)s' not found in"
" '%(group_dn)s'. The user should be removed" " '%(group_dn)s'. The user should be removed"

View File

@@ -578,7 +578,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
self.identity_api.add_user_to_group(user_2_id, group_id) self.identity_api.add_user_to_group(user_2_id, group_id)
# Delete user 2. # Delete user 2.
self.identity_api.user.delete(user_2_id) self.identity_api.delete_user(user_2_id)
# List group users and verify only user 1. # List group users and verify only user 1.
res = self.identity_api.list_users_in_group(group_id) res = self.identity_api.list_users_in_group(group_id)