summaryrefslogtreecommitdiffstats
path: root/keystone
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-08-16 17:34:56 +0000
committerGerrit Code Review <review@openstack.org>2013-08-16 17:34:56 +0000
commit81534a182a4986d838591395aee8590ef61c599d (patch)
tree94570b8b09e12a079085f85e4381a60175a93235 /keystone
parentd695d4a3cd747ba47e2dfa1ca7e688175cbd06be (diff)
parent1ed2046eaa91fa36926d66a5fe1e88ccd65373bb (diff)
downloadkeystone-81534a182a4986d838591395aee8590ef61c599d.tar.gz
keystone-81534a182a4986d838591395aee8590ef61c599d.tar.xz
keystone-81534a182a4986d838591395aee8590ef61c599d.zip
Merge "Implement domain specific Identity backends"
Diffstat (limited to 'keystone')
-rw-r--r--keystone/auth/plugins/password.py4
-rw-r--r--keystone/catalog/backends/templated.py3
-rw-r--r--keystone/common/config.py530
-rw-r--r--keystone/common/controller.py51
-rw-r--r--keystone/common/ldap/fakeldap.py16
-rw-r--r--keystone/common/utils.py1
-rw-r--r--keystone/config.py9
-rw-r--r--keystone/identity/backends/kvs.py3
-rw-r--r--keystone/identity/backends/ldap.py52
-rw-r--r--keystone/identity/backends/pam.py3
-rw-r--r--keystone/identity/backends/sql.py3
-rw-r--r--keystone/identity/controllers.py52
-rw-r--r--keystone/identity/core.py335
-rw-r--r--keystone/tests/backend_multi_ldap_sql.conf35
-rw-r--r--keystone/tests/core.py8
-rw-r--r--keystone/tests/keystone.Default.conf14
-rw-r--r--keystone/tests/keystone.domain1.conf11
-rw-r--r--keystone/tests/keystone.domain2.conf13
-rw-r--r--keystone/tests/test_backend.py7
-rw-r--r--keystone/tests/test_backend_ldap.py281
-rw-r--r--keystone/token/backends/memcache.py2
-rw-r--r--keystone/token/core.py2
22 files changed, 1023 insertions, 412 deletions
diff --git a/keystone/auth/plugins/password.py b/keystone/auth/plugins/password.py
index 66c6d05b..b069f4d9 100644
--- a/keystone/auth/plugins/password.py
+++ b/keystone/auth/plugins/password.py
@@ -94,6 +94,7 @@ class UserAuthInfo(object):
self._assert_user_is_enabled(user_ref)
self.user_ref = user_ref
self.user_id = user_ref['id']
+ self.domain_id = domain_ref['id']
class Password(auth.AuthMethodHandler):
@@ -106,7 +107,8 @@ class Password(auth.AuthMethodHandler):
try:
self.identity_api.authenticate(
user_id=user_info.user_id,
- password=user_info.password)
+ password=user_info.password,
+ domain_scope=user_info.domain_id)
except AssertionError:
# authentication failed because of invalid username or password
msg = _('Invalid username or password')
diff --git a/keystone/catalog/backends/templated.py b/keystone/catalog/backends/templated.py
index 7fe73e91..db99110b 100644
--- a/keystone/catalog/backends/templated.py
+++ b/keystone/catalog/backends/templated.py
@@ -25,9 +25,6 @@ from keystone.openstack.common import log as logging
LOG = logging.getLogger(__name__)
CONF = config.CONF
-config.register_str('template_file',
- default='default_catalog.templates',
- group='catalog')
def parse_templates(template_lines):
diff --git a/keystone/common/config.py b/keystone/common/config.py
index 5a961d4a..61eeac92 100644
--- a/keystone/common/config.py
+++ b/keystone/common/config.py
@@ -24,6 +24,218 @@ _DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
_DEFAULT_AUTH_METHODS = ['external', 'password', 'token']
+FILE_OPTIONS = {
+ '': [
+ cfg.StrOpt('admin_token', secret=True, default='ADMIN'),
+ cfg.StrOpt('bind_host', default='0.0.0.0'),
+ cfg.IntOpt('compute_port', default=8774),
+ cfg.IntOpt('admin_port', default=35357),
+ cfg.IntOpt('public_port', default=5000),
+ cfg.StrOpt('public_endpoint',
+ default='http://localhost:%(public_port)s/'),
+ cfg.StrOpt('admin_endpoint',
+ default='http://localhost:%(admin_port)s/'),
+ cfg.StrOpt('onready'),
+ cfg.StrOpt('auth_admin_prefix', default=''),
+ cfg.StrOpt('policy_file', default='policy.json'),
+ cfg.StrOpt('policy_default_rule', default=None),
+ # default max request size is 112k
+ cfg.IntOpt('max_request_body_size', default=114688),
+ cfg.IntOpt('max_param_size', default=64),
+ # we allow tokens to be a bit larger to accommodate PKI
+ cfg.IntOpt('max_token_size', default=8192),
+ cfg.StrOpt('member_role_id',
+ default='9fe2ff9ee4384b1894a90878d3e92bab'),
+ cfg.StrOpt('member_role_name', default='_member_'),
+ cfg.IntOpt('crypt_strength', default=40000)],
+ 'identity': [
+ cfg.StrOpt('default_domain_id', default='default'),
+ cfg.BoolOpt('domain_specific_drivers_enabled',
+ default=False),
+ cfg.StrOpt('domain_config_dir',
+ default='/etc/keystone/domains'),
+ cfg.StrOpt('driver',
+ default=('keystone.identity.backends'
+ '.sql.Identity')),
+ cfg.IntOpt('max_password_length', default=4096)],
+ 'trust': [
+ cfg.BoolOpt('enabled', default=True),
+ cfg.StrOpt('driver',
+ default='keystone.trust.backends.sql.Trust')],
+ 'os_inherit': [
+ cfg.BoolOpt('enabled', default=False)],
+ 'token': [
+ cfg.ListOpt('bind', default=[]),
+ cfg.StrOpt('enforce_token_bind', default='permissive'),
+ cfg.IntOpt('expiration', default=86400),
+ cfg.StrOpt('provider', default=None),
+ cfg.StrOpt('driver',
+ default='keystone.token.backends.sql.Token')],
+ 'ssl': [
+ cfg.BoolOpt('enable', default=False),
+ cfg.StrOpt('certfile',
+ default="/etc/keystone/ssl/certs/keystone.pem"),
+ cfg.StrOpt('keyfile',
+ default="/etc/keystone/ssl/private/keystonekey.pem"),
+ cfg.StrOpt('ca_certs',
+ default="/etc/keystone/ssl/certs/ca.pem"),
+ cfg.StrOpt('ca_key',
+ default="/etc/keystone/ssl/certs/cakey.pem"),
+ cfg.BoolOpt('cert_required', default=False),
+ cfg.IntOpt('key_size', default=1024),
+ cfg.IntOpt('valid_days', default=3650),
+ cfg.StrOpt('ca_password', default=None),
+ cfg.StrOpt('cert_subject',
+ default='/C=US/ST=Unset/L=Unset/O=Unset/CN=localhost')],
+ 'signing': [
+ cfg.StrOpt('token_format', default=None),
+ cfg.StrOpt('certfile',
+ default="/etc/keystone/ssl/certs/signing_cert.pem"),
+ cfg.StrOpt('keyfile',
+ default="/etc/keystone/ssl/private/signing_key.pem"),
+ cfg.StrOpt('ca_certs',
+ default="/etc/keystone/ssl/certs/ca.pem"),
+ cfg.StrOpt('ca_key',
+ default="/etc/keystone/ssl/certs/cakey.pem"),
+ cfg.IntOpt('key_size', default=2048),
+ cfg.IntOpt('valid_days', default=3650),
+ cfg.StrOpt('ca_password', default=None),
+ cfg.StrOpt('cert_subject',
+ default=('/C=US/ST=Unset/L=Unset/O=Unset/'
+ 'CN=www.example.com'))],
+ 'sql': [
+ cfg.StrOpt('connection', secret=True,
+ default='sqlite:///keystone.db'),
+ cfg.IntOpt('idle_timeout', default=200)],
+ 'assignment': [
+ # assignment has no default for backward compatibility reasons.
+ # If assignment driver is not specified, the identity driver chooses
+ # the backend
+ cfg.StrOpt('driver', default=None)],
+ 'credential': [
+ cfg.StrOpt('driver',
+ default=('keystone.credential.backends'
+ '.sql.Credential'))],
+ 'policy': [
+ cfg.StrOpt('driver',
+ default='keystone.policy.backends.sql.Policy')],
+ 'ec2': [
+ cfg.StrOpt('driver',
+ default='keystone.contrib.ec2.backends.kvs.Ec2')],
+ 'stats': [
+ cfg.StrOpt('driver',
+ default=('keystone.contrib.stats.backends'
+ '.kvs.Stats'))],
+ 'ldap': [
+ cfg.StrOpt('url', default='ldap://localhost'),
+ cfg.StrOpt('user', default=None),
+ cfg.StrOpt('password', secret=True, default=None),
+ cfg.StrOpt('suffix', default='cn=example,cn=com'),
+ cfg.BoolOpt('use_dumb_member', default=False),
+ cfg.StrOpt('dumb_member', default='cn=dumb,dc=nonexistent'),
+ cfg.BoolOpt('allow_subtree_delete', default=False),
+ cfg.StrOpt('query_scope', default='one'),
+ cfg.IntOpt('page_size', default=0),
+ cfg.StrOpt('alias_dereferencing', default='default'),
+
+ cfg.StrOpt('user_tree_dn', default=None),
+ cfg.StrOpt('user_filter', default=None),
+ cfg.StrOpt('user_objectclass', default='inetOrgPerson'),
+ cfg.StrOpt('user_id_attribute', default='cn'),
+ cfg.StrOpt('user_name_attribute', default='sn'),
+ cfg.StrOpt('user_mail_attribute', default='email'),
+ cfg.StrOpt('user_pass_attribute', default='userPassword'),
+ cfg.StrOpt('user_enabled_attribute', default='enabled'),
+ cfg.StrOpt('user_domain_id_attribute',
+ default='businessCategory'),
+ cfg.IntOpt('user_enabled_mask', default=0),
+ cfg.StrOpt('user_enabled_default', default='True'),
+ cfg.ListOpt('user_attribute_ignore',
+ default='tenant_id,tenants'),
+ cfg.BoolOpt('user_allow_create', default=True),
+ cfg.BoolOpt('user_allow_update', default=True),
+ cfg.BoolOpt('user_allow_delete', default=True),
+ cfg.BoolOpt('user_enabled_emulation', default=False),
+ cfg.StrOpt('user_enabled_emulation_dn', default=None),
+ cfg.ListOpt('user_additional_attribute_mapping',
+ default=None),
+
+ cfg.StrOpt('tenant_tree_dn', default=None),
+ cfg.StrOpt('tenant_filter', default=None),
+ cfg.StrOpt('tenant_objectclass', default='groupOfNames'),
+ cfg.StrOpt('tenant_id_attribute', default='cn'),
+ cfg.StrOpt('tenant_member_attribute', default='member'),
+ cfg.StrOpt('tenant_name_attribute', default='ou'),
+ cfg.StrOpt('tenant_desc_attribute', default='description'),
+ cfg.StrOpt('tenant_enabled_attribute', default='enabled'),
+ cfg.StrOpt('tenant_domain_id_attribute',
+ default='businessCategory'),
+ cfg.ListOpt('tenant_attribute_ignore', default=''),
+ cfg.BoolOpt('tenant_allow_create', default=True),
+ cfg.BoolOpt('tenant_allow_update', default=True),
+ cfg.BoolOpt('tenant_allow_delete', default=True),
+ cfg.BoolOpt('tenant_enabled_emulation', default=False),
+ cfg.StrOpt('tenant_enabled_emulation_dn', default=None),
+ cfg.ListOpt('tenant_additional_attribute_mapping',
+ default=None),
+
+ cfg.StrOpt('role_tree_dn', default=None),
+ cfg.StrOpt('role_filter', default=None),
+ cfg.StrOpt('role_objectclass', default='organizationalRole'),
+ cfg.StrOpt('role_id_attribute', default='cn'),
+ cfg.StrOpt('role_name_attribute', default='ou'),
+ cfg.StrOpt('role_member_attribute', default='roleOccupant'),
+ cfg.ListOpt('role_attribute_ignore', default=''),
+ cfg.BoolOpt('role_allow_create', default=True),
+ cfg.BoolOpt('role_allow_update', default=True),
+ cfg.BoolOpt('role_allow_delete', default=True),
+ cfg.ListOpt('role_additional_attribute_mapping',
+ default=None),
+
+ cfg.StrOpt('group_tree_dn', default=None),
+ cfg.StrOpt('group_filter', default=None),
+ cfg.StrOpt('group_objectclass', default='groupOfNames'),
+ cfg.StrOpt('group_id_attribute', default='cn'),
+ cfg.StrOpt('group_name_attribute', default='ou'),
+ cfg.StrOpt('group_member_attribute', default='member'),
+ cfg.StrOpt('group_desc_attribute', default='description'),
+ cfg.StrOpt('group_domain_id_attribute',
+ default='businessCategory'),
+ cfg.ListOpt('group_attribute_ignore', default=''),
+ cfg.BoolOpt('group_allow_create', default=True),
+ cfg.BoolOpt('group_allow_update', default=True),
+ cfg.BoolOpt('group_allow_delete', default=True),
+ cfg.ListOpt('group_additional_attribute_mapping',
+ default=None),
+
+ cfg.StrOpt('tls_cacertfile', default=None),
+ cfg.StrOpt('tls_cacertdir', default=None),
+ cfg.BoolOpt('use_tls', default=False),
+ cfg.StrOpt('tls_req_cert', default='demand')],
+ 'pam': [
+ cfg.StrOpt('userid', default=None),
+ cfg.StrOpt('password', default=None)],
+ 'auth': [
+ cfg.ListOpt('methods', default=_DEFAULT_AUTH_METHODS),
+ cfg.StrOpt('password',
+ default='keystone.auth.plugins.token.Token'),
+ cfg.StrOpt('token',
+ default='keystone.auth.plugins.password.Password'),
+ #deals with REMOTE_USER authentication
+ cfg.StrOpt('external',
+ default='keystone.auth.plugins.external.ExternalDefault')],
+ 'paste_deploy': [
+ cfg.StrOpt('config_file', default=None)],
+ 'memcache': [
+ cfg.StrOpt('servers', default='localhost:11211'),
+ cfg.IntOpt('max_compare_and_set_retry', default=16)],
+ 'catalog': [
+ cfg.StrOpt('template_file',
+ default='default_catalog.templates'),
+ cfg.StrOpt('driver',
+ default='keystone.catalog.backends.sql.Catalog')]}
+
+
CONF = cfg.CONF
@@ -40,297 +252,35 @@ def setup_logging(conf, product_name='keystone'):
logging.setup(product_name)
-def setup_authentication():
+def setup_authentication(conf=None):
# register any non-default auth methods here (used by extensions, etc)
- for method_name in CONF.auth.methods:
+ if conf is None:
+ conf = CONF
+ for method_name in conf.auth.methods:
if method_name not in _DEFAULT_AUTH_METHODS:
- register_str(method_name, group="auth")
-
-
-def register_str(*args, **kw):
- conf = kw.pop('conf', CONF)
- group = kw.pop('group', None)
- return conf.register_opt(cfg.StrOpt(*args, **kw), group=group)
-
-
-def register_cli_str(*args, **kw):
- conf = kw.pop('conf', CONF)
- group = kw.pop('group', None)
- 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)
- return conf.register_opt(cfg.BoolOpt(*args, **kw), group=group)
-
-
-def register_cli_bool(*args, **kw):
- conf = kw.pop('conf', CONF)
- group = kw.pop('group', None)
- return conf.register_cli_opt(cfg.BoolOpt(*args, **kw), group=group)
-
-
-def register_int(*args, **kw):
- conf = kw.pop('conf', CONF)
- group = kw.pop('group', None)
- return conf.register_opt(cfg.IntOpt(*args, **kw), group=group)
-
-
-def register_cli_int(*args, **kw):
- conf = kw.pop('conf', CONF)
- group = kw.pop('group', None)
- return conf.register_cli_opt(cfg.IntOpt(*args, **kw), group=group)
-
-
-def configure():
- register_cli_bool('standard-threads', default=False,
- help='Do not monkey-patch threading system modules.')
+ conf.register_opt(cfg.StrOpt(method_name), group='auth')
+
+
+def configure(conf=None):
+ if conf is None:
+ conf = CONF
+
+ conf.register_cli_opt(
+ cfg.BoolOpt('standard-threads', default=False,
+ help='Do not monkey-patch threading system modules.'))
+ conf.register_cli_opt(
+ cfg.StrOpt('pydev-debug-host', default=None,
+ help='Host to connect to for remote debugger.'))
+ conf.register_cli_opt(
+ cfg.IntOpt('pydev-debug-port', default=None,
+ help='Port to connect to for remote debugger.'))
+
+ for section in FILE_OPTIONS:
+ for option in FILE_OPTIONS[section]:
+ if section:
+ conf.register_opt(option, group=section)
+ else:
+ conf.register_opt(option)
- register_cli_str('pydev-debug-host', default=None,
- help='Host to connect to for remote debugger.')
- register_cli_int('pydev-debug-port', default=None,
- help='Port to connect to for remote debugger.')
-
- register_str('admin_token', secret=True, default='ADMIN')
- register_str('bind_host', default='0.0.0.0')
- register_int('compute_port', default=8774)
- register_int('admin_port', default=35357)
- register_int('public_port', default=5000)
- register_str(
- 'public_endpoint', default='http://localhost:%(public_port)s/')
- register_str('admin_endpoint', default='http://localhost:%(admin_port)s/')
- register_str('onready')
- register_str('auth_admin_prefix', default='')
- register_str('policy_file', default='policy.json')
- register_str('policy_default_rule', default=None)
- # default max request size is 112k
- register_int('max_request_body_size', default=114688)
- register_int('max_param_size', default=64)
- # we allow tokens to be a bit larger to accommodate PKI
- register_int('max_token_size', default=8192)
- register_str(
- 'member_role_id', default='9fe2ff9ee4384b1894a90878d3e92bab')
- register_str('member_role_name', default='_member_')
-
- # identity
- register_str('default_domain_id', group='identity', default='default')
- register_int('max_password_length', group='identity', default=4096)
-
- # trust
- register_bool('enabled', group='trust', default=True)
-
- # os_inherit
- register_bool('enabled', group='os_inherit', default=False)
-
- # binding
- register_list('bind', group='token', default=[])
- register_str('enforce_token_bind', group='token', default='permissive')
-
- # ssl
- register_bool('enable', group='ssl', default=False)
- register_str('certfile', group='ssl',
- default="/etc/keystone/ssl/certs/keystone.pem")
- register_str('keyfile', group='ssl',
- default="/etc/keystone/ssl/private/keystonekey.pem")
- register_str('ca_certs', group='ssl',
- default="/etc/keystone/ssl/certs/ca.pem")
- register_str('ca_key', group='ssl',
- default="/etc/keystone/ssl/certs/cakey.pem")
- register_bool('cert_required', group='ssl', default=False)
- register_int('key_size', group='ssl', default=1024)
- register_int('valid_days', group='ssl', default=3650)
- register_str('ca_password', group='ssl', default=None)
- register_str('cert_subject', group='ssl',
- default='/C=US/ST=Unset/L=Unset/O=Unset/CN=localhost')
-
- # signing
- register_str(
- 'token_format', group='signing', default=None)
- register_str(
- 'certfile',
- group='signing',
- default="/etc/keystone/ssl/certs/signing_cert.pem")
- register_str(
- 'keyfile',
- group='signing',
- default="/etc/keystone/ssl/private/signing_key.pem")
- register_str(
- 'ca_certs',
- group='signing',
- default="/etc/keystone/ssl/certs/ca.pem")
- register_str('ca_key', group='signing',
- default="/etc/keystone/ssl/certs/cakey.pem")
- register_int('key_size', group='signing', default=2048)
- register_int('valid_days', group='signing', default=3650)
- register_str('ca_password', group='signing', default=None)
- register_str('cert_subject', group='signing',
- default='/C=US/ST=Unset/L=Unset/O=Unset/CN=www.example.com')
-
- # sql
- register_str('connection', group='sql', secret=True,
- default='sqlite:///keystone.db')
- register_int('idle_timeout', group='sql', default=200)
-
- #assignment has no default for backward compatibility reasons.
- #If assignment is not specified, the identity driver chooses the backend
- register_str(
- 'driver',
- group='assignment',
- default=None)
- register_str(
- 'driver',
- group='catalog',
- default='keystone.catalog.backends.sql.Catalog')
- register_str(
- 'driver',
- group='identity',
- default='keystone.identity.backends.sql.Identity')
- register_str(
- 'driver',
- group='credential',
- default='keystone.credential.backends.sql.Credential')
- register_str(
- 'driver',
- group='policy',
- default='keystone.policy.backends.sql.Policy')
- register_str(
- 'driver', group='token', default='keystone.token.backends.sql.Token')
- register_str(
- 'driver', group='trust', default='keystone.trust.backends.sql.Trust')
- register_str(
- 'driver', group='ec2', default='keystone.contrib.ec2.backends.kvs.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=None)
- register_str('password', group='ldap', secret=True, default=None)
- register_str('suffix', group='ldap', default='cn=example,cn=com')
- register_bool('use_dumb_member', group='ldap', default=False)
- register_str('dumb_member', group='ldap', default='cn=dumb,dc=nonexistent')
- register_bool('allow_subtree_delete', group='ldap', default=False)
- register_str('query_scope', group='ldap', default='one')
- register_int('page_size', group='ldap', default=0)
- register_str('alias_dereferencing', group='ldap', default='default')
-
- 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_str('user_enabled_attribute', group='ldap', default='enabled')
- register_str(
- 'user_domain_id_attribute', group='ldap', default='businessCategory')
- 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,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)
- register_bool('user_enabled_emulation', group='ldap', default=False)
- register_str('user_enabled_emulation_dn', group='ldap', default=None)
- register_list(
- 'user_additional_attribute_mapping', group='ldap', default=None)
-
- register_str('tenant_tree_dn', group='ldap', default=None)
- register_str('tenant_filter', group='ldap', default=None)
- 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='description')
- register_str('tenant_enabled_attribute', group='ldap', default='enabled')
- register_str(
- 'tenant_domain_id_attribute', group='ldap', default='businessCategory')
- 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_list(
- 'tenant_additional_attribute_mapping', group='ldap', default=None)
-
- 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)
- register_list(
- 'role_additional_attribute_mapping', group='ldap', default=None)
-
- register_str('group_tree_dn', group='ldap', default=None)
- register_str('group_filter', group='ldap', default=None)
- register_str('group_objectclass', group='ldap', default='groupOfNames')
- register_str('group_id_attribute', group='ldap', default='cn')
- register_str('group_name_attribute', group='ldap', default='ou')
- register_str('group_member_attribute', group='ldap', default='member')
- register_str('group_desc_attribute', group='ldap', default='description')
- register_str(
- 'group_domain_id_attribute', group='ldap', default='businessCategory')
- register_list('group_attribute_ignore', group='ldap', default='')
- register_bool('group_allow_create', group='ldap', default=True)
- register_bool('group_allow_update', group='ldap', default=True)
- register_bool('group_allow_delete', group='ldap', default=True)
- register_list(
- 'group_additional_attribute_mapping', group='ldap', default=None)
-
- register_str('tls_cacertfile', group='ldap', default=None)
- register_str('tls_cacertdir', group='ldap', default=None)
- register_bool('use_tls', group='ldap', default=False)
- register_str('tls_req_cert', group='ldap', default='demand')
-
- # pam
- register_str('userid', group='pam', default=None)
- register_str('password', group='pam', default=None)
-
- # default authentication methods
- register_list('methods', group='auth', default=_DEFAULT_AUTH_METHODS)
- register_str(
- 'password', group='auth', default='keystone.auth.plugins.token.Token')
- register_str(
- 'token', group='auth',
- default='keystone.auth.plugins.password.Password')
- #deals with REMOTE_USER authentication
- register_str(
- 'external',
- group='auth',
- default='keystone.auth.plugins.external.ExternalDefault')
# register any non-default auth methods here (used by extensions, etc)
- for method_name in CONF.auth.methods:
- if method_name not in _DEFAULT_AUTH_METHODS:
- register_str(method_name, group='auth')
-
- # PasteDeploy config file
- register_str('config_file', group='paste_deploy', default=None)
-
- # token provider
- register_str(
- 'provider',
- group='token',
- default=None)
+ setup_authentication(conf)
diff --git a/keystone/common/controller.py b/keystone/common/controller.py
index 1bf65cda..90818fb4 100644
--- a/keystone/common/controller.py
+++ b/keystone/common/controller.py
@@ -303,34 +303,35 @@ class V3Controller(V2Controller):
ref['id'] = uuid.uuid4().hex
return ref
+ def _get_domain_id_for_request(self, context):
+ """Get the domain_id for a v3 call."""
+
+ if context['is_admin']:
+ return DEFAULT_DOMAIN_ID
+
+ # Fish the domain_id out of the token
+ #
+ # We could make this more efficient by loading the domain_id
+ # into the context in the wrapper function above (since
+ # this version of normalize_domain will only be called inside
+ # a v3 protected call). However, this optimization is probably not
+ # worth the duplication of state
+ try:
+ token_ref = self.token_api.get_token(
+ token_id=context['token_id'])
+ except exception.TokenNotFound:
+ LOG.warning(_('Invalid token in _get_domain_id_for_request'))
+ raise exception.Unauthorized()
+
+ if 'domain' in token_ref:
+ return token_ref['domain']['id']
+ else:
+ return DEFAULT_DOMAIN_ID
+
def _normalize_domain_id(self, context, ref):
"""Fill in domain_id if not specified in a v3 call."""
-
if 'domain_id' not in ref:
- if context['is_admin']:
- ref['domain_id'] = DEFAULT_DOMAIN_ID
- else:
- # Fish the domain_id out of the token
- #
- # We could make this more efficient by loading the domain_id
- # into the context in the wrapper function above (since
- # this version of normalize_domain will only be called inside
- # a v3 protected call). However, given that we only use this
- # for creating entities, this optimization is probably not
- # worth the duplication of state
- try:
- token_ref = self.token_api.get_token(
- token_id=context['token_id'])
- except exception.TokenNotFound:
- LOG.warning(_('Invalid token in normalize_domain_id'))
- raise exception.Unauthorized()
-
- if 'domain' in token_ref:
- ref['domain_id'] = token_ref['domain']['id']
- else:
- # FIXME(henry-nash) Revisit this once v3 token scoping
- # across domains has been hashed out
- ref['domain_id'] = DEFAULT_DOMAIN_ID
+ ref['domain_id'] = self._get_domain_id_for_request(context)
return ref
def _filter_domain_id(self, ref):
diff --git a/keystone/common/ldap/fakeldap.py b/keystone/common/ldap/fakeldap.py
index c19e1355..e4458874 100644
--- a/keystone/common/ldap/fakeldap.py
+++ b/keystone/common/ldap/fakeldap.py
@@ -123,18 +123,14 @@ server_fail = False
class FakeShelve(dict):
- @classmethod
- def get_instance(cls):
- try:
- return cls.__instance
- except AttributeError:
- cls.__instance = cls()
- return cls.__instance
def sync(self):
pass
+FakeShelves = {}
+
+
class FakeLdap(object):
"""Fake LDAP connection."""
@@ -142,8 +138,10 @@ class FakeLdap(object):
def __init__(self, url):
LOG.debug(_('FakeLdap initialize url=%s'), url)
- if url == 'fake://memory':
- self.db = FakeShelve.get_instance()
+ if url.startswith('fake://memory'):
+ if url not in FakeShelves:
+ FakeShelves[url] = FakeShelve()
+ self.db = FakeShelves[url]
else:
self.db = shelve.open(url[7:])
diff --git a/keystone/common/utils.py b/keystone/common/utils.py
index 4abad57a..27968efc 100644
--- a/keystone/common/utils.py
+++ b/keystone/common/utils.py
@@ -32,7 +32,6 @@ from keystone.openstack.common import log as logging
CONF = config.CONF
-config.register_int('crypt_strength', default=40000)
LOG = logging.getLogger(__name__)
diff --git a/keystone/config.py b/keystone/config.py
index 28f1cf2c..c4a43b47 100644
--- a/keystone/config.py
+++ b/keystone/config.py
@@ -25,15 +25,8 @@ config.configure()
CONF = config.CONF
setup_logging = config.setup_logging
-register_str = config.register_str
-register_cli_str = config.register_cli_str
-register_list = config.register_list
-register_cli_list = config.register_cli_list
-register_bool = config.register_bool
-register_cli_bool = config.register_cli_bool
-register_int = config.register_int
-register_cli_int = config.register_cli_int
setup_authentication = config.setup_authentication
+configure = config.configure
def find_paste_config():
diff --git a/keystone/identity/backends/kvs.py b/keystone/identity/backends/kvs.py
index 0323d3d0..bcfb777b 100644
--- a/keystone/identity/backends/kvs.py
+++ b/keystone/identity/backends/kvs.py
@@ -27,6 +27,9 @@ class Identity(kvs.Base, identity.Driver):
def default_assignment_driver(self):
return "keystone.assignment.backends.kvs.Assignment"
+ def is_domain_aware(self):
+ return True
+
# Public interface
def authenticate(self, user_id, password):
user_ref = None
diff --git a/keystone/identity/backends/ldap.py b/keystone/identity/backends/ldap.py
index ef3b5d61..67380f6e 100644
--- a/keystone/identity/backends/ldap.py
+++ b/keystone/identity/backends/ldap.py
@@ -41,14 +41,19 @@ DEFAULT_DOMAIN = {
@dependency.requires('assignment_api')
class Identity(identity.Driver):
- def __init__(self):
+ def __init__(self, conf=None):
super(Identity, self).__init__()
- self.user = UserApi(CONF)
- self.group = GroupApi(CONF)
+ if conf is None:
+ conf = CONF
+ self.user = UserApi(conf)
+ self.group = GroupApi(conf)
def default_assignment_driver(self):
return "keystone.assignment.backends.ldap.Assignment"
+ def is_domain_aware(self):
+ return False
+
# Identity interface
def create_project(self, project_id, project):
@@ -68,37 +73,31 @@ class Identity(identity.Driver):
raise AssertionError('Invalid user / password')
except Exception:
raise AssertionError('Invalid user / password')
- return self.assignment_api._set_default_domain(
- identity.filter_user(user_ref))
+ return identity.filter_user(user_ref)
def _get_user(self, user_id):
return self.user.get(user_id)
def get_user(self, user_id):
- ref = identity.filter_user(self._get_user(user_id))
- return self.assignment_api._set_default_domain(ref)
+ return identity.filter_user(self._get_user(user_id))
def list_users(self):
- return (self.assignment_api._set_default_domain
- (self.user.get_all_filtered()))
+ return self.user.get_all_filtered()
def get_user_by_name(self, user_name, domain_id):
- self.assignment_api._validate_default_domain_id(domain_id)
- ref = identity.filter_user(self.user.get_by_name(user_name))
- return self.assignment_api._set_default_domain(ref)
+ # domain_id will already have been handled in the Manager layer,
+ # parameter left in so this matches the Driver specification
+ return identity.filter_user(self.user.get_by_name(user_name))
# CRUD
def create_user(self, user_id, user):
- user = self.assignment_api._validate_default_domain(user)
user_ref = self.user.create(user)
tenant_id = user.get('tenant_id')
if tenant_id is not None:
self.assignment_api.add_user_to_project(tenant_id, user_id)
- return (self.assignment_api._set_default_domain
- (identity.filter_user(user_ref)))
+ return identity.filter_user(user_ref)
def update_user(self, user_id, user):
- user = self.assignment_api._validate_default_domain(user)
if 'id' in user and user['id'] != user_id:
raise exception.ValidationError('Cannot change user ID')
old_obj = self.user.get(user_id)
@@ -121,8 +120,7 @@ class Identity(identity.Driver):
user['enabled_nomask'] = old_obj['enabled_nomask']
self.user.mask_enabled_attribute(user)
self.user.update(user_id, user, old_obj)
- return (self.assignment_api._set_default_domain
- (self.user.get_filtered(user_id)))
+ return self.user.get_filtered(user_id)
def delete_user(self, user_id):
self.assignment_api.delete_user(user_id)
@@ -138,21 +136,16 @@ class Identity(identity.Driver):
self.user.delete(user_id)
def create_group(self, group_id, group):
- group = self.assignment_api._validate_default_domain(group)
group['name'] = clean.group_name(group['name'])
- return self.assignment_api._set_default_domain(
- self.group.create(group))
+ return self.group.create(group)
def get_group(self, group_id):
- return self.assignment_api._set_default_domain(
- self.group.get(group_id))
+ return self.group.get(group_id)
def update_group(self, group_id, group):
- group = self.assignment_api._validate_default_domain(group)
if 'name' in group:
group['name'] = clean.group_name(group['name'])
- return (self.assignment_api._set_default_domain
- (self.group.update(group_id, group)))
+ return self.group.update(group_id, group)
def delete_group(self, group_id):
return self.group.delete(group_id)
@@ -172,11 +165,10 @@ class Identity(identity.Driver):
def list_groups_for_user(self, user_id):
self.get_user(user_id)
user_dn = self.user._id_to_dn(user_id)
- return (self.assignment_api._set_default_domain
- (self.group.list_user_groups(user_dn)))
+ return self.group.list_user_groups(user_dn)
def list_groups(self):
- return self.assignment_api._set_default_domain(self.group.get_all())
+ return self.group.get_all()
def list_users_in_group(self, group_id):
self.get_group(group_id)
@@ -190,7 +182,7 @@ class Identity(identity.Driver):
" '%(group_id)s'. The user should be removed"
" from the group. The user will be ignored.") %
dict(user_dn=user_dn, group_id=group_id))
- return self.assignment_api._set_default_domain(users)
+ return users
def check_user_in_group(self, user_id, group_id):
self.get_user(user_id)
diff --git a/keystone/identity/backends/pam.py b/keystone/identity/backends/pam.py
index 2a6ee621..a5459694 100644
--- a/keystone/identity/backends/pam.py
+++ b/keystone/identity/backends/pam.py
@@ -58,6 +58,9 @@ class PamIdentity(identity.Driver):
Tenant is always the same as User, root user has admin role.
"""
+ def is_domain_aware(self):
+ return False
+
def authenticate(self, user_id, password):
auth = pam.authenticate if pam else PAM_authenticate
if not auth(user_id, password):
diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py
index 65a34a8a..84026a58 100644
--- a/keystone/identity/backends/sql.py
+++ b/keystone/identity/backends/sql.py
@@ -85,6 +85,9 @@ class Identity(sql.Base, identity.Driver):
"""
return utils.check_password(password, user_ref.password)
+ def is_domain_aware(self):
+ return True
+
# Identity interface
def authenticate(self, user_id, password):
session = self.get_session()
diff --git a/keystone/identity/controllers.py b/keystone/identity/controllers.py
index 67f3beac..281e3f1b 100644
--- a/keystone/identity/controllers.py
+++ b/keystone/identity/controllers.py
@@ -620,23 +620,30 @@ class UserV3(controller.V3Controller):
@controller.filterprotected('domain_id', 'email', 'enabled', 'name')
def list_users(self, context, filters):
- refs = self.identity_api.list_users()
+ refs = self.identity_api.list_users(
+ domain_scope=self._get_domain_id_for_request(context))
return UserV3.wrap_collection(context, refs, filters)
@controller.filterprotected('domain_id', 'email', 'enabled', 'name')
def list_users_in_group(self, context, filters, group_id):
- refs = self.identity_api.list_users_in_group(group_id)
+ refs = self.identity_api.list_users_in_group(
+ group_id,
+ domain_scope=self._get_domain_id_for_request(context))
return UserV3.wrap_collection(context, refs, filters)
@controller.protected
def get_user(self, context, user_id):
- ref = self.identity_api.get_user(user_id)
+ ref = self.identity_api.get_user(
+ user_id,
+ domain_scope=self._get_domain_id_for_request(context))
return UserV3.wrap_member(context, ref)
@controller.protected
def update_user(self, context, user_id, user):
self._require_matching_id(user_id, user)
- ref = self.identity_api.update_user(user_id, user)
+ ref = self.identity_api.update_user(
+ user_id, user,
+ domain_scope=self._get_domain_id_for_request(context))
if user.get('password') or not user.get('enabled', True):
# revoke all tokens owned by this user
@@ -646,18 +653,24 @@ class UserV3(controller.V3Controller):
@controller.protected
def add_user_to_group(self, context, user_id, group_id):
- self.identity_api.add_user_to_group(user_id, group_id)
+ self.identity_api.add_user_to_group(
+ user_id, group_id,
+ domain_scope=self._get_domain_id_for_request(context))
# Delete any tokens so that group membership can have an
# immediate effect
self._delete_tokens_for_user(user_id)
@controller.protected
def check_user_in_group(self, context, user_id, group_id):
- return self.identity_api.check_user_in_group(user_id, group_id)
+ return self.identity_api.check_user_in_group(
+ user_id, group_id,
+ domain_scope=self._get_domain_id_for_request(context))
@controller.protected
def remove_user_from_group(self, context, user_id, group_id):
- self.identity_api.remove_user_from_group(user_id, group_id)
+ self.identity_api.remove_user_from_group(
+ user_id, group_id,
+ domain_scope=self._get_domain_id_for_request(context))
self._delete_tokens_for_user(user_id)
def _delete_user(self, context, user_id):
@@ -667,11 +680,13 @@ class UserV3(controller.V3Controller):
self.credential_api.delete_credential(cred['id'])
# Make sure any tokens are marked as deleted
+ domain_id = self._get_domain_id_for_request(context)
self._delete_tokens_for_user(user_id)
# Finally delete the user itself - the backend is
# responsible for deleting any role assignments related
# to this user
- return self.identity_api.delete_user(user_id)
+ return self.identity_api.delete_user(
+ user_id, domain_scope=domain_id)
@controller.protected
def delete_user(self, context, user_id):
@@ -693,24 +708,31 @@ class GroupV3(controller.V3Controller):
@controller.filterprotected('domain_id', 'name')
def list_groups(self, context, filters):
- refs = self.identity_api.list_groups()
+ refs = self.identity_api.list_groups(
+ domain_scope=self._get_domain_id_for_request(context))
return GroupV3.wrap_collection(context, refs, filters)
@controller.filterprotected('name')
def list_groups_for_user(self, context, filters, user_id):
- refs = self.identity_api.list_groups_for_user(user_id)
+ refs = self.identity_api.list_groups_for_user(
+ user_id,
+ domain_scope=self._get_domain_id_for_request(context))
return GroupV3.wrap_collection(context, refs, filters)
@controller.protected
def get_group(self, context, group_id):
- ref = self.identity_api.get_group(group_id)
+ ref = self.identity_api.get_group(
+ group_id,
+ domain_scope=self._get_domain_id_for_request(context))
return GroupV3.wrap_member(context, ref)
@controller.protected
def update_group(self, context, group_id, group):
self._require_matching_id(group_id, group)
- ref = self.identity_api.update_group(group_id, group)
+ ref = self.identity_api.update_group(
+ group_id, group,
+ domain_scope=self._get_domain_id_for_request(context))
return GroupV3.wrap_member(context, ref)
def _delete_group(self, context, group_id):
@@ -720,8 +742,10 @@ class GroupV3(controller.V3Controller):
# deletion, so that we can remove these tokens after we know
# the group deletion succeeded.
- user_refs = self.identity_api.list_users_in_group(group_id)
- self.identity_api.delete_group(group_id)
+ domain_id = self._get_domain_id_for_request(context)
+ user_refs = self.identity_api.list_users_in_group(
+ group_id, domain_scope=domain_id)
+ self.identity_api.delete_group(group_id, domain_scope=domain_id)
for user in user_refs:
self._delete_tokens_for_user(user['id'])
diff --git a/keystone/identity/core.py b/keystone/identity/core.py
index 7fb630e2..7d5882e3 100644
--- a/keystone/identity/core.py
+++ b/keystone/identity/core.py
@@ -16,11 +16,17 @@
"""Main entry point into the Identity service."""
+import functools
+import os
+
+from oslo.config import cfg
+
from keystone import clean
from keystone.common import dependency
from keystone.common import manager
from keystone import config
from keystone import exception
+from keystone.openstack.common import importutils
from keystone.openstack.common import log as logging
@@ -51,6 +57,121 @@ def filter_user(user_ref):
return user_ref
+class DomainConfigs(dict):
+ """Discover, store and provide access to domain specifc configs.
+
+ The setup_domain_drives() call will be made via the wrapper from
+ the first call to any driver function handled by this manager. This
+ setup call it will scan the domain config directory for files of the form
+
+ keystone.<domain_name>.conf
+
+ For each file, the domain_name will be turned into a domain_id and then
+ this class will:
+ - Create a new config structure, adding in the specific additional options
+ defined in this config file
+ - Initialise a new instance of the required driver with this new config.
+
+ """
+ configured = False
+ driver = None
+
+ def _load_driver(self, assignment_api, domain_id):
+ domain_config = self[domain_id]
+ domain_config['driver'] = (
+ importutils.import_object(
+ domain_config['cfg'].identity.driver, domain_config['cfg']))
+ domain_config['driver'].assignment_api = assignment_api
+
+ def _load_config(self, assignment_api, file_list, domain_name):
+ try:
+ domain_ref = assignment_api.get_domain_by_name(domain_name)
+ except exception.DomainNotFound:
+ msg = (_('Invalid domain name (%s) found in config file name')
+ % domain_name)
+ LOG.warning(msg)
+
+ if domain_ref:
+ # Create a new entry in the domain config dict, which contains
+ # a new instance of both the conf environment and driver using
+ # options defined in this set of config files. Later, when we
+ # service calls via this Manager, we'll index via this domain
+ # config dict to make sure we call the right driver
+ domain = domain_ref['id']
+ self[domain] = {}
+ self[domain]['cfg'] = cfg.ConfigOpts()
+ config.configure(conf=self[domain]['cfg'])
+ self[domain]['cfg'](args=[], project='keystone',
+ default_config_files=file_list)
+ self._load_driver(assignment_api, domain)
+
+ def setup_domain_drivers(self, standard_driver, assignment_api):
+ # This is called by the api call wrapper
+ self.configured = True
+ self.driver = standard_driver
+
+ conf_dir = CONF.identity.domain_config_dir
+ if not os.path.exists(conf_dir):
+ msg = _('Unable to locate domain config directory: %s') % conf_dir
+ LOG.warning(msg)
+ return
+
+ for r, d, f in os.walk(conf_dir):
+ for file in f:
+ if file.startswith('keystone.') and file.endswith('.conf'):
+ names = file.split('.')
+ if len(names) == 3:
+ self._load_config(assignment_api,
+ [os.path.join(r, file)],
+ names[1])
+ else:
+ msg = (_('Ignoring file (%s) while scanning domain '
+ 'config directory') % file)
+ LOG.debug(msg)
+
+ def get_domain_driver(self, domain_id):
+ if domain_id in self:
+ return self[domain_id]['driver']
+
+ def get_domain_conf(self, domain_id):
+ if domain_id in self:
+ return self[domain_id]['cfg']
+
+ def reload_domain_driver(self, assignment_api, domain_id):
+ # Only used to support unit tests that want to set
+ # new config values. This should only be called once
+ # the domains have been configured, since it relies on
+ # the fact that the configuration files have already been
+ # read.
+ if self.configured:
+ if domain_id in self:
+ self._load_driver(assignment_api, domain_id)
+ else:
+ # The standard driver
+ self.driver = self.driver()
+ self.driver.assignment_api = assignment_api
+
+
+def domains_configured(f):
+ """Wraps API calls to lazy load domain configs after init.
+
+ This is required since the assignment manager needs to be initialized
+ before this manager, and yet this manager's init wants to be
+ able to make assignment calls (to build the domain configs). So
+ instead, we check if the domains have been initialized on entry
+ to each call, and if requires load them,
+
+ """
+ @functools.wraps(f)
+ def wrapper(self, *args, **kwargs):
+ if (not self.domain_configs.configured and
+ CONF.identity.domain_specific_drivers_enabled):
+ self.domain_configs.setup_domain_drivers(
+ self.driver, self.assignment_api)
+ return f(self, *args, **kwargs)
+ return wrapper
+
+
@dependency.provider('identity_api')
@dependency.requires('assignment_api')
class Manager(manager.Manager):
@@ -59,30 +180,228 @@ class Manager(manager.Manager):
See :mod:`keystone.common.manager.Manager` for more details on how this
dynamically calls the backend.
+ This class also handles the support of domain specific backends, by using
+ the DomainConfigs class. The setup call for DomainConfigs is called
+ from with the @domains_configured wrapper in a lazy loading fashion
+ to get around the fact that we can't satisfy the assignment api it needs
+ from within our __init__() function since the assignment driver is not
+ itself yet intitalized.
+
+ Each of the identity calls are pre-processed here to choose, based on
+ domain, which of the drivers should be called. The non-domain-specific
+ driver is still in place, and is used if there is no specific driver for
+ the domain in question.
+
"""
def __init__(self):
super(Manager, self).__init__(CONF.identity.driver)
-
+ self.domain_configs = DomainConfigs()
+
+ # Domain ID normalization methods
+
+ def _set_domain_id(self, ref, domain_id):
+ if isinstance(ref, dict):
+ ref = ref.copy()
+ ref['domain_id'] = domain_id
+ return ref
+ elif isinstance(ref, list):
+ return [self._set_domain_id(x, domain_id) for x in ref]
+ else:
+ raise ValueError(_('Expected dict or list: %s') % type(ref))
+
+ def _clear_domain_id(self, ref):
+ # Clear the domain_id, and then check to ensure that if this
+ # was not the default domain, it is being handled by its own
+ # backend driver.
+ ref = ref.copy()
+ domain_id = ref.pop('domain_id', CONF.identity.default_domain_id)
+ if (domain_id != CONF.identity.default_domain_id and
+ domain_id not in self.domain_configs):
+ raise exception.DomainNotFound(domain_id=domain_id)
+ return ref
+
+ def _normalize_scope(self, domain_scope):
+ if domain_scope is None:
+ return CONF.identity.default_domain_id
+ else:
+ return domain_scope
+
+ def _select_identity_driver(self, domain_id):
+ driver = self.domain_configs.get_domain_driver(domain_id)
+ if driver:
+ return driver
+ else:
+ return self.driver
+
+ def _get_domain_conf(self, domain_id):
+ conf = self.domain_configs.get_domain_conf(domain_id)
+ if conf:
+ return conf
+ else:
+ return CONF
+
+ def _get_domain_id_and_driver(self, domain_scope):
+ domain_id = self._normalize_scope(domain_scope)
+ driver = self._select_identity_driver(domain_id)
+ return (domain_id, driver)
+
+ # The actual driver calls - these are pre/post processed here as
+ # part of the Manager layer to make sure we:
+ #
+ # - select the right driver for this domain
+ # - clear/set domain_ids for drivers that do not support domains
+
+ @domains_configured
+ def authenticate(self, user_id, password, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ ref = driver.authenticate(user_id, password)
+ if not driver.is_domain_aware():
+ ref = self._set_domain_id(ref, domain_id)
+ return ref
+
+ @domains_configured
def create_user(self, user_id, user_ref):
user = user_ref.copy()
user['name'] = clean.user_name(user['name'])
user.setdefault('enabled', True)
user['enabled'] = clean.user_enabled(user['enabled'])
- return self.driver.create_user(user_id, user)
- def update_user(self, user_id, user_ref):
+ # For creating a user, the domain is in the object itself
+ domain_id = user_ref['domain_id']
+ driver = self._select_identity_driver(domain_id)
+ if not driver.is_domain_aware():
+ user = self._clear_domain_id(user)
+ ref = driver.create_user(user_id, user)
+ if not driver.is_domain_aware():
+ ref = self._set_domain_id(ref, domain_id)
+ return ref
+
+ @domains_configured
+ def get_user(self, user_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ ref = driver.get_user(user_id)
+ if not driver.is_domain_aware():
+ ref = self._set_domain_id(ref, domain_id)
+ return ref
+
+ @domains_configured
+ def get_user_by_name(self, user_name, domain_id):
+ driver = self._select_identity_driver(domain_id)
+ ref = driver.get_user_by_name(user_name, domain_id)
+ if not driver.is_domain_aware():
+ ref = self._set_domain_id(ref, domain_id)
+ return ref
+
+ @domains_configured
+ def list_users(self, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ user_list = driver.list_users()
+ if not driver.is_domain_aware():
+ user_list = self._set_domain_id(user_list, domain_id)
+ return user_list
+
+ @domains_configured
+ def update_user(self, user_id, user_ref, domain_scope=None):
user = user_ref.copy()
if 'name' in user:
user['name'] = clean.user_name(user['name'])
if 'enabled' in user:
user['enabled'] = clean.user_enabled(user['enabled'])
- return self.driver.update_user(user_id, user)
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ if not driver.is_domain_aware():
+ user = self._clear_domain_id(user)
+ ref = driver.update_user(user_id, user)
+ if not driver.is_domain_aware():
+ ref = self._set_domain_id(ref, domain_id)
+ return ref
+
+ @domains_configured
+ def delete_user(self, user_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ driver.delete_user(user_id)
+
+ @domains_configured
def create_group(self, group_id, group_ref):
group = group_ref.copy()
group.setdefault('description', '')
- return self.driver.create_group(group_id, group)
+
+ # For creating a group, the domain is in the object itself
+ domain_id = group_ref['domain_id']
+ driver = self._select_identity_driver(domain_id)
+ if not driver.is_domain_aware():
+ group = self._clear_domain_id(group)
+ ref = driver.create_group(group_id, group)
+ if not driver.is_domain_aware():
+ ref = self._set_domain_id(ref, domain_id)
+ return ref
+
+ @domains_configured
+ def get_group(self, group_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ ref = driver.get_group(group_id)
+ if not driver.is_domain_aware():
+ ref = self._set_domain_id(ref, domain_id)
+ return ref
+
+ @domains_configured
+ def update_group(self, group_id, group, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ if not driver.is_domain_aware():
+ group = self._clear_domain_id(group)
+ ref = driver.update_group(group_id, group)
+ if not driver.is_domain_aware():
+ ref = self._set_domain_id(ref, domain_id)
+ return ref
+
+ @domains_configured
+ def delete_group(self, group_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ driver.delete_group(group_id)
+
+ @domains_configured
+ def add_user_to_group(self, user_id, group_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ driver.add_user_to_group(user_id, group_id)
+
+ @domains_configured
+ def remove_user_from_group(self, user_id, group_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ driver.remove_user_from_group(user_id, group_id)
+
+ @domains_configured
+ def list_groups_for_user(self, user_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ group_list = driver.list_groups_for_user(user_id)
+ if not driver.is_domain_aware():
+ group_list = self._set_domain_id(group_list, domain_id)
+ return group_list
+
+ @domains_configured
+ def list_groups(self, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ group_list = driver.list_groups()
+ if not driver.is_domain_aware():
+ group_list = self._set_domain_id(group_list, domain_id)
+ return group_list
+
+ @domains_configured
+ def list_users_in_group(self, group_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ user_list = driver.list_users_in_group(group_id)
+ if not driver.is_domain_aware():
+ user_list = self._set_domain_id(user_list, domain_id)
+ return user_list
+
+ @domains_configured
+ def check_user_in_group(self, user_id, group_id, domain_scope=None):
+ domain_id, driver = self._get_domain_id_and_driver(domain_scope)
+ return driver.check_user_in_group(user_id, group_id)
+
+ # TODO(henry-nash, ayoung) The following cross calls to the assignment
+ # API should be removed, with the controller and tests making the correct
+ # calls direct to assignment.
def create_project(self, tenant_id, tenant_ref):
tenant = tenant_ref.copy()
@@ -358,6 +677,8 @@ class Driver(object):
"""
raise exception.NotImplemented()
- #end of identity
+ def is_domain_aware(self):
+ """Indicates if Driver supports domains."""
+ raise exception.NotImplemented()
- # Assignments
+ #end of identity
diff --git a/keystone/tests/backend_multi_ldap_sql.conf b/keystone/tests/backend_multi_ldap_sql.conf
new file mode 100644
index 00000000..59cff761
--- /dev/null
+++ b/keystone/tests/backend_multi_ldap_sql.conf
@@ -0,0 +1,35 @@
+[sql]
+connection = sqlite://
+#For a file based sqlite use
+#connection = sqlite:////tmp/keystone.db
+#To Test MySQL:
+#connection = mysql://keystone:keystone@localhost/keystone?charset=utf8
+#To Test PostgreSQL:
+#connection = postgresql://keystone:keystone@localhost/keystone?client_encoding=utf8
+idle_timeout = 200
+
+[identity]
+# common identity backend is SQL, domain specific configs will
+# set their backends to ldap
+driver = keystone.identity.backends.sql.Identity
+# The test setup will set this to True, to allow easier creation
+# of initial domain data
+# domain_specific_drivers_enabled = True
+
+[assignment]
+driver = keystone.assignment.backends.sql.Assignment
+
+[token]
+driver = keystone.token.backends.sql.Token
+
+[ec2]
+driver = keystone.contrib.ec2.backends.sql.Ec2
+
+[catalog]
+driver = keystone.catalog.backends.sql.Catalog
+
+[policy]
+driver = keystone.policy.backends.sql.Policy
+
+[trust]
+driver = keystone.trust.backends.sql.Trust
diff --git a/keystone/tests/core.py b/keystone/tests/core.py
index 8d075335..b42a8709 100644
--- a/keystone/tests/core.py
+++ b/keystone/tests/core.py
@@ -292,9 +292,11 @@ class TestCase(NoModule, unittest.TestCase):
for domain in fixtures.DOMAINS:
try:
rv = self.identity_api.create_domain(domain['id'], domain)
- except (exception.Conflict, exception.NotImplemented):
- pass
- setattr(self, 'domain_%s' % domain['id'], domain)
+ except exception.Conflict:
+ rv = self.identity_api.get_domain(domain['id'])
+ except exception.NotImplemented:
+ rv = domain
+ setattr(self, 'domain_%s' % domain['id'], rv)
for tenant in fixtures.TENANTS:
try:
diff --git a/keystone/tests/keystone.Default.conf b/keystone/tests/keystone.Default.conf
new file mode 100644
index 00000000..7049afed
--- /dev/null
+++ b/keystone/tests/keystone.Default.conf
@@ -0,0 +1,14 @@
+# The domain-specific configuration file for the default domain for
+# use with unit tests.
+#
+# The domain_name of the default domain is 'Default', hence the
+# strange mix of upper/lower case in the file name.
+
+[ldap]
+url = fake://memory
+user = cn=Admin
+password = password
+suffix = cn=example,cn=com
+
+[identity]
+driver = keystone.identity.backends.ldap.Identity \ No newline at end of file
diff --git a/keystone/tests/keystone.domain1.conf b/keystone/tests/keystone.domain1.conf
new file mode 100644
index 00000000..6b7e2488
--- /dev/null
+++ b/keystone/tests/keystone.domain1.conf
@@ -0,0 +1,11 @@
+# The domain-specific configuration file for the test domain
+# 'domain1' for use with unit tests.
+
+[ldap]
+url = fake://memory1
+user = cn=Admin
+password = password
+suffix = cn=example,cn=com
+
+[identity]
+driver = keystone.identity.backends.ldap.Identity \ No newline at end of file
diff --git a/keystone/tests/keystone.domain2.conf b/keystone/tests/keystone.domain2.conf
new file mode 100644
index 00000000..0ed68eb9
--- /dev/null
+++ b/keystone/tests/keystone.domain2.conf
@@ -0,0 +1,13 @@
+# The domain-specific configuration file for the test domain
+# 'domain2' for use with unit tests.
+
+[ldap]
+url = fake://memory
+user = cn=Admin
+password = password
+suffix = cn=myroot,cn=com
+group_tree_dn = ou=UserGroups,dc=myroot,dc=org
+user_tree_dn = ou=Users,dc=myroot,dc=org
+
+[identity]
+driver = keystone.identity.backends.ldap.Identity \ No newline at end of file
diff --git a/keystone/tests/test_backend.py b/keystone/tests/test_backend.py
index 52628985..8013deec 100644
--- a/keystone/tests/test_backend.py
+++ b/keystone/tests/test_backend.py
@@ -105,7 +105,9 @@ class IdentityTests(object):
self.assertIn(CONF.member_role_id, role_list)
def test_password_hashed(self):
- user_ref = self.identity_api._get_user(self.user_foo['id'])
+ driver = self.identity_api._select_identity_driver(
+ self.user_foo['domain_id'])
+ user_ref = driver._get_user(self.user_foo['id'])
self.assertNotEqual(user_ref['password'], self.user_foo['password'])
def test_create_unicode_user_name(self):
@@ -1521,7 +1523,8 @@ class IdentityTests(object):
self.assertRaises(exception.UserNotFound,
self.identity_api.update_user,
user_id,
- {'id': user_id})
+ {'id': user_id,
+ 'domain_id': DEFAULT_DOMAIN_ID})
def test_delete_user_with_project_association(self):
user = {'id': uuid.uuid4().hex,
diff --git a/keystone/tests/test_backend_ldap.py b/keystone/tests/test_backend_ldap.py
index 6f9cfef9..e40e0565 100644
--- a/keystone/tests/test_backend_ldap.py
+++ b/keystone/tests/test_backend_ldap.py
@@ -38,8 +38,16 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
return self.identity_api.get_domain(CONF.identity.default_domain_id)
def clear_database(self):
- db = fakeldap.FakeShelve().get_instance()
- db.clear()
+ for shelf in fakeldap.FakeShelves:
+ fakeldap.FakeShelves[shelf].clear()
+
+ def reload_backends(self, domain_id):
+ # Only one backend unless we are using separate domain backends
+ self.load_backends()
+
+ def get_config(self, domain_id):
+ # Only one conf structure unless we are using separate domain backends
+ return CONF
def _set_config(self):
self.config([test.etcdir('keystone.conf.sample'),
@@ -57,6 +65,7 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
user = {'id': 'fake1',
'name': 'fake1',
'password': 'fakepass1',
+ 'domain_id': CONF.identity.default_domain_id,
'tenants': ['bar']}
self.identity_api.create_user('fake1', user)
user_ref = self.identity_api.get_user('fake1')
@@ -71,14 +80,16 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
'fake1')
def test_configurable_forbidden_user_actions(self):
- CONF.ldap.user_allow_create = False
- CONF.ldap.user_allow_update = False
- CONF.ldap.user_allow_delete = False
- self.load_backends()
+ conf = self.get_config(CONF.identity.default_domain_id)
+ conf.ldap.user_allow_create = False
+ conf.ldap.user_allow_update = False
+ conf.ldap.user_allow_delete = False
+ self.reload_backends(CONF.identity.default_domain_id)
user = {'id': 'fake1',
'name': 'fake1',
'password': 'fakepass1',
+ 'domain_id': CONF.identity.default_domain_id,
'tenants': ['bar']}
self.assertRaises(exception.ForbiddenAction,
self.identity_api.create_user,
@@ -100,8 +111,9 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
self.user_foo.pop('password')
self.assertDictEqual(user_ref, self.user_foo)
- CONF.ldap.user_filter = '(CN=DOES_NOT_MATCH)'
- self.load_backends()
+ conf = self.get_config(user_ref['domain_id'])
+ conf.ldap.user_filter = '(CN=DOES_NOT_MATCH)'
+ self.reload_backends(user_ref['domain_id'])
self.assertRaises(exception.UserNotFound,
self.identity_api.get_user,
self.user_foo['id'])
@@ -205,18 +217,21 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
# Create a group
group_id = None
- group = dict(name=uuid.uuid4().hex)
+ group = dict(name=uuid.uuid4().hex,
+ domain_id=CONF.identity.default_domain_id)
group_id = self.identity_api.create_group(group_id, group)['id']
# Create a couple of users and add them to the group.
user_id = None
- user = dict(name=uuid.uuid4().hex, id=uuid.uuid4().hex)
+ user = dict(name=uuid.uuid4().hex, id=uuid.uuid4().hex,
+ domain_id=CONF.identity.default_domain_id)
user_1_id = self.identity_api.create_user(user_id, user)['id']
self.identity_api.add_user_to_group(user_1_id, group_id)
user_id = None
- user = dict(name=uuid.uuid4().hex, id=uuid.uuid4().hex)
+ user = dict(name=uuid.uuid4().hex, id=uuid.uuid4().hex,
+ domain_id=CONF.identity.default_domain_id)
user_2_id = self.identity_api.create_user(user_id, user)['id']
self.identity_api.add_user_to_group(user_2_id, group_id)
@@ -224,7 +239,9 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
# Delete user 2
# NOTE(blk-u): need to go directly to user interface to keep from
# updating the group.
- self.identity_api.driver.user.delete(user_2_id)
+ driver = self.identity_api._select_identity_driver(
+ user['domain_id'])
+ driver.user.delete(user_2_id)
# List group users and verify only user 1.
res = self.identity_api.list_users_in_group(group_id)
@@ -249,13 +266,16 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
self.identity_api.create_user(user['id'], user)
self.identity_api.add_user_to_project(self.tenant_baz['id'],
user['id'])
- self.identity_api.driver.user.LDAP_USER = None
- self.identity_api.driver.user.LDAP_PASSWORD = None
+ driver = self.identity_api._select_identity_driver(
+ user['domain_id'])
+ driver.user.LDAP_USER = None
+ driver.user.LDAP_PASSWORD = None
self.assertRaises(AssertionError,
self.identity_api.authenticate,
user_id=user['id'],
- password=None)
+ password=None,
+ domain_scope=user['domain_id'])
# (spzala)The group and domain crud tests below override the standard ones
# in test_backend.py so that we can exclude the update name test, since we
@@ -460,7 +480,8 @@ class LDAPIdentity(test.TestCase, BaseLDAPIdentity):
self.load_backends()
self.load_fixtures(default_fixtures)
- user = {'id': 'fake1', 'name': 'fake1', 'enabled': True}
+ user = {'id': 'fake1', 'name': 'fake1', 'enabled': True,
+ 'domain_id': CONF.identity.default_domain_id}
self.identity_api.create_user('fake1', user)
user_ref = self.identity_api.get_user('fake1')
self.assertEqual(user_ref['enabled'], True)
@@ -512,6 +533,7 @@ class LDAPIdentity(test.TestCase, BaseLDAPIdentity):
'id': 'extra_attributes',
'name': 'EXTRA_ATTRIBUTES',
'password': 'extra',
+ 'domain_id': CONF.identity.default_domain_id
}
self.identity_api.create_user(user['id'], user)
dn, attrs = self.identity_api.driver.user._ldap_get(user['id'])
@@ -745,3 +767,230 @@ class LdapIdentitySqlAssignment(sql.Base, test.TestCase, BaseLDAPIdentity):
def test_role_filter(self):
self.skipTest(
'N/A: Not part of SQL backend')
+
+
+class MultiLDAPandSQLIdentity(sql.Base, test.TestCase, BaseLDAPIdentity):
+ """Class to test common SQL plus individual LDAP backends.
+
+ We define a set of domains and domain-specific backends:
+
+ - A separate LDAP backend for the default domain
+ - A separate LDAP backend for domain1
+ - domain2 shares the same LDAP as domain1, but uses a different
+ tree attach point
+ - An SQL backend for all other domains (which will include domain3
+ and domain4)
+
+ Normally one would expect that the default domain would be handled as
+ part of the "other domains" - however the above provides better
+ test coverage since most of the existing backend tests use the default
+ domain.
+
+ """
+ def setUp(self):
+ super(MultiLDAPandSQLIdentity, self).setUp()
+
+ self._set_config()
+ self.load_backends()
+ self.engine = self.get_engine()
+ sql.ModelBase.metadata.create_all(bind=self.engine)
+ self._setup_domain_test_data()
+
+ # All initial domain data setup complete, time to switch on support
+ # for separate backends per domain.
+
+ self.orig_config_domains_enabled = (
+ config.CONF.identity.domain_specific_drivers_enabled)
+ self.opt_in_group('identity', domain_specific_drivers_enabled=True)
+ self.orig_config_dir = (
+ config.CONF.identity.domain_config_dir)
+ self.opt_in_group('identity', domain_config_dir=test.TESTSDIR)
+ self._set_domain_configs()
+ self.clear_database()
+ self.load_fixtures(default_fixtures)
+
+ def tearDown(self):
+ super(MultiLDAPandSQLIdentity, self).tearDown()
+ self.opt_in_group(
+ 'identity',
+ domain_config_dir=self.orig_config_dir)
+ self.opt_in_group(
+ 'identity',
+ domain_specific_drivers_enabled=self.orig_config_domains_enabled)
+ sql.ModelBase.metadata.drop_all(bind=self.engine)
+ self.engine.dispose()
+ sql.set_global_engine(None)
+
+ def _set_config(self):
+ self.config([test.etcdir('keystone.conf.sample'),
+ test.testsdir('test_overrides.conf'),
+ test.testsdir('backend_multi_ldap_sql.conf')])
+
+ def _setup_domain_test_data(self):
+
+ def create_domain(domain):
+ try:
+ ref = self.assignment_api.create_domain(
+ domain['id'], domain)
+ except exception.Conflict:
+ ref = (
+ self.assignment_api.get_domain_by_name(domain['name']))
+ return ref
+
+ self.domain_default = create_domain(assignment.DEFAULT_DOMAIN)
+ self.domain1 = create_domain(
+ {'id': uuid.uuid4().hex, 'name': 'domain1'})
+ self.domain2 = create_domain(
+ {'id': uuid.uuid4().hex, 'name': 'domain2'})
+ self.domain3 = create_domain(
+ {'id': uuid.uuid4().hex, 'name': 'domain3'})
+ self.domain4 = create_domain(
+ {'id': uuid.uuid4().hex, 'name': 'domain4'})
+
+ def _set_domain_configs(self):
+ # We need to load the domain configs explicitly to ensure the
+ # test overrides are included.
+ self.identity_api.domain_configs._load_config(
+ self.identity_api.assignment_api,
+ [test.etcdir('keystone.conf.sample'),
+ test.testsdir('test_overrides.conf'),
+ test.testsdir('backend_multi_ldap_sql.conf'),
+ test.testsdir('keystone.Default.conf')],
+ 'Default')
+ self.identity_api.domain_configs._load_config(
+ self.identity_api.assignment_api,
+ [test.etcdir('keystone.conf.sample'),
+ test.testsdir('test_overrides.conf'),
+ test.testsdir('backend_multi_ldap_sql.conf'),
+ test.testsdir('keystone.domain1.conf')],
+ 'domain1')
+ self.identity_api.domain_configs._load_config(
+ self.identity_api.assignment_api,
+ [test.etcdir('keystone.conf.sample'),
+ test.testsdir('test_overrides.conf'),
+ test.testsdir('backend_multi_ldap_sql.conf'),
+ test.testsdir('keystone.domain2.conf')],
+ 'domain2')
+
+ def reload_backends(self, domain_id):
+ # Just reload the driver for this domain - which will pickup
+ # any updated cfg
+ self.identity_api.domain_configs.reload_domain_driver(
+ self.identity_api.assignment_api, domain_id)
+
+ def get_config(self, domain_id):
+ # Get the config for this domain, will return CONF
+ # if no specific config defined for this domain
+ return self.identity_api.domain_configs.get_domain_conf(domain_id)
+
+ def test_list_domains(self):
+ self.skipTest(
+ 'N/A: Not relevant for multi ldap testing')
+
+ def test_domain_segregation(self):
+ """Test that separate configs have segregated the domain.
+
+ Test Plan:
+ - Create a user in each of the domains
+ - Make sure that you can only find a given user in its
+ relevant domain
+ - Make sure that for a backend that supports multiple domains
+ you can get the users via any of the domain scopes
+
+ """
+ def create_user(domain_id):
+ user = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
+ 'domain_id': domain_id,
+ 'password': uuid.uuid4().hex,
+ 'enabled': True}
+ self.identity_api.create_user(user['id'], user)
+ return user
+
+ userd = create_user(CONF.identity.default_domain_id)
+ user1 = create_user(self.domain1['id'])
+ user2 = create_user(self.domain2['id'])
+ user3 = create_user(self.domain3['id'])
+ user4 = create_user(self.domain4['id'])
+
+ # Now check that I can read user1 with the appropriate domain
+ # scope, but won't find it if the wrong scope is used
+
+ ref = self.identity_api.get_user(
+ userd['id'], domain_scope=CONF.identity.default_domain_id)
+ del userd['password']
+ self.assertDictEqual(ref, userd)
+ self.assertRaises(exception.UserNotFound,
+ self.identity_api.get_user,
+ userd['id'],
+ domain_scope=self.domain1['id'])
+ self.assertRaises(exception.UserNotFound,
+ self.identity_api.get_user,
+ userd['id'],
+ domain_scope=self.domain2['id'])
+ self.assertRaises(exception.UserNotFound,
+ self.identity_api.get_user,
+ userd['id'],
+ domain_scope=self.domain3['id'])
+ self.assertRaises(exception.UserNotFound,
+ self.identity_api.get_user,
+ userd['id'],
+ domain_scope=self.domain4['id'])
+
+ ref = self.identity_api.get_user(
+ user1['id'], domain_scope=self.domain1['id'])
+ del user1['password']
+ self.assertDictEqual(ref, user1)
+ ref = self.identity_api.get_user(
+ user2['id'], domain_scope=self.domain2['id'])
+ del user2['password']
+ self.assertDictEqual(ref, user2)
+
+ # Domains 3 and 4 share the same backend, so you should be
+ # able to see user3 and 4 from either
+
+ ref = self.identity_api.get_user(
+ user3['id'], domain_scope=self.domain3['id'])
+ del user3['password']
+ self.assertDictEqual(ref, user3)
+ ref = self.identity_api.get_user(
+ user4['id'], domain_scope=self.domain4['id'])
+ del user4['password']
+ self.assertDictEqual(ref, user4)
+ ref = self.identity_api.get_user(
+ user3['id'], domain_scope=self.domain4['id'])
+ self.assertDictEqual(ref, user3)
+ ref = self.identity_api.get_user(
+ user4['id'], domain_scope=self.domain3['id'])
+ self.assertDictEqual(ref, user4)
+
+ def test_scanning_of_config_dir(self):
+ """Test the Manager class scans the config directory.
+
+ The setup for the main tests above load the domain configs directly
+ so that the test overrides can be included. This test just makes sure
+ that the standard config directory scanning does pick up the relevant
+ domain config files.
+
+ """
+ # Confirm that config has drivers_enabled as True, which we will
+ # check has been set to False later in this test
+ self.assertTrue(config.CONF.identity.domain_specific_drivers_enabled)
+ self.load_backends()
+ # Execute any command to trigger the lazy loading of domain configs
+ self.identity_api.list_users(domain_scope=self.domain1['id'])
+ # ...and now check the domain configs have been set up
+ self.assertIn('default', self.identity_api.domain_configs)
+ self.assertIn(self.domain1['id'], self.identity_api.domain_configs)
+ self.assertIn(self.domain2['id'], self.identity_api.domain_configs)
+ self.assertNotIn(self.domain3['id'], self.identity_api.domain_configs)
+ self.assertNotIn(self.domain4['id'], self.identity_api.domain_configs)
+
+ # Finally check that a domain specific config contains items from both
+ # the primary config and the domain specific config
+ conf = self.identity_api.domain_configs.get_domain_conf(
+ self.domain1['id'])
+ # This should now be false, as is the default, since this is not
+ # set in the standard primary config file
+ self.assertFalse(conf.identity.domain_specific_drivers_enabled)
+ # ..and make sure a domain-specifc options is also set
+ self.assertEqual(conf.ldap.url, 'fake://memory1')
diff --git a/keystone/token/backends/memcache.py b/keystone/token/backends/memcache.py
index a07a516b..d0d59eef 100644
--- a/keystone/token/backends/memcache.py
+++ b/keystone/token/backends/memcache.py
@@ -29,8 +29,6 @@ from keystone import token
CONF = config.CONF
-config.register_str('servers', group='memcache', default='localhost:11211')
-config.register_int('max_compare_and_set_retry', group='memcache', default=16)
LOG = logging.getLogger(__name__)
diff --git a/keystone/token/core.py b/keystone/token/core.py
index 3959586b..e8d04a7e 100644
--- a/keystone/token/core.py
+++ b/keystone/token/core.py
@@ -29,7 +29,7 @@ from keystone.openstack.common import timeutils
CONF = config.CONF
-config.register_int('expiration', group='token', default=86400)
+
LOG = logging.getLogger(__name__)