Skip to content

Commit

Permalink
Remove vestiges of Assignments from LDAP Identity Backend
Browse files Browse the repository at this point in the history
Bug 1204995

Change-Id: Ife92041ffc386b8f9629096ebd65020f3cc7ae26
  • Loading branch information
Adam Young committed Jul 26, 2013
1 parent 0a40152 commit dc6da6a
Show file tree
Hide file tree
Showing 3 changed files with 3 additions and 310 deletions.
12 changes: 0 additions & 12 deletions keystone/assignment/backends/ldap.py
Expand Up @@ -55,20 +55,8 @@ def __init__(self):

self.project = ProjectApi(CONF)
self.role = RoleApi(CONF)

self._identity_api = None

@property
def identity_api(self):
return self._identity_api

@identity_api.setter
def identity_api(self, value):
self._identity_api = value
#TODO(ayoung): only left here to prevent unit test from breaking
#once we remove here. the getter and setter can be removed as well.
self._identity_api.driver.project = self.project

def get_project(self, tenant_id):
return self._set_default_domain(self.project.get(tenant_id))

Expand Down
295 changes: 0 additions & 295 deletions keystone/identity/backends/ldap.py
Expand Up @@ -41,11 +41,6 @@
class Identity(identity.Driver):
def __init__(self):
super(Identity, 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

self.user = UserApi(CONF)
self.group = GroupApi(CONF)

Expand All @@ -57,9 +52,6 @@ def default_assignment_driver(self):
def create_project(self, project_id, project):
return self.assignment_api.create_project(project_id, project)

def get_project(self, project_id):
return self.assignment_api.get_project(project_id)

def authenticate(self, user_id=None, password=None):
try:
user_ref = self._get_user(user_id)
Expand Down Expand Up @@ -273,293 +265,6 @@ def get_filtered(self, user_id):
return identity.filter_user(user)


# TODO(termie): turn this into a data object and move logic to driver
class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):
DEFAULT_OU = 'ou=Projects'
DEFAULT_STRUCTURAL_CLASSES = []
DEFAULT_OBJECTCLASS = 'groupOfNames'
DEFAULT_ID_ATTR = 'cn'
DEFAULT_MEMBER_ATTRIBUTE = 'member'
DEFAULT_ATTRIBUTE_IGNORE = []
NotFound = exception.ProjectNotFound
notfound_arg = 'project_id' # NOTE(yorik-sar): while options_name = tenant
options_name = 'tenant'
attribute_mapping = {'name': 'ou',
'description': 'description',
'tenantId': 'cn',
'enabled': 'enabled',
'domain_id': 'domain_id'}
model = models.Project

def __init__(self, conf):
super(ProjectApi, self).__init__(conf)
self.attribute_mapping['name'] = conf.ldap.tenant_name_attribute
self.attribute_mapping['description'] = conf.ldap.tenant_desc_attribute
self.attribute_mapping['enabled'] = conf.ldap.tenant_enabled_attribute
self.attribute_mapping['domain_id'] = (
conf.ldap.tenant_domain_id_attribute)
self.member_attribute = (getattr(conf.ldap, 'tenant_member_attribute')
or self.DEFAULT_MEMBER_ATTRIBUTE)
self.attribute_ignore = (getattr(conf.ldap, 'tenant_attribute_ignore')
or self.DEFAULT_ATTRIBUTE_IGNORE)

def create(self, values):
self.affirm_unique(values)
data = values.copy()
if data.get('id') is None:
data['id'] = uuid.uuid4().hex
return super(ProjectApi, self).create(data)

def get_user_projects(self, user_dn, associations):
"""Returns list of tenants a user has access to
"""

project_ids = set()
for assoc in associations:
project_ids.add(self._dn_to_id(assoc.project_dn))
projects = []
for project_id in project_ids:
#slower to get them one at a time, but a huge list could blow out
#the connection. This is the safer way
projects.append(self.get(project_id))
return projects

def add_user(self, tenant_id, user_dn):
conn = self.get_connection()
try:
conn.modify_s(
self._id_to_dn(tenant_id),
[(ldap.MOD_ADD,
self.member_attribute,
user_dn)])
except ldap.TYPE_OR_VALUE_EXISTS:
# 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
# just ignore this instead of raising exception.Conflict.
pass

def remove_user(self, tenant_id, user_dn, user_id):
conn = self.get_connection()
try:
conn.modify_s(self._id_to_dn(tenant_id),
[(ldap.MOD_DELETE,
self.member_attribute,
user_dn)])
except ldap.NO_SUCH_ATTRIBUTE:
raise exception.NotFound(user_id)

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.use_dumb_member and user_dn == self.dumb_member:
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)

def update(self, id, values):
old_obj = self.get(id)
if old_obj['name'] != values['name']:
msg = 'Changing Name not supported by LDAP'
raise exception.NotImplemented(message=msg)
return super(ProjectApi, self).update(id, values, old_obj)


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
class RoleApi(common_ldap.BaseLdap):
DEFAULT_OU = 'ou=Roles'
DEFAULT_STRUCTURAL_CLASSES = []
DEFAULT_OBJECTCLASS = 'organizationalRole'
DEFAULT_MEMBER_ATTRIBUTE = 'roleOccupant'
DEFAULT_ATTRIBUTE_IGNORE = []
NotFound = exception.RoleNotFound
options_name = 'role'
attribute_mapping = {'name': 'ou',
#'serviceId': 'service_id',
}
model = models.Role

