summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuriy Taraday <yorik.sar@gmail.com>2011-08-23 16:28:17 +0400
committerYuriy Taraday <yorik.sar@gmail.com>2011-08-23 16:48:49 +0400
commit2f76856dffeb5224fb1612bb03a74a373a1a528b (patch)
tree8c533bed4b52f0b1d21f6fe6b87dac95c2b3d293
parentd349b349c50e0683432f5ae843bd2dc83599c1b6 (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.py30
-rw-r--r--keystone/backends/ldap/api/role.py30
-rw-r--r--keystone/backends/ldap/api/tenant.py13
-rw-r--r--keystone/backends/ldap/api/user.py4
-rw-r--r--keystone/backends/ldap/keystone.ldif28
-rw-r--r--keystone/backends/ldap/keystone.schema28
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 )
)