summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDolph Mathews <dolph.mathews@gmail.com>2012-11-09 08:32:18 -0600
committerGerrit Code Review <review@openstack.org>2012-11-20 17:49:00 +0000
commitddc8c833684ff0db65553b09b87eed7b80c7075d (patch)
treeb7c52bf9ca7edbc52020c0177315cd1a0a8ce755
parentff669f0da9cbf5250d8bb3e904608677f9164b6c (diff)
downloadkeystone-ddc8c833684ff0db65553b09b87eed7b80c7075d.tar.gz
keystone-ddc8c833684ff0db65553b09b87eed7b80c7075d.tar.xz
keystone-ddc8c833684ff0db65553b09b87eed7b80c7075d.zip
v3 Identity
- v3 identity tests (bug 1023930) - v3 identity implementation (bug 1023937) Change-Id: Ic46575afe9760d9da85e262d0cf063ea002d9dcd
-rw-r--r--keystone/clean.py19
-rw-r--r--keystone/common/sql/migrate_repo/versions/007_add_domain_tables.py79
-rw-r--r--keystone/exception.py8
-rw-r--r--keystone/identity/backends/kvs.py25
-rw-r--r--keystone/identity/backends/sql.py525
-rw-r--r--tests/test_v3_identity.py349
6 files changed, 886 insertions, 119 deletions
diff --git a/keystone/clean.py b/keystone/clean.py
index 8eb63b0d..89d56762 100644
--- a/keystone/clean.py
+++ b/keystone/clean.py
@@ -37,15 +37,20 @@ def check_type(property_name, value, expected_type, display_expected_type):
raise exception.ValidationError(msg)
-def tenant_name(name):
- check_type("Tenant name", name, basestring, "string or unicode")
+def check_name(property_name, name):
+ check_type('%s name' % property_name, name, basestring, 'str or unicode')
name = name.strip()
- check_length("Tenant name", name)
+ check_length('%s name' % property_name, name)
return name
+def domain_name(name):
+ return check_name('Domain', name)
+
+
+def tenant_name(name):
+ return check_name('Tenant', name)
+
+
def user_name(name):
- check_type("User name", name, basestring, "string or unicode")
- name = name.strip()
- check_length("User name", name)
- return name
+ return check_name('User', name)
diff --git a/keystone/common/sql/migrate_repo/versions/007_add_domain_tables.py b/keystone/common/sql/migrate_repo/versions/007_add_domain_tables.py
new file mode 100644
index 00000000..cb35f26d
--- /dev/null
+++ b/keystone/common/sql/migrate_repo/versions/007_add_domain_tables.py
@@ -0,0 +1,79 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC
+#
+# 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 migrate
+import sqlalchemy as sql
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine; bind
+ # migrate_engine to your metadata
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ domain_table = sql.Table(
+ 'domain',
+ meta,
+ sql.Column('id', sql.String(64), primary_key=True),
+ sql.Column('name', sql.String(64), unique=True, nullable=False),
+ sql.Column('extra', sql.Text()))
+ domain_table.create(migrate_engine, checkfirst=True)
+
+ sql.Table('user', meta, autoload=True)
+ user_domain_metadata_table = sql.Table(
+ 'user_domain_metadata',
+ meta,
+ sql.Column(
+ 'user_id',
+ sql.String(64),
+ sql.ForeignKey('user.id'),
+ primary_key=True),
+ sql.Column(
+ 'domain_id',
+ sql.String(64),
+ sql.ForeignKey('domain.id'),
+ primary_key=True),
+ sql.Column('data', sql.Text()))
+ user_domain_metadata_table.create(migrate_engine, checkfirst=True)
+
+ sql.Table('tenant', meta, autoload=True)
+ credential_table = sql.Table(
+ 'credential',
+ meta,
+ sql.Column('id', sql.String(64), primary_key=True),
+ sql.Column('user_id',
+ sql.String(64),
+ sql.ForeignKey('user.id'),
+ nullable=False),
+ sql.Column('project_id',
+ sql.String(64),
+ sql.ForeignKey('tenant.id')),
+ sql.Column('blob', sql.Text(), nullable=False),
+ sql.Column('type', sql.String(255), nullable=False),
+ sql.Column('extra', sql.Text()))
+ credential_table.create(migrate_engine, checkfirst=True)
+
+ role = sql.Table('role', meta, autoload=True)
+ extra = sql.Column('extra', sql.Text())
+ role.create_column(extra)
+
+
+def downgrade(migrate_engine):
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ role = sql.Table('role', meta, autoload=True)
+ role.drop_column('extra')
diff --git a/keystone/exception.py b/keystone/exception.py
index cc61a632..4b9721cf 100644
--- a/keystone/exception.py
+++ b/keystone/exception.py
@@ -95,10 +95,18 @@ class ServiceNotFound(NotFound):
"""Could not find service: %(service_id)s"""
+class DomainNotFound(NotFound):
+ """Could not find domain: %(domain_id)s"""
+
+
class TenantNotFound(NotFound):
"""Could not find tenant: %(tenant_id)s"""
+class ProjectNotFound(TenantNotFound):
+ """Could not find project: %(project_id)s"""
+
+
class TokenNotFound(NotFound):
"""Could not find token: %(token_id)s"""
diff --git a/keystone/identity/backends/kvs.py b/keystone/identity/backends/kvs.py
index b64baa38..65917181 100644
--- a/keystone/identity/backends/kvs.py
+++ b/keystone/identity/backends/kvs.py
@@ -357,3 +357,28 @@ class Identity(kvs.Base, identity.Driver):
role_list = set(self.db.get('role_list', []))
role_list.remove(role_id)
self.db.set('role_list', list(role_list))
+
+ # domain crud
+
+ def create_domain(self, domain_id, domain):
+ self.db.set('domain-%s' % domain_id, domain)
+ domain_list = set(self.db.get('domain_list', []))
+ domain_list.add(domain_id)
+ self.db.set('domain_list', list(domain_list))
+ return domain
+
+ def list_domains(self):
+ return self.db.get('domain_list', [])
+
+ def get_domain(self, domain_id):
+ return self.db.get('domain-%s' % domain_id)
+
+ def update_domain(self, domain_id, domain):
+ self.db.set('domain-%s' % domain_id, domain)
+ return domain
+
+ def delete_domain(self, domain_id):
+ self.db.delete('domain-%s' % domain_id)
+ domain_list = set(self.db.get('domain_list', []))
+ domain_list.remove(domain_id)
+ self.db.set('domain_list', list(domain_list))
diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py
index 753a30e2..209ec2f0 100644
--- a/keystone/identity/backends/sql.py
+++ b/keystone/identity/backends/sql.py
@@ -45,7 +45,30 @@ class User(sql.ModelBase, sql.DictBase):
extra = sql.Column(sql.JsonBlob())
+class Credential(sql.ModelBase, sql.DictBase):
+ __tablename__ = 'credential'
+ attributes = ['id', 'user_id', 'project_id', 'blob', 'type']
+ id = sql.Column(sql.String(64), primary_key=True)
+ user_id = sql.Column(sql.String(64),
+ sql.ForeignKey('user.id'),
+ nullable=False)
+ project_id = sql.Column(sql.String(64), sql.ForeignKey('tenant.id'))
+ blob = sql.Column(sql.JsonBlob(), nullable=False)
+ type = sql.Column(sql.String(255), nullable=False)
+ extra = sql.Column(sql.JsonBlob())
+
+
+class Domain(sql.ModelBase, sql.DictBase):
+ __tablename__ = 'domain'
+ attributes = ['id', 'name']
+ id = sql.Column(sql.String(64), primary_key=True)
+ name = sql.Column(sql.String(64), unique=True, nullable=False)
+ extra = sql.Column(sql.JsonBlob())
+
+
+# TODO(dolph): rename to Project
class Tenant(sql.ModelBase, sql.DictBase):
+ # TODO(dolph): rename to project
__tablename__ = 'tenant'
attributes = ['id', 'name']
id = sql.Column(sql.String(64), primary_key=True)
@@ -58,11 +81,14 @@ class Role(sql.ModelBase, sql.DictBase):
attributes = ['id', 'name']
id = sql.Column(sql.String(64), primary_key=True)
name = sql.Column(sql.String(64), unique=True, nullable=False)
+ extra = sql.Column(sql.JsonBlob())
-class Metadata(sql.ModelBase, sql.DictBase):
+class UserProjectMetadata(sql.ModelBase, sql.DictBase):
+ # TODO(dolph): rename to user_project_metadata (needs a migration)
__tablename__ = 'metadata'
user_id = sql.Column(sql.String(64), primary_key=True)
+ # TODO(dolph): rename to project_id (needs a migration)
tenant_id = sql.Column(sql.String(64), primary_key=True)
data = sql.Column(sql.JsonBlob())
@@ -75,6 +101,14 @@ class Metadata(sql.ModelBase, sql.DictBase):
return dict(self.iteritems())
+class UserDomainMetadata(sql.ModelBase, sql.DictBase):
+ __tablename__ = 'user_domain_metadata'
+ user_id = sql.Column(sql.String(64), primary_key=True)
+ domain_id = sql.Column(sql.String(64), primary_key=True)
+ data = sql.Column(sql.JsonBlob())
+
+
+# TODO(dolph): ... do we need this table?
class UserTenantMembership(sql.ModelBase, sql.DictBase):
"""Tenant membership join table."""
__tablename__ = 'user_tenant_membership'
@@ -164,52 +198,79 @@ class Identity(sql.Base, identity.Driver):
return [identity.filter_user(user_ref.to_dict())
for user_ref in user_refs]
- def _get_user(self, user_id):
+ def get_metadata(self, user_id, tenant_id=None, domain_id=None):
session = self.get_session()
- user_ref = session.query(User).filter_by(id=user_id).first()
- if not user_ref:
- raise exception.UserNotFound(user_id=user_id)
- return user_ref.to_dict()
- def _get_user_by_name(self, user_name):
- session = self.get_session()
- user_ref = session.query(User).filter_by(name=user_name).first()
- if not user_ref:
- raise exception.UserNotFound(user_id=user_name)
- return user_ref.to_dict()
+ if tenant_id:
+ q = session.query(UserProjectMetadata)
+ q = q.filter_by(tenant_id=tenant_id)
+ elif domain_id:
+ q = session.query(UserDomainMetadata)
+ q = q.filter_by(domain_id=domain_id)
+ q = q.filter_by(user_id=user_id)
- def get_user(self, user_id):
- return identity.filter_user(self._get_user(user_id))
+ try:
+ return q.one().data
+ except sql.NotFound:
+ raise exception.MetadataNotFound()
- def get_user_by_name(self, user_name):
- return identity.filter_user(self._get_user_by_name(user_name))
+ def create_grant(self, role_id, user_id, domain_id, project_id):
+ self.get_role(role_id)
+ self.get_user(user_id)
+ if domain_id:
+ self.get_domain(domain_id)
+ if project_id:
+ self.get_tenant(project_id)
- def get_metadata(self, user_id, tenant_id):
- session = self.get_session()
- query = session.query(Metadata)
- query = query.filter_by(user_id=user_id)
- query = query.filter_by(tenant_id=tenant_id)
- metadata_ref = query.first()
- if metadata_ref is None:
- raise exception.MetadataNotFound()
- return metadata_ref.data
+ try:
+ metadata_ref = self.get_metadata(user_id, project_id, domain_id)
+ is_new = False
+ except exception.MetadataNotFound:
+ metadata_ref = {}
+ is_new = True
+ roles = set(metadata_ref.get('roles', []))
+ roles.add(role_id)
+ metadata_ref['roles'] = list(roles)
+ if is_new:
+ self.create_metadata(user_id, project_id, metadata_ref, domain_id)
+ else:
+ self.update_metadata(user_id, project_id, metadata_ref, domain_id)
- def get_role(self, role_id):
- session = self.get_session()
- role_ref = session.query(Role).filter_by(id=role_id).first()
- if role_ref is None:
+ def list_grants(self, user_id, domain_id, project_id):
+ metadata_ref = self.get_metadata(user_id, project_id, domain_id)
+ return [self.get_role(x) for x in metadata_ref.get('roles', [])]
+
+ def get_grant(self, role_id, user_id, domain_id, project_id):
+ metadata_ref = self.get_metadata(user_id, project_id, domain_id)
+ role_ids = set(metadata_ref.get('roles', []))
+ if role_id not in role_ids:
raise exception.RoleNotFound(role_id=role_id)
- return role_ref
+ return self.get_role(role_id)
- def list_users(self):
- session = self.get_session()
- user_refs = session.query(User)
- return [identity.filter_user(x.to_dict()) for x in user_refs]
+ def delete_grant(self, role_id, user_id, domain_id, project_id):
+ self.get_role(role_id)
+ self.get_user(user_id)
+ if domain_id:
+ self.get_domain(domain_id)
+ if project_id:
+ self.get_tenant(project_id)
- def list_roles(self):
- session = self.get_session()
- role_refs = session.query(Role)
- return list(role_refs)
+ try:
+ metadata_ref = self.get_metadata(user_id, project_id, domain_id)
+ is_new = False
+ except exception.MetadataNotFound:
+ metadata_ref = {}
+ is_new = True
+ roles = set(metadata_ref.get('roles', []))
+ try:
+ roles.remove(role_id)
+ except KeyError:
+ raise exception.RoleNotFound(role_id=role_id)
+ metadata_ref['roles'] = list(roles)
+ if is_new:
+ self.create_metadata(user_id, project_id, metadata_ref, domain_id)
+ else:
+ self.update_metadata(user_id, project_id, metadata_ref, domain_id)
# These should probably be part of the high-level API
def add_user_to_tenant(self, tenant_id, user_id):
@@ -306,6 +367,198 @@ class Identity(sql.Base, identity.Driver):
self.update_metadata(user_id, tenant_id, metadata_ref)
# CRUD
+ @handle_conflicts(type='tenant')
+ def create_tenant(self, tenant_id, tenant):
+ tenant['name'] = clean.tenant_name(tenant['name'])
+ session = self.get_session()
+ with session.begin():
+ tenant_ref = Tenant.from_dict(tenant)
+ session.add(tenant_ref)
+ session.flush()
+ return tenant_ref.to_dict()
+
+ @handle_conflicts(type='tenant')
+ def update_tenant(self, tenant_id, tenant):
+ session = self.get_session()
+
+ if 'name' in tenant:
+ tenant['name'] = clean.tenant_name(tenant['name'])
+
+ try:
+ tenant_ref = session.query(Tenant).filter_by(id=tenant_id).one()
+ except sql.NotFound:
+ raise exception.TenantNotFound(tenant_id=tenant_id)
+
+ with session.begin():
+ old_tenant_dict = tenant_ref.to_dict()
+ for k in tenant:
+ old_tenant_dict[k] = tenant[k]
+ new_tenant = Tenant.from_dict(old_tenant_dict)
+ tenant_ref.name = new_tenant.name
+ tenant_ref.extra = new_tenant.extra
+ session.flush()
+ return tenant_ref.to_dict(include_extra_dict=True)
+
+ def delete_tenant(self, tenant_id):
+ session = self.get_session()
+
+ try:
+ tenant_ref = session.query(Tenant).filter_by(id=tenant_id).one()
+ except sql.NotFound:
+ raise exception.TenantNotFound(tenant_id=tenant_id)
+
+ with session.begin():
+ q = session.query(UserTenantMembership)
+ q = q.filter_by(tenant_id=tenant_id)
+ q.delete(False)
+
+ q = session.query(UserProjectMetadata)
+ q = q.filter_by(tenant_id=tenant_id)
+ q.delete(False)
+
+ if not session.query(Tenant).filter_by(id=tenant_id).delete(False):
+ raise exception.TenantNotFound(tenant_id=tenant_id)
+
+ session.delete(tenant_ref)
+ session.flush()
+
+ @handle_conflicts(type='metadata')
+ def create_metadata(self, user_id, tenant_id, metadata, domain_id=None):
+ session = self.get_session()
+ with session.begin():
+ if tenant_id:
+ session.add(UserProjectMetadata(user_id=user_id,
+ tenant_id=tenant_id,
+ data=metadata))
+ elif domain_id:
+ session.add(UserDomainMetadata(user_id=user_id,
+ domain_id=domain_id,
+ data=metadata))
+ session.flush()
+ return metadata
+
+ @handle_conflicts(type='metadata')
+ def update_metadata(self, user_id, tenant_id, metadata, domain_id=None):
+ session = self.get_session()
+ with session.begin():
+ if tenant_id:
+ metadata_ref = session.query(UserProjectMetadata)\
+ .filter_by(user_id=user_id)\
+ .filter_by(tenant_id=tenant_id)\
+ .first()
+ elif domain_id:
+ metadata_ref = session.query(UserDomainMetadata)\
+ .filter_by(user_id=user_id)\
+ .filter_by(domain_id=domain_id)\
+ .first()
+ data = metadata_ref.data.copy()
+ data.update(metadata)
+ metadata_ref.data = data
+ session.flush()
+ return metadata_ref
+
+ # domain crud
+
+ @handle_conflicts(type='domain')
+ def create_domain(self, domain_id, domain):
+ session = self.get_session()
+ with session.begin():
+ ref = Domain.from_dict(domain)
+ session.add(ref)
+ session.flush()
+ return ref.to_dict()
+
+ def list_domains(self):
+ session = self.get_session()
+ refs = session.query(Domain).all()
+ return [ref.to_dict() for ref in refs]
+
+ def get_domain(self, domain_id):
+ session = self.get_session()
+ ref = session.query(Domain).filter_by(id=domain_id).first()
+ if ref is None:
+ raise exception.DomainNotFound(domain_id=domain_id)
+ return ref.to_dict()
+
+ @handle_conflicts(type='domain')
+ def update_domain(self, domain_id, domain):
+ session = self.get_session()
+ with session.begin():
+ ref = session.query(Domain).filter_by(id=domain_id).first()
+ if ref is None:
+ raise exception.DomainNotFound(domain_id=domain_id)
+ old_dict = ref.to_dict()
+ for k in domain:
+ old_dict[k] = domain[k]
+ new_domain = Domain.from_dict(old_dict)
+ for attr in Domain.attributes:
+ if attr != 'id':
+ setattr(ref, attr, getattr(new_domain, attr))
+ ref.extra = new_domain.extra
+ session.flush()
+ return ref.to_dict()
+
+ def delete_domain(self, domain_id):
+ session = self.get_session()
+ ref = session.query(Domain).filter_by(id=domain_id).first()
+ if not ref:
+ raise exception.DomainNotFound(domain_id=domain_id)
+ with session.begin():
+ session.delete(ref)
+ session.flush()
+
+ # project crud
+
+ @handle_conflicts(type='project')
+ def create_project(self, project_id, project):
+ return self.create_tenant(project_id, project)
+
+ def get_project(self, project_id):
+ return self.get_tenant(project_id)
+
+ def list_projects(self):
+ return self.get_tenants()
+
+ @handle_conflicts(type='project')
+ def update_project(self, project_id, project):
+ session = self.get_session()
+ with session.begin():
+ ref = session.query(Tenant).filter_by(id=project_id).first()
+ if ref is None:
+ raise exception.TenantNotFound(project_id=project_id)
+ old_dict = ref.to_dict()
+ for k in project:
+ old_dict[k] = project[k]
+ new_project = Tenant.from_dict(old_dict)
+ for attr in Tenant.attributes:
+ if attr != 'id':
+ setattr(ref, attr, getattr(new_project, attr))
+ ref.extra = new_project.extra
+ session.flush()
+ return ref.to_dict()
+
+ def delete_project(self, project_id):
+ return self.delete_tenant(project_id)
+
+ def list_user_projects(self, user_id):
+ session = self.get_session()
+ user = self.get_user(user_id)
+ metadata_refs = session\
+ .query(UserProjectMetadata)\
+ .filter_by(user_id=user_id)
+ project_ids = set([x.tenant_id for x in metadata_refs
+ if x.data.get('roles')])
+ if user.get('project_id'):
+ project_ids.add(user['project_id'])
+
+ # FIXME(dolph): this should be removed with proper migrations
+ if user.get('tenant_id'):
+ project_ids.add(user['tenant_id'])
+
+ return [self.get_project(x) for x in project_ids]
+
+ # user crud
+
@handle_conflicts(type='user')
def create_user(self, user_id, user):
user['name'] = clean.user_name(user['name'])
@@ -317,6 +570,31 @@ class Identity(sql.Base, identity.Driver):
session.flush()
return identity.filter_user(user_ref.to_dict())
+ def list_users(self):
+ session = self.get_session()
+ user_refs = session.query(User)
+ return [identity.filter_user(x.to_dict()) for x in user_refs]
+
+ def _get_user(self, user_id):
+ session = self.get_session()
+ user_ref = session.query(User).filter_by(id=user_id).first()
+ if not user_ref:
+ raise exception.UserNotFound(user_id=user_id)
+ return user_ref.to_dict()
+
+ def _get_user_by_name(self, user_name):
+ session = self.get_session()
+ user_ref = session.query(User).filter_by(name=user_name).first()
+ if not user_ref:
+ raise exception.UserNotFound(user_id=user_name)
+ return user_ref.to_dict()
+
+ def get_user(self, user_id):
+ return identity.filter_user(self._get_user(user_id))
+
+ def get_user_by_name(self, user_name):
+ return identity.filter_user(self._get_user_by_name(user_name))
+
@handle_conflicts(type='user')
def update_user(self, user_id, user):
if 'name' in user:
@@ -333,128 +611,151 @@ class Identity(sql.Base, identity.Driver):
for k in user:
old_user_dict[k] = user[k]
new_user = User.from_dict(old_user_dict)
-
- user_ref.name = new_user.name
+ for attr in User.attributes:
+ if attr != 'id':
+ setattr(user_ref, attr, getattr(new_user, attr))
user_ref.extra = new_user.extra
session.flush()
return identity.filter_user(user_ref.to_dict(include_extra_dict=True))
def delete_user(self, user_id):
session = self.get_session()
+
+ try:
+ ref = session.query(User).filter_by(id=user_id).one()
+ except sql.NotFound:
+ raise exception.UserNotFound(user_id=user_id)
+
with session.begin():
- query = session.query(UserTenantMembership)
- query = query.filter_by(user_id=user_id)
- query.delete(False)
- query = session.query(Metadata)
- query = query.filter_by(user_id=user_id)
- query.delete(False)
+ q = session.query(UserTenantMembership)
+ q = q.filter_by(user_id=user_id)
+ q.delete(False)
+
+ q = session.query(UserProjectMetadata)
+ q = q.filter_by(user_id=user_id)
+ q.delete(False)
+
if not session.query(User).filter_by(id=user_id).delete(False):
raise exception.UserNotFound(user_id=user_id)
- @handle_conflicts(type='tenant')
- def create_tenant(self, tenant_id, tenant):
- tenant['name'] = clean.tenant_name(tenant['name'])
- session = self.get_session()
- with session.begin():
- tenant_ref = Tenant.from_dict(tenant)
- session.add(tenant_ref)
+ session.delete(ref)
session.flush()
- return tenant_ref.to_dict()
- @handle_conflicts(type='tenant')
- def update_tenant(self, tenant_id, tenant):
- if 'name' in tenant:
- tenant['name'] = clean.tenant_name(tenant['name'])
+ # credential crud
+
+ @handle_conflicts(type='credential')
+ def create_credential(self, credential_id, credential):
session = self.get_session()
with session.begin():
- tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first()
- if tenant_ref is None:
- raise exception.TenantNotFound(tenant_id=tenant_id)
- old_tenant_dict = tenant_ref.to_dict()
- for k in tenant:
- old_tenant_dict[k] = tenant[k]
- new_tenant = Tenant.from_dict(old_tenant_dict)
-
- tenant_ref.name = new_tenant.name
- tenant_ref.extra = new_tenant.extra
+ ref = Credential.from_dict(credential)
+ session.add(ref)
session.flush()
- return tenant_ref.to_dict(include_extra_dict=True)
+ return ref.to_dict()
- def delete_tenant(self, tenant_id):
+ def list_credentials(self):
session = self.get_session()
- with session.begin():
- query = session.query(UserTenantMembership)
- query = query.filter_by(tenant_id=tenant_id)
- query.delete(False)
- query = session.query(Metadata)
- query = query.filter_by(tenant_id=tenant_id)
- query.delete(False)
- if not session.query(Tenant).filter_by(id=tenant_id).delete(False):
- raise exception.TenantNotFound(tenant_id=tenant_id)
+ refs = session.query(Credential).all()
+ return [ref.to_dict() for ref in refs]
- @handle_conflicts(type='metadata')
- def create_metadata(self, user_id, tenant_id, metadata):
+ def get_credential(self, credential_id):
+ session = self.get_session()
+ ref = session.query(Credential).filter_by(id=credential_id).first()
+ if ref is None:
+ raise exception.CredentialNotFound(credential_id=credential_id)
+ return ref.to_dict()
+
+ @handle_conflicts(type='credential')
+ def update_credential(self, credential_id, credential):
session = self.get_session()
with session.begin():
- session.add(Metadata(user_id=user_id,
- tenant_id=tenant_id,
- data=metadata))
+ ref = session.query(Credential).filter_by(id=credential_id).first()
+ if ref is None:
+ raise exception.CredentialNotFound(credential_id=credential_id)
+ old_dict = ref.to_dict()
+ for k in credential:
+ old_dict[k] = credential[k]
+ new_credential = Credential.from_dict(old_dict)
+ for attr in Credential.attributes:
+ if attr != 'id':
+ setattr(ref, attr, getattr(new_credential, attr))
+ ref.extra = new_credential.extra
session.flush()
- return metadata
+ return ref.to_dict()
- @handle_conflicts(type='metadata')
- def update_metadata(self, user_id, tenant_id, metadata):
+ def delete_credential(self, credential_id):
session = self.get_session()
+
+ try:
+ ref = session.query(Credential).filter_by(id=credential_id).one()
+ except sql.NotFound:
+ raise exception.CredentialNotFound(credential_id=credential_id)
+
with session.begin():
- query = session.query(Metadata)
- query = query.filter_by(user_id=user_id)
- query = query.filter_by(tenant_id=tenant_id)
- metadata_ref = query.first()
- data = metadata_ref.data.copy()
- for k in metadata:
- data[k] = metadata[k]
- metadata_ref.data = data
+ session.delete(ref)
session.flush()
- return metadata_ref
- def delete_metadata(self, user_id, tenant_id):
- self.db.delete('metadata-%s-%s' % (tenant_id, user_id))
- return None
+ # role crud
@handle_conflicts(type='role')
def create_role(self, role_id, role):
session = self.get_session()
with session.begin():
- session.add(Role(**role))
+ ref = Role.from_dict(role)
+ session.add(ref)
session.flush()
- return role
+ return ref.to_dict()
+
+ def list_roles(self):
+ session = self.get_session()
+ refs = session.query(Role).all()
+ return [ref.to_dict() for ref in refs]
+
+ def get_role(self, role_id):
+ session = self.get_session()
+ ref = session.query(Role).filter_by(id=role_id).first()
+ if ref is None:
+ raise exception.RoleNotFound(role_id=role_id)
+ return ref.to_dict()
@handle_conflicts(type='role')
def update_role(self, role_id, role):
session = self.get_session()
with session.begin():
- role_ref = session.query(Role).filter_by(id=role_id).first()
- if role_ref is None:
+ ref = session.query(Role).filter_by(id=role_id).first()
+ if ref is None:
raise exception.RoleNotFound(role_id=role_id)
+ old_dict = ref.to_dict()
for k in role:
- role_ref[k] = role[k]
+ old_dict[k] = role[k]
+ new_role = Role.from_dict(old_dict)
+ for attr in Role.attributes:
+ if attr != 'id':
+ setattr(ref, attr, getattr(new_role, attr))
+ ref.extra = new_role.extra
session.flush()
- return role_ref
+ return ref.to_dict()
def delete_role(self, role_id):
session = self.get_session()
+
+ try:
+ ref = session.query(Role).filter_by(id=role_id).one()
+ except sql.NotFound:
+ raise exception.RoleNotFound(role_id=role_id)
+
with session.begin():
- metadata_refs = session.query(Metadata)
- for metadata_ref in metadata_refs:
+ for metadata_ref in session.query(UserProjectMetadata):
metadata = metadata_ref.to_dict()
- user_id = metadata['user_id']
- tenant_id = metadata['tenant_id']
try:
- self.remove_role_from_user_and_tenant(user_id,
- tenant_id,
- role_id)
+ self.remove_role_from_user_and_tenant(
+ metadata['user_id'], metadata['tenant_id'], role_id)
except exception.RoleNotFound:
pass
+
+ # FIXME(dolph): user-domain metadata needs to be updated
+
if not session.query(Role).filter_by(id=role_id).delete():
raise exception.RoleNotFound(role_id=role_id)
+
+ session.delete(ref)
session.flush()
diff --git a/tests/test_v3_identity.py b/tests/test_v3_identity.py
new file mode 100644
index 00000000..43bbf293
--- /dev/null
+++ b/tests/test_v3_identity.py
@@ -0,0 +1,349 @@
+import uuid
+
+import test_v3
+
+
+class IdentityTestCase(test_v3.RestfulTestCase):
+ """Test domains, projects, users, credential & role CRUD"""
+
+ def setUp(self):
+ super(IdentityTestCase, self).setUp()
+
+ self.domain_id = uuid.uuid4().hex
+ self.domain = self.new_domain_ref()
+ self.domain['id'] = self.domain_id
+ self.identity_api.create_domain(
+ self.domain_id,
+ self.domain.copy())
+
+ self.project_id = uuid.uuid4().hex
+ self.project = self.new_project_ref(
+ domain_id=self.domain_id)
+ self.project['id'] = self.project_id
+ self.identity_api.create_project(
+ self.project_id,
+ self.project.copy())
+
+ self.user_id = uuid.uuid4().hex
+ self.user = self.new_user_ref(
+ domain_id=self.domain_id,
+ project_id=self.project_id)
+ self.user['id'] = self.user_id
+ self.identity_api.create_user(
+ self.user_id,
+ self.user.copy())
+
+ self.credential_id = uuid.uuid4().hex
+ self.credential = self.new_credential_ref(
+ user_id=self.user_id,
+ project_id=self.project_id)
+ self.credential['id'] = self.credential_id
+ self.identity_api.create_credential(
+ self.credential_id,
+ self.credential.copy())
+
+ self.role_id = uuid.uuid4().hex
+ self.role = self.new_role_ref()
+ self.role['id'] = self.role_id
+ self.identity_api.create_role(
+ self.role_id,
+ self.role.copy())
+
+ # domain validation
+
+ def assertValidDomainListResponse(self, resp, ref):
+ return self.assertValidListResponse(
+ resp,
+ 'domains',
+ self.assertValidDomain,
+ ref)
+
+ def assertValidDomainResponse(self, resp, ref):
+ return self.assertValidResponse(
+ resp,
+ 'domain',
+ self.assertValidDomain,
+ ref)
+
+ def assertValidDomain(self, entity, ref=None):
+ if ref:
+ pass
+ return entity
+
+ # project validation
+
+ def assertValidProjectListResponse(self, resp, ref):
+ return self.assertValidListResponse(
+ resp,
+ 'projects',
+ self.assertValidProject,
+ ref)
+
+ def assertValidProjectResponse(self, resp, ref):
+ return self.assertValidResponse(
+ resp,
+ 'project',
+ self.assertValidProject,
+ ref)
+
+ def assertValidProject(self, entity, ref=None):
+ self.assertIsNotNone(entity.get('domain_id'))
+ if ref:
+ self.assertEqual(ref['domain_id'], entity['domain_id'])
+ return entity
+
+ # user validation
+
+ def assertValidUserListResponse(self, resp, ref):
+ return self.assertValidListResponse(
+ resp,
+ 'users',
+ self.assertValidUser,
+ ref)
+
+ def assertValidUserResponse(self, resp, ref):
+ return self.assertValidResponse(
+ resp,
+ 'user',
+ self.assertValidUser,
+ ref)
+
+ def assertValidUser(self, entity, ref=None):
+ self.assertIsNotNone(entity.get('domain_id'))
+ self.assertIsNotNone(entity.get('email'))
+ self.assertIsNone(entity.get('password'))
+ if ref:
+ self.assertEqual(ref['domain_id'], entity['domain_id'])
+ self.assertEqual(ref['email'], entity['email'])
+ return entity
+
+ # credential validation
+
+ def assertValidCredentialListResponse(self, resp, ref):
+ return self.assertValidListResponse(
+ resp,
+ 'credentials',
+ self.assertValidCredential,
+ ref)
+
+ def assertValidCredentialResponse(self, resp, ref):
+ return self.assertValidResponse(
+ resp,
+ 'credential',
+ self.assertValidCredential,
+ ref)
+
+ def assertValidCredential(self, entity, ref=None):
+ self.assertIsNotNone(entity.get('user_id'))
+ self.assertIsNotNone(entity.get('blob'))
+ self.assertIsNotNone(entity.get('type'))
+ if ref:
+ self.assertEqual(ref['user_id'], entity['user_id'])
+ self.assertEqual(ref['blob'], entity['blob'])
+ self.assertEqual(ref['type'], entity['type'])
+ self.assertEqual(ref.get('project_id'), entity.get('project_id'))
+ return entity
+
+ # role validation
+
+ def assertValidRoleListResponse(self, resp, ref):
+ return self.assertValidListResponse(
+ resp,
+ 'roles',
+ self.assertValidRole,
+ ref)
+
+ def assertValidRoleResponse(self, resp, ref):
+ return self.assertValidResponse(
+ resp,
+ 'role',
+ self.assertValidRole,
+ ref)
+
+ def assertValidRole(self, entity, ref=None):
+ if ref:
+ pass
+ return entity
+
+ # domain crud tests
+
+ def test_create_domain(self):
+ """POST /domains"""
+ ref = self.new_domain_ref()
+ r = self.post(
+ '/domains',
+ body={'domain': ref})
+ return self.assertValidDomainResponse(r, ref)
+
+ def test_list_domains(self):
+ """GET /domains"""
+ r = self.get('/domains')
+ self.assertValidDomainListResponse(r, self.domain)
+
+ def test_get_domain(self):
+ """GET /domains/{domain_id}"""
+ r = self.get('/domains/%(domain_id)s' % {
+ 'domain_id': self.domain_id})
+ self.assertValidDomainResponse(r, self.domain)
+
+ def test_update_domain(self):
+ """PATCH /domains/{domain_id}"""
+ ref = self.new_domain_ref()
+ del ref['id']
+ r = self.patch('/domains/%(domain_id)s' % {
+ 'domain_id': self.domain_id},
+ body={'domain': ref})
+ self.assertValidDomainResponse(r, ref)
+
+ def test_delete_domain(self):
+ """DELETE /domains/{domain_id}"""
+ self.delete('/domains/%(domain_id)s' % {
+ 'domain_id': self.domain_id})
+
+ # project crud tests
+
+ def test_list_projects(self):
+ """GET /projects"""
+ r = self.get('/projects')
+ self.assertValidProjectListResponse(r, self.project)
+
+ def test_create_project(self):
+ """POST /projects"""
+ ref = self.new_project_ref(domain_id=self.domain_id)
+ r = self.post(
+ '/projects',
+ body={'project': ref})
+ self.assertValidProjectResponse(r, ref)
+
+ def test_get_project(self):
+ """GET /projects/{project_id}"""
+ r = self.get(
+ '/projects/%(project_id)s' % {
+ 'project_id': self.project_id})
+ self.assertValidProjectResponse(r, self.project)
+
+ def test_update_project(self):
+ """PATCH /projects/{project_id}"""
+ ref = self.new_project_ref(domain_id=self.domain_id)
+ del ref['id']
+ r = self.patch(
+ '/projects/%(project_id)s' % {
+ 'project_id': self.project_id},
+ body={'project': ref})
+ self.assertValidProjectResponse(r, ref)
+
+ def test_delete_project(self):
+ """DELETE /projects/{project_id}"""
+ self.delete(
+ '/projects/%(project_id)s' % {
+ 'project_id': self.project_id})
+
+ # user crud tests
+
+ def test_create_user(self):
+ """POST /users"""
+ ref = self.new_user_ref(domain_id=self.domain_id)
+ r = self.post(
+ '/users',
+ body={'user': ref})
+ return self.assertValidUserResponse(r, ref)
+
+ def test_list_users(self):
+ """GET /users"""
+ r = self.get('/users')
+ self.assertValidUserListResponse(r, self.user)
+
+ def test_get_user(self):
+ """GET /users/{user_id}"""
+ r = self.get('/users/%(user_id)s' % {
+ 'user_id': self.user_id})
+ self.assertValidUserResponse(r, self.user)
+
+ def test_update_user(self):
+ """PATCH /users/{user_id}"""
+ user = self.new_user_ref(domain_id=self.domain_id)
+ del user['id']
+ r = self.patch('/users/%(user_id)s' % {
+ 'user_id': self.user_id},
+ body={'user': user})
+ self.assertValidUserResponse(r, user)
+
+ def test_delete_user(self):
+ """DELETE /users/{user_id}"""
+ self.delete('/users/%(user_id)s' % {
+ 'user_id': self.user_id})
+
+ # credential crud tests
+
+ def test_list_credentials(self):
+ """GET /credentials"""
+ r = self.get('/credentials')
+ self.assertValidCredentialListResponse(r, self.credential)
+
+ def test_create_credential(self):
+ """POST /credentials"""
+ ref = self.new_credential_ref(user_id=self.user_id)
+ r = self.post(
+ '/credentials',
+ body={'credential': ref})
+ self.assertValidCredentialResponse(r, ref)
+
+ def test_get_credential(self):
+ """GET /credentials/{credential_id}"""
+ r = self.get(
+ '/credentials/%(credential_id)s' % {
+ 'credential_id': self.credential_id})
+ self.assertValidCredentialResponse(r, self.credential)
+
+ def test_update_credential(self):
+ """PATCH /credentials/{credential_id}"""
+ ref = self.new_credential_ref(
+ user_id=self.user_id,
+ project_id=self.project_id)
+ del ref['id']
+ r = self.patch(
+ '/credentials/%(credential_id)s' % {
+ 'credential_id': self.credential_id},
+ body={'credential': ref})
+ self.assertValidCredentialResponse(r, ref)
+
+ def test_delete_credential(self):
+ """DELETE /credentials/{credential_id}"""
+ self.delete(
+ '/credentials/%(credential_id)s' % {
+ 'credential_id': self.credential_id})
+
+ # role crud tests
+
+ def test_create_role(self):
+ """POST /roles"""
+ ref = self.new_role_ref()
+ r = self.post(
+ '/roles',
+ body={'role': ref})
+ return self.assertValidRoleResponse(r, ref)
+
+ def test_list_roles(self):
+ """GET /roles"""
+ r = self.get('/roles')
+ self.assertValidRoleListResponse(r, self.role)
+
+ def test_get_role(self):
+ """GET /roles/{role_id}"""
+ r = self.get('/roles/%(role_id)s' % {
+ 'role_id': self.role_id})
+ self.assertValidRoleResponse(r, self.role)
+
+ def test_update_role(self):
+ """PATCH /roles/{role_id}"""
+ ref = self.new_role_ref()
+ del ref['id']
+ r = self.patch('/roles/%(role_id)s' % {
+ 'role_id': self.role_id},
+ body={'role': ref})
+ self.assertValidRoleResponse(r, ref)
+
+ def test_delete_role(self):
+ """DELETE /roles/{role_id}"""
+ self.delete('/roles/%(role_id)s' % {
+ 'role_id': self.role_id})