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
This commit is contained in:
parent
d349b349c5
commit
2f76856dff
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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'])
|
||||
|
@ -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 = {
|
||||
|
@ -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 'keystoneRole'
|
||||
NAME 'keystoneUser'
|
||||
SUP top
|
||||
STRUCTURAL
|
||||
MUST ( cn )
|
||||
MAY ( member $ description $ serviceId )
|
||||
AUXILIARY
|
||||
MUST ( keystoneEnabled )
|
||||
MAY ( mail $ userPassword )
|
||||
)
|
||||
olcObjectClasses: (
|
||||
1.3.6.1.3.1.666.667.4.3
|
||||
NAME 'keystoneTenant'
|
||||
NAME 'keystoneRole'
|
||||
SUP top
|
||||
STRUCTURAL
|
||||
MUST ( cn $ keystoneEnabled )
|
||||
MAY ( member $ description )
|
||||
AUXILIARY
|
||||
MAY ( member $ description $ serviceId )
|
||||
)
|
||||
olcObjectClasses: (
|
||||
1.3.6.1.3.1.666.667.4.4
|
||||
NAME 'keystoneTenant'
|
||||
SUP top
|
||||
AUXILIARY
|
||||
MUST ( keystoneEnabled )
|
||||
MAY ( member $ description )
|
||||
)
|
||||
olcObjectClasses: (
|
||||
1.3.6.1.3.1.666.667.4.5
|
||||
NAME 'keystoneTenantRole'
|
||||
SUP top
|
||||
STRUCTURAL
|
||||
MUST ( cn $ keystoneRole)
|
||||
AUXILIARY
|
||||
MUST ( keystoneRole )
|
||||
MAY ( member )
|
||||
)
|
||||
|
@ -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 )
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user