diff options
| author | Jenkins <jenkins@review.openstack.org> | 2013-02-21 02:41:33 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2013-02-21 02:41:33 +0000 |
| commit | c6b978cbb80adbe33f70fa021d60a73802601f20 (patch) | |
| tree | 188e10406cb10f7f550ceac4ab36b4b3d57f2f42 | |
| parent | d036db145d51f8b134ffa36165065a8986e4f8a1 (diff) | |
| parent | 408a1d57d729461056507283c58d6c48403554b8 (diff) | |
Merge "enabled attribute emulation support"
| -rw-r--r-- | etc/keystone.conf.sample | 4 | ||||
| -rw-r--r-- | keystone/common/ldap/core.py | 114 | ||||
| -rw-r--r-- | keystone/config.py | 4 | ||||
| -rw-r--r-- | keystone/identity/backends/ldap/core.py | 5 | ||||
| -rw-r--r-- | tests/test_backend_ldap.py | 66 |
5 files changed, 191 insertions, 2 deletions
diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample index 6e810fc6..4a481576 100644 --- a/etc/keystone.conf.sample +++ b/etc/keystone.conf.sample @@ -156,6 +156,8 @@ # user_allow_create = True # user_allow_update = True # user_allow_delete = True +# user_enabled_emulation = False +# user_enabled_emulation_dn = # tenant_tree_dn = ou=Groups,dc=example,dc=com # tenant_filter = @@ -169,6 +171,8 @@ # tenant_allow_create = True # tenant_allow_update = True # tenant_allow_delete = True +# tenant_enabled_emulation = False +# tenant_enabled_emulation_dn = # role_tree_dn = ou=Roles,dc=example,dc=com # role_filter = diff --git a/keystone/common/ldap/core.py b/keystone/common/ldap/core.py index 2c1d0817..f9099df7 100644 --- a/keystone/common/ldap/core.py +++ b/keystone/common/ldap/core.py @@ -410,3 +410,117 @@ class LdapWrapper(object): def delete_ext_s(self, dn, serverctrls): LOG.debug(_("LDAP delete_ext: dn=%s, serverctrls=%s"), dn, serverctrls) return self.conn.delete_ext_s(dn, serverctrls) + + +class EnabledEmuMixIn(BaseLdap): + """Emulates boolean 'enabled' attribute if turned on. + + Creates groupOfNames holding all enabled objects of this class, all missing + objects are considered disabled. + + Options: + + * $name_enabled_emulation - boolean, on/off + * $name_enabled_emulation_dn - DN of that groupOfNames, default is + cn=enabled_$name,$tree_dn + + Where $name is self.options_name ('user' or 'tenant'), $tree_dn is + self.tree_dn. + """ + + def __init__(self, conf): + super(EnabledEmuMixIn, self).__init__(conf) + enabled_emulation = '%s_enabled_emulation' % self.options_name + self.enabled_emulation = getattr(conf.ldap, enabled_emulation) + + enabled_emulation_dn = '%s_enabled_emulation_dn' % self.options_name + self.enabled_emulation_dn = getattr(conf.ldap, enabled_emulation_dn) + if not self.enabled_emulation_dn: + self.enabled_emulation_dn = ('cn=enabled_%ss,%s' % + (self.options_name, self.tree_dn)) + + def _get_enabled(self, object_id): + conn = self.get_connection() + dn = self._id_to_dn(object_id) + query = '(member=%s)' % dn + try: + enabled_value = conn.search_s(self.enabled_emulation_dn, + ldap.SCOPE_BASE, + query) + except ldap.NO_SUCH_OBJECT: + return False + else: + return bool(enabled_value) + + def _add_enabled(self, object_id): + conn = self.get_connection() + modlist = [(ldap.MOD_ADD, + 'member', + [self._id_to_dn(object_id)])] + try: + conn.modify_s(self.enabled_emulation_dn, modlist) + except ldap.NO_SUCH_OBJECT: + attr_list = [('objectClass', ['groupOfNames']), + ('member', + [self._id_to_dn(object_id)])] + if self.use_dumb_member: + attr_list[1][1].append(self.dumb_member) + conn.add_s(self.enabled_emulation_dn, attr_list) + + def _remove_enabled(self, object_id): + conn = self.get_connection() + modlist = [(ldap.MOD_DELETE, + 'member', + [self._id_to_dn(object_id)])] + try: + conn.modify_s(self.enabled_emulation_dn, modlist) + except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE): + pass + + def create(self, values): + if self.enabled_emulation: + enabled_value = values.pop('enabled', True) + ref = super(EnabledEmuMixIn, self).create(values) + if 'enabled' not in self.attribute_ignore: + if enabled_value: + self._add_enabled(ref['id']) + ref['enabled'] = enabled_value + return ref + else: + return super(EnabledEmuMixIn, self).create(values) + + def get(self, object_id, filter=None): + ref = super(EnabledEmuMixIn, self).get(object_id, filter) + if 'enabled' not in self.attribute_ignore and self.enabled_emulation: + ref['enabled'] = self._get_enabled(object_id) + return ref + + def get_all(self, filter=None): + if 'enabled' not in self.attribute_ignore and self.enabled_emulation: + # had to copy BaseLdap.get_all here to filter by DN + tenant_list = [self._ldap_res_to_model(x) + for x in self._ldap_get_all(filter) + if x[0] != self.enabled_emulation_dn] + for tenant_ref in tenant_list: + tenant_ref['enabled'] = self._get_enabled(tenant_ref['id']) + return tenant_list + else: + return super(EnabledEmuMixIn, self).get_all(filter) + + def update(self, object_id, values, old_obj=None): + if 'enabled' not in self.attribute_ignore and self.enabled_emulation: + data = values.copy() + enabled_value = data.pop('enabled', None) + super(EnabledEmuMixIn, self).update(object_id, data, old_obj) + if enabled_value is not None: + if enabled_value: + self._add_enabled(object_id) + else: + self._remove_enabled(object_id) + else: + super(EnabledEmuMixIn, self).update(object_id, values, old_obj) + + def delete(self, object_id): + if self.enabled_emulation: + self._remove_enabled(object_id) + super(EnabledEmuMixIn, self).delete(object_id) diff --git a/keystone/config.py b/keystone/config.py index 1c18350d..2049091d 100644 --- a/keystone/config.py +++ b/keystone/config.py @@ -267,6 +267,8 @@ register_list('user_attribute_ignore', group='ldap', 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) +register_bool('user_enabled_emulation', group='ldap', default=False) +register_str('user_enabled_emulation_dn', group='ldap', default=None) register_str('tenant_tree_dn', group='ldap', default=None) register_str('tenant_filter', group='ldap', default=None) @@ -281,6 +283,8 @@ register_list('tenant_attribute_ignore', group='ldap', default='') 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) +register_bool('tenant_enabled_emulation', group='ldap', default=False) +register_str('tenant_enabled_emulation_dn', group='ldap', default=None) register_str('role_tree_dn', group='ldap', default=None) register_str('role_filter', group='ldap', default=None) diff --git a/keystone/identity/backends/ldap/core.py b/keystone/identity/backends/ldap/core.py index ec9df209..24c8a2b3 100644 --- a/keystone/identity/backends/ldap/core.py +++ b/keystone/identity/backends/ldap/core.py @@ -326,7 +326,7 @@ class ApiShimMixin(object): # TODO(termie): turn this into a data object and move logic to driver -class UserApi(common_ldap.BaseLdap, ApiShimMixin): +class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap, ApiShimMixin): DEFAULT_OU = 'ou=Users' DEFAULT_STRUCTURAL_CLASSES = ['person'] DEFAULT_ID_ATTR = 'cn' @@ -486,7 +486,8 @@ class UserApi(common_ldap.BaseLdap, ApiShimMixin): # TODO(termie): turn this into a data object and move logic to driver -class ProjectApi(common_ldap.BaseLdap, ApiShimMixin): +class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap, + ApiShimMixin): DEFAULT_OU = 'ou=Groups' DEFAULT_STRUCTURAL_CLASSES = [] DEFAULT_OBJECTCLASS = 'groupOfNames' diff --git a/tests/test_backend_ldap.py b/tests/test_backend_ldap.py index 63499bd1..d4e96884 100644 --- a/tests/test_backend_ldap.py +++ b/tests/test_backend_ldap.py @@ -480,3 +480,69 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests): def test_move_project_between_domains_with_clashing_names_fails(self): raise nose.exc.SkipTest('Blocked by bug 1101276') + + +class LDAPIdentityEnabledEmulation(LDAPIdentity): + def setUp(self): + super(LDAPIdentityEnabledEmulation, self).setUp() + self.config([test.etcdir('keystone.conf.sample'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_ldap.conf')]) + CONF.ldap.user_enabled_emulation = True + CONF.ldap.tenant_enabled_emulation = True + clear_database() + self.identity_api = identity_ldap.Identity() + self.load_fixtures(default_fixtures) + for obj in [self.tenant_bar, self.tenant_baz, self.user_foo, + self.user_two, self.user_badguy]: + obj.setdefault('enabled', True) + + def test_authenticate_no_metadata(self): + user = { + 'id': 'no_meta', + 'name': 'NO_META', + 'domain_id': test_backend.DEFAULT_DOMAIN_ID, + 'password': 'no_meta2', + 'enabled': True, + } + self.identity_api.create_user(user['id'], user) + self.identity_api.add_user_to_project(self.tenant_baz['id'], + user['id']) + user_ref, tenant_ref, metadata_ref = self.identity_api.authenticate( + user_id=user['id'], + tenant_id=self.tenant_baz['id'], + password=user['password']) + # NOTE(termie): the password field is left in user_foo to make + # it easier to authenticate in tests, but should + # not be returned by the api + user.pop('password') + self.assertEquals(metadata_ref, {"roles": + [CONF.member_role_id]}) + self.assertDictEqual(user_ref, user) + self.assertDictEqual(tenant_ref, self.tenant_baz) + + def test_user_crud(self): + user = {'domain_id': uuid.uuid4().hex, 'id': uuid.uuid4().hex, + 'name': uuid.uuid4().hex, 'password': 'passw0rd'} + self.identity_api.create_user(user['id'], user) + user['enabled'] = True + user_ref = self.identity_api.get_user(user['id']) + del user['password'] + user_ref_dict = dict((x, user_ref[x]) for x in user_ref) + self.assertDictEqual(user_ref_dict, user) + + user['password'] = uuid.uuid4().hex + self.identity_api.update_user(user['id'], user) + user_ref = self.identity_api.get_user(user['id']) + del user['password'] + user_ref_dict = dict((x, user_ref[x]) for x in user_ref) + self.assertDictEqual(user_ref_dict, user) + + self.identity_api.delete_user(user['id']) + self.assertRaises(exception.UserNotFound, + self.identity_api.get_user, + user['id']) + + def test_user_enable_attribute_mask(self): + raise nose.exc.SkipTest( + "Enabled emulation conflicts with enabled mask") |
