From 3f70358bc3c893c6e85e9a0ee87d835fb54619ef Mon Sep 17 00:00:00 2001 From: Ziad Sawalha Date: Fri, 6 Jan 2012 11:18:06 -0600 Subject: Add 'tenants' to Auth & Validate Response - Addresses bug 906442 - Preserves compatibility (keeps 'tenant') - No changes in XML (this is a json-only gap) Change-Id: Ia6f373d0c7e40b05892c6cdffeed8d1e4b6f65ca --- keystone/content/common/samples/auth.json | 6 +- keystone/logic/types/auth.py | 8 +- keystone/test/functional/test_auth.py | 7 +- keystone/test/functional/test_token.py | 2 +- keystone/test/unit/test_authn_ec2.py | 180 ++++++++++++++++++++++++++++++ keystone/test/unit/test_authn_password.py | 65 +++++++++++ keystone/test/unit/test_ec2_authn.py | 180 ------------------------------ 7 files changed, 263 insertions(+), 185 deletions(-) create mode 100644 keystone/test/unit/test_authn_ec2.py create mode 100644 keystone/test/unit/test_authn_password.py delete mode 100644 keystone/test/unit/test_ec2_authn.py diff --git a/keystone/content/common/samples/auth.json b/keystone/content/common/samples/auth.json index 5f5ef064..6730360f 100644 --- a/keystone/content/common/samples/auth.json +++ b/keystone/content/common/samples/auth.json @@ -6,7 +6,11 @@ "tenant": { "id": "t1000", "name": "My Project" - } + }, + "tenants": [{ + "id": "t1000", + "name": "My Project" + }] }, "user": { "id": "u123", diff --git a/keystone/logic/types/auth.py b/keystone/logic/types/auth.py index 936e634a..f7b88a54 100755 --- a/keystone/logic/types/auth.py +++ b/keystone/logic/types/auth.py @@ -387,9 +387,11 @@ class AuthData(object): token["id"] = self.token.id token["expires"] = self.token.expires.isoformat() if self.token.tenant: - token['tenant'] = { + tenant = { 'id': unicode(self.token.tenant.id), 'name': unicode(self.token.tenant.name)} + token['tenant'] = tenant # v2.0/Diablo contract + token['tenants'] = [tenant] # missed use case in v2.0 auth = {} auth["token"] = token auth['user'] = { @@ -487,9 +489,11 @@ class ValidateData(object): "expires": self.token.expires.isoformat()} if self.token.tenant: - token['tenant'] = { + tenant = { 'id': unicode(self.token.tenant.id), 'name': unicode(self.token.tenant.name)} + token['tenant'] = tenant # v2.0/Diablo contract + token['tenants'] = [tenant] # missed use case in v2.0 user = { "id": unicode(self.user.id), diff --git a/keystone/test/functional/test_auth.py b/keystone/test/functional/test_auth.py index 0846a5ef..145f049e 100644 --- a/keystone/test/functional/test_auth.py +++ b/keystone/test/functional/test_auth.py @@ -157,7 +157,12 @@ class TestServiceAuthentication(common.FunctionalTestCase): 'tenantId': self.tenant['id']}}).json['access'] self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'],\ + self.assertEqual(scoped['token']['tenant']['name'], + self.tenant['name']) + self.assertIn('tenants', scoped['token']) + self.assertEqual(scoped['token']['tenants'][0]['id'], + self.tenant['id']) + self.assertEqual(scoped['token']['tenants'][0]['name'], self.tenant['name']) self.assertEqual( scoped['user']['roles'][0]['id'], role['id']) diff --git a/keystone/test/functional/test_token.py b/keystone/test/functional/test_token.py index 514003ed..4956ba0b 100644 --- a/keystone/test/functional/test_token.py +++ b/keystone/test/functional/test_token.py @@ -35,7 +35,6 @@ class ValidateToken(common.FunctionalTestCase): def test_validate_token_true(self): r = self.get_token_belongsto(self.token['id'], self.tenant['id'], assert_status=200) - self.assertIsNotNone(r.json['access']['user']["roles"]) self.assertEqual(r.json['access']['user']["roles"][0]['id'], self.role['id']) @@ -44,6 +43,7 @@ class ValidateToken(common.FunctionalTestCase): self.assertIsNotNone(r.json['access']['user']['id'], self.user['id']) self.assertIsNotNone(r.json['access']['user']['name'], self.user['name']) + self.assertIn('tenants', r.json['access']['token']) def test_validate_token_true_using_service_token(self): self.fixture_create_service_admin() diff --git a/keystone/test/unit/test_authn_ec2.py b/keystone/test/unit/test_authn_ec2.py new file mode 100644 index 00000000..e950fc7f --- /dev/null +++ b/keystone/test/unit/test_authn_ec2.py @@ -0,0 +1,180 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2011 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 json +import logging +import unittest2 as unittest + +import base +from keystone.test.unit.decorators import jsonify +from keystone.logic import signer +from keystone.logic.types import auth + +LOGGER = logging.getLogger(__name__) + + +class EC2AuthnMethods(base.ServiceAPITest): + + @jsonify + def test_authn_ec2_success_json(self): + """ + Test that good ec2 credentials returns a 200 OK + """ + access = "xpd285.access" + secret = "345fgi.secret" + kwargs = { + "user_name": self.auth_user['name'], + "tenant_id": self.auth_user['tenant_id'], + "type": "EC2", + "key": access, + "secret": secret, + } + self.fixture_create_credentials(**kwargs) + url = "/ec2tokens" + req = self.get_request('POST', url) + params = { + "SignatureVersion": "2", + "one_param": "5", + "two_params": "happy", + } + credentials = { + "access": access, + "verb": "GET", + "params": params, + "host": "some.host.com:8773", + "path": "services/Cloud", + "signature": None, + } + sign = signer.Signer(secret) + obj_creds = auth.Ec2Credentials(**credentials) + credentials['signature'] = sign.generate(obj_creds) + body = { + "ec2Credentials": credentials, + } + req.body = json.dumps(body) + self.get_response() + + expected = { + u'access': { + u'token': { + u'id': self.auth_token_id, + u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, + u'user': { + u'id': unicode(self.auth_user['id']), + u'name': self.auth_user['name'], + u'roles': [{u'description': u'regular role', + u'id': u'0', + u'name': u'regular_role'}]}}} + + self.assert_dict_equal(expected, json.loads(self.res.body)) + self.status_ok() + + @jsonify + def test_authn_ec2_success_json_bad_user(self): + """ + Test that bad credentials returns a 401 + """ + access = "xpd285.access" + secret = "345fgi.secret" + url = "/ec2tokens" + req = self.get_request('POST', url) + params = { + "SignatureVersion": "2", + "one_param": "5", + "two_params": "happy", + } + credentials = { + "access": access, + "verb": "GET", + "params": params, + "host": "some.host.com:8773", + "path": "services/Cloud", + "signature": None, + } + sign = signer.Signer(secret) + obj_creds = auth.Ec2Credentials(**credentials) + credentials['signature'] = sign.generate(obj_creds) + body = { + "ec2Credentials": credentials, + } + req.body = json.dumps(body) + self.get_response() + + expected = { + u'unauthorized': { + u'code': u'401', + u'message': u'No credentials found for %s' % access, + } + } + self.assert_dict_equal(expected, json.loads(self.res.body)) + self.assertEqual(self.res.status_int, 401) + + @jsonify + def test_authn_ec2_success_json_bad_tenant(self): + """ + Test that bad credentials returns a 401 + """ + # Create dummy tenant (or adding creds will fail) + self.fixture_create_tenant(id='bad', name='bad') + access = "xpd285.access" + secret = "345fgi.secret" + kwargs = { + "user_name": self.auth_user['name'], + "tenant_id": 'bad', + "type": "EC2", + "key": access, + "secret": secret, + } + self.fixture_create_credentials(**kwargs) + # Delete the 'bad' tenant, orphaning the creds + self.get_request('DELETE', '/tenants/bad') + + url = "/ec2tokens" + req = self.get_request('POST', url) + params = { + "SignatureVersion": "2", + "one_param": "5", + "two_params": "happy", + } + credentials = { + "access": access, + "verb": "GET", + "params": params, + "host": "some.host.com:8773", + "path": "services/Cloud", + "signature": None, + } + sign = signer.Signer(secret) + obj_creds = auth.Ec2Credentials(**credentials) + credentials['signature'] = sign.generate(obj_creds) + body = { + "ec2Credentials": credentials, + } + req.body = json.dumps(body) + self.get_response() + + expected = { + u'unauthorized': { + u'code': u'401', + u'message': u'Unauthorized on this tenant', + } + } + self.assert_dict_equal(expected, json.loads(self.res.body)) + self.assertEqual(self.res.status_int, 401) + + +if __name__ == '__main__': + unittest.main() diff --git a/keystone/test/unit/test_authn_password.py b/keystone/test/unit/test_authn_password.py new file mode 100644 index 00000000..947dfc60 --- /dev/null +++ b/keystone/test/unit/test_authn_password.py @@ -0,0 +1,65 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2011 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 json +import logging +import unittest2 as unittest + +import base +from keystone.test.unit.decorators import jsonify +from keystone.logic.types import auth + +LOGGER = logging.getLogger(__name__) + + +class PasswordAuthnMethods(base.ServiceAPITest): + + @jsonify + def test_authn_password_success_json(self): + """ + Test that good password credentials returns a 200 OK + """ + url = "/tokens" + req = self.get_request('POST', url) + credentials = { + "username": self.auth_user['name'], + "password": "auth_pass", + } + body = {"auth": { + "passwordCredentials": credentials, + "tenantId": self.auth_user['tenant_id'], + } + } + req.body = json.dumps(body) + self.get_response() + + expected = { + u'access': { + u'token': { + u'id': self.auth_token_id, + u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, + u'user': { + u'id': unicode(self.auth_user['id']), + u'name': self.auth_user['name'], + u'roles': [{u'description': u'regular role', u'id': u'0', + u'name': u'regular_role'}]}}} + + self.assert_dict_equal(expected, json.loads(self.res.body)) + self.status_ok() + + +if __name__ == '__main__': + unittest.main() diff --git a/keystone/test/unit/test_ec2_authn.py b/keystone/test/unit/test_ec2_authn.py deleted file mode 100644 index 60477e87..00000000 --- a/keystone/test/unit/test_ec2_authn.py +++ /dev/null @@ -1,180 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2011 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 json -import logging -import unittest2 as unittest - -import base -from keystone.test.unit.decorators import jsonify -from keystone.logic import signer -from keystone.logic.types import auth - -LOGGER = logging.getLogger('test.unit.test_ec2_authn') - - -class EC2AuthnMethods(base.ServiceAPITest): - - @jsonify - def test_authn_ec2_success_json(self): - """ - Test that good ec2 credentials returns a 200 OK - """ - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - url = "/ec2tokens" - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_ec2_success_json_bad_user(self): - """ - Test that bad credentials returns a 401 - """ - access = "xpd285.access" - secret = "345fgi.secret" - url = "/ec2tokens" - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'No credentials found for %s' % access, - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - @jsonify - def test_authn_ec2_success_json_bad_tenant(self): - """ - Test that bad credentials returns a 401 - """ - # Create dummy tenant (or adding creds will fail) - self.fixture_create_tenant(id='bad', name='bad') - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": 'bad', - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - # Delete the 'bad' tenant, orphaning the creds - self.get_request('DELETE', '/tenants/bad') - - url = "/ec2tokens" - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'Unauthorized on this tenant', - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - -if __name__ == '__main__': - unittest.main() -- cgit