get tenant_add_and_remove_user test working

this actually required a lot of stuff, basically we have to
emulate some legacy features where user-tenant association is only
handled through roles.

in the process we added a few high level calls that will make
abstracting the backend easier, too
This commit is contained in:
termie 2012-01-04 14:33:43 -08:00
parent 94e9d6bcc2
commit c6d6d43b7e
5 changed files with 179 additions and 7 deletions

View File

@ -63,6 +63,52 @@ class KvsIdentity(object):
role_ref = self.db.get('role-%s' % role_id)
return role_ref
# These should probably be part of the high-level API
def add_user_to_tenant(self, tenant_id, user_id):
user_ref = self.get_user(user_id)
tenants = set(user_ref.get('tenants', []))
tenants.add(tenant_id)
user_ref['tenants'] = list(tenants)
self.update_user(user_id, user_ref)
def remove_user_from_tenant(self, tenant_id, user_id):
user_ref = self.get_user(user_id)
tenants = set(user_ref.get('tenants', []))
tenants.remove(tenant_id)
user_ref['tenants'] = list(tenants)
self.update_user(user_id, user_ref)
def get_tenants_for_user(self, user_id):
user_ref = self.get_user(user_id)
return user_ref.get('tenants', [])
def get_roles_for_user_and_tenant(self, user_id, tenant_id):
extras_ref = self.get_extras(user_id, tenant_id)
if not extras_ref:
extras_ref = {}
return extras_ref.get('roles', [])
def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id):
extras_ref = self.get_extras(user_id, tenant_id)
if not extras_ref:
extras_ref = {}
roles = set(extras_ref.get('roles', []))
roles.add(role_id)
extras_ref['roles'] = list(roles)
self.update_extras(user_id, tenant_id, extras_ref)
def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id):
extras_ref = self.get_extras(user_id, tenant_id)
if not extras_ref:
extras_ref = {}
roles = set(extras_ref.get('roles', []))
roles.remove(role_id)
extras_ref['roles'] = list(roles)
self.update_extras(user_id, tenant_id, extras_ref)
# CRUD
def create_user(self, id, user):
self.db.set('user-%s' % id, user)
self.db.set('user_name-%s' % user['name'], user)
@ -101,6 +147,7 @@ class KvsIdentity(object):
self.db.delete('tenant-%s' % id)
return None
def create_extras(self, user_id, tenant_id, extras):
self.db.set('extras-%s-%s' % (tenant_id, user_id), extras)
return extras

View File

@ -37,6 +37,27 @@ class Manager(object):
def get_role(self, context, role_id):
return self.driver.get_role(role_id)
# These should probably be the high-level API calls
def add_user_to_tenant(self, context, user_id, tenant_id):
self.driver.add_user_to_tenant(user_id, tenant_id)
def remove_user_from_tenant(self, context, user_id, tenant_id):
self.driver.remove_user_from_tenant(user_id, tenant_id)
def get_tenants_for_user(self, context, user_id):
return self.driver.get_tenants_for_user(user_id)
def get_roles_for_user_and_tenant(self, context, user_id, tenant_id):
return self.driver.get_roles_for_user_and_tenant(user_id, tenant_id)
def add_role_to_user_and_tenant(self, context, user_id, tenant_id, role_id):
return self.driver.add_role_to_user_and_tenant(user_id, tenant_id, role_id)
def remove_role_from_user_and_tenant(self, context, user_id,
tenant_id, role_id):
return self.driver.remove_role_from_user_and_tenant(
user_id, tenant_id, role_id)
# CRUD operations
def create_user(self, context, user_id, data):
return self.driver.create_user(user_id, data)

View File

