Added support for HEAD /tokens/{token_id}

Changed POST /tokens response container from 'auth' to 'access'

Change-Id: Ibb8cf43948bce0de08cdbdb0619c4d640ced56cd
This commit is contained in:
Yogeshwar Srikrishnan 2011-09-20 14:54:53 -05:00 committed by Dolph Mathews
parent 012b2125fb
commit 64ce8c8a3b
17 changed files with 98 additions and 42 deletions

View File

@ -24,14 +24,22 @@ class AuthController(wsgi.Controller):
return utils.send_result(200, req,
config.SERVICE.authenticate_ec2(creds))
@utils.wrap_error
def validate_token(self, req, token_id):
belongs_to = req.GET.get("belongsTo", None)
rval = config.SERVICE.validate_token(
def _validate_token(self, req, token_id):
"""Validates the token, and that it belongs to the specified tenant"""
belongs_to = req.GET.get('belongsTo')
return config.SERVICE.validate_token(
utils.get_auth_token(req), token_id, belongs_to)
return utils.send_result(200, req, rval)
@utils.wrap_error
def validate_token(self, req, token_id):
result = self._validate_token(req, token_id)
return utils.send_result(200, req, result)
@utils.wrap_error
def check_token(self, req, token_id):
"""Validates the token, but only returns a status code (HEAD)"""
self._validate_token(req, token_id)
return utils.send_result(200, req)
@utils.wrap_error
def delete_token(self, req, token_id):

View File

@ -85,8 +85,8 @@ class AuthProtocol(object):
def __transform_headers(self, content):
"""Transform Keystone auth to legacy headers"""
headers = {}
if "auth" in content:
auth = content["auth"]
if "access" in content:
auth = content["access"]
if "token" in auth:
headers["X-Auth-Token"] = auth["token"]["id"]
if "serviceCatalog" in auth:

View File

@ -123,11 +123,17 @@ class IdentityService(object):
if not api.TOKEN.get(token_id):
raise fault.UnauthorizedFault("Bad token, please reauthenticate")
(token, user) = self.__validate_token(token_id, belongs_to)
return self.__get_validate_data(token, user)
def check_token(self, admin_token, token_id, belongs_to=None):
self.__validate_service_or_keystone_admin_token(admin_token)
if not api.TOKEN.get(token_id):
raise fault.UnauthorizedFault("Bad token, please reauthenticate")
self.__validate_token(token_id, belongs_to)
def revoke_token(self, admin_token, token_id):
self.__validate_admin_token(admin_token)

View File

@ -226,7 +226,7 @@ class AuthData(object):
self.__convert_baseurls_to_dict()
def to_xml(self):
dom = etree.Element("auth",
dom = etree.Element("access",
xmlns="http://docs.openstack.org/identity/api/v2.0")
token = etree.Element("token",
expires=self.token.expires.isoformat())
@ -245,8 +245,8 @@ class AuthData(object):
base_url_item = getattr(base_url, url_kind + "_url")
if base_url_item:
endpoint.set(url_kind + "URL", base_url_item.\
replace('%tenant_id%', self.token.tenant_id)
if self.token.tenant_id else base_url_item)
replace('%tenant_id%', str(self.token.tenant_id))
if self.token.tenant_id else base_url_item)
service.append(endpoint)
service_catalog.append(service)
dom.append(service_catalog)
@ -283,7 +283,7 @@ class AuthData(object):
service_catalog[key] = endpoints
auth["serviceCatalog"] = service_catalog
ret = {}
ret["auth"] = auth
ret["access"] = auth
return json.dumps(ret)

View File

@ -279,18 +279,18 @@ class AuthProtocol(object):
token_info = json.loads(data)
roles = []
role_refs = token_info["auth"]["user"]["roleRefs"]
role_refs = token_info["access"]["user"]["roleRefs"]
if role_refs != None:
for role_ref in role_refs:
roles.append(role_ref["roleId"])
try:
tenant = token_info['auth']['token']['tenantId']
tenant = token_info['access']['token']['tenantId']
except:
tenant = None
if not tenant:
tenant = token_info['auth']['user']['tenantId']
verified_claims = {'user': token_info['auth']['user']['username'],
tenant = token_info['access']['user']['tenantId']
verified_claims = {'user': token_info['access']['user']['username'],
'tenant': tenant,
'roles': roles}
return verified_claims

View File

@ -212,19 +212,19 @@ class AuthProtocol(object):
identity_info = json.loads(data)
roles = []
role_refs = identity_info["auth"]["user"]["roleRefs"]
role_refs = identity_info["access"]["user"]["roleRefs"]
if role_refs is not None:
for role_ref in role_refs:
roles.append(role_ref["roleId"])
try:
tenant = identity_info['auth']['token']['tenantId']
tenant = identity_info['access']['token']['tenantId']
except:
tenant = None
if not tenant:
tenant = identity_info['auth']['user']['tenantId']
tenant = identity_info['access']['user']['tenantId']
# TODO(Ziad): add groups back in
identity = {'user': identity_info['auth']['user']['username'],
identity = {'user': identity_info['access']['user']['username'],
'tenant': tenant,
'roles': roles}

View File

@ -46,6 +46,9 @@ class AdminApi(wsgi.Router):
mapper.connect("/tokens/{token_id}", controller=auth_controller,
action="validate_token",
conditions=dict(method=["GET"]))
mapper.connect("/tokens/{token_id}", controller=auth_controller,
action="check_token",
conditions=dict(method=["HEAD"]))
mapper.connect("/tokens/{token_id}", controller=auth_controller,
action="delete_token",
conditions=dict(method=["DELETE"]))

View File

@ -107,10 +107,11 @@ class RestfulTestCase(HttpTestCase):
def _decode_response_body(self, response):
"""Detects response body type, and attempts to decode it"""
if 'application/json' in response.getheader('Content-Type', ''):
response.json = self._decode_json(response.body)
elif 'application/xml' in response.getheader('Content-Type', ''):
response.xml = self._decode_xml(response.body)
if response.body != None and response.body.strip():
if 'application/json' in response.getheader('Content-Type', ''):
response.json = self._decode_json(response.body)
elif 'application/xml' in response.getheader('Content-Type', ''):
response.xml = self._decode_xml(response.body)
return response
@staticmethod
@ -186,6 +187,16 @@ class ApiTestCase(RestfulTestCase):
return self.admin_request(method='GET',
path='/tokens/%s?belongsTo=%s' % (token_id, tenant_id), **kwargs)
def check_token(self, token_id, **kwargs):
"""HEAD /tokens/{token_id}"""
return self.admin_request(method='HEAD',
path='/tokens/%s' % (token_id,), **kwargs)
def check_token_belongs_to(self, token_id, tenant_id, **kwargs):
"""HEAD /tokens/{token_id}?belongsTo={tenant_id}"""
return self.admin_request(method='HEAD',
path='/tokens/%s?belongsTo=%s' % (token_id, tenant_id), **kwargs)
def delete_token(self, token_id, **kwargs):
"""DELETE /tokens/{token_id}"""
return self.admin_request(method='DELETE',
@ -453,7 +464,7 @@ class FunctionalTestCase(ApiTestCase):
"""Prepare keystone for system tests"""
# Authenticate as admin user to establish admin_token
self.admin_token = self.authenticate(self.admin_username,
self.admin_password).json['auth']['token']['id']
self.admin_password).json['access']['token']['id']
self.admin_user_id = self.fetch_user_by_name('admin').\
json['users']['values'][0]['id']
@ -467,7 +478,6 @@ class FunctionalTestCase(ApiTestCase):
"passwordCredentials": {
"username": user_name,
"password": user_password}}}
if tenant_id:
data["auth"]["tenantId"] = tenant_id

View File

@ -15,8 +15,8 @@ class TestAdminAuthentication(common.FunctionalTestCase):
r = self.authenticate(self.admin_username, self.admin_password)
# Assert we get back a token with an expiration date
self.assertTrue(r.json['auth']['token']['id'])
self.assertTrue(r.json['auth']['token']['expires'])
self.assertTrue(r.json['access']['token']['id'])
self.assertTrue(r.json['access']['token']['expires'])
class TestAdminAuthenticationNegative(common.FunctionalTestCase):
@ -32,7 +32,7 @@ class TestAdminAuthenticationNegative(common.FunctionalTestCase):
# Replace our admin_token with a mere service token
self.admin_token = self.authenticate(user['name'], user['password']).\
json['auth']['token']['id']
json['access']['token']['id']
# Try creating another user using the wrong token
self.create_user(assert_status=401)
@ -57,7 +57,7 @@ class TestServiceAuthentication(common.FunctionalTestCase):
'passwordCredentials': {
'username': self.user['name'],
'password': self.user['password']}}}).\
json['auth']['token']['id']
json['access']['token']['id']
# In the real world, the service user would then pass his/her token
# to some service that depends on keystone, which would then need to

