diff --git a/openstack/auth/identity/v2.py b/openstack/auth/identity/v2.py index 05e0bc591..bf99f59ce 100644 --- a/openstack/auth/identity/v2.py +++ b/openstack/auth/identity/v2.py @@ -106,6 +106,9 @@ class Auth(base.BaseIdentityPlugin): """ +_NOT_PASSED = object() + + class Password(Auth): #: Valid options for Password plugin @@ -121,8 +124,8 @@ class Password(Auth): 'trust_id', ] - def __init__(self, auth_url, user_name=None, password=None, user_id=None, - **kwargs): + def __init__(self, auth_url, user_name=_NOT_PASSED, password=None, + user_id=_NOT_PASSED, **kwargs): """A plugin for authenticating with a user_name and password. A user_name or user_id must be provided. @@ -136,9 +139,13 @@ class Password(Auth): """ super(Password, self).__init__(auth_url, **kwargs) - if not (user_id or user_name): + if user_name is _NOT_PASSED and user_id is _NOT_PASSED: msg = 'You need to specify either a user_name or user_id' raise TypeError(msg) + if user_name is _NOT_PASSED: + user_name = None + if user_id is _NOT_PASSED: + user_id = None self.user_id = user_id self.user_name = user_name diff --git a/openstack/auth/identity/v3.py b/openstack/auth/identity/v3.py index a38cf2d8e..0c48f1669 100644 --- a/openstack/auth/identity/v3.py +++ b/openstack/auth/identity/v3.py @@ -50,7 +50,8 @@ class Auth(base.BaseIdentityPlugin): project_name=None, project_domain_id=None, project_domain_name=None, - reauthenticate=True): + reauthenticate=True, + include_catalog=True): """Construct an Identity V3 Authentication Plugin. :param string auth_url: Identity service endpoint for authentication. @@ -65,6 +66,8 @@ class Auth(base.BaseIdentityPlugin): :param bool reauthenticate: Allow fetching a new token if the current one is going to expire. (optional) default True + :param bool include_catalog: Include the service catalog in the + returned token. (optional) default True. """ super(Auth, self).__init__(auth_url=auth_url, @@ -78,6 +81,7 @@ class Auth(base.BaseIdentityPlugin): self.project_name = project_name self.project_domain_id = project_domain_id self.project_domain_name = project_domain_name + self.include_catalog = include_catalog @property def token_url(self): @@ -126,8 +130,14 @@ class Auth(base.BaseIdentityPlugin): elif self.trust_id: body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}} - _logger.debug('Making authentication request to %s', self.token_url) - resp = transport.post(self.token_url, json=body, headers=headers) + # NOTE(jamielennox): we add nocatalog here rather than in token_url + # directly as some federation plugins require the base token_url + token_url = self.token_url + if not self.include_catalog: + token_url += '?nocatalog' + + _logger.debug('Making authentication request to %s', token_url) + resp = transport.post(token_url, json=body, headers=headers) try: resp_data = resp.json()['token'] @@ -181,7 +191,7 @@ class AuthMethod(object): @six.add_metaclass(abc.ABCMeta) -class _AuthConstructor(Auth): +class AuthConstructor(Auth): """AuthConstructor creates an authentication plugin with one method. AuthConstructor is a means of creating an authentication plugin that @@ -198,7 +208,7 @@ class _AuthConstructor(Auth): def __init__(self, auth_url, *args, **kwargs): method_kwargs = self._auth_method_class._extract_kwargs(kwargs) method = self._auth_method_class(*args, **method_kwargs) - super(_AuthConstructor, self).__init__(auth_url, [method], **kwargs) + super(AuthConstructor, self).__init__(auth_url, [method], **kwargs) class PasswordMethod(AuthMethod): @@ -237,7 +247,7 @@ class PasswordMethod(AuthMethod): return 'password', {'user': user} -class Password(_AuthConstructor): +class Password(AuthConstructor): #: Valid options for this plugin valid_options = [ @@ -279,7 +289,7 @@ class TokenMethod(AuthMethod): return 'token', {'id': self.token} -class Token(_AuthConstructor): +class Token(AuthConstructor): #: Valid options for this plugin valid_options = [ diff --git a/openstack/tests/auth/identity/test_v2.py b/openstack/tests/auth/identity/test_v2.py index ca4f0c320..75346ade6 100644 --- a/openstack/tests/auth/identity/test_v2.py +++ b/openstack/tests/auth/identity/test_v2.py @@ -41,6 +41,25 @@ class TestV2Auth(testtools.TestCase): 'username': common.TEST_USER}} self.assertEqual(expected, sot.get_auth_data()) + def test_password_no_user(self): + kargs = {'trust_id': common.TEST_TRUST_ID, + 'project_id': common.TEST_TENANT_ID, + 'project_name': common.TEST_TENANT_NAME} + + sot = v2.Password(TEST_URL, None, common.TEST_PASS, + **kargs) + + self.assertEqual(None, sot.user_name) + self.assertEqual(common.TEST_PASS, sot.password) + self.assertEqual(common.TEST_TRUST_ID, sot.trust_id) + self.assertEqual(common.TEST_TENANT_ID, sot.tenant_id) + self.assertEqual(common.TEST_TENANT_NAME, sot.tenant_name) + expected = {'passwordCredentials': {'password': common.TEST_PASS}} + self.assertEqual(expected, sot.get_auth_data()) + + def test_password_no_nothing(self): + self.assertRaises(TypeError, v2.Password, TEST_URL) + def test_token(self): kargs = {'trust_id': common.TEST_TRUST_ID, 'tenant_id': common.TEST_TENANT_ID, diff --git a/openstack/tests/auth/identity/test_v3.py b/openstack/tests/auth/identity/test_v3.py index 31f1e110d..ee29f5f6a 100644 --- a/openstack/tests/auth/identity/test_v3.py +++ b/openstack/tests/auth/identity/test_v3.py @@ -51,6 +51,7 @@ class TestV3Auth(testtools.TestCase): self.assertEqual(None, sot.project_domain_id) self.assertEqual(None, sot.project_domain_name) self.assertEqual(TEST_URL + '/auth/tokens', sot.token_url) + self.assertTrue(sot.include_catalog) def test_password_domain(self): kargs = {'domain_id': common.TEST_DOMAIN_ID, @@ -82,6 +83,7 @@ class TestV3Auth(testtools.TestCase): self.assertEqual(None, sot.project_domain_id) self.assertEqual(None, sot.project_domain_name) self.assertEqual(TEST_URL + '/auth/tokens', sot.token_url) + self.assertTrue(sot.include_catalog) def test_token_project_domain(self): kargs = {'project_domain_id': common.TEST_DOMAIN_ID, @@ -135,6 +137,25 @@ class TestV3Auth(testtools.TestCase): ecatalog['version'] = 'v3' self.assertEqual(ecatalog, resp._info) + def test_authorize_token_include_catalog(self): + kargs = {'include_catalog': False} + methods = [v3.TokenMethod(token=common.TEST_TOKEN)] + sot = v3.Auth(TEST_URL, methods, **kargs) + xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) + + resp = sot.authorize(xport) + + eurl = TEST_URL + '/auth/tokens?nocatalog' + eheaders = {'Accept': 'application/json', + 'X-Auth-Token': common.TEST_TOKEN} + ejson = {'auth': {'identity': {'token': {'id': common.TEST_TOKEN}, + 'methods': ['token']}}} + xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) + ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() + ecatalog['auth_token'] = common.TEST_SUBJECT + ecatalog['version'] = 'v3' + self.assertEqual(ecatalog, resp._info) + def test_authorize_token_domain_id(self): kargs = {'domain_id': common.TEST_DOMAIN_ID} methods = [v3.TokenMethod(token=common.TEST_TOKEN)]