diff options
| author | Ziad Sawalha <github@highbridgellc.com> | 2011-06-03 00:29:40 -0500 |
|---|---|---|
| committer | Ziad Sawalha <github@highbridgellc.com> | 2011-06-03 00:29:40 -0500 |
| commit | c40b96a7aea557cf0d62ef133a383343fcc8a07d (patch) | |
| tree | 5928d29ea3891167b491b1461fc106231c1c137f | |
| parent | f0be6794fbba426c2cf047265e4dffe730721504 (diff) | |
| parent | acfebcfe511937a4d1065cb909fdaacdc0de84c5 (diff) | |
| download | keystone-c40b96a7aea557cf0d62ef133a383343fcc8a07d.tar.gz keystone-c40b96a7aea557cf0d62ef133a383343fcc8a07d.tar.xz keystone-c40b96a7aea557cf0d62ef133a383343fcc8a07d.zip | |
Merge branch 'master' of https://github.com/rackspace/keystone
| -rw-r--r-- | examples/echo/echo/server.py | 4 | ||||
| -rwxr-xr-x[-rw-r--r--] | keystone/auth_protocols/auth_token.py | 18 | ||||
| -rw-r--r-- | keystone/db/sqlalchemy/api.py | 67 | ||||
| -rwxr-xr-x[-rw-r--r--] | keystone/logic/service.py | 64 | ||||
| -rw-r--r-- | keystone/logic/types/auth.py | 10 | ||||
| -rw-r--r-- | keystone/logic/types/role.py | 11 | ||||
| -rw-r--r-- | test/unit/test_common.py | 8 | ||||
| -rw-r--r-- | test/unit/test_tenants.py | 12 | ||||
| -rw-r--r-- | test/unit/test_token.py | 48 |
9 files changed, 208 insertions, 34 deletions
diff --git a/examples/echo/echo/server.py b/examples/echo/echo/server.py index 8c24aa8f..bde8341a 100644 --- a/examples/echo/echo/server.py +++ b/examples/echo/echo/server.py @@ -31,6 +31,7 @@ POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'echo', '__init__.py')): # also use the local keystone KEYSTONE_TOPDIR = os.path.normpath(os.path.join(POSSIBLE_TOPDIR, + os.pardir, os.pardir)) if os.path.exists(os.path.join(KEYSTONE_TOPDIR, 'keystone', @@ -75,6 +76,9 @@ class EchoApp(object): print ' Tenant :', self.envr['HTTP_X_TENANT'] if 'HTTP_X_GROUP' in self.envr: print ' Group :', self.envr['HTTP_X_GROUP'] + if 'HTTP_X_ROLE' in self.envr: + print ' Roles :', self.envr['HTTP_X_ROLE'] + accept = self.envr.get("HTTP_ACCEPT", "application/json") if accept == "application/xml": diff --git a/keystone/auth_protocols/auth_token.py b/keystone/auth_protocols/auth_token.py index 1484bf58..95d8fa2a 100644..100755 --- a/keystone/auth_protocols/auth_token.py +++ b/keystone/auth_protocols/auth_token.py @@ -167,6 +167,14 @@ class AuthProtocol(object): self._decorate_request('X_USER', claims['user']) if 'group' in claims: self._decorate_request('X_GROUP', claims['group']) + if 'roles' in claims and len(claims['roles']) > 0: + if claims['roles'] != None: + roles = '' + for role in claims['roles']: + if len(roles) > 0: + roles += ',' + roles += role + self._decorate_request('X_ROLE', roles) self.expanded = True #Send request downstream @@ -263,8 +271,16 @@ class AuthProtocol(object): token_info = json.loads(data) #TODO(Ziad): make this more robust #first_group = token_info['auth']['user']['groups']['group'][0] + roles =[] + role_refs =token_info["auth"]["user"]["roleRefs"] + if role_refs != None: + for role_ref in role_refs: + roles.append(role_ref["roleId"]) + verified_claims = {'user': token_info['auth']['user']['username'], - 'tenant': token_info['auth']['user']['tenantId']} + 'tenant': token_info['auth']['user']['tenantId'], 'roles':roles} + + # TODO(Ziad): removed groups for now # ,'group': '%s/%s' % (first_group['id'], # first_group['tenantId'])} diff --git a/keystone/db/sqlalchemy/api.py b/keystone/db/sqlalchemy/api.py index 6ddd17b2..6c3c0799 100644 --- a/keystone/db/sqlalchemy/api.py +++ b/keystone/db/sqlalchemy/api.py @@ -129,6 +129,11 @@ def role_ref_get_all_global_roles(user_id,session=None): if not session: session = get_session() return session.query(models.UserRoleAssociation).filter_by(user_id=user_id).filter("tenant_id is null").all() + +def role_ref_get_all_tenant_roles(user_id, tenant_id, session=None): + if not session: + session = get_session() + return session.query(models.UserRoleAssociation).filter_by(user_id=user_id).filter_by(tenant_id = tenant_id).all() def role_ref_get(id, session=None): if not session: @@ -164,6 +169,65 @@ def tenant_get_all(session=None): session = get_session() return session.query(models.Tenant).all() +def tenants_for_user_get_page(user, marker, limit, session=None): + if not session: + session = get_session() + ura = aliased(models.UserRoleAssociation) + tenant = aliased(models.Tenant) + q1 = session.query(tenant).join((ura, ura.tenant_id == tenant.id)).\ + filter(ura.user_id == user.id) + q2 = session.query(tenant).filter(tenant.id == user.tenant_id) + q3 = q1.union(q2) + if marker: + return q3.filter("tenant.id>:marker").params(\ + marker='%s' % marker).order_by(\ + tenant.id.desc()).limit(limit).all() + else: + return q3.order_by(\ + tenant.id.desc()).limit(limit).all() + +def tenants_for_user_get_page_markers(user, marker, limit, session=None): + if not session: + session = get_session() + ura = aliased(models.UserRoleAssociation) + tenant = aliased(models.Tenant) + q1 = session.query(tenant).join((ura, ura.tenant_id == tenant.id)).\ + filter(ura.user_id == user.id) + q2 = session.query(tenant).filter(tenant.id == user.tenant_id) + q3 = q1.union(q2) + + first = q3.order_by(\ + tenant.id).first() + last = q3.order_by(\ + tenant.id.desc()).first() + if first is None: + return (None, None) + if marker is None: + marker = first.id + next = q3.filter(tenant.id > marker).order_by(\ + tenant.id).limit(limit).all() + prev = q3.filter(tenant.id > marker).order_by(\ + tenant.id.desc()).limit(int(limit)).all() + if len(next) == 0: + next = last + else: + for t in next: + next = t + if len(prev) == 0: + prev = first + else: + for t in prev: + prev = t + if prev.id == marker: + prev = None + else: + prev = prev.id + if next.id == last.id: + next = None + else: + next = next.id + return (prev, next) + def tenant_get_page(marker, limit, session=None): if not session: @@ -176,8 +240,7 @@ def tenant_get_page(marker, limit, session=None): else: return session.query(models.Tenant).order_by(\ models.Tenant.id.desc()).limit(limit).all() - - + def tenant_get_page_markers(marker, limit, session=None): if not session: session = get_session() diff --git a/keystone/logic/service.py b/keystone/logic/service.py index 90610cd8..43b45e12 100644..100755 --- a/keystone/logic/service.py +++ b/keystone/logic/service.py @@ -129,22 +129,40 @@ class IdentityService(object): ## GET Tenants with Pagination ## def get_tenants(self, admin_token, marker, limit, url): - self.__validate_token(admin_token) - - ts = [] - dtenants = db_api.tenant_get_page(marker, limit) - for dtenant in dtenants: - ts.append(tenants.Tenant(dtenant.id, - dtenant.desc, dtenant.enabled)) - prev, next = db_api.tenant_get_page_markers(marker, limit) - links = [] - if prev: - links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \ - % (url, prev, limit))) - if next: - links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" \ - % (url, next, limit))) - return tenants.Tenants(ts, links) + try: + (token, user) = self.__validate_token(admin_token) + # If Global admin return all tenants. + ts = [] + dtenants = db_api.tenant_get_page(marker, limit) + for dtenant in dtenants: + ts.append(tenants.Tenant(dtenant.id, + dtenant.desc, dtenant.enabled)) + prev, next = db_api.tenant_get_page_markers(marker, limit) + links = [] + if prev: + links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \ + % (url, prev, limit))) + if next: + links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" \ + % (url, next, limit))) + return tenants.Tenants(ts, links) + except fault.UnauthorizedFault: + #If not global admin ,return tenants specific to user. + (token, user) = self.__validate_token(admin_token, False) + ts = [] + dtenants = db_api.tenants_for_user_get_page(user, marker, limit) + for dtenant in dtenants: + ts.append(tenants.Tenant(dtenant.id, + dtenant.desc, dtenant.enabled)) + prev, next = db_api.tenants_for_user_get_page_markers(user, marker, limit) + links = [] + if prev: + links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \ + % (url, prev, limit))) + if next: + links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" \ + % (url, next, limit))) + return tenants.Tenants(ts, links) def get_tenant(self, admin_token, tenant_id): self.__validate_token(admin_token) @@ -830,9 +848,17 @@ class IdentityService(object): """return ValidateData object for a token/user pair""" token = auth.Token(dtoken.expires, dtoken.token_id, dtoken.tenant_id) - - user = auth.User(duser.id, duser.tenant_id, None) - + ts=[] + if dtoken.tenant_id: + droleRefs = db_api.role_ref_get_all_tenant_roles(duser.id, dtoken.tenant_id) + for droleRef in droleRefs: + ts.append(roles.RoleRef(droleRef.id, droleRef.role_id, + droleRef.tenant_id)) + droleRefs = db_api.role_ref_get_all_global_roles(duser.id) + for droleRef in droleRefs: + ts.append(roles.RoleRef(droleRef.id, droleRef.role_id, + droleRef.tenant_id)) + user = auth.User(duser.id, duser.tenant_id, None, roles.RoleRefs(ts, [])) return auth.ValidateData(token, user) def __validate_token(self, token_id, admin=True): diff --git a/keystone/logic/types/auth.py b/keystone/logic/types/auth.py index 2b40ffc2..b010eea5 100644 --- a/keystone/logic/types/auth.py +++ b/keystone/logic/types/auth.py @@ -19,7 +19,7 @@ import json from lxml import etree import keystone.logic.types.fault as fault - +import keystone.logic.types.role as roles class PasswordCredentials(object): """Credentials based on username, password, and (optional) tenant_id. @@ -109,10 +109,11 @@ class Groups(object): class User(object): "A user." - def __init__(self, username, tenant_id, groups): + def __init__(self, username, tenant_id, groups , role_refs = None): self.username = username self.tenant_id = tenant_id self.groups = groups + self.role_refs = role_refs class AuthData(object): @@ -167,6 +168,8 @@ class ValidateData(object): groups.append(g) user.append(groups) """ + if self.user.role_refs != None: + user.append(self.user.role_refs.to_dom()) dom.append(token) dom.append(user) return etree.tostring(dom) @@ -180,6 +183,9 @@ class ValidateData(object): user = {} user["username"] = self.user.username user["tenantId"] = self.user.tenant_id + if self.user.role_refs != None: + user["roleRefs"] = self.user.role_refs.to_json_values() + """group = [] for g in self.user.groups.values: grp = {} diff --git a/keystone/logic/types/role.py b/keystone/logic/types/role.py index 5033fb4c..3441c795 100644 --- a/keystone/logic/types/role.py +++ b/keystone/logic/types/role.py @@ -193,6 +193,10 @@ class RoleRefs(object): self.links = links def to_xml(self): + dom = self.to_dom() + return etree.tostring(dom) + + def to_dom(self): dom = etree.Element("roleRefs") dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") @@ -202,9 +206,14 @@ class RoleRefs(object): for t in self.links: dom.append(t.to_dom()) - return etree.tostring(dom) + return dom + def to_json(self): values = [t.to_dict()["roleRef"] for t in self.values] links = [t.to_dict()["links"] for t in self.links] return json.dumps({"roleRefs": {"values": values, "links": links}}) + + def to_json_values(self): + values = [t.to_dict()["roleRef"] for t in self.values] + return values diff --git a/test/unit/test_common.py b/test/unit/test_common.py index 11d6a33d..e77f7fc8 100644 --- a/test/unit/test_common.py +++ b/test/unit/test_common.py @@ -739,6 +739,14 @@ def create_role_ref_xml(user_id, role_id, tenant_id, auth_token): "X-Auth-Token": auth_token, "ACCEPT": "application/xml"}) return (resp, content) + +def delete_role_ref(user, role_ref_id, auth_token): + header = httplib2.Http(".cache") + url = '%susers/%s/roleRefs/%s' % (URL, user, role_ref_id) + resp, content = header.request(url, "DELETE", body='', + headers={"Content-Type": "application/json", + "X-Auth-Token": str(auth_token)}) + return (resp, content) def create_role_xml(role_id, auth_token): header = httplib2.Http(".cache") diff --git a/test/unit/test_tenants.py b/test/unit/test_tenants.py index ec2a2569..bc639904 100644 --- a/test/unit/test_tenants.py +++ b/test/unit/test_tenants.py @@ -328,7 +328,7 @@ class CreateTenantTest(TenantTest): class GetTenantsTest(TenantTest): - def test_get_tenants(self): + def test_get_tenants_using_admin_token(self): header = httplib2.Http(".cache") resp, content = utils.create_tenant(self.tenant, str(self.auth_token)) url = '%stenants' % (utils.URL) @@ -342,7 +342,7 @@ class GetTenantsTest(TenantTest): self.fail('Service Not Available') self.assertEqual(200, int(resp['status'])) - def test_get_tenants_xml(self): + def test_get_tenants_using_admin_token_xml(self): header = httplib2.Http(".cache") resp, content = utils.create_tenant(self.tenant, str(self.auth_token)) url = '%stenants' % (utils.URL) @@ -357,7 +357,7 @@ class GetTenantsTest(TenantTest): self.fail('Service Not Available') self.assertEqual(200, int(resp['status'])) - def test_get_tenants_unauthorized_token(self): + def test_get_tenants_using_user_token(self): header = httplib2.Http(".cache") resp, content = utils.create_tenant(self.tenant, str(self.auth_token)) url = '%stenants' % (utils.URL) @@ -369,9 +369,9 @@ class GetTenantsTest(TenantTest): self.fail('Identity Fault') elif int(resp['status']) == 503: self.fail('Service Not Available') - self.assertEqual(401, int(resp['status'])) + self.assertEqual(200, int(resp['status'])) - def test_get_tenants_unauthorized_token_xml(self): + def test_get_tenants_using_user_token_xml(self): header = httplib2.Http(".cache") resp, content = utils.create_tenant(self.tenant, str(self.auth_token)) url = '%stenants' % (utils.URL) @@ -384,7 +384,7 @@ class GetTenantsTest(TenantTest): self.fail('Identity Fault') elif int(resp['status']) == 503: self.fail('Service Not Available') - self.assertEqual(401, int(resp['status'])) + self.assertEqual(200, int(resp['status'])) def test_get_tenants_exp_token(self): header = httplib2.Http(".cache") diff --git a/test/unit/test_token.py b/test/unit/test_token.py index bfef2be3..0754f777 100644 --- a/test/unit/test_token.py +++ b/test/unit/test_token.py @@ -21,14 +21,16 @@ import sys sys.path.append(os.path.abspath(os.path.join(os.path.abspath(__file__), '..', '..', '..', '..', 'keystone'))) import unittest - import test_common as utils - +import json +import keystone.logic.types.fault as fault +from lxml import etree class ValidateToken(unittest.TestCase): def setUp(self): self.tenant = utils.get_tenant() + self.user = 'joeuser' self.token = utils.get_token('joeuser', 'secrete', self.tenant, 'token') #self.user = utils.get_user() @@ -36,8 +38,19 @@ class ValidateToken(unittest.TestCase): self.auth_token = utils.get_auth_token() self.exp_auth_token = utils.get_exp_auth_token() #self.disabled_token = utils.get_disabled_token() + resp, content = utils.create_role_ref(self.user, 'Admin', self.tenant, str(self.auth_token)) + obj = json.loads(content) + if not "roleRef" in obj: + raise fault.BadRequestFault("Expecting RoleRef") + roleRef = obj["roleRef"] + if not "id" in roleRef: + self.role_ref_id = None + else: + self.role_ref_id = roleRef["id"] + def tearDown(self): + resp, content = utils.delete_role_ref(self.user, self.role_ref_id, self.auth_token) utils.delete_token(self.token, self.auth_token) def test_validate_token_true(self): @@ -53,6 +66,14 @@ class ValidateToken(unittest.TestCase): self.fail('Service Not Available') self.assertEqual(200, int(resp['status'])) self.assertEqual('application/json', utils.content_type(resp)) + #verify content + obj = json.loads(content) + if not "auth" in obj: + raise self.fail("Expecting Auth") + role_refs = obj["auth"]["user"]["roleRefs"] + role_ref = role_refs[0] + role_ref_id = role_ref["id"] + self.assertEqual(self.role_ref_id, role_ref_id) def test_validate_token_true_xml(self): header = httplib2.Http(".cache") @@ -67,7 +88,28 @@ class ValidateToken(unittest.TestCase): self.fail('Service Not Available') self.assertEqual(200, int(resp['status'])) self.assertEqual('application/xml', utils.content_type(resp)) - + #verify content + dom = etree.Element("root") + dom.append(etree.fromstring(content)) + auth = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \ + "auth") + if auth == None: + self.fail("Expecting Auth") + + user = auth.find("{http://docs.openstack.org/identity/api/v2.0}" \ + "user") + if user == None: + self.fail("Expecting User") + roleRefs = user.find("{http://docs.openstack.org/identity/api/v2.0}" \ + "roleRefs") + if roleRefs == None: + self.fail("Expecting Role Refs") + roleRef = roleRefs.find("{http://docs.openstack.org/identity/api/v2.0}" \ + "roleRef") + if roleRef == None: + self.fail("Expecting Role Refs") + self.assertEqual(str(self.role_ref_id), roleRef.get("id")) + def test_validate_token_expired(self): header = httplib2.Http(".cache") url = '%stokens/%s?belongsTo=%s' % (utils.URL, self.exp_auth_token, |
