summaryrefslogtreecommitdiffstats
path: root/keystone/identity
diff options
context:
space:
mode:
authorHenry Nash <henryn@linux.vnet.ibm.com>2013-02-14 09:54:38 +0000
committerHenry Nash <henryn@linux.vnet.ibm.com>2013-02-19 07:06:22 +0000
commitec326b39fa99c909862b7ea94c0261328a8d4776 (patch)
tree170d0b0d3f45ba46a829d4a8aca5f945209f9327 /keystone/identity
parentb9d8a20fff3518d3027cb95d37c1b9a13a6dea32 (diff)
downloadkeystone-ec326b39fa99c909862b7ea94c0261328a8d4776.tar.gz
keystone-ec326b39fa99c909862b7ea94c0261328a8d4776.tar.xz
keystone-ec326b39fa99c909862b7ea94c0261328a8d4776.zip
Implement name space for domains
Creates a separate name space for each domain for the name attribute of user, groups and projects - meaning that the names of these entities only have to be unique within that domain. Implementation of this within the SQL backends is handled by simply changing the uniqueness constraints on the relevant attributes. KVS and LDAP backends do not yet support domain separation (blocked by existing restrictions, already raised as bugs). An issue exists for the downward migration with this change in that if the database has been used and populated with the name space in place then the downward migration may fail due to clashing names when you try and revert to a global name space (raised as a separate bug) This patch also improves the group support in the KVS backend and cleans up string quoting in the 016 migration fucntions, and fixes an issue where the SQL update_project was not updating a change in domain_id. Change-Id: I8f0df0e1bf84bfd26b8ef5505fe5fafd930dc78b
Diffstat (limited to 'keystone/identity')
-rw-r--r--keystone/identity/backends/kvs.py40
-rw-r--r--keystone/identity/backends/sql.py28
2 files changed, 58 insertions, 10 deletions
diff --git a/keystone/identity/backends/kvs.py b/keystone/identity/backends/kvs.py
index 6c59fa03..65b3f042 100644
--- a/keystone/identity/backends/kvs.py
+++ b/keystone/identity/backends/kvs.py
@@ -573,7 +573,23 @@ class Identity(kvs.Base, identity.Driver):
# group crud
def create_group(self, group_id, group):
+ try:
+ return self.db.get('group-%s' % group_id)
+ except exception.NotFound:
+ pass
+ else:
+ msg = _('Duplicate ID, %s.') % group_id
+ raise exception.Conflict(type='group', details=msg)
+ try:
+ self.db.get('group_name-%s' % group['name'])
+ except exception.NotFound:
+ pass
+ else:
+ msg = _('Duplicate name, %s.') % group['name']
+ raise exception.Conflict(type='group', details=msg)
+
self.db.set('group-%s' % group_id, group)
+ self.db.set('group_name-%s' % group['name'], group)
group_list = set(self.db.get('group_list', []))
group_list.add(group_id)
self.db.set('group_list', list(group_list))
@@ -590,10 +606,33 @@ class Identity(kvs.Base, identity.Driver):
raise exception.GroupNotFound(group_id=group_id)
def update_group(self, group_id, group):
+ # First, make sure we are not trying to change the
+ # name to one that is already in use
+ try:
+ self.db.get('group_name-%s' % group['name'])
+ except exception.NotFound:
+ pass
+ else:
+ msg = _('Duplicate name, %s.') % group['name']
+ raise exception.Conflict(type='group', details=msg)
+
+ # Now, get the old name and delete it
+ try:
+ old_group = self.db.get('group-%s' % group_id)
+ except exception.NotFound:
+ raise exception.GroupNotFound(group_id=group_id)
+ self.db.delete('group_name-%s' % old_group['name'])
+
+ # Finally, actually do the update
self.db.set('group-%s' % group_id, group)
+ self.db.set('group_name-%s' % group['name'], group)
return group
def delete_group(self, group_id):
+ try:
+ group = self.db.get('group-%s' % group_id)
+ except exception.NotFound:
+ raise exception.GroupNotFound(group_id=group_id)
# Delete any entries in the group lists of all users
user_keys = filter(lambda x: x.startswith("user-"), self.db.keys())
user_refs = [self.db.get(key) for key in user_keys]
@@ -605,6 +644,7 @@ class Identity(kvs.Base, identity.Driver):
# Now delete the group itself
self.db.delete('group-%s' % group_id)
+ self.db.delete('group_name-%s' % group['name'])
group_list = set(self.db.get('group_list', []))
group_list.remove(group_id)
self.db.set('group_list', list(group_list))
diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py
index bda2bf93..100c8902 100644
--- a/keystone/identity/backends/sql.py
+++ b/keystone/identity/backends/sql.py
@@ -42,23 +42,29 @@ class User(sql.ModelBase, sql.DictBase):
__tablename__ = 'user'
attributes = ['id', 'name', 'domain_id', 'password', 'enabled']
id = sql.Column(sql.String(64), primary_key=True)
- name = sql.Column(sql.String(64), unique=True, nullable=False)
+ name = sql.Column(sql.String(64), nullable=False)
domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'),
nullable=False)
password = sql.Column(sql.String(128))
enabled = sql.Column(sql.Boolean)
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 Group(sql.ModelBase, sql.DictBase):
__tablename__ = 'group'
attributes = ['id', 'name', 'domain_id']
id = sql.Column(sql.String(64), primary_key=True)
- name = sql.Column(sql.String(64), unique=True, nullable=False)
+ name = sql.Column(sql.String(64), nullable=False)
domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'),
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 Credential(sql.ModelBase, sql.DictBase):
@@ -87,12 +93,15 @@ class Project(sql.ModelBase, sql.DictBase):
__tablename__ = 'project'
attributes = ['id', 'name', 'domain_id']
id = sql.Column(sql.String(64), primary_key=True)
- name = sql.Column(sql.String(64), unique=True, nullable=False)
+ name = sql.Column(sql.String(64), nullable=False)
domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'),
nullable=False)
description = sql.Column(sql.Text())
enabled = sql.Column(sql.Boolean)
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 Role(sql.ModelBase, sql.DictBase):
@@ -451,14 +460,15 @@ class Identity(sql.Base, identity.Driver):
tenant_ref = session.query(Project).filter_by(id=tenant_id).one()
except sql.NotFound:
raise exception.ProjectNotFound(project_id=tenant_id)
- # FIXME(henry-nash) Think about how we detect potential name clash
- # when we move domains
+
with session.begin():
old_project_dict = tenant_ref.to_dict()
for k in tenant:
old_project_dict[k] = tenant[k]
new_project = Project.from_dict(old_project_dict)
- tenant_ref.name = new_project.name
+ for attr in Project.attributes:
+ if attr != 'id':
+ setattr(tenant_ref, attr, getattr(new_project, attr))
tenant_ref.extra = new_project.extra
session.flush()
return tenant_ref.to_dict(include_extra_dict=True)
@@ -675,8 +685,7 @@ class Identity(sql.Base, identity.Driver):
session = self.get_session()
if 'id' in user and user_id != user['id']:
raise exception.ValidationError('Cannot change user ID')
- # FIXME(henry-nash) Think about how we detect potential name clash
- # when we move domains
+
with session.begin():
user_ref = session.query(User).filter_by(id=user_id).first()
if user_ref is None:
@@ -806,8 +815,7 @@ class Identity(sql.Base, identity.Driver):
@handle_conflicts(type='group')
def update_group(self, group_id, group):
session = self.get_session()
- # FIXME(henry-nash) Think about how we detect potential name clash
- # when we move domains
+
with session.begin():
ref = session.query(Group).filter_by(id=group_id).first()
if ref is None: