From 4fd8531fd5e7ad6b2fc49d312b1104eefc66e311 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Thu, 15 Oct 2015 17:03:23 +1100 Subject: [PATCH] Expose bind data via AccessInfo The bind information is a standard part of the token data and can be access from auth_token middleware so it should be exposed as part of the AccessInfo object. Change-Id: I45fc6eeed43f335aa1d771bdf1a11257432cb85c --- keystoneauth1/access/access.py | 26 ++++++++++++++++++- keystoneauth1/fixture/v2.py | 3 +++ keystoneauth1/fixture/v3.py | 3 +++ .../tests/unit/access/test_v2_access.py | 11 ++++++++ .../tests/unit/access/test_v3_access.py | 11 ++++++++ keystoneauth1/tests/unit/test_fixtures.py | 26 +++++++++++++++++++ 6 files changed, 79 insertions(+), 1 deletion(-) diff --git a/keystoneauth1/access/access.py b/keystoneauth1/access/access.py index 5dd6a337..bd25a2f2 100644 --- a/keystoneauth1/access/access.py +++ b/keystoneauth1/access/access.py @@ -369,6 +369,22 @@ class AccessInfo(object): """ raise NotImplementedError() + @property + def bind(self): + """Information about external mechanisms the token is bound to. + + If a token is bound to an external authentication mechanism it can only + be used in conjunction with that mechanism. For example if bound to a + kerberos principal it may only be accepted if there is also kerberos + authentication performed on the request. + + :returns: A dictionary or None. The key will be the bind type the value + is a dictionary that is specific to the format of the bind + type. Returns None if there is no bind information in the + token. + """ + raise NotImplementedError() + class AccessInfoV2(AccessInfo): """An object for encapsulating a raw v2 auth token from identity @@ -386,7 +402,7 @@ class AccessInfoV2(AccessInfo): set_token = super(AccessInfoV2, self).auth_token return set_token or self._data['access']['token']['id'] - @_missingproperty + @property def _token(self): return self._data['access']['token'] @@ -544,6 +560,10 @@ class AccessInfoV2(AccessInfo): def service_providers(self): return None + @_missingproperty + def bind(self): + return self._token['bind'] + class AccessInfoV3(AccessInfo): """An object for encapsulating a raw v3 auth token from identity @@ -708,3 +728,7 @@ class AccessInfoV3(AccessInfo): service_providers.ServiceProviders.from_token(self._data)) return self._service_providers + + @_missingproperty + def bind(self): + return self._data['token']['bind'] diff --git a/keystoneauth1/fixture/v2.py b/keystoneauth1/fixture/v2.py index 7b7a8844..4588d60e 100644 --- a/keystoneauth1/fixture/v2.py +++ b/keystoneauth1/fixture/v2.py @@ -242,3 +242,6 @@ class Token(dict): def set_trust(self, id=None, trustee_user_id=None): self.trust_id = id or uuid.uuid4().hex self.trustee_user_id = trustee_user_id or uuid.uuid4().hex + + def set_bind(self, name, data): + self._token.setdefault('bind', {})[name] = data diff --git a/keystoneauth1/fixture/v3.py b/keystoneauth1/fixture/v3.py index f5acbf9f..f7cc3ebe 100644 --- a/keystoneauth1/fixture/v3.py +++ b/keystoneauth1/fixture/v3.py @@ -405,6 +405,9 @@ class Token(dict): _service_providers.append(sp) return sp + def set_bind(self, name, data): + self.root.setdefault('bind', {})[name] = data + class V3FederationToken(Token): """A V3 Keystone Federation token that can be used for testing. diff --git a/keystoneauth1/tests/unit/access/test_v2_access.py b/keystoneauth1/tests/unit/access/test_v2_access.py index 78227f6a..8f7bb503 100644 --- a/keystoneauth1/tests/unit/access/test_v2_access.py +++ b/keystoneauth1/tests/unit/access/test_v2_access.py @@ -55,6 +55,7 @@ class AccessV2Test(utils.TestCase): self.assertEqual(token.audit_id, auth_ref.audit_id) self.assertIsNone(auth_ref.audit_chain_id) self.assertIsNone(token.audit_chain_id) + self.assertIsNone(auth_ref.bind) def test_will_expire_soon(self): token = fixture.V2Token() @@ -199,3 +200,13 @@ class AccessV2Test(utils.TestCase): self.assertEqual(user_id, auth_ref.trustee_user_id) self.assertEqual(trust_id, token['access']['trust']['id']) + + def test_binding(self): + token = fixture.V2Token() + principal = uuid.uuid4().hex + token.set_bind('kerberos', principal) + + auth_ref = access.create(body=token) + self.assertIsInstance(auth_ref, access.AccessInfoV2) + + self.assertEqual({'kerberos': principal}, auth_ref.bind) diff --git a/keystoneauth1/tests/unit/access/test_v3_access.py b/keystoneauth1/tests/unit/access/test_v3_access.py index ebfb997f..7e9792c3 100644 --- a/keystoneauth1/tests/unit/access/test_v3_access.py +++ b/keystoneauth1/tests/unit/access/test_v3_access.py @@ -62,6 +62,7 @@ class AccessV3Test(utils.TestCase): self.assertEqual(auth_ref.audit_id, token.audit_id) self.assertIsNone(auth_ref.audit_chain_id) self.assertIsNone(token.audit_chain_id) + self.assertIsNone(auth_ref.bind) def test_will_expire_soon(self): expires = timeutils.utcnow() + datetime.timedelta(minutes=5) @@ -184,3 +185,13 @@ class AccessV3Test(utils.TestCase): token.set_project_scope() auth_ref = access.create(body=token) self.assertFalse(auth_ref.is_federated) + + def test_binding(self): + token = fixture.V3Token() + principal = uuid.uuid4().hex + token.set_bind('kerberos', principal) + + auth_ref = access.create(body=token) + self.assertIsInstance(auth_ref, access.AccessInfoV3) + + self.assertEqual({'kerberos': principal}, auth_ref.bind) diff --git a/keystoneauth1/tests/unit/test_fixtures.py b/keystoneauth1/tests/unit/test_fixtures.py index 92baa1c4..3b955c0d 100644 --- a/keystoneauth1/tests/unit/test_fixtures.py +++ b/keystoneauth1/tests/unit/test_fixtures.py @@ -109,6 +109,19 @@ class V2TokenTests(utils.TestCase): self.assertEqual(region, service['region']) self.assertEqual(endpoint_id, service['id']) + def test_token_bind(self): + name1 = uuid.uuid4().hex + data1 = uuid.uuid4().hex + name2 = uuid.uuid4().hex + data2 = {uuid.uuid4().hex: uuid.uuid4().hex} + + token = fixture.V2Token() + token.set_bind(name1, data1) + token.set_bind(name2, data2) + + self.assertEqual({name1: data1, name2: data2}, + token['access']['token']['bind']) + class V3TokenTests(utils.TestCase): @@ -271,3 +284,16 @@ class V3TokenTests(utils.TestCase): self.assertEqual(ref_service_providers, token.service_providers) self.assertEqual(ref_service_providers, token['token']['service_providers']) + + def test_token_bind(self): + name1 = uuid.uuid4().hex + data1 = uuid.uuid4().hex + name2 = uuid.uuid4().hex + data2 = {uuid.uuid4().hex: uuid.uuid4().hex} + + token = fixture.V3Token() + token.set_bind(name1, data1) + token.set_bind(name2, data2) + + self.assertEqual({name1: data1, name2: data2}, + token['token']['bind'])