Provide config file fields for enable users in LDAP backend (bug1067516)
DocImpact Change-Id: I1ee9a1e2505cdd8c9ee8acba5c0e89a4f25c7262
This commit is contained in:
parent
8dcafd81df
commit
001f708e7d
@ -921,15 +921,26 @@ for openstack would look like this::
|
||||
dn: ou=Roles,cn=openstack,cn=org
|
||||
objectClass: top
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
ou: roles
|
||||
|
||||
The corresponding entries in the Keystone configuration file are::
|
||||
|
||||
[ldap]
|
||||
url = ldap://localhost
|
||||
suffix = dc=openstack,dc=org
|
||||
user = dc=Manager,dc=openstack,dc=org
|
||||
password = badpassword
|
||||
suffix = dc=openstack,dc=org
|
||||
use_dumb_member = False
|
||||
allow_subtree_delete = False
|
||||
|
||||
user_tree_dn = ou=Users,dc=openstack,dc=com
|
||||
user_objectclass = inetOrgPerson
|
||||
|
||||
tenant_tree_dn = ou=Groups,dc=openstack,dc=com
|
||||
tenant_objectclass = groupOfNames
|
||||
|
||||
role_tree_dn = ou=Roles,dc=example,dc=com
|
||||
role_objectclass = organizationalRole
|
||||
|
||||
The default object classes and attributes are intentionally simplistic. They
|
||||
reflect the common standard objects according to the LDAP RFCs. However,
|
||||
@ -943,3 +954,77 @@ corresponding entries in the Keystone configuration file are::
|
||||
[ldap]
|
||||
user_id_attribute = uidNumber
|
||||
user_name_attribute = cn
|
||||
|
||||
|
||||
There is a set of allowed actions per object type that you can modify
|
||||
depending on your specific deployment. For example, the users are managed by
|
||||
another tool and you have only read access, in such case the configuration
|
||||
is::
|
||||
|
||||
[ldap]
|
||||
user_allow_create = False
|
||||
user_allow_update = False
|
||||
user_allow_delete = False
|
||||
|
||||
tenant_allow_create = True
|
||||
tenant_allow_update = True
|
||||
tenant_allow_delete = True
|
||||
|
||||
role_allow_create = True
|
||||
role_allow_update = True
|
||||
role_allow_delete = True
|
||||
|
||||
There are some configuration options for filtering users, tenants and roles,
|
||||
if the backend is providing too much output, in such case the configuration
|
||||
will look like::
|
||||
|
||||
[ldap]
|
||||
user_filter = (memberof=CN=openstack-users,OU=workgroups,DC=openstack,DC=com)
|
||||
tenant_filter =
|
||||
role_filter =
|
||||
|
||||
In case that the directory server does not have an attribute enabled of type
|
||||
boolean for the user, there is several configuration parameters that can be used
|
||||
to extract the value from an integer attribute like in Active Directory::
|
||||
|
||||
[ldap]
|
||||
user_enabled_attribute = userAccountControl
|
||||
user_enabled_mask = 2
|
||||
user_enabled_default = 512
|
||||
|
||||
In this case the attribute is an integer and the enabled attribute is listed
|
||||
in bit 1, so the if the mask configured *user_enabled_mask* is different from 0,
|
||||
it gets the value from the field *user_enabled_attribute* and it makes an ADD
|
||||
operation with the value indicated on *user_enabled_mask* and if the value matches
|
||||
the mask then the account is disabled.
|
||||
|
||||
It also saves the value without mask to the user identity in the attribute
|
||||
*enabled_nomask*. This is needed in order to set it back in case that we need to
|
||||
change it to enable/disable a user because it contains more information than the
|
||||
status like password expiration. Last setting *user_enabled_mask* is needed in order
|
||||
to create a default value on the integer attribute (512 = NORMAL ACCOUNT on AD)
|
||||
|
||||
In case of Active Directory the classes and attributes could not match the
|
||||
specified classes in the LDAP module so you can configure them like::
|
||||
|
||||
[ldap]
|
||||
user_objectclass = person
|
||||
user_id_attribute = cn
|
||||
user_name_attribute = cn
|
||||
user_mail_attribute = mail
|
||||
user_enabled_attribute = userAccountControl
|
||||
user_enabled_mask = 2
|
||||
user_enabled_default = 512
|
||||
user_attribute_ignore = tenant_id,tenants
|
||||
tenant_objectclass = groupOfNames
|
||||
tenant_id_attribute = cn
|
||||
tenant_member_attribute = member
|
||||
tenant_name_attribute = ou
|
||||
tenant_desc_attribute = description
|
||||
tenant_enabled_attribute = extensionName
|
||||
tenant_attribute_ignore =
|
||||
role_objectclass = organizationalRole
|
||||
role_id_attribute = cn
|
||||
role_name_attribute = ou
|
||||
role_member_attribute = roleOccupant
|
||||
role_attribute_ignore =
|
||||
|
@ -116,7 +116,10 @@
|
||||
# user_name_attribute = sn
|
||||
# user_mail_attribute = email
|
||||
# user_pass_attribute = userPassword
|
||||
# user_attribute_ignore = tenant_id,enabled,tenants
|
||||
# user_enabled_attribute = enabled
|
||||
# user_enabled_mask = 0
|
||||
# user_enabled_default = True
|
||||
# user_attribute_ignore = tenant_id,tenants
|
||||
# user_allow_create = True
|
||||
# user_allow_update = True
|
||||
# user_allow_delete = True
|
||||
@ -128,7 +131,8 @@
|
||||
# tenant_member_attribute = member
|
||||
# tenant_name_attribute = ou
|
||||
# tenant_desc_attribute = desc
|
||||
# tenant_attribute_ignore = enabled
|
||||
# tenant_enabled_attribute = enabled
|
||||
# tenant_attribute_ignore =
|
||||
# tenant_allow_create = True
|
||||
# tenant_allow_update = True
|
||||
# tenant_allow_delete = True
|
||||
|
@ -186,8 +186,11 @@ 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_str('user_enabled_attribute', group='ldap', default='enabled')
|
||||
register_int('user_enabled_mask', group='ldap', default=0)
|
||||
register_str('user_enabled_default', group='ldap', default='True')
|
||||
register_list('user_attribute_ignore', group='ldap',
|
||||
default='tenant_id,enable,tenants')
|
||||
default='tenant_id,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)
|
||||
@ -199,7 +202,8 @@ 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_str('tenant_enabled_attribute', group='ldap', default='enabled')
|
||||
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)
|
||||
|
@ -317,17 +317,13 @@ class UserApi(common_ldap.BaseLdap, ApiShimMixin):
|
||||
DEFAULT_STRUCTURAL_CLASSES = ['person']
|
||||
DEFAULT_ID_ATTR = 'cn'
|
||||
DEFAULT_OBJECTCLASS = 'inetOrgPerson'
|
||||
DEFAULT_ATTRIBUTE_IGNORE = ['tenant_id', 'enabled', 'tenants']
|
||||
DEFAULT_ATTRIBUTE_IGNORE = ['tenant_id', 'tenants']
|
||||
options_name = 'user'
|
||||
attribute_mapping = {'password': 'userPassword',
|
||||
'email': 'mail',
|
||||
'name': 'sn'}
|
||||
'name': 'sn',
|
||||
'enabled': 'enabled'}
|
||||
|
||||
# NOTE(ayoung): The RFC based schemas don't have a way to indicate
|
||||
# 'enabled' the closest is the nsAccount lock, which is on defined to
|
||||
# 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
|
||||
model = models.User
|
||||
|
||||
def __init__(self, conf):
|
||||
@ -335,10 +331,30 @@ class UserApi(common_ldap.BaseLdap, ApiShimMixin):
|
||||
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_mapping['enabled'] = conf.ldap.user_enabled_attribute
|
||||
self.enabled_mask = conf.ldap.user_enabled_mask
|
||||
self.enabled_default = conf.ldap.user_enabled_default
|
||||
self.attribute_ignore = (getattr(conf.ldap, 'user_attribute_ignore')
|
||||
or self.DEFAULT_ATTRIBUTE_IGNORE)
|
||||
self.api = ApiShim(conf)
|
||||
|
||||
def _ldap_res_to_model(self, res):
|
||||
obj = super(UserApi, self)._ldap_res_to_model(res)
|
||||
if self.enabled_mask != 0:
|
||||
obj['enabled_nomask'] = obj['enabled']
|
||||
obj['enabled'] = ((obj['enabled'] & self.enabled_mask) !=
|
||||
self.enabled_mask)
|
||||
return obj
|
||||
|
||||
def mask_enabled_attribute(self, values):
|
||||
value = values['enabled']
|
||||
values.setdefault('enabled_nomask', self.enabled_default)
|
||||
if value != ((values['enabled_nomask'] & self.enabled_mask) !=
|
||||
self.enabled_mask):
|
||||
values['enabled_nomask'] ^= self.enabled_mask
|
||||
values['enabled'] = values['enabled_nomask']
|
||||
del values['enabled_nomask']
|
||||
|
||||
def get(self, id, filter=None):
|
||||
"""Replaces exception.NotFound with exception.UserNotFound."""
|
||||
try:
|
||||
@ -358,6 +374,8 @@ class UserApi(common_ldap.BaseLdap, ApiShimMixin):
|
||||
def create(self, values):
|
||||
self.affirm_unique(values)
|
||||
values = utils.hash_ldap_user_password(values)
|
||||
if self.enabled_mask:
|
||||
self.mask_enabled_attribute(values)
|
||||
values = super(UserApi, self).create(values)
|
||||
tenant_id = values.get('tenant_id')
|
||||
if tenant_id is not None:
|
||||
@ -385,6 +403,9 @@ class UserApi(common_ldap.BaseLdap, ApiShimMixin):
|
||||
self.tenant_api.add_user(new_tenant, id)
|
||||
|
||||
values = utils.hash_ldap_user_password(values)
|
||||
if self.enabled_mask:
|
||||
values['enabled_nomask'] = old_obj['enabled_nomask']
|
||||
self.mask_enabled_attribute(values)
|
||||
super(UserApi, self).update(id, values, old_obj)
|
||||
|
||||
def delete(self, id):
|
||||
@ -456,9 +477,12 @@ class TenantApi(common_ldap.BaseLdap, ApiShimMixin):
|
||||
DEFAULT_OBJECTCLASS = 'groupOfNames'
|
||||
DEFAULT_ID_ATTR = 'cn'
|
||||
DEFAULT_MEMBER_ATTRIBUTE = 'member'
|
||||
DEFAULT_ATTRIBUTE_IGNORE = ['enabled']
|
||||
DEFAULT_ATTRIBUTE_IGNORE = []
|
||||
options_name = 'tenant'
|
||||
attribute_mapping = {'name': 'ou', 'description': 'desc', 'tenantId': 'cn'}
|
||||
attribute_mapping = {'name': 'ou',
|
||||
'description': 'desc',
|
||||
'tenantId': 'cn',
|
||||
'enabled': 'enabled'}
|
||||
model = models.Tenant
|
||||
|
||||
def __init__(self, conf):
|
||||
@ -466,6 +490,7 @@ class TenantApi(common_ldap.BaseLdap, ApiShimMixin):
|
||||
self.api = ApiShim(conf)
|
||||
self.attribute_mapping['name'] = conf.ldap.tenant_name_attribute
|
||||
self.attribute_mapping['description'] = conf.ldap.tenant_desc_attribute
|
||||
self.attribute_mapping['enabled'] = conf.ldap.tenant_enabled_attribute
|
||||
self.member_attribute = (getattr(conf.ldap, 'tenant_member_attribute')
|
||||
or self.DEFAULT_MEMBER_ATTRIBUTE)
|
||||
self.attribute_ignore = (getattr(conf.ldap, 'tenant_attribute_ignore')
|
||||
|
@ -659,6 +659,38 @@ class IdentityTests(object):
|
||||
self.identity_api.create_user('user_id', new_user)
|
||||
self.assertDictEqual(original_user, new_user)
|
||||
|
||||
def test_update_user_enable(self):
|
||||
user = {'id': 'fake1', 'name': 'fake1', 'enabled': 'True'}
|
||||
self.identity_api.create_user('fake1', user)
|
||||
user_ref = self.identity_api.get_user('fake1')
|
||||
self.assertEqual(user_ref['enabled'], 'True')
|
||||
|
||||
user['enabled'] = 'False'
|
||||
self.identity_api.update_user('fake1', user)
|
||||
user_ref = self.identity_api.get_user('fake1')
|
||||
self.assertEqual(user_ref['enabled'], user['enabled'])
|
||||
|
||||
user['enabled'] = 'True'
|
||||
self.identity_api.update_user('fake1', user)
|
||||
user_ref = self.identity_api.get_user('fake1')
|
||||
self.assertEqual(user_ref['enabled'], user['enabled'])
|
||||
|
||||
def test_update_tenant_enable(self):
|
||||
tenant = {'id': 'fake1', 'name': 'fake1', 'enabled': 'True'}
|
||||
self.identity_api.create_tenant('fake1', tenant)
|
||||
tenant_ref = self.identity_api.get_tenant('fake1')
|
||||
self.assertEqual(tenant_ref['enabled'], 'True')
|
||||
|
||||
tenant['enabled'] = 'False'
|
||||
self.identity_api.update_tenant('fake1', tenant)
|
||||
tenant_ref = self.identity_api.get_tenant('fake1')
|
||||
self.assertEqual(tenant_ref['enabled'], tenant['enabled'])
|
||||
|
||||
tenant['enabled'] = 'True'
|
||||
self.identity_api.update_tenant('fake1', tenant)
|
||||
tenant_ref = self.identity_api.get_tenant('fake1')
|
||||
self.assertEqual(tenant_ref['enabled'], tenant['enabled'])
|
||||
|
||||
|
||||
class TokenTests(object):
|
||||
def test_token_crud(self):
|
||||
|
@ -122,7 +122,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
|
||||
test.testsdir('backend_ldap.conf')])
|
||||
self.identity_api = identity_ldap.Identity()
|
||||
|
||||
tenant = {'id': 'fake1', 'name': 'fake1'}
|
||||
tenant = {'id': 'fake1', 'name': 'fake1', 'enabled': True}
|
||||
self.identity_api.create_tenant('fake1', tenant)
|
||||
tenant_ref = self.identity_api.get_tenant('fake1')
|
||||
self.assertEqual(tenant_ref['id'], 'fake1')
|
||||
@ -262,6 +262,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
|
||||
test.testsdir('backend_ldap.conf')])
|
||||
CONF.ldap.user_name_attribute = 'sn'
|
||||
CONF.ldap.user_mail_attribute = 'email'
|
||||
CONF.ldap.user_enabled_attribute = 'enabled'
|
||||
clear_database()
|
||||
self.identity_api = identity_ldap.Identity()
|
||||
self.load_fixtures(default_fixtures)
|
||||
@ -269,6 +270,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
|
||||
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'])
|
||||
self.assertEqual(user_ref['enabled'], self.user_attr['enabled'])
|
||||
|
||||
CONF.ldap.user_name_attribute = 'email'
|
||||
CONF.ldap.user_mail_attribute = 'sn'
|
||||
@ -277,6 +279,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
|
||||
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'])
|
||||
self.assertEqual(user_ref['enabled'], self.user_attr['enabled'])
|
||||
|
||||
def test_user_attribute_ignore(self):
|
||||
self.config([test.etcdir('keystone.conf.sample'),
|
||||
@ -302,6 +305,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
|
||||
test.testsdir('backend_ldap.conf')])
|
||||
CONF.ldap.tenant_name_attribute = 'ou'
|
||||
CONF.ldap.tenant_desc_attribute = 'desc'
|
||||
CONF.ldap.tenant_enabled_attribute = 'enabled'
|
||||
clear_database()
|
||||
self.identity_api = identity_ldap.Identity()
|
||||
self.load_fixtures(default_fixtures)
|
||||
@ -310,6 +314,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
|
||||
self.assertEqual(tenant_ref['name'], self.tenant_attr['name'])
|
||||
self.assertEqual(tenant_ref['description'],
|
||||
self.tenant_attr['description'])
|
||||
self.assertEqual(tenant_ref['enabled'], self.tenant_attr['enabled'])
|
||||
|
||||
CONF.ldap.tenant_name_attribute = 'desc'
|
||||
CONF.ldap.tenant_desc_attribute = 'ou'
|
||||
@ -318,6 +323,7 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
|
||||
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'])
|
||||
self.assertEqual(tenant_ref['enabled'], self.tenant_attr['enabled'])
|
||||
|
||||
def test_tenant_attribute_ignore(self):
|
||||
self.config([test.etcdir('keystone.conf.sample'),
|
||||
@ -364,3 +370,27 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
|
||||
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_user_enable_attribute_mask(self):
|
||||
self.config([test.etcdir('keystone.conf.sample'),
|
||||
test.testsdir('test_overrides.conf'),
|
||||
test.testsdir('backend_ldap.conf')])
|
||||
CONF.ldap.user_enabled_attribute = 'enabled'
|
||||
CONF.ldap.user_enabled_mask = 2
|
||||
CONF.ldap.user_enabled_default = 512
|
||||
clear_database()
|
||||
self.identity_api = identity_ldap.Identity()
|
||||
user = {'id': 'fake1', 'name': 'fake1', 'enabled': True}
|
||||
self.identity_api.create_user('fake1', user)
|
||||
user_ref = self.identity_api.get_user('fake1')
|
||||
self.assertEqual(user_ref['enabled'], True)
|
||||
|
||||
user['enabled'] = False
|
||||
self.identity_api.update_user('fake1', user)
|
||||
user_ref = self.identity_api.get_user('fake1')
|
||||
self.assertEqual(user_ref['enabled'], False)
|
||||
|
||||
user['enabled'] = True
|
||||
self.identity_api.update_user('fake1', user)
|
||||
user_ref = self.identity_api.get_user('fake1')
|
||||
self.assertEqual(user_ref['enabled'], True)
|
||||
|
Loading…
x
Reference in New Issue
Block a user