def __init__(self, conf):
super(RoleApi, self).__init__(conf)
self.attribute_mapping['name'] = conf.ldap.role_name_attribute
self.member_attribute = (getattr(conf.ldap, 'role_member_attribute')
or self.DEFAULT_MEMBER_ATTRIBUTE)
self.attribute_ignore = (getattr(conf.ldap, 'role_attribute_ignore')
or self.DEFAULT_ATTRIBUTE_IGNORE)

def get(self, id, filter=None):
model = super(RoleApi, self).get(id, filter)
return model

def create(self, values):
return super(RoleApi, self).create(values)

def add_user(self, role_id, role_dn, user_dn, user_id, tenant_id=None):
conn = self.get_connection()
try:
conn.modify_s(role_dn, [(ldap.MOD_ADD,
self.member_attribute, user_dn)])
except ldap.TYPE_OR_VALUE_EXISTS:
msg = ('User %s already has role %s in tenant %s'
% (user_id, role_id, tenant_id))
raise exception.Conflict(type='role grant', details=msg)
except ldap.NO_SUCH_OBJECT:
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])]

if self.use_dumb_member:
attrs[1][1].append(self.dumb_member)
try:
conn.add_s(role_dn, attrs)
except Exception as inst:
raise inst

def delete_user(self, role_dn, user_dn, tenant_dn,
user_id, role_id):
conn = self.get_connection()
try:
conn.modify_s(role_dn, [(ldap.MOD_DELETE,
self.member_attribute, user_dn)])
except ldap.NO_SUCH_OBJECT:
if tenant_dn is None:
raise exception.RoleNotFound(role_id=role_id)
attrs = [('objectClass', [self.object_class]),
(self.member_attribute, [user_dn])]

if self.use_dumb_member:
attrs[1][1].append(self.dumb_member)
try:
conn.add_s(role_dn, attrs)
except Exception as inst:
raise inst
except ldap.NO_SUCH_ATTRIBUTE:
raise exception.UserNotFound(user_id=user_id)

def get_role_assignments(self, tenant_dn):
conn = self.get_connection()
query = '(objectClass=%s)' % self.object_class

try:
roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query)
except ldap.NO_SUCH_OBJECT:
return []

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.use_dumb_member and user_dn == self.dumb_member:
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):
roles = self.get_all('(%s=%s)' % (self.member_attribute, user_dn))
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):
conn = self.get_connection()
query = '(&(objectClass=%s)(%s=%s))' % (self.object_class,
self.member_attribute,
user_dn)
try:
roles = conn.search_s(project_subtree,
ldap.SCOPE_SUBTREE,
query)
except ldap.NO_SUCH_OBJECT:
return []

res = []
for role_dn, _ 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.
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 roles_delete_subtree_by_project(self, tenant_dn):
conn = self.get_connection()
query = '(objectClass=%s)' % self.object_class
try:
roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query)
for role_dn, _ in roles:
try:
conn.delete_s(role_dn)
except Exception as inst:
raise inst
except ldap.NO_SUCH_OBJECT:
pass

def update(self, role_id, role):
if role['id'] != role_id:
raise exception.ValidationError('Cannot change role ID')
try:
old_name = self.get_by_name(role['name'])
raise exception.Conflict('Cannot duplicate name %s' % old_name)
except exception.NotFound:
pass
return super(RoleApi, self).update(role_id, role)

def delete(self, id, tenant_dn):
conn = self.get_connection()
query = '(&(objectClass=%s)(%s=%s))' % (self.object_class,
self.id_attr, id)
try:
for role_dn, _ in conn.search_s(tenant_dn,
ldap.SCOPE_SUBTREE,
query):
conn.delete_s(role_dn)
except ldap.NO_SUCH_OBJECT:
pass
super(RoleApi, self).delete(id)


class GroupApi(common_ldap.BaseLdap):
DEFAULT_OU = 'ou=UserGroups'
DEFAULT_STRUCTURAL_CLASSES = []
Expand Down
6 changes: 3 additions & 3 deletions tests/test_backend_ldap.py
Expand Up @@ -562,12 +562,12 @@ def test_project_crud(self):
'domain_id': CONF.identity.default_domain_id,
'description': uuid.uuid4().hex
}
self.identity_api.driver.create_project(project['id'], project)
project_ref = self.identity_api.driver.get_project(project['id'])
self.assignment_api.create_project(project['id'], project)
project_ref = self.assignment_api.get_project(project['id'])

# NOTE(crazed): If running live test with emulation, there will be
# an enabled key in the project_ref.
if self.identity_api.driver.project.enabled_emulation:
if self.assignment_api.driver.project.enabled_emulation:
project['enabled'] = True
self.assertDictEqual(project_ref, project)

Expand Down

0 comments on commit dc6da6a

Please sign in to comment.