aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/identity
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/identity')
-rw-r--r--keystone-moon/keystone/identity/__init__.py17
-rw-r--r--keystone-moon/keystone/identity/backends/__init__.py0
-rw-r--r--keystone-moon/keystone/identity/backends/ldap.py425
-rw-r--r--keystone-moon/keystone/identity/backends/sql.py402
-rw-r--r--keystone-moon/keystone/identity/controllers.py344
-rw-r--r--keystone-moon/keystone/identity/core.py1613
-rw-r--r--keystone-moon/keystone/identity/generator.py55
-rw-r--r--keystone-moon/keystone/identity/id_generators/__init__.py0
-rw-r--r--keystone-moon/keystone/identity/id_generators/sha256.py28
-rw-r--r--keystone-moon/keystone/identity/mapping_backends/__init__.py0
-rw-r--r--keystone-moon/keystone/identity/mapping_backends/mapping.py18
-rw-r--r--keystone-moon/keystone/identity/mapping_backends/sql.py98
-rw-r--r--keystone-moon/keystone/identity/routers.py84
-rw-r--r--keystone-moon/keystone/identity/schema.py67
-rw-r--r--keystone-moon/keystone/identity/shadow_backends/__init__.py0
-rw-r--r--keystone-moon/keystone/identity/shadow_backends/sql.py73
16 files changed, 0 insertions, 3224 deletions
diff --git a/keystone-moon/keystone/identity/__init__.py b/keystone-moon/keystone/identity/__init__.py
deleted file mode 100644
index 96b3ee77..00000000
--- a/keystone-moon/keystone/identity/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from keystone.identity import controllers # noqa
-from keystone.identity.core import * # noqa
-from keystone.identity import generator # noqa
diff --git a/keystone-moon/keystone/identity/backends/__init__.py b/keystone-moon/keystone/identity/backends/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/keystone-moon/keystone/identity/backends/__init__.py
+++ /dev/null
diff --git a/keystone-moon/keystone/identity/backends/ldap.py b/keystone-moon/keystone/identity/backends/ldap.py
deleted file mode 100644
index fe8e8477..00000000
--- a/keystone-moon/keystone/identity/backends/ldap.py
+++ /dev/null
@@ -1,425 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import absolute_import
-import uuid
-
-import ldap.filter
-from oslo_config import cfg
-from oslo_log import log
-from oslo_log import versionutils
-import six
-
-from keystone.common import clean
-from keystone.common import driver_hints
-from keystone.common import ldap as common_ldap
-from keystone.common import models
-from keystone import exception
-from keystone.i18n import _
-from keystone import identity
-
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-_DEPRECATION_MSG = _('%s for the LDAP identity backend has been deprecated in '
- 'the Mitaka release in favor of read-only identity LDAP '
- 'access. It will be removed in the "O" release.')
-
-
-class Identity(identity.IdentityDriverV8):
- def __init__(self, conf=None):
- super(Identity, self).__init__()
- if conf is None:
- self.conf = CONF
- else:
- self.conf = conf
- self.user = UserApi(self.conf)
- self.group = GroupApi(self.conf)
-
- def is_domain_aware(self):
- return False
-
- def generates_uuids(self):
- return False
-
- # Identity interface
-
- def authenticate(self, user_id, password):
- try:
- user_ref = self._get_user(user_id)
- except exception.UserNotFound:
- raise AssertionError(_('Invalid user / password'))
- if not user_id or not password:
- raise AssertionError(_('Invalid user / password'))
- conn = None
- try:
- conn = self.user.get_connection(user_ref['dn'],
- password, end_user_auth=True)
- if not conn:
- raise AssertionError(_('Invalid user / password'))
- except Exception:
- raise AssertionError(_('Invalid user / password'))
- finally:
- if conn:
- conn.unbind_s()
- return self.user.filter_attributes(user_ref)
-
- def _get_user(self, user_id):
- return self.user.get(user_id)
-
- def get_user(self, user_id):
- return self.user.get_filtered(user_id)
-
- def list_users(self, hints):
- return self.user.get_all_filtered(hints)
-
- def get_user_by_name(self, user_name, domain_id):
- # domain_id will already have been handled in the Manager layer,
- # parameter left in so this matches the Driver specification
- return self.user.filter_attributes(self.user.get_by_name(user_name))
-
- # CRUD
- def create_user(self, user_id, user):
- msg = _DEPRECATION_MSG % "create_user"
- versionutils.report_deprecated_feature(LOG, msg)
- self.user.check_allow_create()
- user_ref = self.user.create(user)
- return self.user.filter_attributes(user_ref)
-
- def update_user(self, user_id, user):
- msg = _DEPRECATION_MSG % "update_user"
- versionutils.report_deprecated_feature(LOG, msg)
- self.user.check_allow_update()
- old_obj = self.user.get(user_id)
- if 'name' in user and old_obj.get('name') != user['name']:
- raise exception.Conflict(_('Cannot change user name'))
-
- if self.user.enabled_mask:
- self.user.mask_enabled_attribute(user)
- elif self.user.enabled_invert and not self.user.enabled_emulation:
- # We need to invert the enabled value for the old model object
- # to prevent the LDAP update code from thinking that the enabled
- # values are already equal.
- user['enabled'] = not user['enabled']
- old_obj['enabled'] = not old_obj['enabled']
-
- self.user.update(user_id, user, old_obj)
- return self.user.get_filtered(user_id)
-
- def delete_user(self, user_id):
- msg = _DEPRECATION_MSG % "delete_user"
- versionutils.report_deprecated_feature(LOG, msg)
- self.user.check_allow_delete()
- user = self.user.get(user_id)
- user_dn = user['dn']
- groups = self.group.list_user_groups(user_dn)
- for group in groups:
- self.group.remove_user(user_dn, group['id'], user_id)
-
- if hasattr(user, 'tenant_id'):
- self.project.remove_user(user.tenant_id, user_dn)
- self.user.delete(user_id)
-
- def create_group(self, group_id, group):
- msg = _DEPRECATION_MSG % "create_group"
- versionutils.report_deprecated_feature(LOG, msg)
- self.group.check_allow_create()
- group['name'] = clean.group_name(group['name'])
- return common_ldap.filter_entity(self.group.create(group))
-
- def get_group(self, group_id):
- return self.group.get_filtered(group_id)
-
- def get_group_by_name(self, group_name, domain_id):
- # domain_id will already have been handled in the Manager layer,
- # parameter left in so this matches the Driver specification
- return self.group.get_filtered_by_name(group_name)
-
- def update_group(self, group_id, group):
- msg = _DEPRECATION_MSG % "update_group"
- versionutils.report_deprecated_feature(LOG, msg)
- self.group.check_allow_update()
- if 'name' in group:
- group['name'] = clean.group_name(group['name'])
- return common_ldap.filter_entity(self.group.update(group_id, group))
-
- def delete_group(self, group_id):
- msg = _DEPRECATION_MSG % "delete_group"
- versionutils.report_deprecated_feature(LOG, msg)
- self.group.check_allow_delete()
- return self.group.delete(group_id)
-
- def add_user_to_group(self, user_id, group_id):
- msg = _DEPRECATION_MSG % "add_user_to_group"
- versionutils.report_deprecated_feature(LOG, msg)
- user_ref = self._get_user(user_id)
- user_dn = user_ref['dn']
- self.group.add_user(user_dn, group_id, user_id)
-
- def remove_user_from_group(self, user_id, group_id):
- msg = _DEPRECATION_MSG % "remove_user_from_group"
- versionutils.report_deprecated_feature(LOG, msg)
- user_ref = self._get_user(user_id)
- user_dn = user_ref['dn']
- self.group.remove_user(user_dn, group_id, user_id)
-
- def list_groups_for_user(self, user_id, hints):
- user_ref = self._get_user(user_id)
- if self.conf.ldap.group_members_are_ids:
- user_dn = user_ref['id']
- else:
- user_dn = user_ref['dn']
- return self.group.list_user_groups_filtered(user_dn, hints)
-
- def list_groups(self, hints):
- return self.group.get_all_filtered(hints)
-
- def list_users_in_group(self, group_id, hints):
- users = []
- for user_key in self.group.list_group_users(group_id):
- if self.conf.ldap.group_members_are_ids:
- user_id = user_key
- else:
- user_id = self.user._dn_to_id(user_key)
-
- try:
- users.append(self.user.get_filtered(user_id))
- except exception.UserNotFound:
- LOG.debug(("Group member '%(user_key)s' not found in"
- " '%(group_id)s'. The user should be removed"
- " from the group. The user will be ignored."),
- dict(user_key=user_key, group_id=group_id))
- return users
-
- def check_user_in_group(self, user_id, group_id):
- user_refs = self.list_users_in_group(group_id, driver_hints.Hints())
- for x in user_refs:
- if x['id'] == user_id:
- break
- else:
- # Try to fetch the user to see if it even exists. This
- # will raise a more accurate exception.
- self.get_user(user_id)
- raise exception.NotFound(_("User '%(user_id)s' not found in"
- " group '%(group_id)s'") %
- {'user_id': user_id,
- 'group_id': group_id})
-
-
-# TODO(termie): turn this into a data object and move logic to driver
-class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):
- DEFAULT_OU = 'ou=Users'
- DEFAULT_STRUCTURAL_CLASSES = ['person']
- DEFAULT_ID_ATTR = 'cn'
- DEFAULT_OBJECTCLASS = 'inetOrgPerson'
- NotFound = exception.UserNotFound
- options_name = 'user'
- attribute_options_names = {'password': 'pass',
- 'email': 'mail',
- 'name': 'name',
- 'description': 'description',
- 'enabled': 'enabled',
- 'default_project_id': 'default_project_id'}
- immutable_attrs = ['id']
-
- model = models.User
-
- def __init__(self, conf):
- super(UserApi, self).__init__(conf)
- self.enabled_mask = conf.ldap.user_enabled_mask
- self.enabled_default = conf.ldap.user_enabled_default
- self.enabled_invert = conf.ldap.user_enabled_invert
- self.enabled_emulation = conf.ldap.user_enabled_emulation
-
- def _ldap_res_to_model(self, res):
- obj = super(UserApi, self)._ldap_res_to_model(res)
- if self.enabled_mask != 0:
- enabled = int(obj.get('enabled', self.enabled_default))
- obj['enabled'] = ((enabled & self.enabled_mask) !=
- self.enabled_mask)
- elif self.enabled_invert and not self.enabled_emulation:
- # This could be a bool or a string. If it's a string,
- # we need to convert it so we can invert it properly.
- enabled = obj.get('enabled', self.enabled_default)
- if isinstance(enabled, six.string_types):
- if enabled.lower() == 'true':
- enabled = True
- else:
- enabled = False
- obj['enabled'] = not enabled
- obj['dn'] = res[0]
-
- return obj
-
- def mask_enabled_attribute(self, values):
- value = values['enabled']
- values.setdefault('enabled_nomask', int(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 create(self, values):
- if self.enabled_mask:
- orig_enabled = values['enabled']
- self.mask_enabled_attribute(values)
- elif self.enabled_invert and not self.enabled_emulation:
- orig_enabled = values['enabled']
- if orig_enabled is not None:
- values['enabled'] = not orig_enabled
- else:
- values['enabled'] = self.enabled_default
- values = super(UserApi, self).create(values)
- if self.enabled_mask or (self.enabled_invert and
- not self.enabled_emulation):
- values['enabled'] = orig_enabled
- return values
-
- def get_filtered(self, user_id):
- user = self.get(user_id)
- return self.filter_attributes(user)
-
- def get_all_filtered(self, hints):
- query = self.filter_query(hints, self.ldap_filter)
- return [self.filter_attributes(user)
- for user in self.get_all(query, hints)]
-
- def filter_attributes(self, user):
- return identity.filter_user(common_ldap.filter_entity(user))
-
- def is_user(self, dn):
- """Returns True if the entry is a user."""
- # NOTE(blk-u): It's easy to check if the DN is under the User tree,
- # but may not be accurate. A more accurate test would be to fetch the
- # entry to see if it's got the user objectclass, but this could be
- # really expensive considering how this is used.
-
- return common_ldap.dn_startswith(dn, self.tree_dn)
-
-
-class GroupApi(common_ldap.BaseLdap):
- DEFAULT_OU = 'ou=UserGroups'
- DEFAULT_STRUCTURAL_CLASSES = []
- DEFAULT_OBJECTCLASS = 'groupOfNames'
- DEFAULT_ID_ATTR = 'cn'
- DEFAULT_MEMBER_ATTRIBUTE = 'member'
- NotFound = exception.GroupNotFound
- options_name = 'group'
- attribute_options_names = {'description': 'desc',
- 'name': 'name'}
- immutable_attrs = ['name']
- model = models.Group
-
- def _ldap_res_to_model(self, res):
- model = super(GroupApi, self)._ldap_res_to_model(res)
- model['dn'] = res[0]
- return model
-
- def __init__(self, conf):
- super(GroupApi, self).__init__(conf)
- self.member_attribute = (conf.ldap.group_member_attribute
- or self.DEFAULT_MEMBER_ATTRIBUTE)
-
- def create(self, values):
- data = values.copy()
- if data.get('id') is None:
- data['id'] = uuid.uuid4().hex
- if 'description' in data and data['description'] in ['', None]:
- data.pop('description')
- return super(GroupApi, self).create(data)
-
- def delete(self, group_id):
- if self.subtree_delete_enabled:
- super(GroupApi, self).delete_tree(group_id)
- else:
- # TODO(spzala): this is only placeholder for group and domain
- # role support which will be added under bug 1101287
-
- group_ref = self.get(group_id)
- group_dn = group_ref['dn']
- if group_dn:
- self._delete_tree_nodes(group_dn, ldap.SCOPE_ONELEVEL)
- super(GroupApi, self).delete(group_id)
-
- def update(self, group_id, values):
- old_obj = self.get(group_id)
- return super(GroupApi, self).update(group_id, values, old_obj)
-
- def add_user(self, user_dn, group_id, user_id):
- group_ref = self.get(group_id)
- group_dn = group_ref['dn']
- try:
- super(GroupApi, self).add_member(user_dn, group_dn)
- except exception.Conflict:
- raise exception.Conflict(_(
- 'User %(user_id)s is already a member of group %(group_id)s') %
- {'user_id': user_id, 'group_id': group_id})
-
- def remove_user(self, user_dn, group_id, user_id):
- group_ref = self.get(group_id)
- group_dn = group_ref['dn']
- try:
- super(GroupApi, self).remove_member(user_dn, group_dn)
- except ldap.NO_SUCH_ATTRIBUTE:
- raise exception.UserNotFound(user_id=user_id)
-
- def list_user_groups(self, user_dn):
- """Return a list of groups for which the user is a member."""
- user_dn_esc = ldap.filter.escape_filter_chars(user_dn)
- query = '(%s=%s)%s' % (self.member_attribute,
- user_dn_esc,
- self.ldap_filter or '')
- return self.get_all(query)
-
- def list_user_groups_filtered(self, user_dn, hints):
- """Return a filtered list of groups for which the user is a member."""
- user_dn_esc = ldap.filter.escape_filter_chars(user_dn)
- query = '(%s=%s)%s' % (self.member_attribute,
- user_dn_esc,
- self.ldap_filter or '')
- return self.get_all_filtered(hints, query)
-
- def list_group_users(self, group_id):
- """Return a list of user dns which are members of a group."""
- group_ref = self.get(group_id)
- group_dn = group_ref['dn']
-
- try:
- attrs = self._ldap_get_list(group_dn, ldap.SCOPE_BASE,
- attrlist=[self.member_attribute])
- except ldap.NO_SUCH_OBJECT:
- raise self.NotFound(group_id=group_id)
-
- users = []
- for dn, member in attrs:
- user_dns = member.get(self.member_attribute, [])
- for user_dn in user_dns:
- if self._is_dumb_member(user_dn):
- continue
- users.append(user_dn)
- return users
-
- def get_filtered(self, group_id):
- group = self.get(group_id)
- return common_ldap.filter_entity(group)
-
- def get_filtered_by_name(self, group_name):
- group = self.get_by_name(group_name)
- return common_ldap.filter_entity(group)
-
- def get_all_filtered(self, hints, query=None):
- query = self.filter_query(hints, query)
- return [common_ldap.filter_entity(group)
- for group in self.get_all(query, hints)]
diff --git a/keystone-moon/keystone/identity/backends/sql.py b/keystone-moon/keystone/identity/backends/sql.py
deleted file mode 100644
index 5680a8a2..00000000
--- a/keystone-moon/keystone/identity/backends/sql.py
+++ /dev/null
@@ -1,402 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import sqlalchemy
-from sqlalchemy.ext.hybrid import hybrid_property
-from sqlalchemy import orm
-
-from keystone.common import driver_hints
-from keystone.common import sql
-from keystone.common import utils
-from keystone import exception
-from keystone.i18n import _
-from keystone import identity
-
-
-class User(sql.ModelBase, sql.DictBase):
- __tablename__ = 'user'
- attributes = ['id', 'name', 'domain_id', 'password', 'enabled',
- 'default_project_id']
- id = sql.Column(sql.String(64), primary_key=True)
- enabled = sql.Column(sql.Boolean)
- extra = sql.Column(sql.JsonBlob())
- default_project_id = sql.Column(sql.String(64))
- local_user = orm.relationship('LocalUser', uselist=False,
- single_parent=True, lazy='subquery',
- cascade='all,delete-orphan', backref='user')
- federated_users = orm.relationship('FederatedUser',
- single_parent=True,
- lazy='subquery',
- cascade='all,delete-orphan',
- backref='user')
-
- # name property
- @hybrid_property
- def name(self):
- if self.local_user:
- return self.local_user.name
- elif self.federated_users:
- return self.federated_users[0].display_name
- else:
- return None
-
- @name.setter
- def name(self, value):
- if not self.local_user:
- self.local_user = LocalUser()
- self.local_user.name = value
-
- @name.expression
- def name(cls):
- return LocalUser.name
-
- # password property
- @hybrid_property
- def password(self):
- if self.local_user and self.local_user.passwords:
- return self.local_user.passwords[0].password
- else:
- return None
-
- @password.setter
- def password(self, value):
- if not value:
- if self.local_user and self.local_user.passwords:
- self.local_user.passwords = []
- else:
- if not self.local_user:
- self.local_user = LocalUser()
- if not self.local_user.passwords:
- self.local_user.passwords.append(Password())
- self.local_user.passwords[0].password = value
-
- @password.expression
- def password(cls):
- return Password.password
-
- # domain_id property
- @hybrid_property
- def domain_id(self):
- if self.local_user:
- return self.local_user.domain_id
- else:
- return None
-
- @domain_id.setter
- def domain_id(self, value):
- if not self.local_user:
- self.local_user = LocalUser()
- self.local_user.domain_id = value
-
- @domain_id.expression
- def domain_id(cls):
- return LocalUser.domain_id
-
- def to_dict(self, include_extra_dict=False):
- d = super(User, self).to_dict(include_extra_dict=include_extra_dict)
- if 'default_project_id' in d and d['default_project_id'] is None:
- del d['default_project_id']
- return d
-
-
-class LocalUser(sql.ModelBase, sql.DictBase):
- __tablename__ = 'local_user'
- attributes = ['id', 'user_id', 'domain_id', 'name']
- id = sql.Column(sql.Integer, primary_key=True)
- user_id = sql.Column(sql.String(64), sql.ForeignKey('user.id',
- ondelete='CASCADE'), unique=True)
- domain_id = sql.Column(sql.String(64), nullable=False)
- name = sql.Column(sql.String(255), nullable=False)
- passwords = orm.relationship('Password', single_parent=True,
- cascade='all,delete-orphan',
- backref='local_user')
- __table_args__ = (sql.UniqueConstraint('domain_id', 'name'), {})
-
-
-class Password(sql.ModelBase, sql.DictBase):
- __tablename__ = 'password'
- attributes = ['id', 'local_user_id', 'password']
- id = sql.Column(sql.Integer, primary_key=True)
- local_user_id = sql.Column(sql.Integer, sql.ForeignKey('local_user.id',
- ondelete='CASCADE'))
- password = sql.Column(sql.String(128))
-
-
-class FederatedUser(sql.ModelBase, sql.ModelDictMixin):
- __tablename__ = 'federated_user'
- attributes = ['id', 'user_id', 'idp_id', 'protocol_id', 'unique_id',
- 'display_name']
- id = sql.Column(sql.Integer, primary_key=True)
- user_id = sql.Column(sql.String(64), sql.ForeignKey('user.id',
- ondelete='CASCADE'))
- idp_id = sql.Column(sql.String(64), sql.ForeignKey('identity_provider.id',
- ondelete='CASCADE'))
- protocol_id = sql.Column(sql.String(64), nullable=False)
- unique_id = sql.Column(sql.String(255), nullable=False)
- display_name = sql.Column(sql.String(255), nullable=True)
- __table_args__ = (
- sql.UniqueConstraint('idp_id', 'protocol_id', 'unique_id'),
- sqlalchemy.ForeignKeyConstraint(['protocol_id', 'idp_id'],
- ['federation_protocol.id',
- 'federation_protocol.idp_id'])
- )
-
-
-class Group(sql.ModelBase, sql.DictBase):
- __tablename__ = 'group'
- attributes = ['id', 'name', 'domain_id', 'description']
- id = sql.Column(sql.String(64), primary_key=True)
- name = sql.Column(sql.String(64), nullable=False)
- domain_id = sql.Column(sql.String(64), nullable=False)
- description = sql.Column(sql.Text())
- extra = sql.Column(sql.JsonBlob())
- # Unique constraint across two columns to create the separation
- # rather than just only 'name' being unique
- __table_args__ = (sql.UniqueConstraint('domain_id', 'name'),)
-
-
-class UserGroupMembership(sql.ModelBase, sql.DictBase):
- """Group membership join table."""
-
- __tablename__ = 'user_group_membership'
- user_id = sql.Column(sql.String(64),
- sql.ForeignKey('user.id'),
- primary_key=True)
- group_id = sql.Column(sql.String(64),
- sql.ForeignKey('group.id'),
- primary_key=True)
-
-
-class Identity(identity.IdentityDriverV8):
- # NOTE(henry-nash): Override the __init__() method so as to take a
- # config parameter to enable sql to be used as a domain-specific driver.
- def __init__(self, conf=None):
- self.conf = conf
- super(Identity, self).__init__()
-
- @property
- def is_sql(self):
- return True
-
- def _check_password(self, password, user_ref):
- """Check the specified password against the data store.
-
- Note that we'll pass in the entire user_ref in case the subclass
- needs things like user_ref.get('name')
- For further justification, please see the follow up suggestion at
- https://blueprints.launchpad.net/keystone/+spec/sql-identiy-pam
-
- """
- return utils.check_password(password, user_ref.password)
-
- # Identity interface
- def authenticate(self, user_id, password):
- with sql.session_for_read() as session:
- user_ref = None
- try:
- user_ref = self._get_user(session, user_id)
- except exception.UserNotFound:
- raise AssertionError(_('Invalid user / password'))
- if not self._check_password(password, user_ref):
- raise AssertionError(_('Invalid user / password'))
- return identity.filter_user(user_ref.to_dict())
-
- # user crud
-
- @sql.handle_conflicts(conflict_type='user')
- def create_user(self, user_id, user):
- user = utils.hash_user_password(user)
- with sql.session_for_write() as session:
- user_ref = User.from_dict(user)
- session.add(user_ref)
- return identity.filter_user(user_ref.to_dict())
-
- @driver_hints.truncated
- def list_users(self, hints):
- with sql.session_for_read() as session:
- query = session.query(User).outerjoin(LocalUser)
- user_refs = sql.filter_limit_query(User, query, hints)
- return [identity.filter_user(x.to_dict()) for x in user_refs]
-
- def _get_user(self, session, user_id):
- user_ref = session.query(User).get(user_id)
- if not user_ref:
- raise exception.UserNotFound(user_id=user_id)
- return user_ref
-
- def get_user(self, user_id):
- with sql.session_for_read() as session:
- return identity.filter_user(
- self._get_user(session, user_id).to_dict())
-
- def get_user_by_name(self, user_name, domain_id):
- with sql.session_for_read() as session:
- query = session.query(User).join(LocalUser)
- query = query.filter(sqlalchemy.and_(LocalUser.name == user_name,
- LocalUser.domain_id == domain_id))
- try:
- user_ref = query.one()
- except sql.NotFound:
- raise exception.UserNotFound(user_id=user_name)
- return identity.filter_user(user_ref.to_dict())
-
- @sql.handle_conflicts(conflict_type='user')
- def update_user(self, user_id, user):
- with sql.session_for_write() as session:
- user_ref = self._get_user(session, user_id)
- old_user_dict = user_ref.to_dict()
- user = utils.hash_user_password(user)
- for k in user:
- old_user_dict[k] = user[k]
- new_user = User.from_dict(old_user_dict)
- for attr in User.attributes:
- if attr != 'id':
- setattr(user_ref, attr, getattr(new_user, attr))
- user_ref.extra = new_user.extra
- return identity.filter_user(
- user_ref.to_dict(include_extra_dict=True))
-
- def add_user_to_group(self, user_id, group_id):
- with sql.session_for_write() as session:
- self.get_group(group_id)
- self.get_user(user_id)
- query = session.query(UserGroupMembership)
- query = query.filter_by(user_id=user_id)
- query = query.filter_by(group_id=group_id)
- rv = query.first()
- if rv:
- return
-
- session.add(UserGroupMembership(user_id=user_id,
- group_id=group_id))
-
- def check_user_in_group(self, user_id, group_id):
- with sql.session_for_read() as session:
- self.get_group(group_id)
- self.get_user(user_id)
- query = session.query(UserGroupMembership)
- query = query.filter_by(user_id=user_id)
- query = query.filter_by(group_id=group_id)
- if not query.first():
- raise exception.NotFound(_("User '%(user_id)s' not found in"
- " group '%(group_id)s'") %
- {'user_id': user_id,
- 'group_id': group_id})
-
- def remove_user_from_group(self, user_id, group_id):
- # We don't check if user or group are still valid and let the remove
- # be tried anyway - in case this is some kind of clean-up operation
- with sql.session_for_write() as session:
- query = session.query(UserGroupMembership)
- query = query.filter_by(user_id=user_id)
- query = query.filter_by(group_id=group_id)
- membership_ref = query.first()
- if membership_ref is None:
- # Check if the group and user exist to return descriptive
- # exceptions.
- self.get_group(group_id)
- self.get_user(user_id)
- raise exception.NotFound(_("User '%(user_id)s' not found in"
- " group '%(group_id)s'") %
- {'user_id': user_id,
- 'group_id': group_id})
- session.delete(membership_ref)
-
- def list_groups_for_user(self, user_id, hints):
- with sql.session_for_read() as session:
- self.get_user(user_id)
- query = session.query(Group).join(UserGroupMembership)
- query = query.filter(UserGroupMembership.user_id == user_id)
- query = sql.filter_limit_query(Group, query, hints)
- return [g.to_dict() for g in query]
-
- def list_users_in_group(self, group_id, hints):
- with sql.session_for_read() as session:
- self.get_group(group_id)
- query = session.query(User).outerjoin(LocalUser)
- query = query.join(UserGroupMembership)
- query = query.filter(UserGroupMembership.group_id == group_id)
- query = sql.filter_limit_query(User, query, hints)
- return [identity.filter_user(u.to_dict()) for u in query]
-
- def delete_user(self, user_id):
- with sql.session_for_write() as session:
- ref = self._get_user(session, user_id)
-
- q = session.query(UserGroupMembership)
- q = q.filter_by(user_id=user_id)
- q.delete(False)
-
- session.delete(ref)
-
- # group crud
-
- @sql.handle_conflicts(conflict_type='group')
- def create_group(self, group_id, group):
- with sql.session_for_write() as session:
- ref = Group.from_dict(group)
- session.add(ref)
- return ref.to_dict()
-
- @driver_hints.truncated
- def list_groups(self, hints):
- with sql.session_for_read() as session:
- query = session.query(Group)
- refs = sql.filter_limit_query(Group, query, hints)
- return [ref.to_dict() for ref in refs]
-
- def _get_group(self, session, group_id):
- ref = session.query(Group).get(group_id)
- if not ref:
- raise exception.GroupNotFound(group_id=group_id)
- return ref
-
- def get_group(self, group_id):
- with sql.session_for_read() as session:
- return self._get_group(session, group_id).to_dict()
-
- def get_group_by_name(self, group_name, domain_id):
- with sql.session_for_read() as session:
- query = session.query(Group)
- query = query.filter_by(name=group_name)
- query = query.filter_by(domain_id=domain_id)
- try:
- group_ref = query.one()
- except sql.NotFound:
- raise exception.GroupNotFound(group_id=group_name)
- return group_ref.to_dict()
-
- @sql.handle_conflicts(conflict_type='group')
- def update_group(self, group_id, group):
- with sql.session_for_write() as session:
- ref = self._get_group(session, group_id)
- old_dict = ref.to_dict()
- for k in group:
- old_dict[k] = group[k]
- new_group = Group.from_dict(old_dict)
- for attr in Group.attributes:
- if attr != 'id':
- setattr(ref, attr, getattr(new_group, attr))
- ref.extra = new_group.extra
- return ref.to_dict()
-
- def delete_group(self, group_id):
- with sql.session_for_write() as session:
- ref = self._get_group(session, group_id)
-
- q = session.query(UserGroupMembership)
- q = q.filter_by(group_id=group_id)
- q.delete(False)
-
- session.delete(ref)
diff --git a/keystone-moon/keystone/identity/controllers.py b/keystone-moon/keystone/identity/controllers.py
deleted file mode 100644
index 9e8ba6fc..00000000
--- a/keystone-moon/keystone/identity/controllers.py
+++ /dev/null
@@ -1,344 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""Workflow Logic the Identity service."""
-
-from oslo_config import cfg
-from oslo_log import log
-
-from keystone.common import controller
-from keystone.common import dependency
-from keystone.common import validation
-from keystone import exception
-from keystone.i18n import _, _LW
-from keystone.identity import schema
-from keystone import notifications
-
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-@dependency.requires('assignment_api', 'identity_api', 'resource_api')
-class User(controller.V2Controller):
-
- @controller.v2_deprecated
- def get_user(self, context, user_id):
- self.assert_admin(context)
- ref = self.identity_api.get_user(user_id)
- return {'user': self.v3_to_v2_user(ref)}
-
- @controller.v2_deprecated
- def get_users(self, context):
- # NOTE(termie): i can't imagine that this really wants all the data
- # about every single user in the system...
- if 'name' in context['query_string']:
- return self.get_user_by_name(
- context, context['query_string'].get('name'))
-
- self.assert_admin(context)
- user_list = self.identity_api.list_users(
- CONF.identity.default_domain_id)
- return {'users': self.v3_to_v2_user(user_list)}
-
- @controller.v2_deprecated
- def get_user_by_name(self, context, user_name):
- self.assert_admin(context)
- ref = self.identity_api.get_user_by_name(
- user_name, CONF.identity.default_domain_id)
- return {'user': self.v3_to_v2_user(ref)}
-
- # CRUD extension
- @controller.v2_deprecated
- def create_user(self, context, user):
- user = self._normalize_OSKSADM_password_on_request(user)
- user = self.normalize_username_in_request(user)
- user = self._normalize_dict(user)
- self.assert_admin(context)
-
- if 'name' not in user or not user['name']:
- msg = _('Name field is required and cannot be empty')
- raise exception.ValidationError(message=msg)
- if 'enabled' in user and not isinstance(user['enabled'], bool):
- msg = _('Enabled field must be a boolean')
- raise exception.ValidationError(message=msg)
-
- default_project_id = user.pop('tenantId', None)
- if default_project_id is not None:
- # Check to see if the project is valid before moving on.
- self.resource_api.get_project(default_project_id)
- user['default_project_id'] = default_project_id
-
- self.resource_api.ensure_default_domain_exists()
-
- # The manager layer will generate the unique ID for users
- user_ref = self._normalize_domain_id(context, user.copy())
- initiator = notifications._get_request_audit_info(context)
- new_user_ref = self.v3_to_v2_user(
- self.identity_api.create_user(user_ref, initiator))
-
- if default_project_id is not None:
- self.assignment_api.add_user_to_project(default_project_id,
- new_user_ref['id'])
- return {'user': new_user_ref}
-
- @controller.v2_deprecated
- def update_user(self, context, user_id, user):
- # NOTE(termie): this is really more of a patch than a put
- user = self.normalize_username_in_request(user)
- self.assert_admin(context)
-
- if 'enabled' in user and not isinstance(user['enabled'], bool):
- msg = _('Enabled field should be a boolean')
- raise exception.ValidationError(message=msg)
-
- default_project_id = user.pop('tenantId', None)
- if default_project_id is not None:
- user['default_project_id'] = default_project_id
-
- old_user_ref = self.v3_to_v2_user(
- self.identity_api.get_user(user_id))
-
- # Check whether a tenant is being added or changed for the user.
- # Catch the case where the tenant is being changed for a user and also
- # where a user previously had no tenant but a tenant is now being
- # added for the user.
- if (('tenantId' in old_user_ref and
- old_user_ref['tenantId'] != default_project_id and
- default_project_id is not None) or
- ('tenantId' not in old_user_ref and
- default_project_id is not None)):
- # Make sure the new project actually exists before we perform the
- # user update.
- self.resource_api.get_project(default_project_id)
-
- initiator = notifications._get_request_audit_info(context)
- user_ref = self.v3_to_v2_user(
- self.identity_api.update_user(user_id, user, initiator))
-
- # If 'tenantId' is in either ref, we might need to add or remove the
- # user from a project.
- if 'tenantId' in user_ref or 'tenantId' in old_user_ref:
- if user_ref['tenantId'] != old_user_ref.get('tenantId'):
- if old_user_ref.get('tenantId'):
- try:
- member_role_id = CONF.member_role_id
- self.assignment_api.remove_role_from_user_and_project(
- user_id, old_user_ref['tenantId'], member_role_id)
- except exception.NotFound:
- # NOTE(morganfainberg): This is not a critical error it
- # just means that the user cannot be removed from the
- # old tenant. This could occur if roles aren't found
- # or if the project is invalid or if there are no roles
- # for the user on that project.
- msg = _LW('Unable to remove user %(user)s from '
- '%(tenant)s.')
- LOG.warning(msg, {'user': user_id,
- 'tenant': old_user_ref['tenantId']})
-
- if user_ref['tenantId']:
- try:
- self.assignment_api.add_user_to_project(
- user_ref['tenantId'], user_id)
- except exception.Conflict: # nosec
- # We are already a member of that tenant
- pass
- except exception.NotFound:
- # NOTE(morganfainberg): Log this and move on. This is
- # not the end of the world if we can't add the user to
- # the appropriate tenant. Most of the time this means
- # that the project is invalid or roles are some how
- # incorrect. This shouldn't prevent the return of the
- # new ref.
- msg = _LW('Unable to add user %(user)s to %(tenant)s.')
- LOG.warning(msg, {'user': user_id,
- 'tenant': user_ref['tenantId']})
-
- return {'user': user_ref}
-
- @controller.v2_deprecated
- def delete_user(self, context, user_id):
- self.assert_admin(context)
- initiator = notifications._get_request_audit_info(context)
- self.identity_api.delete_user(user_id, initiator)
-
- @controller.v2_deprecated
- def set_user_enabled(self, context, user_id, user):
- return self.update_user(context, user_id, user)
-
- @controller.v2_deprecated
- def set_user_password(self, context, user_id, user):
- user = self._normalize_OSKSADM_password_on_request(user)
- return self.update_user(context, user_id, user)
-
- @staticmethod
- def _normalize_OSKSADM_password_on_request(ref):
- """Sets the password from the OS-KSADM Admin Extension.
-
- The OS-KSADM Admin Extension documentation says that
- `OS-KSADM:password` can be used in place of `password`.
-
- """
- if 'OS-KSADM:password' in ref:
- ref['password'] = ref.pop('OS-KSADM:password')
- return ref
-
-
-@dependency.requires('identity_api')
-class UserV3(controller.V3Controller):
- collection_name = 'users'
- member_name = 'user'
-
- def __init__(self):
- super(UserV3, self).__init__()
- self.get_member_from_driver = self.identity_api.get_user
-
- def _check_user_and_group_protection(self, context, prep_info,
- user_id, group_id):
- ref = {}
- ref['user'] = self.identity_api.get_user(user_id)
- ref['group'] = self.identity_api.get_group(group_id)
- self.check_protection(context, prep_info, ref)
-
- @controller.protected()
- @validation.validated(schema.user_create, 'user')
- def create_user(self, context, user):
- # The manager layer will generate the unique ID for users
- ref = self._normalize_dict(user)
- ref = self._normalize_domain_id(context, ref)
- initiator = notifications._get_request_audit_info(context)
- ref = self.identity_api.create_user(ref, initiator)
- return UserV3.wrap_member(context, ref)
-
- @controller.filterprotected('domain_id', 'enabled', 'name')
- def list_users(self, context, filters):
- hints = UserV3.build_driver_hints(context, filters)
- refs = self.identity_api.list_users(
- domain_scope=self._get_domain_id_for_list_request(context),
- hints=hints)
- return UserV3.wrap_collection(context, refs, hints=hints)
-
- @controller.filterprotected('domain_id', 'enabled', 'name')
- def list_users_in_group(self, context, filters, group_id):
- hints = UserV3.build_driver_hints(context, filters)
- refs = self.identity_api.list_users_in_group(group_id, hints=hints)
- return UserV3.wrap_collection(context, refs, hints=hints)
-
- @controller.protected()
- def get_user(self, context, user_id):
- ref = self.identity_api.get_user(user_id)
- return UserV3.wrap_member(context, ref)
-
- def _update_user(self, context, user_id, user):
- self._require_matching_id(user_id, user)
- self._require_matching_domain_id(
- user_id, user, self.identity_api.get_user)
- initiator = notifications._get_request_audit_info(context)
- ref = self.identity_api.update_user(user_id, user, initiator)
- return UserV3.wrap_member(context, ref)
-
- @controller.protected()
- @validation.validated(schema.user_update, 'user')
- def update_user(self, context, user_id, user):
- return self._update_user(context, user_id, user)
-
- @controller.protected(callback=_check_user_and_group_protection)
- def add_user_to_group(self, context, user_id, group_id):
- initiator = notifications._get_request_audit_info(context)
- self.identity_api.add_user_to_group(user_id, group_id, initiator)
-
- @controller.protected(callback=_check_user_and_group_protection)
- def check_user_in_group(self, context, user_id, group_id):
- return self.identity_api.check_user_in_group(user_id, group_id)
-
- @controller.protected(callback=_check_user_and_group_protection)
- def remove_user_from_group(self, context, user_id, group_id):
- initiator = notifications._get_request_audit_info(context)
- self.identity_api.remove_user_from_group(user_id, group_id, initiator)
-
- @controller.protected()
- def delete_user(self, context, user_id):
- initiator = notifications._get_request_audit_info(context)
- return self.identity_api.delete_user(user_id, initiator)
-
- @controller.protected()
- def change_password(self, context, user_id, user):
- original_password = user.get('original_password')
- if original_password is None:
- raise exception.ValidationError(target='user',
- attribute='original_password')
-
- password = user.get('password')
- if password is None:
- raise exception.ValidationError(target='user',
- attribute='password')
- try:
- self.identity_api.change_password(
- context, user_id, original_password, password)
- except AssertionError:
- raise exception.Unauthorized()
-
-
-@dependency.requires('identity_api')
-class GroupV3(controller.V3Controller):
- collection_name = 'groups'
- member_name = 'group'
-
- def __init__(self):
- super(GroupV3, self).__init__()
- self.get_member_from_driver = self.identity_api.get_group
-
- @controller.protected()
- @validation.validated(schema.group_create, 'group')
- def create_group(self, context, group):
- # The manager layer will generate the unique ID for groups
- ref = self._normalize_dict(group)
- ref = self._normalize_domain_id(context, ref)
- initiator = notifications._get_request_audit_info(context)
- ref = self.identity_api.create_group(ref, initiator)
- return GroupV3.wrap_member(context, ref)
-
- @controller.filterprotected('domain_id', 'name')
- def list_groups(self, context, filters):
- hints = GroupV3.build_driver_hints(context, filters)
- refs = self.identity_api.list_groups(
- domain_scope=self._get_domain_id_for_list_request(context),
- hints=hints)
- return GroupV3.wrap_collection(context, refs, hints=hints)
-
- @controller.filterprotected('name')
- def list_groups_for_user(self, context, filters, user_id):
- hints = GroupV3.build_driver_hints(context, filters)
- refs = self.identity_api.list_groups_for_user(user_id, hints=hints)
- return GroupV3.wrap_collection(context, refs, hints=hints)
-
- @controller.protected()
- def get_group(self, context, group_id):
- ref = self.identity_api.get_group(group_id)
- return GroupV3.wrap_member(context, ref)
-
- @controller.protected()
- @validation.validated(schema.group_update, 'group')
- def update_group(self, context, group_id, group):
- self._require_matching_id(group_id, group)
- self._require_matching_domain_id(
- group_id, group, self.identity_api.get_group)
- initiator = notifications._get_request_audit_info(context)
- ref = self.identity_api.update_group(group_id, group, initiator)
- return GroupV3.wrap_member(context, ref)
-
- @controller.protected()
- def delete_group(self, context, group_id):
- initiator = notifications._get_request_audit_info(context)
- self.identity_api.delete_group(group_id, initiator)
diff --git a/keystone-moon/keystone/identity/core.py b/keystone-moon/keystone/identity/core.py
deleted file mode 100644
index 2f52a358..00000000
--- a/keystone-moon/keystone/identity/core.py
+++ /dev/null
@@ -1,1613 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""Main entry point into the Identity service."""
-
-import abc
-import functools
-import os
-import threading
-import uuid
-
-from oslo_config import cfg
-from oslo_log import log
-from oslo_log import versionutils
-import six
-
-from keystone import assignment # TODO(lbragstad): Decouple this dependency
-from keystone.common import cache
-from keystone.common import clean
-from keystone.common import config
-from keystone.common import dependency
-from keystone.common import driver_hints
-from keystone.common import manager
-from keystone import exception
-from keystone.i18n import _, _LW
-from keystone.identity.mapping_backends import mapping
-from keystone import notifications
-
-
-CONF = cfg.CONF
-
-LOG = log.getLogger(__name__)
-
-MEMOIZE = cache.get_memoization_decorator(group='identity')
-
-DOMAIN_CONF_FHEAD = 'keystone.'
-DOMAIN_CONF_FTAIL = '.conf'
-
-# The number of times we will attempt to register a domain to use the SQL
-# driver, if we find that another process is in the middle of registering or
-# releasing at the same time as us.
-REGISTRATION_ATTEMPTS = 10
-
-# Config Registration Types
-SQL_DRIVER = 'SQL'
-
-
-def filter_user(user_ref):
- """Filter out private items in a user dict.
-
- 'password', 'tenants' and 'groups' are never returned.
-
- :returns: user_ref
-
- """
- if user_ref:
- user_ref = user_ref.copy()
- user_ref.pop('password', None)
- user_ref.pop('tenants', None)
- user_ref.pop('groups', None)
- user_ref.pop('domains', None)
- try:
- user_ref['extra'].pop('password', None)
- user_ref['extra'].pop('tenants', None)
- except KeyError: # nosec
- # ok to not have extra in the user_ref.
- pass
- return user_ref
-
-
-@dependency.requires('domain_config_api', 'resource_api')
-class DomainConfigs(dict):
- """Discover, store and provide access to domain specific configs.
-
- The setup_domain_drivers() call will be made via the wrapper from
- the first call to any driver function handled by this manager.
-
- Domain specific configurations are only supported for the identity backend
- and the individual configurations are either specified in the resource
- database or in individual domain configuration files, depending on the
- setting of the 'domain_configurations_from_database' config option.
-
- The result will be that for each domain with a specific configuration,
- this class will hold a reference to a ConfigOpts and driver object that
- the identity manager and driver can use.
-
- """
-
- configured = False
- driver = None
- _any_sql = False
- lock = threading.Lock()
-
- def _load_driver(self, domain_config):
- return manager.load_driver(Manager.driver_namespace,
- domain_config['cfg'].identity.driver,
- domain_config['cfg'])
-
- def _load_config_from_file(self, resource_api, file_list, domain_name):
-
- def _assert_no_more_than_one_sql_driver(domain_id, new_config,
- config_file):
- """Ensure there is no more than one sql driver.
-
- Check to see if the addition of the driver in this new config
- would cause there to be more than one sql driver.
-
- """
- if (new_config['driver'].is_sql and
- (self.driver.is_sql or self._any_sql)):
- # The addition of this driver would cause us to have more than
- # one sql driver, so raise an exception.
- raise exception.MultipleSQLDriversInConfig(source=config_file)
- self._any_sql = self._any_sql or new_config['driver'].is_sql
-
- try:
- domain_ref = resource_api.get_domain_by_name(domain_name)
- except exception.DomainNotFound:
- LOG.warning(
- _LW('Invalid domain name (%s) found in config file name'),
- domain_name)
- return
-
- # 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_config = {}
- domain_config['cfg'] = cfg.ConfigOpts()
- config.configure(conf=domain_config['cfg'])
- domain_config['cfg'](args=[], project='keystone',
- default_config_files=file_list)
- domain_config['driver'] = self._load_driver(domain_config)
- _assert_no_more_than_one_sql_driver(domain_ref['id'],
- domain_config,
- file_list)
- self[domain_ref['id']] = domain_config
-
- def _setup_domain_drivers_from_files(self, standard_driver, resource_api):
- """Read the domain specific configuration files and load the drivers.
-
- Domain configuration files are stored in the domain config directory,
- and must be named of the form:
-
- keystone.<domain_name>.conf
-
- For each file, call the load config method where the domain_name
- will be turned into a domain_id and then:
-
- - 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
-
- """
- conf_dir = CONF.identity.domain_config_dir
- if not os.path.exists(conf_dir):
- LOG.warning(_LW('Unable to locate domain config directory: %s'),
- conf_dir)
- return
-
- for r, d, f in os.walk(conf_dir):
- for fname in f:
- if (fname.startswith(DOMAIN_CONF_FHEAD) and
- fname.endswith(DOMAIN_CONF_FTAIL)):
- if fname.count('.') >= 2:
- self._load_config_from_file(
- resource_api, [os.path.join(r, fname)],
- fname[len(DOMAIN_CONF_FHEAD):
- -len(DOMAIN_CONF_FTAIL)])
- else:
- LOG.debug(('Ignoring file (%s) while scanning domain '
- 'config directory'),
- fname)
-
- def _load_config_from_database(self, domain_id, specific_config):
-
- def _assert_no_more_than_one_sql_driver(domain_id, new_config):
- """Ensure adding driver doesn't push us over the limit of 1
-
- The checks we make in this method need to take into account that
- we may be in a multiple process configuration and ensure that
- any race conditions are avoided.
-
- """
- if not new_config['driver'].is_sql:
- self.domain_config_api.release_registration(domain_id)
- return
-
- # To ensure the current domain is the only SQL driver, we attempt
- # to register our use of SQL. If we get it we know we are good,
- # if we fail to register it then we should:
- #
- # - First check if another process has registered for SQL for our
- # domain, in which case we are fine
- # - If a different domain has it, we should check that this domain
- # is still valid, in case, for example, domain deletion somehow
- # failed to remove its registration (i.e. we self heal for these
- # kinds of issues).
-
- domain_registered = 'Unknown'
- for attempt in range(REGISTRATION_ATTEMPTS):
- if self.domain_config_api.obtain_registration(
- domain_id, SQL_DRIVER):
- LOG.debug('Domain %s successfully registered to use the '
- 'SQL driver.', domain_id)
- return
-
- # We failed to register our use, let's find out who is using it
- try:
- domain_registered = (
- self.domain_config_api.read_registration(
- SQL_DRIVER))
- except exception.ConfigRegistrationNotFound:
- msg = ('While attempting to register domain %(domain)s to '
- 'use the SQL driver, another process released it, '
- 'retrying (attempt %(attempt)s).')
- LOG.debug(msg, {'domain': domain_id,
- 'attempt': attempt + 1})
- continue
-
- if domain_registered == domain_id:
- # Another process already registered it for us, so we are
- # fine. In the race condition when another process is
- # in the middle of deleting this domain, we know the domain
- # is already disabled and hence telling the caller that we
- # are registered is benign.
- LOG.debug('While attempting to register domain %s to use '
- 'the SQL driver, found that another process had '
- 'already registered this domain. This is normal '
- 'in multi-process configurations.', domain_id)
- return
-
- # So we don't have it, but someone else does...let's check that
- # this domain is still valid
- try:
- self.resource_api.get_domain(domain_registered)
- except exception.DomainNotFound:
- msg = ('While attempting to register domain %(domain)s to '
- 'use the SQL driver, found that it was already '
- 'registered to a domain that no longer exists '
- '(%(old_domain)s). Removing this stale '
- 'registration and retrying (attempt %(attempt)s).')
- LOG.debug(msg, {'domain': domain_id,
- 'old_domain': domain_registered,
- 'attempt': attempt + 1})
- self.domain_config_api.release_registration(
- domain_registered, type=SQL_DRIVER)
- continue
-
- # The domain is valid, so we really do have an attempt at more
- # than one SQL driver.
- details = (
- _('Config API entity at /domains/%s/config') % domain_id)
- raise exception.MultipleSQLDriversInConfig(source=details)
-
- # We fell out of the loop without either registering our domain or
- # being able to find who has it...either we were very very very
- # unlucky or something is awry.
- msg = _('Exceeded attempts to register domain %(domain)s to use '
- 'the SQL driver, the last domain that appears to have '
- 'had it is %(last_domain)s, giving up') % {
- 'domain': domain_id, 'last_domain': domain_registered}
- raise exception.UnexpectedError(msg)
-
- domain_config = {}
- domain_config['cfg'] = cfg.ConfigOpts()
- config.configure(conf=domain_config['cfg'])
- domain_config['cfg'](args=[], project='keystone',
- default_config_files=[])
-
- # Override any options that have been passed in as specified in the
- # database.
- for group in specific_config:
- for option in specific_config[group]:
- domain_config['cfg'].set_override(
- option, specific_config[group][option],
- group, enforce_type=True)
-
- domain_config['cfg_overrides'] = specific_config
- domain_config['driver'] = self._load_driver(domain_config)
- _assert_no_more_than_one_sql_driver(domain_id, domain_config)
- self[domain_id] = domain_config
-
- def _setup_domain_drivers_from_database(self, standard_driver,
- resource_api):
- """Read domain specific configuration from database and load drivers.
-
- Domain configurations are stored in the domain-config backend,
- so we go through each domain to find those that have a specific config
- defined, and for those that do we:
-
- - Create a new config structure, overriding any specific options
- defined in the resource backend
- - Initialise a new instance of the required driver with this new config
-
- """
- for domain in resource_api.list_domains():
- domain_config_options = (
- self.domain_config_api.
- get_config_with_sensitive_info(domain['id']))
- if domain_config_options:
- self._load_config_from_database(domain['id'],
- domain_config_options)
-
- def setup_domain_drivers(self, standard_driver, resource_api):
- # This is called by the api call wrapper
- self.driver = standard_driver
-
- if CONF.identity.domain_configurations_from_database:
- self._setup_domain_drivers_from_database(standard_driver,
- resource_api)
- else:
- self._setup_domain_drivers_from_files(standard_driver,
- resource_api)
- self.configured = True
-
- def get_domain_driver(self, domain_id):
- self.check_config_and_reload_domain_driver_if_required(domain_id)
- if domain_id in self:
- return self[domain_id]['driver']
-
- def get_domain_conf(self, domain_id):
- self.check_config_and_reload_domain_driver_if_required(domain_id)
- if domain_id in self:
- return self[domain_id]['cfg']
- else:
- return CONF
-
- def reload_domain_driver(self, 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/database have already been
- # read.
- if self.configured:
- if domain_id in self:
- self[domain_id]['driver'] = (
- self._load_driver(self[domain_id]))
- else:
- # The standard driver
- self.driver = self.driver()
-
- def check_config_and_reload_domain_driver_if_required(self, domain_id):
- """Check for, and load, any new domain specific config for this domain.
-
- This is only supported for the database-stored domain specific
- configuration.
-
- When the domain specific drivers were set up, we stored away the
- specific config for this domain that was available at that time. So we
- now read the current version and compare. While this might seem
- somewhat inefficient, the sensitive config call is cached, so should be
- light weight. More importantly, when the cache timeout is reached, we
- will get any config that has been updated from any other keystone
- process.
-
- This cache-timeout approach works for both multi-process and
- multi-threaded keystone configurations. In multi-threaded
- configurations, even though we might remove a driver object (that
- could be in use by another thread), this won't actually be thrown away
- until all references to it have been broken. When that other
- thread is released back and is restarted with another command to
- process, next time it accesses the driver it will pickup the new one.
-
- """
- if (not CONF.identity.domain_specific_drivers_enabled or
- not CONF.identity.domain_configurations_from_database):
- # If specific drivers are not enabled, then there is nothing to do.
- # If we are not storing the configurations in the database, then
- # we'll only re-read the domain specific config files on startup
- # of keystone.
- return
-
- latest_domain_config = (
- self.domain_config_api.
- get_config_with_sensitive_info(domain_id))
- domain_config_in_use = domain_id in self
-
- if latest_domain_config:
- if (not domain_config_in_use or
- latest_domain_config != self[domain_id]['cfg_overrides']):
- self._load_config_from_database(domain_id,
- latest_domain_config)
- elif domain_config_in_use:
- # The domain specific config has been deleted, so should remove the
- # specific driver for this domain.
- try:
- del self[domain_id]
- except KeyError: # nosec
- # Allow this error in case we are unlucky and in a
- # multi-threaded situation, two threads happen to be running
- # in lock step.
- pass
- # If we fall into the else condition, this means there is no domain
- # config set, and there is none in use either, so we have nothing
- # to do.
-
-
-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):
- # If domain specific driver has not been configured, acquire the
- # lock and proceed with loading the driver.
- with self.domain_configs.lock:
- # Check again just in case some other thread has already
- # completed domain config.
- if not self.domain_configs.configured:
- self.domain_configs.setup_domain_drivers(
- self.driver, self.resource_api)
- return f(self, *args, **kwargs)
- return wrapper
-
-
-def exception_translated(exception_type):
- """Wraps API calls to map to correct exception."""
- def _exception_translated(f):
- @functools.wraps(f)
- def wrapper(self, *args, **kwargs):
- try:
- return f(self, *args, **kwargs)
- except exception.PublicIDNotFound as e:
- if exception_type == 'user':
- raise exception.UserNotFound(user_id=str(e))
- elif exception_type == 'group':
- raise exception.GroupNotFound(group_id=str(e))
- elif exception_type == 'assertion':
- raise AssertionError(_('Invalid user / password'))
- else:
- raise
- return wrapper
- return _exception_translated
-
-
-@notifications.listener
-@dependency.provider('identity_api')
-@dependency.requires('assignment_api', 'credential_api', 'id_mapping_api',
- 'resource_api', 'revoke_api', 'shadow_users_api')
-class Manager(manager.Manager):
- """Default pivot point for the Identity backend.
-
- 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 initialized.
-
- 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 (or we are not using multiple domain drivers).
-
- Starting with Juno, in order to be able to obtain the domain from
- just an ID being presented as part of an API call, a public ID to domain
- and local ID mapping is maintained. This mapping also allows for the local
- ID of drivers that do not provide simple UUIDs (such as LDAP) to be
- referenced via a public facing ID. The mapping itself is automatically
- generated as entities are accessed via the driver.
-
- This mapping is only used when:
- - the entity is being handled by anything other than the default driver, or
- - the entity is being handled by the default LDAP driver and backward
- compatible IDs are not required.
-
- This means that in the standard case of a single SQL backend or the default
- settings of a single LDAP backend (since backward compatible IDs is set to
- True by default), no mapping is used. An alternative approach would be to
- always use the mapping table, but in the cases where we don't need it to
- make the public and local IDs the same. It is felt that not using the
- mapping by default is a more prudent way to introduce this functionality.
-
- """
-
- driver_namespace = 'keystone.identity'
-
- _USER = 'user'
- _GROUP = 'group'
-
- def __init__(self):
- super(Manager, self).__init__(CONF.identity.driver)
- self.domain_configs = DomainConfigs()
-
- self.event_callbacks = {
- notifications.ACTIONS.deleted: {
- 'domain': [self._domain_deleted],
- },
- }
-
- def _domain_deleted(self, service, resource_type, operation,
- payload):
- domain_id = payload['resource_info']
-
- user_refs = self.list_users(domain_scope=domain_id)
- group_refs = self.list_groups(domain_scope=domain_id)
-
- for group in group_refs:
- # Cleanup any existing groups.
- try:
- self.delete_group(group['id'])
- except exception.GroupNotFound:
- LOG.debug(('Group %(groupid)s not found when deleting domain '
- 'contents for %(domainid)s, continuing with '
- 'cleanup.'),
- {'groupid': group['id'], 'domainid': domain_id})
-
- # And finally, delete the users themselves
- for user in user_refs:
- try:
- self.delete_user(user['id'])
- except exception.UserNotFound:
- LOG.debug(('User %(userid)s not found when deleting domain '
- 'contents for %(domainid)s, continuing with '
- 'cleanup.'),
- {'userid': user['id'], 'domainid': domain_id})
-
- # Domain ID normalization methods
-
- def _set_domain_id_and_mapping(self, ref, domain_id, driver,
- entity_type):
- """Patch the domain_id/public_id into the resulting entity(ies).
-
- :param ref: the entity or list of entities to post process
- :param domain_id: the domain scope used for the call
- :param driver: the driver used to execute the call
- :param entity_type: whether this is a user or group
-
- :returns: post processed entity or list or entities
-
- Called to post-process the entity being returned, using a mapping
- to substitute a public facing ID as necessary. This method must
- take into account:
-
- - If the driver is not domain aware, then we must set the domain
- attribute of all entities irrespective of mapping.
- - If the driver does not support UUIDs, then we always want to provide
- a mapping, except for the special case of this being the default
- driver and backward_compatible_ids is set to True. This is to ensure
- that entity IDs do not change for an existing LDAP installation (only
- single domain/driver LDAP configurations were previously supported).
- - If the driver does support UUIDs, then we always create a mapping
- entry, but use the local UUID as the public ID. The exception to
- - this is that if we just have single driver (i.e. not using specific
- multi-domain configs), then we don't both with the mapping at all.
-
- """
- conf = CONF.identity
-
- if not self._needs_post_processing(driver):
- # a classic case would be when running with a single SQL driver
- return ref
-
- LOG.debug('ID Mapping - Domain ID: %(domain)s, '
- 'Default Driver: %(driver)s, '
- 'Domains: %(aware)s, UUIDs: %(generate)s, '
- 'Compatible IDs: %(compat)s',
- {'domain': domain_id,
- 'driver': (driver == self.driver),
- 'aware': driver.is_domain_aware(),
- 'generate': driver.generates_uuids(),
- 'compat': CONF.identity_mapping.backward_compatible_ids})
-
- if isinstance(ref, dict):
- return self._set_domain_id_and_mapping_for_single_ref(
- ref, domain_id, driver, entity_type, conf)
- elif isinstance(ref, list):
- return [self._set_domain_id_and_mapping(
- x, domain_id, driver, entity_type) for x in ref]
- else:
- raise ValueError(_('Expected dict or list: %s') % type(ref))
-
- def _needs_post_processing(self, driver):
- """Returns whether entity from driver needs domain added or mapping."""
- return (driver is not self.driver or not driver.generates_uuids() or
- not driver.is_domain_aware())
-
- def _set_domain_id_and_mapping_for_single_ref(self, ref, domain_id,
- driver, entity_type, conf):
- LOG.debug('Local ID: %s', ref['id'])
- ref = ref.copy()
-
- self._insert_domain_id_if_needed(ref, driver, domain_id, conf)
-
- if self._is_mapping_needed(driver):
- local_entity = {'domain_id': ref['domain_id'],
- 'local_id': ref['id'],
- 'entity_type': entity_type}
- public_id = self.id_mapping_api.get_public_id(local_entity)
- if public_id:
- ref['id'] = public_id
- LOG.debug('Found existing mapping to public ID: %s',
- ref['id'])
- else:
- # Need to create a mapping. If the driver generates UUIDs
- # then pass the local UUID in as the public ID to use.
- if driver.generates_uuids():
- public_id = ref['id']
- ref['id'] = self.id_mapping_api.create_id_mapping(
- local_entity, public_id)
- LOG.debug('Created new mapping to public ID: %s',
- ref['id'])
- return ref
-
- def _insert_domain_id_if_needed(self, ref, driver, domain_id, conf):
- """Inserts the domain ID into the ref, if required.
-
- If the driver can't handle domains, then we need to insert the
- domain_id into the entity being returned. If the domain_id is
- None that means we are running in a single backend mode, so to
- remain backwardly compatible, we put in the default domain ID.
- """
- if not driver.is_domain_aware():
- if domain_id is None:
- domain_id = conf.default_domain_id
- ref['domain_id'] = domain_id
-
- def _is_mapping_needed(self, driver):
- """Returns whether mapping is needed.
-
- There are two situations where we must use the mapping:
- - this isn't the default driver (i.e. multiple backends), or
- - we have a single backend that doesn't use UUIDs
- The exception to the above is that we must honor backward
- compatibility if this is the default driver (e.g. to support
- current LDAP)
- """
- is_not_default_driver = driver is not self.driver
- return (is_not_default_driver or (
- not driver.generates_uuids() and
- not CONF.identity_mapping.backward_compatible_ids))
-
- def _clear_domain_id_if_domain_unaware(self, driver, ref):
- """Clear domain_id details if driver is not domain aware."""
- if not driver.is_domain_aware() and 'domain_id' in ref:
- ref = ref.copy()
- ref.pop('domain_id')
- return ref
-
- def _select_identity_driver(self, domain_id):
- """Choose a backend driver for the given domain_id.
-
- :param domain_id: The domain_id for which we want to find a driver. If
- the domain_id is specified as None, then this means
- we need a driver that handles multiple domains.
-
- :returns: chosen backend driver
-
- If there is a specific driver defined for this domain then choose it.
- If the domain is None, or there no specific backend for the given
- domain is found, then we chose the default driver.
-
- """
- if domain_id is None:
- driver = self.driver
- else:
- driver = (self.domain_configs.get_domain_driver(domain_id) or
- self.driver)
-
- # If the driver is not domain aware (e.g. LDAP) then check to
- # ensure we are not mapping multiple domains onto it - the only way
- # that would happen is that the default driver is LDAP and the
- # domain is anything other than None or the default domain.
- if (not driver.is_domain_aware() and driver == self.driver and
- domain_id != CONF.identity.default_domain_id and
- domain_id is not None):
- LOG.warning(_LW('Found multiple domains being mapped to a '
- 'driver that does not support that (e.g. '
- 'LDAP) - Domain ID: %(domain)s, '
- 'Default Driver: %(driver)s'),
- {'domain': domain_id,
- 'driver': (driver == self.driver)})
- raise exception.DomainNotFound(domain_id=domain_id)
- return driver
-
- def _get_domain_driver_and_entity_id(self, public_id):
- """Look up details using the public ID.
-
- :param public_id: the ID provided in the call
-
- :returns: domain_id, which can be None to indicate that the driver
- in question supports multiple domains
- driver selected based on this domain
- entity_id which will is understood by the driver.
-
- Use the mapping table to look up the domain, driver and local entity
- that is represented by the provided public ID. Handle the situations
- where we do not use the mapping (e.g. single driver that understands
- UUIDs etc.)
-
- """
- conf = CONF.identity
- # First, since we don't know anything about the entity yet, we must
- # assume it needs mapping, so long as we are using domain specific
- # drivers.
- if conf.domain_specific_drivers_enabled:
- local_id_ref = self.id_mapping_api.get_id_mapping(public_id)
- if local_id_ref:
- return (
- local_id_ref['domain_id'],
- self._select_identity_driver(local_id_ref['domain_id']),
- local_id_ref['local_id'])
-
- # So either we are using multiple drivers but the public ID is invalid
- # (and hence was not found in the mapping table), or the public ID is
- # being handled by the default driver. Either way, the only place left
- # to look is in that standard driver. However, we don't yet know if
- # this driver also needs mapping (e.g. LDAP in non backward
- # compatibility mode).
- driver = self.driver
- if driver.generates_uuids():
- if driver.is_domain_aware:
- # No mapping required, and the driver can handle the domain
- # information itself. The classic case of this is the
- # current SQL driver.
- return (None, driver, public_id)
- else:
- # Although we don't have any drivers of this type, i.e. that
- # understand UUIDs but not domains, conceptually you could.
- return (conf.default_domain_id, driver, public_id)
-
- # So the only place left to find the ID is in the default driver which
- # we now know doesn't generate UUIDs
- if not CONF.identity_mapping.backward_compatible_ids:
- # We are not running in backward compatibility mode, so we
- # must use a mapping.
- local_id_ref = self.id_mapping_api.get_id_mapping(public_id)
- if local_id_ref:
- return (
- local_id_ref['domain_id'],
- driver,
- local_id_ref['local_id'])
- else:
- raise exception.PublicIDNotFound(id=public_id)
-
- # If we reach here, this means that the default driver
- # requires no mapping - but also doesn't understand domains
- # (e.g. the classic single LDAP driver situation). Hence we pass
- # back the public_ID unmodified and use the default domain (to
- # keep backwards compatibility with existing installations).
- #
- # It is still possible that the public ID is just invalid in
- # which case we leave this to the caller to check.
- return (conf.default_domain_id, driver, public_id)
-
- def _assert_user_and_group_in_same_backend(
- self, user_entity_id, user_driver, group_entity_id, group_driver):
- """Ensures that user and group IDs are backed by the same backend.
-
- Raise a CrossBackendNotAllowed exception if they are not from the same
- backend, otherwise return None.
-
- """
- if user_driver is not group_driver:
- # Determine first if either IDs don't exist by calling
- # the driver.get methods (which will raise a NotFound
- # exception).
- user_driver.get_user(user_entity_id)
- group_driver.get_group(group_entity_id)
- # If we get here, then someone is attempting to create a cross
- # backend membership, which is not allowed.
- raise exception.CrossBackendNotAllowed(group_id=group_entity_id,
- user_id=user_entity_id)
-
- def _mark_domain_id_filter_satisfied(self, hints):
- if hints:
- for filter in hints.filters:
- if (filter['name'] == 'domain_id' and
- filter['comparator'] == 'equals'):
- hints.filters.remove(filter)
-
- def _ensure_domain_id_in_hints(self, hints, domain_id):
- if (domain_id is not None and
- not hints.get_exact_filter_by_name('domain_id')):
- hints.add_filter('domain_id', domain_id)
-
- def _set_list_limit_in_hints(self, hints, driver):
- """Set list limit in hints from driver
-
- If a hints list is provided, the wrapper will insert the relevant
- limit into the hints so that the underlying driver call can try and
- honor it. If the driver does truncate the response, it will update the
- 'truncated' attribute in the 'limit' entry in the hints list, which
- enables the caller of this function to know if truncation has taken
- place. If, however, the driver layer is unable to perform truncation,
- the 'limit' entry is simply left in the hints list for the caller to
- handle.
-
- A _get_list_limit() method is required to be present in the object
- class hierarchy, which returns the limit for this backend to which
- we will truncate.
-
- If a hints list is not provided in the arguments of the wrapped call
- then any limits set in the config file are ignored. This allows
- internal use of such wrapped methods where the entire data set is
- needed as input for the calculations of some other API (e.g. get role
- assignments for a given project).
-
- This method, specific to identity manager, is used instead of more
- general response_truncated, because the limit for identity entities
- can be overriden in domain-specific config files. The driver to use
- is determined during processing of the passed parameters and
- response_truncated is designed to set the limit before any processing.
- """
- if hints is None:
- return
-
- list_limit = driver._get_list_limit()
- if list_limit:
- hints.set_limit(list_limit)
-
- # 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
- # - create any ID mapping that might be required
-
- @notifications.emit_event('authenticate')
- @domains_configured
- @exception_translated('assertion')
- def authenticate(self, context, user_id, password):
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(user_id))
- ref = driver.authenticate(entity_id, password)
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.USER)
-
- @domains_configured
- @exception_translated('user')
- def create_user(self, user_ref, initiator=None):
- user = user_ref.copy()
- user['name'] = clean.user_name(user['name'])
- user.setdefault('enabled', True)
- user['enabled'] = clean.user_enabled(user['enabled'])
- domain_id = user['domain_id']
- self.resource_api.get_domain(domain_id)
-
- # For creating a user, the domain is in the object itself
- domain_id = user_ref['domain_id']
- driver = self._select_identity_driver(domain_id)
- user = self._clear_domain_id_if_domain_unaware(driver, user)
- # Generate a local ID - in the future this might become a function of
- # the underlying driver so that it could conform to rules set down by
- # that particular driver type.
- user['id'] = uuid.uuid4().hex
- ref = driver.create_user(user['id'], user)
- notifications.Audit.created(self._USER, user['id'], initiator)
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.USER)
-
- @domains_configured
- @exception_translated('user')
- @MEMOIZE
- def get_user(self, user_id):
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(user_id))
- ref = driver.get_user(entity_id)
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.USER)
-
- def assert_user_enabled(self, user_id, user=None):
- """Assert the user and the user's domain are enabled.
-
- :raise AssertionError if the user or the user's domain is disabled.
- """
- if user is None:
- user = self.get_user(user_id)
- self.resource_api.assert_domain_enabled(user['domain_id'])
- if not user.get('enabled', True):
- raise AssertionError(_('User is disabled: %s') % user_id)
-
- @domains_configured
- @exception_translated('user')
- @MEMOIZE
- 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)
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.USER)
-
- @domains_configured
- @exception_translated('user')
- def list_users(self, domain_scope=None, hints=None):
- driver = self._select_identity_driver(domain_scope)
- self._set_list_limit_in_hints(hints, driver)
- hints = hints or driver_hints.Hints()
- if driver.is_domain_aware():
- # Force the domain_scope into the hint to ensure that we only get
- # back domains for that scope.
- self._ensure_domain_id_in_hints(hints, domain_scope)
- else:
- # We are effectively satisfying any domain_id filter by the above
- # driver selection, so remove any such filter.
- self._mark_domain_id_filter_satisfied(hints)
- ref_list = driver.list_users(hints)
- return self._set_domain_id_and_mapping(
- ref_list, domain_scope, driver, mapping.EntityType.USER)
-
- def _check_update_of_domain_id(self, new_domain, old_domain):
- if new_domain != old_domain:
- versionutils.report_deprecated_feature(
- LOG,
- _('update of domain_id is deprecated as of Mitaka '
- 'and will be removed in O.')
- )
-
- @domains_configured
- @exception_translated('user')
- def update_user(self, user_id, user_ref, initiator=None):
- old_user_ref = self.get_user(user_id)
- 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'])
- if 'domain_id' in user:
- self._check_update_of_domain_id(user['domain_id'],
- old_user_ref['domain_id'])
- self.resource_api.get_domain(user['domain_id'])
- if 'id' in user:
- if user_id != user['id']:
- raise exception.ValidationError(_('Cannot change user ID'))
- # Since any ID in the user dict is now irrelevant, remove its so as
- # the driver layer won't be confused by the fact the this is the
- # public ID not the local ID
- user.pop('id')
-
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(user_id))
- user = self._clear_domain_id_if_domain_unaware(driver, user)
- self.get_user.invalidate(self, old_user_ref['id'])
- self.get_user_by_name.invalidate(self, old_user_ref['name'],
- old_user_ref['domain_id'])
-
- ref = driver.update_user(entity_id, user)
-
- notifications.Audit.updated(self._USER, user_id, initiator)
-
- enabled_change = ((user.get('enabled') is False) and
- user['enabled'] != old_user_ref.get('enabled'))
- if enabled_change or user.get('password') is not None:
- self.emit_invalidate_user_token_persistence(user_id)
-
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.USER)
-
- @domains_configured
- @exception_translated('user')
- def delete_user(self, user_id, initiator=None):
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(user_id))
- # Get user details to invalidate the cache.
- user_old = self.get_user(user_id)
- driver.delete_user(entity_id)
- self.assignment_api.delete_user_assignments(user_id)
- self.get_user.invalidate(self, user_id)
- self.get_user_by_name.invalidate(self, user_old['name'],
- user_old['domain_id'])
- self.credential_api.delete_credentials_for_user(user_id)
- self.id_mapping_api.delete_id_mapping(user_id)
- notifications.Audit.deleted(self._USER, user_id, initiator)
-
- # Invalidate user role assignments cache region, as it may be caching
- # role assignments where the actor is the specified user
- assignment.COMPUTED_ASSIGNMENTS_REGION.invalidate()
-
- @domains_configured
- @exception_translated('group')
- def create_group(self, group_ref, initiator=None):
- group = group_ref.copy()
- group.setdefault('description', '')
- domain_id = group['domain_id']
- self.resource_api.get_domain(domain_id)
-
- # For creating a group, the domain is in the object itself
- domain_id = group_ref['domain_id']
- driver = self._select_identity_driver(domain_id)
- group = self._clear_domain_id_if_domain_unaware(driver, group)
- # Generate a local ID - in the future this might become a function of
- # the underlying driver so that it could conform to rules set down by
- # that particular driver type.
- group['id'] = uuid.uuid4().hex
- ref = driver.create_group(group['id'], group)
-
- notifications.Audit.created(self._GROUP, group['id'], initiator)
-
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.GROUP)
-
- @domains_configured
- @exception_translated('group')
- @MEMOIZE
- def get_group(self, group_id):
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(group_id))
- ref = driver.get_group(entity_id)
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.GROUP)
-
- @domains_configured
- @exception_translated('group')
- def get_group_by_name(self, group_name, domain_id):
- driver = self._select_identity_driver(domain_id)
- ref = driver.get_group_by_name(group_name, domain_id)
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.GROUP)
-
- @domains_configured
- @exception_translated('group')
- def update_group(self, group_id, group, initiator=None):
- if 'domain_id' in group:
- old_group_ref = self.get_group(group_id)
- self._check_update_of_domain_id(group['domain_id'],
- old_group_ref['domain_id'])
- self.resource_api.get_domain(group['domain_id'])
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(group_id))
- group = self._clear_domain_id_if_domain_unaware(driver, group)
- ref = driver.update_group(entity_id, group)
- self.get_group.invalidate(self, group_id)
- notifications.Audit.updated(self._GROUP, group_id, initiator)
- return self._set_domain_id_and_mapping(
- ref, domain_id, driver, mapping.EntityType.GROUP)
-
- @domains_configured
- @exception_translated('group')
- def delete_group(self, group_id, initiator=None):
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(group_id))
- user_ids = (u['id'] for u in self.list_users_in_group(group_id))
- driver.delete_group(entity_id)
- self.get_group.invalidate(self, group_id)
- self.id_mapping_api.delete_id_mapping(group_id)
- self.assignment_api.delete_group_assignments(group_id)
-
- notifications.Audit.deleted(self._GROUP, group_id, initiator)
-
- for uid in user_ids:
- self.emit_invalidate_user_token_persistence(uid)
-
- # Invalidate user role assignments cache region, as it may be caching
- # role assignments expanded from the specified group to its users
- assignment.COMPUTED_ASSIGNMENTS_REGION.invalidate()
-
- @domains_configured
- @exception_translated('group')
- def add_user_to_group(self, user_id, group_id, initiator=None):
- @exception_translated('user')
- def get_entity_info_for_user(public_id):
- return self._get_domain_driver_and_entity_id(public_id)
-
- _domain_id, group_driver, group_entity_id = (
- self._get_domain_driver_and_entity_id(group_id))
- # Get the same info for the user_id, taking care to map any
- # exceptions correctly
- _domain_id, user_driver, user_entity_id = (
- get_entity_info_for_user(user_id))
-
- self._assert_user_and_group_in_same_backend(
- user_entity_id, user_driver, group_entity_id, group_driver)
-
- group_driver.add_user_to_group(user_entity_id, group_entity_id)
-
- # Invalidate user role assignments cache region, as it may now need to
- # include role assignments from the specified group to its users
- assignment.COMPUTED_ASSIGNMENTS_REGION.invalidate()
- notifications.Audit.added_to(self._GROUP, group_id, self._USER,
- user_id, initiator)
-
- @domains_configured
- @exception_translated('group')
- def remove_user_from_group(self, user_id, group_id, initiator=None):
- @exception_translated('user')
- def get_entity_info_for_user(public_id):
- return self._get_domain_driver_and_entity_id(public_id)
-
- _domain_id, group_driver, group_entity_id = (
- self._get_domain_driver_and_entity_id(group_id))
- # Get the same info for the user_id, taking care to map any
- # exceptions correctly
- _domain_id, user_driver, user_entity_id = (
- get_entity_info_for_user(user_id))
-
- self._assert_user_and_group_in_same_backend(
- user_entity_id, user_driver, group_entity_id, group_driver)
-
- group_driver.remove_user_from_group(user_entity_id, group_entity_id)
- self.emit_invalidate_user_token_persistence(user_id)
-
- # Invalidate user role assignments cache region, as it may be caching
- # role assignments expanded from this group to this user
- assignment.COMPUTED_ASSIGNMENTS_REGION.invalidate()
- notifications.Audit.removed_from(self._GROUP, group_id, self._USER,
- user_id, initiator)
-
- def emit_invalidate_user_token_persistence(self, user_id):
- """Emit a notification to the callback system to revoke user tokens.
-
- This method and associated callback listener removes the need for
- making a direct call to another manager to delete and revoke tokens.
-
- :param user_id: user identifier
- :type user_id: string
- """
- notifications.Audit.internal(
- notifications.INVALIDATE_USER_TOKEN_PERSISTENCE, user_id
- )
-
- def emit_invalidate_grant_token_persistence(self, user_project):
- """Emit a notification to the callback system to revoke grant tokens.
-
- This method and associated callback listener removes the need for
- making a direct call to another manager to delete and revoke tokens.
-
- :param user_project: {'user_id': user_id, 'project_id': project_id}
- :type user_project: dict
- """
- notifications.Audit.internal(
- notifications.INVALIDATE_USER_PROJECT_TOKEN_PERSISTENCE,
- user_project
- )
-
- @domains_configured
- @exception_translated('user')
- def list_groups_for_user(self, user_id, hints=None):
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(user_id))
- self._set_list_limit_in_hints(hints, driver)
- hints = hints or driver_hints.Hints()
- if not driver.is_domain_aware():
- # We are effectively satisfying any domain_id filter by the above
- # driver selection, so remove any such filter
- self._mark_domain_id_filter_satisfied(hints)
- ref_list = driver.list_groups_for_user(entity_id, hints)
- return self._set_domain_id_and_mapping(
- ref_list, domain_id, driver, mapping.EntityType.GROUP)
-
- @domains_configured
- @exception_translated('group')
- def list_groups(self, domain_scope=None, hints=None):
- driver = self._select_identity_driver(domain_scope)
- self._set_list_limit_in_hints(hints, driver)
- hints = hints or driver_hints.Hints()
- if driver.is_domain_aware():
- # Force the domain_scope into the hint to ensure that we only get
- # back domains for that scope.
- self._ensure_domain_id_in_hints(hints, domain_scope)
- else:
- # We are effectively satisfying any domain_id filter by the above
- # driver selection, so remove any such filter.
- self._mark_domain_id_filter_satisfied(hints)
- ref_list = driver.list_groups(hints)
- return self._set_domain_id_and_mapping(
- ref_list, domain_scope, driver, mapping.EntityType.GROUP)
-
- @domains_configured
- @exception_translated('group')
- def list_users_in_group(self, group_id, hints=None):
- domain_id, driver, entity_id = (
- self._get_domain_driver_and_entity_id(group_id))
- self._set_list_limit_in_hints(hints, driver)
- hints = hints or driver_hints.Hints()
- if not driver.is_domain_aware():
- # We are effectively satisfying any domain_id filter by the above
- # driver selection, so remove any such filter
- self._mark_domain_id_filter_satisfied(hints)
- ref_list = driver.list_users_in_group(entity_id, hints)
- return self._set_domain_id_and_mapping(
- ref_list, domain_id, driver, mapping.EntityType.USER)
-
- @domains_configured
- @exception_translated('group')
- def check_user_in_group(self, user_id, group_id):
- @exception_translated('user')
- def get_entity_info_for_user(public_id):
- return self._get_domain_driver_and_entity_id(public_id)
-
- _domain_id, group_driver, group_entity_id = (
- self._get_domain_driver_and_entity_id(group_id))
- # Get the same info for the user_id, taking care to map any
- # exceptions correctly
- _domain_id, user_driver, user_entity_id = (
- get_entity_info_for_user(user_id))
-
- self._assert_user_and_group_in_same_backend(
- user_entity_id, user_driver, group_entity_id, group_driver)
-
- return group_driver.check_user_in_group(user_entity_id,
- group_entity_id)
-
- @domains_configured
- def change_password(self, context, user_id, original_password,
- new_password):
-
- # authenticate() will raise an AssertionError if authentication fails
- self.authenticate(context, user_id, original_password)
-
- update_dict = {'password': new_password}
- self.update_user(user_id, update_dict)
-
- @MEMOIZE
- def shadow_federated_user(self, idp_id, protocol_id, unique_id,
- display_name):
- """Shadows a federated user by mapping to a user.
-
- :param idp_id: identity provider id
- :param protocol_id: protocol id
- :param unique_id: unique id for the user within the IdP
- :param display_name: user's display name
-
- :returns: dictionary of the mapped User entity
- """
- user_dict = {}
- try:
- self.shadow_users_api.update_federated_user_display_name(
- idp_id, protocol_id, unique_id, display_name)
- user_dict = self.shadow_users_api.get_federated_user(
- idp_id, protocol_id, unique_id)
- except exception.UserNotFound:
- federated_dict = {
- 'idp_id': idp_id,
- 'protocol_id': protocol_id,
- 'unique_id': unique_id,
- 'display_name': display_name
- }
- user_dict = self.shadow_users_api.create_federated_user(
- federated_dict)
- return user_dict
-
-
-@six.add_metaclass(abc.ABCMeta)
-class IdentityDriverV8(object):
- """Interface description for an Identity driver."""
-
- def _get_conf(self):
- try:
- return self.conf or CONF
- except AttributeError:
- return CONF
-
- def _get_list_limit(self):
- conf = self._get_conf()
- # use list_limit from domain-specific config. If list_limit in
- # domain-specific config is not set, look it up in the default config
- return (conf.identity.list_limit or conf.list_limit or
- CONF.identity.list_limit or CONF.list_limit)
-
- def is_domain_aware(self):
- """Indicates if Driver supports domains."""
- return True
-
- def default_assignment_driver(self):
- # TODO(morganfainberg): To be removed when assignment driver based
- # upon [identity]/driver option is removed in the "O" release.
- return 'sql'
-
- @property
- def is_sql(self):
- """Indicates if this Driver uses SQL."""
- return False
-
- @property
- def multiple_domains_supported(self):
- return (self.is_domain_aware() or
- CONF.identity.domain_specific_drivers_enabled)
-
- def generates_uuids(self):
- """Indicates if Driver generates UUIDs as the local entity ID."""
- return True
-
- @abc.abstractmethod
- def authenticate(self, user_id, password):
- """Authenticate a given user and password.
-
- :returns: user_ref
- :raises AssertionError: If user or password is invalid.
- """
- raise exception.NotImplemented() # pragma: no cover
-
- # user crud
-
- @abc.abstractmethod
- def create_user(self, user_id, user):
- """Creates a new user.
-
- :raises keystone.exception.Conflict: If a duplicate user exists.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def list_users(self, hints):
- """List users in the system.
-
- :param hints: filter hints which the driver should
- implement if at all possible.
-
- :returns: a list of user_refs or an empty list.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def list_users_in_group(self, group_id, hints):
- """List users in a group.
-
- :param group_id: the group in question
- :param hints: filter hints which the driver should
- implement if at all possible.
-
- :returns: a list of user_refs or an empty list.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def get_user(self, user_id):
- """Get a user by ID.
-
- :returns: user_ref
- :raises keystone.exception.UserNotFound: If the user doesn't exist.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def update_user(self, user_id, user):
- """Updates an existing user.
-
- :raises keystone.exception.UserNotFound: If the user doesn't exist.
- :raises keystone.exception.Conflict: If a duplicate user exists.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def add_user_to_group(self, user_id, group_id):
- """Adds a user to a group.
-
- :raises keystone.exception.UserNotFound: If the user doesn't exist.
- :raises keystone.exception.GroupNotFound: If the group doesn't exist.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def check_user_in_group(self, user_id, group_id):
- """Checks if a user is a member of a group.
-
- :raises keystone.exception.UserNotFound: If the user doesn't exist.
- :raises keystone.exception.GroupNotFound: If the group doesn't exist.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def remove_user_from_group(self, user_id, group_id):
- """Removes a user from a group.
-
- :raises keystone.exception.NotFound: If the entity not found.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def delete_user(self, user_id):
- """Deletes an existing user.
-
- :raises keystone.exception.UserNotFound: If the user doesn't exist.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def get_user_by_name(self, user_name, domain_id):
- """Get a user by name.
-
- :returns: user_ref
- :raises keystone.exception.UserNotFound: If the user doesn't exist.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- # group crud
-
- @abc.abstractmethod
- def create_group(self, group_id, group):
- """Creates a new group.
-
- :raises keystone.exception.Conflict: If a duplicate group exists.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def list_groups(self, hints):
- """List groups in the system.
-
- :param hints: filter hints which the driver should
- implement if at all possible.
-
- :returns: a list of group_refs or an empty list.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def list_groups_for_user(self, user_id, hints):
- """List groups a user is in
-
- :param user_id: the user in question
- :param hints: filter hints which the driver should
- implement if at all possible.
-
- :returns: a list of group_refs or an empty list.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def get_group(self, group_id):
- """Get a group by ID.
-
- :returns: group_ref
- :raises keystone.exception.GroupNotFound: If the group doesn't exist.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def get_group_by_name(self, group_name, domain_id):
- """Get a group by name.
-
- :returns: group_ref
- :raises keystone.exception.GroupNotFound: If the group doesn't exist.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def update_group(self, group_id, group):
- """Updates an existing group.
-
- :raises keystone.exception.GroupNotFound: If the group doesn't exist.
- :raises keystone.exception.Conflict: If a duplicate group exists.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def delete_group(self, group_id):
- """Deletes an existing group.
-
- :raises keystone.exception.GroupNotFound: If the group doesn't exist.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- # end of identity
-
-
-Driver = manager.create_legacy_driver(IdentityDriverV8)
-
-
-@dependency.provider('id_mapping_api')
-class MappingManager(manager.Manager):
- """Default pivot point for the ID Mapping backend."""
-
- driver_namespace = 'keystone.identity.id_mapping'
-
- def __init__(self):
- super(MappingManager, self).__init__(CONF.identity_mapping.driver)
-
-
-@six.add_metaclass(abc.ABCMeta)
-class MappingDriverV8(object):
- """Interface description for an ID Mapping driver."""
-
- @abc.abstractmethod
- def get_public_id(self, local_entity):
- """Returns the public ID for the given local entity.
-
- :param dict local_entity: Containing the entity domain, local ID and
- type ('user' or 'group').
- :returns: public ID, or None if no mapping is found.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def get_id_mapping(self, public_id):
- """Returns the local mapping.
-
- :param public_id: The public ID for the mapping required.
- :returns dict: Containing the entity domain, local ID and type. If no
- mapping is found, it returns None.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def create_id_mapping(self, local_entity, public_id=None):
- """Create and store a mapping to a public_id.
-
- :param dict local_entity: Containing the entity domain, local ID and
- type ('user' or 'group').
- :param public_id: If specified, this will be the public ID. If this
- is not specified, a public ID will be generated.
- :returns: public ID
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def delete_id_mapping(self, public_id):
- """Deletes an entry for the given public_id.
-
- :param public_id: The public ID for the mapping to be deleted.
-
- The method is silent if no mapping is found.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
- @abc.abstractmethod
- def purge_mappings(self, purge_filter):
- """Purge selected identity mappings.
-
- :param dict purge_filter: Containing the attributes of the filter that
- defines which entries to purge. An empty
- filter means purge all mappings.
-
- """
- raise exception.NotImplemented() # pragma: no cover
-
-
-MappingDriver = manager.create_legacy_driver(MappingDriverV8)
-
-
-@dependency.provider('shadow_users_api')
-class ShadowUsersManager(manager.Manager):
- """Default pivot point for the Shadow Users backend."""
-
- driver_namespace = 'keystone.identity.shadow_users'
-
- def __init__(self):
- super(ShadowUsersManager, self).__init__(CONF.shadow_users.driver)
-
-
-@six.add_metaclass(abc.ABCMeta)
-class ShadowUsersDriverV9(object):
- """Interface description for an Shadow Users driver."""
-
- @abc.abstractmethod
- def create_federated_user(self, federated_dict):
- """Create a new user with the federated identity
-
- :param dict federated_dict: Reference to the federated user
- :param user_id: user ID for linking to the federated identity
- :returns dict: Containing the user reference
-
- """
- raise exception.NotImplemented()
-
- @abc.abstractmethod
- def get_federated_user(self, idp_id, protocol_id, unique_id):
- """Returns the found user for the federated identity
-
- :param idp_id: The identity provider ID
- :param protocol_id: The federation protocol ID
- :param unique_id: The unique ID for the user
- :returns dict: Containing the user reference
-
- """
- raise exception.NotImplemented()
-
- @abc.abstractmethod
- def update_federated_user_display_name(self, idp_id, protocol_id,
- unique_id, display_name):
- """Updates federated user's display name if changed
-
- :param idp_id: The identity provider ID
- :param protocol_id: The federation protocol ID
- :param unique_id: The unique ID for the user
- :param display_name: The user's display name
-
- """
- raise exception.NotImplemented()
diff --git a/keystone-moon/keystone/identity/generator.py b/keystone-moon/keystone/identity/generator.py
deleted file mode 100644
index 05ad2df5..00000000
--- a/keystone-moon/keystone/identity/generator.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2014 IBM Corp.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""ID Generator provider interface."""
-
-import abc
-
-from oslo_config import cfg
-import six
-
-from keystone.common import dependency
-from keystone.common import manager
-from keystone import exception
-
-
-CONF = cfg.CONF
-
-
-@dependency.provider('id_generator_api')
-class Manager(manager.Manager):
- """Default pivot point for the identifier generator backend."""
-
- driver_namespace = 'keystone.identity.id_generator'
-
- def __init__(self):
- super(Manager, self).__init__(CONF.identity_mapping.generator)
-
-
-@six.add_metaclass(abc.ABCMeta)
-class IDGenerator(object):
- """Interface description for an ID Generator provider."""
-
- @abc.abstractmethod
- def generate_public_ID(self, mapping):
- """Return a Public ID for the given mapping dict.
-
- :param dict mapping: The items to be hashed.
-
- The ID must be reproducible and no more than 64 chars in length.
- The ID generated should be independent of the order of the items
- in the mapping dict.
-
- """
- raise exception.NotImplemented() # pragma: no cover
diff --git a/keystone-moon/keystone/identity/id_generators/__init__.py b/keystone-moon/keystone/identity/id_generators/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/keystone-moon/keystone/identity/id_generators/__init__.py
+++ /dev/null
diff --git a/keystone-moon/keystone/identity/id_generators/sha256.py b/keystone-moon/keystone/identity/id_generators/sha256.py
deleted file mode 100644
index e3a8b416..00000000
--- a/keystone-moon/keystone/identity/id_generators/sha256.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2014 IBM Corp.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import hashlib
-
-import six
-
-from keystone.identity import generator
-
-
-class Generator(generator.IDGenerator):
-
- def generate_public_ID(self, mapping):
- m = hashlib.sha256()
- for key in sorted(six.iterkeys(mapping)):
- m.update(mapping[key].encode('utf-8'))
- return m.hexdigest()
diff --git a/keystone-moon/keystone/identity/mapping_backends/__init__.py b/keystone-moon/keystone/identity/mapping_backends/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/keystone-moon/keystone/identity/mapping_backends/__init__.py
+++ /dev/null
diff --git a/keystone-moon/keystone/identity/mapping_backends/mapping.py b/keystone-moon/keystone/identity/mapping_backends/mapping.py
deleted file mode 100644
index dddf36c1..00000000
--- a/keystone-moon/keystone/identity/mapping_backends/mapping.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2014 IBM Corp.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-class EntityType(object):
- USER = 'user'
- GROUP = 'group'
diff --git a/keystone-moon/keystone/identity/mapping_backends/sql.py b/keystone-moon/keystone/identity/mapping_backends/sql.py
deleted file mode 100644
index 91b33dd7..00000000
--- a/keystone-moon/keystone/identity/mapping_backends/sql.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# Copyright 2014 IBM Corp.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from keystone.common import dependency
-from keystone.common import sql
-from keystone import identity
-from keystone.identity.mapping_backends import mapping as identity_mapping
-
-
-class IDMapping(sql.ModelBase, sql.ModelDictMixin):
- __tablename__ = 'id_mapping'
- public_id = sql.Column(sql.String(64), primary_key=True)
- domain_id = sql.Column(sql.String(64), nullable=False)
- local_id = sql.Column(sql.String(64), nullable=False)
- # NOTE(henry-nash): Postgres requires a name to be defined for an Enum
- entity_type = sql.Column(
- sql.Enum(identity_mapping.EntityType.USER,
- identity_mapping.EntityType.GROUP,
- name='entity_type'),
- nullable=False)
- # Unique constraint to ensure you can't store more than one mapping to the
- # same underlying values
- __table_args__ = (
- sql.UniqueConstraint('domain_id', 'local_id', 'entity_type'),)
-
-
-@dependency.requires('id_generator_api')
-class Mapping(identity.MappingDriverV8):
-
- def get_public_id(self, local_entity):
- # NOTE(henry-nash): Since the Public ID is regeneratable, rather
- # than search for the entry using the local entity values, we
- # could create the hash and do a PK lookup. However this would only
- # work if we hashed all the entries, even those that already generate
- # UUIDs, like SQL. Further, this would only work if the generation
- # algorithm was immutable (e.g. it had always been sha256).
- with sql.session_for_read() as session:
- query = session.query(IDMapping.public_id)
- query = query.filter_by(domain_id=local_entity['domain_id'])
- query = query.filter_by(local_id=local_entity['local_id'])
- query = query.filter_by(entity_type=local_entity['entity_type'])
- try:
- public_ref = query.one()
- public_id = public_ref.public_id
- return public_id
- except sql.NotFound:
- return None
-
- def get_id_mapping(self, public_id):
- with sql.session_for_read() as session:
- mapping_ref = session.query(IDMapping).get(public_id)
- if mapping_ref:
- return mapping_ref.to_dict()
-
- def create_id_mapping(self, local_entity, public_id=None):
- entity = local_entity.copy()
- with sql.session_for_write() as session:
- if public_id is None:
- public_id = self.id_generator_api.generate_public_ID(entity)
- entity['public_id'] = public_id
- mapping_ref = IDMapping.from_dict(entity)
- session.add(mapping_ref)
- return public_id
-
- def delete_id_mapping(self, public_id):
- with sql.session_for_write() as session:
- try:
- session.query(IDMapping).filter(
- IDMapping.public_id == public_id).delete()
- except sql.NotFound: # nosec
- # NOTE(morganfainberg): There is nothing to delete and nothing
- # to do.
- pass
-
- def purge_mappings(self, purge_filter):
- with sql.session_for_write() as session:
- query = session.query(IDMapping)
- if 'domain_id' in purge_filter:
- query = query.filter_by(domain_id=purge_filter['domain_id'])
- if 'public_id' in purge_filter:
- query = query.filter_by(public_id=purge_filter['public_id'])
- if 'local_id' in purge_filter:
- query = query.filter_by(local_id=purge_filter['local_id'])
- if 'entity_type' in purge_filter:
- query = query.filter_by(
- entity_type=purge_filter['entity_type'])
- query.delete()
diff --git a/keystone-moon/keystone/identity/routers.py b/keystone-moon/keystone/identity/routers.py
deleted file mode 100644
index e274d6f4..00000000
--- a/keystone-moon/keystone/identity/routers.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""WSGI Routers for the Identity service."""
-
-from keystone.common import json_home
-from keystone.common import router
-from keystone.common import wsgi
-from keystone.identity import controllers
-
-
-class Admin(wsgi.ComposableRouter):
- def add_routes(self, mapper):
- # User Operations
- user_controller = controllers.User()
- mapper.connect('/users/{user_id}',
- controller=user_controller,
- action='get_user',
- conditions=dict(method=['GET']))
-
-
-class Routers(wsgi.RoutersBase):
-
- def append_v3_routers(self, mapper, routers):
- user_controller = controllers.UserV3()
- routers.append(
- router.Router(user_controller,
- 'users', 'user',
- resource_descriptions=self.v3_resources))
-
- self._add_resource(
- mapper, user_controller,
- path='/users/{user_id}/password',
- post_action='change_password',
- rel=json_home.build_v3_resource_relation('user_change_password'),
- path_vars={
- 'user_id': json_home.Parameters.USER_ID,
- })
-
- self._add_resource(
- mapper, user_controller,
- path='/groups/{group_id}/users',
- get_action='list_users_in_group',
- rel=json_home.build_v3_resource_relation('group_users'),
- path_vars={
- 'group_id': json_home.Parameters.GROUP_ID,
- })
-
- self._add_resource(
- mapper, user_controller,
- path='/groups/{group_id}/users/{user_id}',
- put_action='add_user_to_group',
- get_head_action='check_user_in_group',
- delete_action='remove_user_from_group',
- rel=json_home.build_v3_resource_relation('group_user'),
- path_vars={
- 'group_id': json_home.Parameters.GROUP_ID,
- 'user_id': json_home.Parameters.USER_ID,
- })
-
- group_controller = controllers.GroupV3()
- routers.append(
- router.Router(group_controller,
- 'groups', 'group',
- resource_descriptions=self.v3_resources))
-
- self._add_resource(
- mapper, group_controller,
- path='/users/{user_id}/groups',
- get_action='list_groups_for_user',
- rel=json_home.build_v3_resource_relation('user_groups'),
- path_vars={
- 'user_id': json_home.Parameters.USER_ID,
- })
diff --git a/keystone-moon/keystone/identity/schema.py b/keystone-moon/keystone/identity/schema.py
deleted file mode 100644
index 047fcf02..00000000
--- a/keystone-moon/keystone/identity/schema.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from keystone.common import validation
-from keystone.common.validation import parameter_types
-
-
-# NOTE(lhcheng): the max length is not applicable since it is specific
-# to the SQL backend, LDAP does not have length limitation.
-_identity_name = {
- 'type': 'string',
- 'minLength': 1
-}
-
-_user_properties = {
- 'default_project_id': validation.nullable(parameter_types.id_string),
- 'description': validation.nullable(parameter_types.description),
- 'domain_id': parameter_types.id_string,
- 'enabled': parameter_types.boolean,
- 'name': _identity_name,
- 'password': {
- 'type': ['string', 'null']
- }
-}
-
-user_create = {
- 'type': 'object',
- 'properties': _user_properties,
- 'required': ['name'],
- 'additionalProperties': True
-}
-
-user_update = {
- 'type': 'object',
- 'properties': _user_properties,
- 'minProperties': 1,
- 'additionalProperties': True
-}
-
-_group_properties = {
- 'description': validation.nullable(parameter_types.description),
- 'domain_id': parameter_types.id_string,
- 'name': _identity_name
-}
-
-group_create = {
- 'type': 'object',
- 'properties': _group_properties,
- 'required': ['name'],
- 'additionalProperties': True
-}
-
-group_update = {
- 'type': 'object',
- 'properties': _group_properties,
- 'minProperties': 1,
- 'additionalProperties': True
-}
diff --git a/keystone-moon/keystone/identity/shadow_backends/__init__.py b/keystone-moon/keystone/identity/shadow_backends/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/keystone-moon/keystone/identity/shadow_backends/__init__.py
+++ /dev/null
diff --git a/keystone-moon/keystone/identity/shadow_backends/sql.py b/keystone-moon/keystone/identity/shadow_backends/sql.py
deleted file mode 100644
index af5a995b..00000000
--- a/keystone-moon/keystone/identity/shadow_backends/sql.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import uuid
-
-from keystone.common import sql
-from keystone import exception
-from keystone import identity
-from keystone.identity.backends import sql as model
-
-
-class ShadowUsers(identity.ShadowUsersDriverV9):
- @sql.handle_conflicts(conflict_type='federated_user')
- def create_federated_user(self, federated_dict):
- user = {
- 'id': uuid.uuid4().hex,
- 'enabled': True
- }
- with sql.session_for_write() as session:
- federated_ref = model.FederatedUser.from_dict(federated_dict)
- user_ref = model.User.from_dict(user)
- user_ref.federated_users.append(federated_ref)
- session.add(user_ref)
- return identity.filter_user(user_ref.to_dict())
-
- def get_federated_user(self, idp_id, protocol_id, unique_id):
- user_ref = self._get_federated_user(idp_id, protocol_id, unique_id)
- return identity.filter_user(user_ref.to_dict())
-
- def _get_federated_user(self, idp_id, protocol_id, unique_id):
- """Returns the found user for the federated identity
-
- :param idp_id: The identity provider ID
- :param protocol_id: The federation protocol ID
- :param unique_id: The user's unique ID (unique within the IdP)
- :returns User: Returns a reference to the User
-
- """
- with sql.session_for_read() as session:
- query = session.query(model.User).outerjoin(model.LocalUser)
- query = query.join(model.FederatedUser)
- query = query.filter(model.FederatedUser.idp_id == idp_id)
- query = query.filter(model.FederatedUser.protocol_id ==
- protocol_id)
- query = query.filter(model.FederatedUser.unique_id == unique_id)
- try:
- user_ref = query.one()
- except sql.NotFound:
- raise exception.UserNotFound(user_id=unique_id)
- return user_ref
-
- @sql.handle_conflicts(conflict_type='federated_user')
- def update_federated_user_display_name(self, idp_id, protocol_id,
- unique_id, display_name):
- with sql.session_for_write() as session:
- query = session.query(model.FederatedUser)
- query = query.filter(model.FederatedUser.idp_id == idp_id)
- query = query.filter(model.FederatedUser.protocol_id ==
- protocol_id)
- query = query.filter(model.FederatedUser.unique_id == unique_id)
- query = query.filter(model.FederatedUser.display_name !=
- display_name)
- query.update({'display_name': display_name})
- return