diff options
| author | Yuriy Taraday <yorik.sar@gmail.com> | 2011-08-23 16:28:17 +0400 |
|---|---|---|
| committer | Yuriy Taraday <yorik.sar@gmail.com> | 2011-08-23 16:48:49 +0400 |
| commit | 2f76856dffeb5224fb1612bb03a74a373a1a528b (patch) | |
| tree | 8c533bed4b52f0b1d21f6fe6b87dac95c2b3d293 | |
| parent | d349b349c50e0683432f5ae843bd2dc83599c1b6 (diff) | |
Made it possible to integrate with external LDAP.
All object classes are made auxiliary, add ability to specify what strutural
classes to use and what attribute type should be treated as object identifier.
Change-Id: I4b2cc8cc6f2a1d93e82005543ce99be4bae23b5b
| -rw-r--r-- | keystone/backends/ldap/api/base.py | 30 | ||||
| -rw-r--r-- | keystone/backends/ldap/api/role.py | 30 | ||||
| -rw-r--r-- | keystone/backends/ldap/api/tenant.py | 13 | ||||
| -rw-r--r-- | keystone/backends/ldap/api/user.py | 4 | ||||
| -rw-r--r-- | keystone/backends/ldap/keystone.ldif | 28 | ||||
| -rw-r--r-- | keystone/backends/ldap/keystone.schema | 28 |
6 files changed, 93 insertions, 40 deletions
diff --git a/keystone/backends/ldap/api/base.py b/keystone/backends/ldap/api/base.py index 80d21479..bec7dc2f 100644 --- a/keystone/backends/ldap/api/base.py +++ b/keystone/backends/ldap/api/base.py @@ -1,3 +1,4 @@ +import ast import ldap from itertools import izip, count @@ -15,6 +16,9 @@ def add_redirects(loc, cls, methods): class BaseLdapAPI(object): DEFAULT_TREE_DN = None + DEFAULT_STRUCTURAL_CLASSES = None + DEFAULT_ID_ATTR = 'cn' + DUMB_MEMBER_DN = 'cn=dumb,dc=nonexistent' options_name = None object_class = 'top' model = None @@ -23,13 +27,28 @@ class BaseLdapAPI(object): def __init__(self, api, options): self.api = api - self.tree_dn = options.get(self.options_name, self.DEFAULT_TREE_DN) + if self.options_name is not None: + self.tree_dn = options.get('%s_tree_dn' % (self.options_name,), + self.DEFAULT_TREE_DN) + try: + lst = options['%s_structural_classes' % (self.options_name,)] + except KeyError: + self.structural_classes = self.DEFAULT_STRUCTURAL_CLASSES + else: + self.structural_classes = ast.literal_eval(lst) + self.id_attr = options.get('%s_id_attr' % (self.options_name,), + self.DEFAULT_ID_ATTR) + self.use_dumb_member = options.get('use_dumb_member', True) def _id_to_dn(self, id): - return 'cn=%s,%s' % (ldap.dn.escape_dn_chars(str(id)), self.tree_dn) + return '%s=%s,%s' % (self.id_attr, ldap.dn.escape_dn_chars(str(id)), + self.tree_dn) + + def _dn_to_id(self, dn): + return ldap.dn.str2dn(dn)[0][0][1] def _ldap_res_to_model(self, res): - obj = self.model(id=ldap.dn.str2dn(res[0])[0][0][1]) + obj = self.model(id=self._dn_to_id(res[0])) for k in obj: if k in self.attribute_ignore: continue @@ -43,13 +62,16 @@ class BaseLdapAPI(object): def create(self, values): conn = self.api.get_connection() - attrs = [('objectClass', [self.object_class])] + object_classes = self.structural_classes + [self.object_class] + attrs = [('objectClass', object_classes)] for k, v in values.iteritems(): if k == 'id' or k in self.attribute_ignore: continue if v is not None: attr_type = self.attribute_mapping.get(k, k) attrs.append((attr_type, [v])) + if 'groupOfNames' in object_classes and self.use_dumb_member: + attrs.append(('member', [self.DUMB_MEMBER_DN])) conn.add_s(self._id_to_dn(values['id']), attrs) return self.model(values) diff --git a/keystone/backends/ldap/api/role.py b/keystone/backends/ldap/api/role.py index e79d70d0..a1a0a023 100644 --- a/keystone/backends/ldap/api/role.py +++ b/keystone/backends/ldap/api/role.py @@ -9,7 +9,8 @@ from .base import BaseLdapAPI class RoleAPI(BaseLdapAPI, BaseTenantAPI): DEFAULT_TREE_DN = 'ou=Groups,dc=example,dc=com' - options_name = 'role_tree_dn' + DEFAULT_STRUCTURAL_CLASSES = ['groupOfNames'] + options_name = 'role' object_class = 'keystoneRole' model = models.Role attribute_mapping = {'desc': 'description', 'service_id': 'serviceId'} @@ -63,10 +64,12 @@ class RoleAPI(BaseLdapAPI, BaseTenantAPI): else: tenant_dn = None attrs = [ - ('objectClass', 'keystoneTenantRole'), - ('member', user_dn), + ('objectClass', ['keystoneTenantRole', 'groupOfNames']), + ('member', [user_dn]), ('keystoneRole', self._id_to_dn(role_id)), ] + if self.use_dumb_member: + attrs[1][1].append(self.DUMB_MEMBER_DN) conn.add_s(role_dn, attrs) return models.UserRoleAssociation( id=self._create_ref(role_id, tenant_id, user_id), @@ -98,8 +101,10 @@ class RoleAPI(BaseLdapAPI, BaseTenantAPI): except KeyError: continue for user_dn in user_dns: - user_id = ldap.dn.str2dn(user_dn)[0][0][1] - role_id = ldap.dn.str2dn(role_dn)[0][0][1] + if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: + continue + user_id = self.api.user._dn_to_id(user_dn) + role_id = self._dn_to_id(role_dn) res.append(models.UserRoleAssociation( id=self._create_ref(role_id, tenant_id, user_id), user_id=user_id, @@ -127,7 +132,7 @@ class RoleAPI(BaseLdapAPI, BaseTenantAPI): return [] res = [] for role_dn, _ in roles: - role_id = ldap.dn.str2dn(role_dn)[0][0][1] + role_id = self._dn_to_id(role_dn) res.append(models.UserRoleAssociation( id=self._create_ref(role_id, tenant_id, user_id), user_id=user_id, @@ -142,7 +147,7 @@ class RoleAPI(BaseLdapAPI, BaseTenantAPI): return [] res = [] for role_dn, _ in roles: - role_id = ldap.dn.str2dn(role_dn)[0][0][1] + role_id = self._dn_to_id(role_dn) tenant_id = ldap.dn.str2dn(role_dn)[1][0][1] res.append(models.UserRoleAssociation( id=self._create_ref(role_id, tenant_id, user_id), @@ -156,8 +161,9 @@ class RoleAPI(BaseLdapAPI, BaseTenantAPI): user_dn = self.api.user._id_to_dn(user_id) role_dn = self._subrole_id_to_dn(role_id, tenant_id) query = '(&(objectClass=keystoneTenantRole)(member=%s))' % (user_dn,) + conn = self.api.get_connection() try: - res = search_s(role_dn, ldap.SCOPE_BASE, query) + res = conn.search_s(role_dn, ldap.SCOPE_BASE, query) except ldap.NO_SUCH_OBJECT: return None if len(res) == 0: @@ -201,12 +207,14 @@ class RoleAPI(BaseLdapAPI, BaseTenantAPI): except KeyError: continue for user_dn in user_dns: - user_id = ldap.dn.str2dn(user_dn)[0][0][1] + if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: + continue + user_id = self.api.user._dn_to_id(user_dn) tenant_id = None if tenant_dns != None: for tenant_dn in tenant_dns: - tenant_id = ldap.dn.str2dn(tenant_dn)[0][0][1] - role_id = ldap.dn.str2dn(role_dn)[0][0][1] + tenant_id = self.api.tenant._dn_to_id(tenant_dn) + role_id = self._dn_to_id(role_dn) res.append(models.UserRoleAssociation( id=self._create_ref(role_id, tenant_id, user_id), user_id=user_id, diff --git a/keystone/backends/ldap/api/tenant.py b/keystone/backends/ldap/api/tenant.py index 210e6a48..5c8d64ee 100644 --- a/keystone/backends/ldap/api/tenant.py +++ b/keystone/backends/ldap/api/tenant.py @@ -9,7 +9,8 @@ from .base import BaseLdapAPI, add_redirects class TenantAPI(BaseLdapAPI, BaseTenantAPI): DEFAULT_TREE_DN = 'ou=Groups,dc=example,dc=com' - options_name = 'tenant_tree_dn' + DEFAULT_STRUCTURAL_CLASSES = ['groupOfNames'] + options_name = 'tenant' object_class = 'keystoneTenant' model = models.Tenant attribute_mapping = {'desc': 'description', 'enabled': 'keystoneEnabled'} @@ -28,7 +29,11 @@ class TenantAPI(BaseLdapAPI, BaseTenantAPI): def is_empty(self, id): tenant = self._ldap_get(id) - empty = len(tenant[1].get('member', [])) == 0 + members = tenant[1].get('member', []) + if self.use_dumb_member: + empty = members == [self.DUMB_MEMBER_DN] + else: + empty = len(members) == 0 return empty and len(self.api.role.get_role_assignments(id)) == 0 def get_role_assignments(self, tenant_id): @@ -48,7 +53,9 @@ class TenantAPI(BaseLdapAPI, BaseTenantAPI): tenant = self._ldap_get(tenant_id) res = [] for user_dn in tenant[1].get('member', []): - res.append(self.api.user.get(ldap.dn.str2dn(user_dn)[0][0][1])) + if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: + continue + res.append(self.api.user.get(self.api.user._dn_to_id(user_dn))) return res add_redirects(locals(), SQLTenantAPI, ['get_all_endpoints']) diff --git a/keystone/backends/ldap/api/user.py b/keystone/backends/ldap/api/user.py index 2eab1194..32199c67 100644 --- a/keystone/backends/ldap/api/user.py +++ b/keystone/backends/ldap/api/user.py @@ -11,7 +11,9 @@ from .base import BaseLdapAPI, add_redirects class UserAPI(BaseLdapAPI, BaseUserAPI): DEFAULT_TREE_DN = 'ou=Users,dc=example,dc=com' - options_name = 'user_tree_dn' + DEFAULT_STRUCTURAL_CLASSES = ['keystoneUidObject'] + DEFAULT_ID_ATTR = 'uid' + options_name = 'user' object_class = 'keystoneUser' model = models.User attribute_mapping = { diff --git a/keystone/backends/ldap/keystone.ldif b/keystone/backends/ldap/keystone.ldif index cfa50625..b4a0539d 100644 --- a/keystone/backends/ldap/keystone.ldif +++ b/keystone/backends/ldap/keystone.ldif @@ -30,33 +30,39 @@ olcAttributeTypes: ( ) olcObjectClasses: ( 1.3.6.1.3.1.666.667.4.1 - NAME 'keystoneUser' + NAME 'keystoneUidObject' SUP top STRUCTURAL - MUST ( cn $ keystoneEnabled ) - MAY ( mail $ userPassword ) + MUST ( uid ) ) olcObjectClasses: ( 1.3.6.1.3.1.666.667.4.2 + NAME 'keystoneUser' + SUP top + AUXILIARY + MUST ( keystoneEnabled ) + MAY ( mail $ userPassword ) + ) +olcObjectClasses: ( + 1.3.6.1.3.1.666.667.4.3 NAME 'keystoneRole' SUP top - STRUCTURAL - MUST ( cn ) + AUXILIARY MAY ( member $ description $ serviceId ) ) olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.3 + 1.3.6.1.3.1.666.667.4.4 NAME 'keystoneTenant' SUP top - STRUCTURAL - MUST ( cn $ keystoneEnabled ) + AUXILIARY + MUST ( keystoneEnabled ) MAY ( member $ description ) ) olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.4 + 1.3.6.1.3.1.666.667.4.5 NAME 'keystoneTenantRole' SUP top - STRUCTURAL - MUST ( cn $ keystoneRole) + AUXILIARY + MUST ( keystoneRole ) MAY ( member ) ) diff --git a/keystone/backends/ldap/keystone.schema b/keystone/backends/ldap/keystone.schema index 518e0bc0..7aa7370a 100644 --- a/keystone/backends/ldap/keystone.schema +++ b/keystone/backends/ldap/keystone.schema @@ -35,36 +35,44 @@ attributetype ( objectClass ( keystoneOCs:1 - NAME 'keystoneUser' + NAME 'keystoneUidObject' SUP top STRUCTURAL - MUST ( cn $ keystoneEnabled ) - MAY ( mail $ userPassword ) + MUST ( uid ) ) objectClass ( keystoneOCs:2 + NAME 'keystoneUser' + SUP top + AUXILIARY + MUST ( keystoneEnabled ) + MAY ( mail $ userPassword ) + ) + +objectClass ( + keystoneOCs:3 NAME 'keystoneRole' SUP top - STRUCTURAL + AUXILIARY MUST ( cn ) MAY ( member $ description $ serviceId ) ) objectClass ( - keystoneOCs:3 + keystoneOCs:4 NAME 'keystoneTenant' SUP top - STRUCTURAL - MUST ( cn $ keystoneEnabled ) + AUXILIARY + MUST ( keystoneEnabled ) MAY ( member $ description ) ) objectClass ( - keystoneOCs:4 + keystoneOCs:5 NAME 'keystoneTenantRole' SUP top - STRUCTURAL - MUST ( cn $ keystoneRole) + AUXILIARY + MUST ( keystoneRole ) MAY ( member ) ) |