View File

@ -43,8 +43,8 @@ class AuthenticationTest(common.FunctionalTestCase):
r = self.authenticate(self.user['name'], self.user['password'],
self.tenant['id'], assert_status=200)
self.assertIsNotNone(r.json['auth']['token'])
self.assertIsNotNone(r.json['auth']['serviceCatalog'])
self.assertIsNotNone(r.json['access']['token'])
self.assertIsNotNone(r.json['access']['serviceCatalog'])
def test_authorize_xml(self):
data = ('<?xml version="1.0" encoding="UTF-8"?> '
@ -55,7 +55,7 @@ class AuthenticationTest(common.FunctionalTestCase):
self.user['name'], self.user['password'])
r = self.post_token(as_xml=data, assert_status=200)
self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns)
self.assertEquals(r.xml.tag, '{%s}access' % self.xmlns)
serviceCatalog = r.xml.find('{%s}serviceCatalog' % self.xmlns)
self.assertIsNotNone(serviceCatalog)
@ -79,7 +79,7 @@ class AuthenticationTest(common.FunctionalTestCase):
"passwordCredentials": {
"username-field-completely-wrong": self.user['name'],
"password": self.user['password']},
"tenantId": self.tenant['id']}}
"tenantId": self.tenant['id']}}
self.post_token(as_json=data, assert_status=400)
def test_authorize_user_wrong_xml(self):

