summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZiad Sawalha <github@highbridgellc.com>2011-06-03 00:29:40 -0500
committerZiad Sawalha <github@highbridgellc.com>2011-06-03 00:29:40 -0500
commitc40b96a7aea557cf0d62ef133a383343fcc8a07d (patch)
tree5928d29ea3891167b491b1461fc106231c1c137f
parentf0be6794fbba426c2cf047265e4dffe730721504 (diff)
parentacfebcfe511937a4d1065cb909fdaacdc0de84c5 (diff)
downloadkeystone-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.py4
-rwxr-xr-x[-rw-r--r--]keystone/auth_protocols/auth_token.py18
-rw-r--r--keystone/db/sqlalchemy/api.py67
-rwxr-xr-x[-rw-r--r--]keystone/logic/service.py64
-rw-r--r--keystone/logic/types/auth.py10
-rw-r--r--keystone/logic/types/role.py11
-rw-r--r--test/unit/test_common.py8
-rw-r--r--test/unit/test_tenants.py12
-rw-r--r--test/unit/test_token.py48
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,