diff options
| -rw-r--r-- | etc/keystone.conf.sample | 8 | ||||
| -rw-r--r-- | keystone/common/ldap/core.py | 7 | ||||
| -rw-r--r-- | keystone/config.py | 24 | ||||
| -rw-r--r-- | keystone/identity/backends/ldap/core.py | 29 | ||||
| -rw-r--r-- | tests/default_fixtures.py | 12 | ||||
| -rw-r--r-- | tests/test_backend_ldap.py | 122 |
6 files changed, 190 insertions, 12 deletions
diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample index c81d3b6c..2eabba41 100644 --- a/etc/keystone.conf.sample +++ b/etc/keystone.conf.sample @@ -108,12 +108,16 @@ # suffix = cn=example,cn=com # use_dumb_member = False # allow_subtree_delete = False +# dumb_member = cn=dumb,dc=example,dc=com # user_tree_dn = ou=Users,dc=example,dc=com # user_filter = # user_objectclass = inetOrgPerson # user_id_attribute = cn # user_name_attribute = sn +# user_mail_attribute = email +# user_pass_attribute = userPassword +# user_attribute_ignore = tenant_id,enabled,tenants # user_allow_create = True # user_allow_update = True # user_allow_delete = True @@ -124,6 +128,8 @@ # tenant_id_attribute = cn # tenant_member_attribute = member # tenant_name_attribute = ou +# tenant_desc_attribute = desc +# tenant_attribute_ignore = enabled # tenant_allow_create = True # tenant_allow_update = True # tenant_allow_delete = True @@ -132,7 +138,9 @@ # role_filter = # role_objectclass = organizationalRole # role_id_attribute = cn +# role_name_attribute = ou # role_member_attribute = roleOccupant +# role_attribute_ignore = # role_allow_create = True # role_allow_update = True # role_allow_delete = True diff --git a/keystone/common/ldap/core.py b/keystone/common/ldap/core.py index 4a80d66a..53e20468 100644 --- a/keystone/common/ldap/core.py +++ b/keystone/common/ldap/core.py @@ -107,7 +107,10 @@ class BaseLdap(object): self.allow_delete = getattr(conf.ldap, allow_delete) self.structural_classes = self.DEFAULT_STRUCTURAL_CLASSES - self.use_dumb_member = getattr(conf.ldap, 'use_dumb_member') or True + self.use_dumb_member = getattr(conf.ldap, 'use_dumb_member') + self.dumb_member = (getattr(conf.ldap, 'dumb_member') or + self.DUMB_MEMBER_DN) + self.subtree_delete_enabled = getattr(conf.ldap, 'allow_subtree_delete') @@ -194,7 +197,7 @@ class BaseLdap(object): attrs.append((attr_type, [v])) if 'groupOfNames' in object_classes and self.use_dumb_member: - attrs.append(('member', [self.DUMB_MEMBER_DN])) + attrs.append(('member', [self.dumb_member])) conn.add_s(self._id_to_dn(values['id']), attrs) return values diff --git a/keystone/config.py b/keystone/config.py index dc6c41d2..86a425ec 100644 --- a/keystone/config.py +++ b/keystone/config.py @@ -87,6 +87,18 @@ def register_cli_str(*args, **kw): return conf.register_cli_opt(cfg.StrOpt(*args, **kw), group=group) +def register_list(*args, **kw): + conf = kw.pop('conf', CONF) + group = kw.pop('group', None) + return conf.register_opt(cfg.ListOpt(*args, **kw), group=group) + + +def register_cli_list(*args, **kw): + conf = kw.pop('conf', CONF) + group = kw.pop('group', None) + return conf.register_cli_opt(cfg.ListOpt(*args, **kw), group=group) + + def register_bool(*args, **kw): conf = kw.pop('conf', CONF) group = kw.pop('group', None) @@ -157,19 +169,25 @@ register_str('driver', group='ec2', register_str('driver', group='stats', default='keystone.contrib.stats.backends.kvs.Stats') + #ldap register_str('url', group='ldap', default='ldap://localhost') register_str('user', group='ldap', default='dc=Manager,dc=example,dc=com') register_str('password', group='ldap', default='freeipa4all') register_str('suffix', group='ldap', default='cn=example,cn=com') register_bool('use_dumb_member', group='ldap', default=False) -register_str('user_name_attribute', group='ldap', default='sn') +register_str('dumb_member', group='ldap', default='cn=dumb,dc=nonexistent') register_bool('allow_subtree_delete', group='ldap', default=False) register_str('user_tree_dn', group='ldap', default=None) register_str('user_filter', group='ldap', default=None) register_str('user_objectclass', group='ldap', default='inetOrgPerson') register_str('user_id_attribute', group='ldap', default='cn') +register_str('user_name_attribute', group='ldap', default='sn') +register_str('user_mail_attribute', group='ldap', default='email') +register_str('user_pass_attribute', group='ldap', default='userPassword') +register_list('user_attribute_ignore', group='ldap', + default='tenant_id,enable,tenants') register_bool('user_allow_create', group='ldap', default=True) register_bool('user_allow_update', group='ldap', default=True) register_bool('user_allow_delete', group='ldap', default=True) @@ -180,6 +198,8 @@ register_str('tenant_objectclass', group='ldap', default='groupOfNames') register_str('tenant_id_attribute', group='ldap', default='cn') register_str('tenant_member_attribute', group='ldap', default='member') register_str('tenant_name_attribute', group='ldap', default='ou') +register_str('tenant_desc_attribute', group='ldap', default='desc') +register_list('tenant_attribute_ignore', group='ldap', default='enabled') register_bool('tenant_allow_create', group='ldap', default=True) register_bool('tenant_allow_update', group='ldap', default=True) register_bool('tenant_allow_delete', group='ldap', default=True) @@ -188,7 +208,9 @@ register_str('role_tree_dn', group='ldap', default=None) register_str('role_filter', group='ldap', default=None) register_str('role_objectclass', group='ldap', default='organizationalRole') register_str('role_id_attribute', group='ldap', default='cn') +register_str('role_name_attribute', group='ldap', default='ou') register_str('role_member_attribute', group='ldap', default='roleOccupant') +register_list('role_attribute_ignore', group='ldap', default='') register_bool('role_allow_create', group='ldap', default=True) register_bool('role_allow_update', group='ldap', default=True) register_bool('role_allow_delete', group='ldap', default=True) diff --git a/keystone/identity/backends/ldap/core.py b/keystone/identity/backends/ldap/core.py index 540679c0..ebccde02 100644 --- a/keystone/identity/backends/ldap/core.py +++ b/keystone/identity/backends/ldap/core.py @@ -331,6 +331,7 @@ class UserApi(common_ldap.BaseLdap, ApiShimMixin): DEFAULT_STRUCTURAL_CLASSES = ['person'] DEFAULT_ID_ATTR = 'cn' DEFAULT_OBJECTCLASS = 'inetOrgPerson' + DEFAULT_ATTRIBUTE_IGNORE = ['tenant_id', 'enabled', 'tenants'] options_name = 'user' attribute_mapping = {'password': 'userPassword', 'email': 'mail', @@ -341,12 +342,15 @@ class UserApi(common_ldap.BaseLdap, ApiShimMixin): # be part of any objectclass. # in the future, we need to provide a way for the end user to # indicate the field to use and what it indicates - attribute_ignore = ['tenantId', 'enabled', 'tenants'] model = models.User def __init__(self, conf): super(UserApi, self).__init__(conf) self.attribute_mapping['name'] = conf.ldap.user_name_attribute + self.attribute_mapping['email'] = conf.ldap.user_mail_attribute + self.attribute_mapping['password'] = conf.ldap.user_pass_attribute + self.attribute_ignore = (getattr(conf.ldap, 'user_attribute_ignore') + or self.DEFAULT_ATTRIBUTE_IGNORE) self.api = ApiShim(conf) def get(self, id, filter=None): @@ -466,17 +470,20 @@ class TenantApi(common_ldap.BaseLdap, ApiShimMixin): DEFAULT_OBJECTCLASS = 'groupOfNames' DEFAULT_ID_ATTR = 'cn' DEFAULT_MEMBER_ATTRIBUTE = 'member' + DEFAULT_ATTRIBUTE_IGNORE = ['enabled'] options_name = 'tenant' - attribute_mapping = {'name': 'ou', 'tenantId': 'cn'} - attribute_ignore = ['enabled'] + attribute_mapping = {'name': 'ou', 'description': 'desc', 'tenantId': 'cn'} model = models.Tenant def __init__(self, conf): super(TenantApi, self).__init__(conf) self.api = ApiShim(conf) self.attribute_mapping['name'] = conf.ldap.tenant_name_attribute + self.attribute_mapping['description'] = conf.ldap.tenant_desc_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 get(self, id, filter=None): """Replaces exception.NotFound with exception.TenantNotFound.""" @@ -523,7 +530,7 @@ class TenantApi(common_ldap.BaseLdap, ApiShimMixin): tenant = self._ldap_get(id) members = tenant[1].get(self.member_attribute, []) if self.use_dumb_member: - empty = members == [self.DUMB_MEMBER_DN] + empty = members == [self.dumb_member] else: empty = len(members) == 0 return empty and len(self.role_api.get_role_assignments(id)) == 0 @@ -561,7 +568,7 @@ class TenantApi(common_ldap.BaseLdap, ApiShimMixin): if not role_id: # 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_DN: + if self.use_dumb_member and user_dn == self.dumb_member: continue res.add(self.user_api.get(self.user_api._dn_to_id(user_dn))) @@ -607,6 +614,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin): options_name = 'role' DEFAULT_OBJECTCLASS = 'organizationalRole' DEFAULT_MEMBER_ATTRIBUTE = 'roleOccupant' + DEFAULT_ATTRIBUTE_IGNORE = [] attribute_mapping = {'name': 'cn', #'serviceId': 'service_id', } @@ -615,8 +623,11 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin): def __init__(self, conf): super(RoleApi, self).__init__(conf) self.api = ApiShim(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) @staticmethod def _create_ref(role_id, tenant_id, user_id): @@ -688,7 +699,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin): (self.member_attribute, [user_dn])] if self.use_dumb_member: - attrs[1][1].append(self.DUMB_MEMBER_DN) + attrs[1][1].append(self.dumb_member) try: conn.add_s(role_dn, attrs) except Exception as inst: @@ -714,7 +725,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin): (self.member_attribute, [user_dn])] if self.use_dumb_member: - attrs[1][1].append(self.DUMB_MEMBER_DN) + attrs[1][1].append(self.dumb_member) try: conn.add_s(role_dn, attrs) except Exception as inst: @@ -751,7 +762,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin): except KeyError: continue for user_dn in user_dns: - if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: + if self.use_dumb_member and user_dn == self.dumb_member: continue user_id = self.user_api._dn_to_id(user_dn) role_id = self._dn_to_id(role_dn) @@ -884,7 +895,7 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin): continue for user_dn in user_dns: - if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: + if self.use_dumb_member and user_dn == self.dumb_member: continue user_id = self.user_api._dn_to_id(user_dn) tenant_id = None diff --git a/tests/default_fixtures.py b/tests/default_fixtures.py index cb0d0e2e..8b5020ff 100644 --- a/tests/default_fixtures.py +++ b/tests/default_fixtures.py @@ -18,6 +18,10 @@ TENANTS = [ {'id': 'bar', 'name': 'BAR'}, {'id': 'baz', 'name': 'BAZ'}, {'id': 'tenent4add', 'name': 'tenant4add'}, + {'id': 'attr', + 'name': 'attr_name', + 'description': 'description', + 'enabled': True}, ] # NOTE(ja): a role of keystone_admin and attribute "is_admin" is done in setUp @@ -28,6 +32,13 @@ USERS = [ 'name': 'NO_META', 'password': 'no_meta2', 'tenants': ['baz']}, + {'id': 'attr', + 'name': 'attr', + 'email': 'attr@example.com', + 'enabled': True, + 'tenant_id': 'baz', + 'password': 'attr_passwd', + 'tenants': ['baz']}, ] METADATA = [ @@ -38,6 +49,7 @@ METADATA = [ ROLES = [ {'id': 'keystone_admin', 'name': 'Keystone Admin'}, {'id': 'useless', 'name': 'Useless'}, + {'id': 'attr', 'name': 'attr'}, ] SERVICES = [ diff --git a/tests/test_backend_ldap.py b/tests/test_backend_ldap.py index 1b3d4b90..cbb94e44 100644 --- a/tests/test_backend_ldap.py +++ b/tests/test_backend_ldap.py @@ -242,3 +242,125 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests): self.assertRaises(exception.RoleNotFound, self.identity_api.get_role, self.role_useless['id']) + + def test_dumb_member(self): + self.config([test.etcdir('keystone.conf.sample'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_ldap.conf')]) + CONF.ldap.use_dumb_member = True + CONF.ldap.dumb_member = 'cn=dumb,cn=example,cn=com' + clear_database() + self.identity_api = identity_ldap.Identity() + self.load_fixtures(default_fixtures) + self.assertRaises(exception.UserNotFound, + self.identity_api.get_user, + 'dumb') + + def test_user_attribute_mapping(self): + self.config([test.etcdir('keystone.conf.sample'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_ldap.conf')]) + CONF.ldap.user_name_attribute = 'sn' + CONF.ldap.user_mail_attribute = 'email' + clear_database() + self.identity_api = identity_ldap.Identity() + self.load_fixtures(default_fixtures) + user_ref = self.identity_api.get_user(self.user_attr['id']) + self.assertEqual(user_ref['id'], self.user_attr['id']) + self.assertEqual(user_ref['name'], self.user_attr['name']) + self.assertEqual(user_ref['email'], self.user_attr['email']) + + CONF.ldap.user_name_attribute = 'email' + CONF.ldap.user_mail_attribute = 'sn' + self.identity_api = identity_ldap.Identity() + user_ref = self.identity_api.get_user(self.user_attr['id']) + self.assertEqual(user_ref['id'], self.user_attr['id']) + self.assertEqual(user_ref['name'], self.user_attr['email']) + self.assertEqual(user_ref['email'], self.user_attr['name']) + + def test_user_attribute_ignore(self): + self.config([test.etcdir('keystone.conf.sample'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_ldap.conf')]) + CONF.ldap.user_attribute_ignore = ['name', 'email', 'password', + 'tenant_id', 'enabled', 'tenants'] + clear_database() + self.identity_api = identity_ldap.Identity() + self.load_fixtures(default_fixtures) + user_ref = self.identity_api.get_user(self.user_attr['id']) + self.assertEqual(user_ref['id'], self.user_attr['id']) + self.assertNotIn('name', user_ref) + self.assertNotIn('email', user_ref) + self.assertNotIn('password', user_ref) + self.assertNotIn('tenant_id', user_ref) + self.assertNotIn('enabled', user_ref) + self.assertNotIn('tenants', user_ref) + + def test_tenant_attribute_mapping(self): + self.config([test.etcdir('keystone.conf.sample'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_ldap.conf')]) + CONF.ldap.tenant_name_attribute = 'ou' + CONF.ldap.tenant_desc_attribute = 'desc' + clear_database() + self.identity_api = identity_ldap.Identity() + self.load_fixtures(default_fixtures) + tenant_ref = self.identity_api.get_tenant(self.tenant_attr['id']) + self.assertEqual(tenant_ref['id'], self.tenant_attr['id']) + self.assertEqual(tenant_ref['name'], self.tenant_attr['name']) + self.assertEqual(tenant_ref['description'], + self.tenant_attr['description']) + + CONF.ldap.tenant_name_attribute = 'desc' + CONF.ldap.tenant_desc_attribute = 'ou' + self.identity_api = identity_ldap.Identity() + tenant_ref = self.identity_api.get_tenant(self.tenant_attr['id']) + self.assertEqual(tenant_ref['id'], self.tenant_attr['id']) + self.assertEqual(tenant_ref['name'], self.tenant_attr['description']) + self.assertEqual(tenant_ref['description'], self.tenant_attr['name']) + + def test_tenant_attribute_ignore(self): + self.config([test.etcdir('keystone.conf.sample'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_ldap.conf')]) + CONF.ldap.tenant_attribute_ignore = ['name', + 'description', + 'enabled'] + clear_database() + self.identity_api = identity_ldap.Identity() + self.load_fixtures(default_fixtures) + tenant_ref = self.identity_api.get_tenant(self.tenant_attr['id']) + self.assertEqual(tenant_ref['id'], self.tenant_attr['id']) + self.assertNotIn('name', tenant_ref) + self.assertNotIn('description', tenant_ref) + self.assertNotIn('enabled', tenant_ref) + + def test_role_attribute_mapping(self): + self.config([test.etcdir('keystone.conf.sample'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_ldap.conf')]) + CONF.ldap.role_name_attribute = 'ou' + clear_database() + self.identity_api = identity_ldap.Identity() + self.load_fixtures(default_fixtures) + role_ref = self.identity_api.get_role(self.role_attr['id']) + self.assertEqual(role_ref['id'], self.role_attr['id']) + self.assertEqual(role_ref['name'], self.role_attr['name']) + + CONF.ldap.role_name_attribute = 'sn' + self.identity_api = identity_ldap.Identity() + role_ref = self.identity_api.get_role(self.role_attr['id']) + self.assertEqual(role_ref['id'], self.role_attr['id']) + self.assertNotIn('name', role_ref) + + def test_role_attribute_ignore(self): + self.config([test.etcdir('keystone.conf.sample'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_ldap.conf')]) + CONF.ldap.role_attribute_ignore = ['name'] + clear_database() + self.identity_api = identity_ldap.Identity() + self.load_fixtures(default_fixtures) + role_ref = self.identity_api.get_role(self.role_attr['id']) + self.assertEqual(role_ref['id'], self.role_attr['id']) + self.assertNotIn('name', role_ref) |