@ -2,6 +2,8 @@
# this is the web service frontend that emulates keystone
import logging
import urllib
import urlparse
import uuid
import routes
@ -196,6 +198,17 @@ class KeystoneAdminCrudExtension(wsgi.ExtensionRouter):
controller=role_controller, action="delete_role_from_user",
conditions=dict(method=["DELETE"]))
# COMPAT(diablo): User Roles
mapper.connect("/users/{user_id}/roleRefs",
controller=role_controller, action="get_role_refs",
conditions=dict(method=["GET"]))
mapper.connect("/users/{user_id}/roleRefs",
controller=role_controller, action="create_role_ref",
conditions=dict(method=["POST"]))
mapper.connect("/users/{user_id}/roleRefs/{role_ref_id}",
controller=role_controller, action="delete_role_ref",
conditions=dict(method=["DELETE"]))
# User-Tenant Roles
mapper.connect(
"/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}",
@ -533,7 +546,7 @@ class KeystoneTenantController(service.BaseApplication):
def get_tenant_users(self, context, **kw):
self.assert_admin(context)
pass
raise NotImplementedError()
def _format_tenants_for_token(self, tenant_refs):
for x in tenant_refs:
@ -562,11 +575,81 @@ class KeystoneUserController(service.BaseApplication):
class KeystoneRoleController(service.BaseApplication):
def __init__(self, options):
self.options = options
self.identity_api = identity.Manager(options)
self.token_api = token.Manager(options)
self.policy_api = policy.Manager(options)
super(KeystoneRoleController, self).__init__()
def get_user_roles(self, context, user_id, tenant_id=None):
raise NotImplemented()
# COMPAT(diablo): CRUD extension
def get_role_refs(self, context, user_id):
"""Ultimate hack to get around having to make role_refs first-class.
This will basically iterate over the various roles the user has in
all tenants the user is a member of and create fake role_refs where
the id encodes the user-tenant-role information so we can look
up the appropriate data when we need to delete them.
"""
self.assert_admin(context)
user_ref = self.identity_api.get_user(context, user_id)
tenant_ids = self.identity_api.get_tenants_for_user(context, user_id)
o = []
for tenant_id in tenant_ids:
role_ids = self.identity_api.get_roles_for_user_and_tenant(
context, user_id, tenant_id)
for role_id in role_ids:
ref = {'roleId': role_id,
'tenantId': tenant_id,
'userId': user_id}
ref['id'] = urllib.urlencode(ref)
o.append(ref)
return {'roles': o}
def create_role_ref(self, context, user_id, role):
"""This is actually used for adding a user to a tenant.
In the legacy data model adding a user to a tenant required setting
a role.
"""
self.assert_admin(context)
# TODO(termie): for now we're ignoring the actual role
tenant_id = role.get('tenantId')
role_id = role.get('roleId')
self.identity_api.add_user_to_tenant(context, tenant_id, user_id)
self.identity_api.add_role_to_user_and_tenant(
context, user_id, tenant_id, role_id)
role_ref = self.identity_api.get_role(context, role_id)
return {'role': role_ref}
def delete_role_ref(self, context, user_id, role_ref_id):
"""This is actually used for deleting a user from a tenant.
In the legacy data model removing a user from a tenant required
deleting a role.
To emulate this, we encode the tenant and role in the role_ref_id,
and if this happens to be the last role for the user-tenant pair,
we remove the user from the tenant.
"""
self.assert_admin(context)
# TODO(termie): for now we're ignoring the actual role
role_ref_ref = urlparse.parse_qs(role_ref_id)
tenant_id = role_ref_ref.get('tenantId')[0]
role_id = role_ref_ref.get('roleId')[0]
self.identity_api.remove_role_from_user_and_tenant(
context, user_id, tenant_id, role_id)
roles = self.identity_api.get_roles_for_user_and_tenant(
context, user_id, tenant_id)
if not roles:
self.identity_api.remove_user_from_tenant(
context, tenant_id, user_id)
class KeystoneServiceController(service.BaseApplication):
def __init__(self, options):

View File

@ -1,5 +1,6 @@
TENANTS = [
{'id': 'bar', 'name': 'BAR'},
{'id': 'baz', 'name': 'BAZ'},
]
USERS = [
@ -12,4 +13,5 @@ EXTRAS = [
ROLES = [
{'id': 'keystone_admin', 'name': 'Keystone Admin'},
{'id': 'useless', 'name': 'Useless'},
]

View File

@ -118,13 +118,32 @@ class MasterCompatTestCase(CompatTestCase):
tenants = client.tenants.list()
self.assertEquals(len(tenants), 1)
def test_tenant_add_user(self):
raise NotImplementedError()
#client.roles.add_user_to_tenant(tenant_id, user_id, role_id)
def test_tenant_add_and_remove_user(self):
client = self.foo_client()
client.roles.add_user_to_tenant(self.tenant_baz['id'],
self.user_foo['id'],
self.role_useless['id'])
tenant_refs = client.tenants.list()
self.assert_(self.tenant_baz['id'] in
[x.id for x in tenant_refs])
def test_tenant_remove_user(self):
raise NotImplementedError()
#client.roles.remove_user_from_tenant(tenant_id, user_id, role_id)
# get the "role_refs" so we get the proper id, this is how the clients
# do it
roleref_refs = client.roles.get_user_role_refs(self.user_foo['id'])
for roleref_ref in roleref_refs:
if (roleref_ref.roleId == self.role_useless['id'] and
roleref_ref.tenantId == self.tenant_baz['id']):
# use python's scope fall through to leave roleref_ref set
break
client.roles.remove_user_from_tenant(self.tenant_baz['id'],
self.user_foo['id'],
roleref_ref.id)
tenant_refs = client.tenants.list()
self.assert_(self.tenant_baz['id'] not in
[x.id for x in tenant_refs])
def test_user_create_update_delete(self):
from keystoneclient import exceptions as client_exceptions