View File

@ -16,7 +16,7 @@ class TestIssue85(common.FunctionalTestCase):
# Authenticate as user to get a token *for a specific tenant*
user_token = self.authenticate(user['name'], user['password'],
tenant['id']).json['auth']['token']['id']
tenant['id']).json['access']['token']['id']
# Validate and check that token belongs to tenant
tenantid = self.get_token(user_token).\

View File

@ -128,7 +128,7 @@ class GetTenantsTest(TenantTest):
user = self.create_user_with_known_password(tenant_id=tenant['id']).\
json['user']
token = self.authenticate(user['name'], user['password'],
tenant['id']).json['auth']['token']
tenant['id']).json['access']['token']
self.service_token = token['id']
tenants = self.service_request(method='GET', path='/tenants',
assert_status=200).json['tenants']['values']
@ -140,7 +140,7 @@ class GetTenantsTest(TenantTest):
user = self.create_user_with_known_password(tenant_id=tenant['id']).\
json['user']
token = self.authenticate(user['name'], user['password'],
tenant['id']).json['auth']['token']
tenant['id']).json['access']['token']
self.service_token = token['id']
r = self.service_request(method='GET', path='/tenants',

View File

@ -30,7 +30,7 @@ class ValidateToken(common.FunctionalTestCase):
self.role_ref = self.grant_role_to_user(self.user['id'],
self.role['id'], self.tenant['id']).json['role']
self.token = self.authenticate(self.user['name'],
self.user['password'], self.tenant['id']).json['auth']['token']
self.user['password'], self.tenant['id']).json['access']['token']
def test_validate_token_true(self):
r = self.get_token_belongsto(self.token['id'], self.tenant['id'],
@ -85,5 +85,34 @@ class ValidateToken(common.FunctionalTestCase):
'Accept': 'application/xml'})
class CheckToken(common.FunctionalTestCase):
def setUp(self, *args, **kwargs):
super(CheckToken, self).setUp(*args, **kwargs)
self.tenant = self.create_tenant().json['tenant']
self.user = self.create_user_with_known_password(
tenant_id=self.tenant['id']).json['user']
self.token = self.authenticate(self.user['name'],
self.user['password'], self.tenant['id']).json['access']['token']
def test_validate_token_true(self):
self.check_token_belongs_to(self.token['id'], self.tenant['id'],
assert_status=200)
def test_validate_token_true_using_service_token(self):
self.admin_token = self.service_admin_token
self.check_token_belongs_to(self.token['id'], self.tenant['id'],
assert_status=200)
def test_validate_token_expired(self):
self.check_token(self.expired_admin_token, assert_status=403)
def test_validate_token_expired_xml(self):
self.check_token(self.expired_admin_token, assert_status=403, headers={
'Accept': 'application/xml'})
def test_validate_token_invalid(self):
self.check_token(common.unique_str(), assert_status=401)
if __name__ == '__main__':
unittest.main()

View File

@ -68,7 +68,7 @@ class EC2AuthnMethods(base.ServiceAPITest):
self.get_response()
expected = {
u'auth': {
u'access': {
u'serviceCatalog': {},
u'token': {
u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f"),

View File

@ -106,7 +106,7 @@ def send_error(code, req, result):
return resp
def send_result(code, req, result):
def send_result(code, req, result=None):
content = None
resp = Response()