From f66badae63b1544c3bcc83e0c1afc8e9cdc71bab Mon Sep 17 00:00:00 2001 From: Lance Bragstad Date: Mon, 29 Jun 2015 18:12:37 +0000 Subject: [PATCH] Consolidate the fernet provider issue_v3_token() When the Fernet token provider was implemented, it extended the provider.common.py:BaseProvider class. It also overrode most all common methods the BaseProvider implemented. Other token providers in Keystone (like the UUID an PKI providers) just implemente a _get_token_id method because token ids may be different across providers. This commit removes the issue_v3_token() method from the fernet.Provider. This ensure that the Fernet provider uses the same issue_v3_token() that all the other token providers use. Subsequent patches will do the same for issue_v2_token, validate_v3_token, and validate_v2_token. Conflicts: keystone/token/providers/fernet/core.py Closes-Bug: #1515485 Change-Id: I03f56c9c84389a6d6cdb3a6863fcbfca486af337 (cherry picked from commit 91a0b29809cb71c1b2df1642d6c34a3f60a801ed) --- keystone/tests/unit/test_v3_auth.py | 2 +- .../tests/unit/token/test_fernet_provider.py | 7 +- .../tests/unit/token/test_pki_provider.py | 26 +++++ .../tests/unit/token/test_pkiz_provider.py | 26 +++++ .../tests/unit/token/test_uuid_provider.py | 26 +++++ keystone/token/providers/common.py | 6 ++ keystone/token/providers/fernet/core.py | 98 +++++-------------- keystone/token/providers/pki.py | 8 ++ keystone/token/providers/pkiz.py | 8 ++ keystone/token/providers/uuid.py | 8 ++ 10 files changed, 137 insertions(+), 78 deletions(-) create mode 100644 keystone/tests/unit/token/test_pki_provider.py create mode 100644 keystone/tests/unit/token/test_pkiz_provider.py create mode 100755 keystone/tests/unit/token/test_uuid_provider.py diff --git a/keystone/tests/unit/test_v3_auth.py b/keystone/tests/unit/test_v3_auth.py index a76df71956..4d4d1232d9 100644 --- a/keystone/tests/unit/test_v3_auth.py +++ b/keystone/tests/unit/test_v3_auth.py @@ -4207,7 +4207,7 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): trustor_user_id=self.user_id, trustee_user_id=trustee_user['id'], project_id=self.project_id, - impersonation=True, + impersonation=False, role_ids=[self.role_id]) # Create a trust diff --git a/keystone/tests/unit/token/test_fernet_provider.py b/keystone/tests/unit/token/test_fernet_provider.py index 7ab9834848..9fdf00403e 100644 --- a/keystone/tests/unit/token/test_fernet_provider.py +++ b/keystone/tests/unit/token/test_fernet_provider.py @@ -36,11 +36,8 @@ class TestFernetTokenProvider(tests.TestCase): self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) self.provider = fernet.Provider() - def test_get_token_id_raises_not_implemented(self): - """Test that an exception is raised when calling _get_token_id.""" - token_data = {} - self.assertRaises(exception.NotImplemented, - self.provider._get_token_id, token_data) + def test_supports_bind_authentication_returns_false(self): + self.assertFalse(self.provider._supports_bind_authentication) def test_needs_persistence_returns_false(self): self.assertFalse(self.provider.needs_persistence()) diff --git a/keystone/tests/unit/token/test_pki_provider.py b/keystone/tests/unit/token/test_pki_provider.py new file mode 100644 index 0000000000..cc043c8121 --- /dev/null +++ b/keystone/tests/unit/token/test_pki_provider.py @@ -0,0 +1,26 @@ +# 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. + +from keystone.tests import unit as tests +from keystone.token.providers import pki + + +class TestPkiTokenProvider(tests.TestCase): + def setUp(self): + super(TestPkiTokenProvider, self).setUp() + self.provider = pki.Provider() + + def test_supports_bind_authentication_returns_true(self): + self.assertTrue(self.provider._supports_bind_authentication) + + def test_need_persistence_return_true(self): + self.assertTrue(self.provider.needs_persistence) diff --git a/keystone/tests/unit/token/test_pkiz_provider.py b/keystone/tests/unit/token/test_pkiz_provider.py new file mode 100644 index 0000000000..f1feef3240 --- /dev/null +++ b/keystone/tests/unit/token/test_pkiz_provider.py @@ -0,0 +1,26 @@ +# 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. + +from keystone.tests import unit as tests +from keystone.token.providers import pkiz + + +class TestPkizTokenProvider(tests.TestCase): + def setUp(self): + super(TestPkizTokenProvider, self).setUp() + self.provider = pkiz.Provider() + + def test_supports_bind_authentication_returns_true(self): + self.assertTrue(self.provider._supports_bind_authentication) + + def test_need_persistence_return_true(self): + self.assertTrue(self.provider.needs_persistence) diff --git a/keystone/tests/unit/token/test_uuid_provider.py b/keystone/tests/unit/token/test_uuid_provider.py new file mode 100755 index 0000000000..fa82243bef --- /dev/null +++ b/keystone/tests/unit/token/test_uuid_provider.py @@ -0,0 +1,26 @@ +# 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. + +from keystone.tests import unit as tests +from keystone.token.providers import uuid + + +class TestUuidTokenProvider(tests.TestCase): + def setUp(self): + super(TestUuidTokenProvider, self).setUp() + self.provider = uuid.Provider() + + def test_supports_bind_authentication_returns_true(self): + self.assertTrue(self.provider._supports_bind_authentication) + + def test_need_persistence_return_true(self): + self.assertTrue(self.provider.needs_persistence) diff --git a/keystone/token/providers/common.py b/keystone/token/providers/common.py index 1b3a17add8..72583d7e2d 100644 --- a/keystone/token/providers/common.py +++ b/keystone/token/providers/common.py @@ -523,6 +523,12 @@ class BaseProvider(provider.Provider): project_id=None, domain_id=None, auth_context=None, trust=None, metadata_ref=None, include_catalog=True, parent_audit_id=None): + if auth_context and auth_context.get('bind'): + # NOTE(lbragstad): Check if the token provider being used actually + # supports bind authentication methods before proceeding. + if not self._supports_bind_authentication: + raise exception.NotImplemented() + # for V2, trust is stashed in metadata_ref if (CONF.trust.enabled and not trust and metadata_ref and 'trust_id' in metadata_ref): diff --git a/keystone/token/providers/fernet/core.py b/keystone/token/providers/fernet/core.py index 2d25d4bc9d..f0050647cc 100644 --- a/keystone/token/providers/fernet/core.py +++ b/keystone/token/providers/fernet/core.py @@ -121,17 +121,19 @@ class Provider(common.BaseProvider): which unpacks the values and builds the Fernet token. """ - group_ids = token_data.get('user', {}).get( + group_ids = token_data['token'].get('user', {}).get( federation.FEDERATION, {}).get('groups') - idp_id = token_data.get('user', {}).get( + idp_id = token_data['token'].get('user', {}).get( federation.FEDERATION, {}).get('identity_provider', {}).get('id') - protocol_id = token_data.get('user', {}).get( + protocol_id = token_data['token'].get('user', {}).get( federation.FEDERATION, {}).get('protocol', {}).get('id') if not group_ids: group_ids = list() - federated_dict = dict(group_ids=group_ids, idp_id=idp_id, - protocol_id=protocol_id) - return federated_dict + if group_ids: + federated_dict = dict(group_ids=group_ids, idp_id=idp_id, + protocol_id=protocol_id) + return federated_dict + return None def _rebuild_federated_info(self, federated_dict, user_id): """Format federated information into the token reference. @@ -154,71 +156,6 @@ class Provider(common.BaseProvider): token_dict['user']['name'] = user_id return token_dict - def issue_v3_token(self, user_id, method_names, expires_at=None, - project_id=None, domain_id=None, auth_context=None, - trust=None, metadata_ref=None, include_catalog=True, - parent_audit_id=None): - """Issue a V3 formatted token. - - Here is where we need to detect what is given to us, and what kind of - token the user is expecting. Depending on the outcome of that, we can - pass all the information to be packed to the proper token format - handler. - - :param user_id: ID of the user - :param method_names: method of authentication - :param expires_at: token expiration time - :param project_id: ID of the project being scoped to - :param domain_id: ID of the domain being scoped to - :param auth_context: authentication context - :param trust: ID of the trust - :param metadata_ref: metadata reference - :param include_catalog: return the catalog in the response if True, - otherwise don't return the catalog - :param parent_audit_id: ID of the parent audit entity - :returns: tuple containing the id of the token and the token data - - """ - # TODO(lbragstad): Currently, Fernet tokens don't support bind in the - # token format. Raise a 501 if we're dealing with bind. - if auth_context.get('bind'): - raise exception.NotImplemented() - - token_ref = None - # NOTE(lbragstad): This determines if we are dealing with a federated - # token or not. The groups for the user will be in the returned token - # reference. - federated_dict = None - if auth_context and self._is_mapped_token(auth_context): - token_ref = self._handle_mapped_tokens( - auth_context, project_id, domain_id) - federated_dict = self._build_federated_info(token_ref) - - token_data = self.v3_token_data_helper.get_token_data( - user_id, - method_names, - auth_context.get('extras') if auth_context else None, - domain_id=domain_id, - project_id=project_id, - expires=expires_at, - trust=trust, - bind=auth_context.get('bind') if auth_context else None, - token=token_ref, - include_catalog=include_catalog, - audit_info=parent_audit_id) - - token = self.token_formatter.create_token( - user_id, - token_data['token']['expires_at'], - token_data['token']['audit_ids'], - methods=method_names, - domain_id=domain_id, - project_id=project_id, - trust_id=token_data['token'].get('OS-TRUST:trust', {}).get('id'), - federated_info=federated_dict) - self._build_issued_at_info(token, token_data) - return token, token_data - def validate_v2_token(self, token_ref): """Validate a V2 formatted token. @@ -293,4 +230,21 @@ class Provider(common.BaseProvider): :type token_data: dict :raises keystone.exception.NotImplemented: when called """ - raise exception.NotImplemented() + return self.token_formatter.create_token( + token_data['token']['user']['id'], + token_data['token']['expires_at'], + token_data['token']['audit_ids'], + methods=token_data['token'].get('methods'), + domain_id=token_data['token'].get('domain', {}).get('id'), + project_id=token_data['token'].get('project', {}).get('id'), + trust_id=token_data['token'].get('OS-TRUST:trust', {}).get('id'), + federated_info=self._build_federated_info(token_data) + ) + + @property + def _supports_bind_authentication(self): + """Return if the token provider supports bind authentication methods. + + :returns: False + """ + return False diff --git a/keystone/token/providers/pki.py b/keystone/token/providers/pki.py index 61b4281765..af8dc73973 100644 --- a/keystone/token/providers/pki.py +++ b/keystone/token/providers/pki.py @@ -48,6 +48,14 @@ class Provider(common.BaseProvider): raise exception.UnexpectedError(_( 'Unable to sign token.')) + @property + def _supports_bind_authentication(self): + """Return if the token provider supports bind authentication methods. + + :returns: True + """ + return True + def needs_persistence(self): """Should the token be written to a backend.""" return True diff --git a/keystone/token/providers/pkiz.py b/keystone/token/providers/pkiz.py index b6f2944dd1..b4e3191887 100644 --- a/keystone/token/providers/pkiz.py +++ b/keystone/token/providers/pkiz.py @@ -46,6 +46,14 @@ class Provider(common.BaseProvider): LOG.exception(ERROR_MESSAGE) raise exception.UnexpectedError(ERROR_MESSAGE) + @property + def _supports_bind_authentication(self): + """Return if the token provider supports bind authentication methods. + + :returns: True + """ + return True + def needs_persistence(self): """Should the token be written to a backend.""" return True diff --git a/keystone/token/providers/uuid.py b/keystone/token/providers/uuid.py index 15118d82d1..f9a9161740 100644 --- a/keystone/token/providers/uuid.py +++ b/keystone/token/providers/uuid.py @@ -28,6 +28,14 @@ class Provider(common.BaseProvider): def _get_token_id(self, token_data): return uuid.uuid4().hex + @property + def _supports_bind_authentication(self): + """Return if the token provider supports bind authentication methods. + + :returns: True + """ + return True + def needs_persistence(self): """Should the token be written to a backend.""" return True