From 4cc1631665ac3f6d778b2d7ff538f1156ae793e4 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Wed, 3 Sep 2014 12:51:26 +1000 Subject: [PATCH] Allow fetching user_id/project_id from auth This would ideally not be required however when building certain URLs the current user_id is needed. And when communicating with certain services we need to have access to the current project id. It seems better to allow plugins to give up the information if they have it than do various hacks to try and get it from them. Change-Id: Ib61b0628702806268be623a9987a922a60b04165 Closes-Bug: #1364724 --- keystoneclient/auth/base.py | 30 +++++++++++++++++++ keystoneclient/auth/identity/base.py | 6 ++++ keystoneclient/httpclient.py | 6 ++++ .../tests/auth/test_identity_common.py | 14 +++++++++ .../tests/auth/test_token_endpoint.py | 8 +++++ keystoneclient/tests/v2_0/test_client.py | 11 +++++-- keystoneclient/tests/v3/test_client.py | 8 +++++ 7 files changed, 81 insertions(+), 2 deletions(-) diff --git a/keystoneclient/auth/base.py b/keystoneclient/auth/base.py index 4c743d950..ecbcf9635 100644 --- a/keystoneclient/auth/base.py +++ b/keystoneclient/auth/base.py @@ -108,6 +108,36 @@ class BaseAuthPlugin(object): """ return False + def get_user_id(self, session, **kwargs): + """Return a unique user identifier of the plugin. + + Wherever possible the user id should be inferred from the token however + there are certain URLs and other places that require access to the + currently authenticated user id. + + :param session: A session object so the plugin can make HTTP calls. + :type session: keystoneclient.session.Session + + :returns: A user identifier or None if one is not available. + :rtype: str + """ + return None + + def get_project_id(self, session, **kwargs): + """Return the project id that we are authenticated to. + + Wherever possible the project id should be inferred from the token + however there are certain URLs and other places that require access to + the currently authenticated project id. + + :param session: A session object so the plugin can make HTTP calls. + :type session: keystoneclient.session.Session + + :returns: A project identifier or None if one is not available. + :rtype: str + """ + return None + @classmethod def get_options(cls): """Return the list of parameters associated with the auth plugin. diff --git a/keystoneclient/auth/identity/base.py b/keystoneclient/auth/identity/base.py index 94b071219..610039a4d 100644 --- a/keystoneclient/auth/identity/base.py +++ b/keystoneclient/auth/identity/base.py @@ -236,6 +236,12 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): return url + def get_user_id(self, session, **kwargs): + return self.get_access(session).user_id + + def get_project_id(self, session, **kwargs): + return self.get_access(session).project_id + @utils.positional() def get_discovery(self, session, url, authenticated=None): """Return the discovery object for a URL. diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py index 458745f61..84b314e98 100644 --- a/keystoneclient/httpclient.py +++ b/keystoneclient/httpclient.py @@ -362,6 +362,12 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin): else: return self.management_url + def get_user_id(self, session, **kwargs): + return self.auth_ref.user_id + + def get_project_id(self, session, **kwargs): + return self.auth_ref.project_id + @auth_token.setter def auth_token(self, value): """Override the auth_token. diff --git a/keystoneclient/tests/auth/test_identity_common.py b/keystoneclient/tests/auth/test_identity_common.py index 4a0cf5729..0b62159af 100644 --- a/keystoneclient/tests/auth/test_identity_common.py +++ b/keystoneclient/tests/auth/test_identity_common.py @@ -65,6 +65,13 @@ class CommonIdentityTests(object): def stub_auth_data(self, **kwargs): token = self.get_auth_data(**kwargs) + self.user_id = token.user_id + + try: + self.project_id = token.project_id + except AttributeError: + self.project_id = token.tenant_id + self.stub_auth(json=token) @abc.abstractproperty @@ -221,6 +228,13 @@ class CommonIdentityTests(object): self.assertIsNone(a.auth_ref) self.assertFalse(a.invalidate()) + def test_get_auth_properties(self): + a = self.create_auth_plugin() + s = session.Session() + + self.assertEqual(self.user_id, a.get_user_id(s)) + self.assertEqual(self.project_id, a.get_project_id(s)) + class V3(CommonIdentityTests, utils.TestCase): diff --git a/keystoneclient/tests/auth/test_token_endpoint.py b/keystoneclient/tests/auth/test_token_endpoint.py index a9028e374..a53c1b814 100644 --- a/keystoneclient/tests/auth/test_token_endpoint.py +++ b/keystoneclient/tests/auth/test_token_endpoint.py @@ -53,3 +53,11 @@ class TokenEndpointTest(utils.TestCase): self.assertIn('token', opt_names) self.assertIn('endpoint', opt_names) + + def test_token_endpoint_user_id(self): + a = token_endpoint.Token(self.TEST_URL, self.TEST_TOKEN) + s = session.Session() + + # we can't know this information about this sort of plugin + self.assertIsNone(a.get_user_id(s)) + self.assertIsNone(a.get_project_id(s)) diff --git a/keystoneclient/tests/v2_0/test_client.py b/keystoneclient/tests/v2_0/test_client.py index 5fbedf460..d09f326e6 100644 --- a/keystoneclient/tests/v2_0/test_client.py +++ b/keystoneclient/tests/v2_0/test_client.py @@ -27,7 +27,8 @@ from keystoneclient.v2_0 import client class KeystoneClientTest(utils.TestCase): def test_unscoped_init(self): - self.stub_auth(json=client_fixtures.unscoped_token()) + token = client_fixtures.unscoped_token() + self.stub_auth(json=token) c = client.Client(username='exampleuser', password='password', @@ -38,9 +39,12 @@ class KeystoneClientTest(utils.TestCase): self.assertFalse(c.auth_ref.project_scoped) self.assertIsNone(c.auth_ref.trust_id) self.assertFalse(c.auth_ref.trust_scoped) + self.assertIsNone(c.get_project_id(session=None)) + self.assertEqual(token.user_id, c.get_user_id(session=None)) def test_scoped_init(self): - self.stub_auth(json=client_fixtures.project_scoped_token()) + token = client_fixtures.project_scoped_token() + self.stub_auth(json=token) c = client.Client(username='exampleuser', password='password', @@ -53,6 +57,9 @@ class KeystoneClientTest(utils.TestCase): self.assertIsNone(c.auth_ref.trust_id) self.assertFalse(c.auth_ref.trust_scoped) + self.assertEqual(token.tenant_id, c.get_project_id(session=None)) + self.assertEqual(token.user_id, c.get_user_id(session=None)) + def test_auth_ref_load(self): self.stub_auth(json=client_fixtures.project_scoped_token()) diff --git a/keystoneclient/tests/v3/test_client.py b/keystoneclient/tests/v3/test_client.py index bf450321d..e1a85424d 100644 --- a/keystoneclient/tests/v3/test_client.py +++ b/keystoneclient/tests/v3/test_client.py @@ -40,6 +40,10 @@ class KeystoneClientTest(utils.TestCase): 'c4da488862bd435c9e6c0275a0d0e49a') self.assertFalse(c.has_service_catalog()) + self.assertEqual('c4da488862bd435c9e6c0275a0d0e49a', + c.get_user_id(session=None)) + self.assertIsNone(c.get_project_id(session=None)) + def test_domain_scoped_init(self): self.stub_auth(json=client_fixtures.domain_scoped_token()) @@ -70,6 +74,10 @@ class KeystoneClientTest(utils.TestCase): 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(c.auth_tenant_id, '225da22d3ce34b15877ea70b2a575f58') + self.assertEqual('c4da488862bd435c9e6c0275a0d0e49a', + c.get_user_id(session=None)) + self.assertEqual('225da22d3ce34b15877ea70b2a575f58', + c.get_project_id(session=None)) def test_auth_ref_load(self): self.stub_auth(json=client_fixtures.project_scoped_token())