Allow requesting an unscoped Token

The keystone server understands that specifying unscoped in the scope
section of an auth request means that it should ignore the
default_project_id of a user and return an unscoped token.

This is the client side change to allow requesting these tokens via an
auth plugin.

Change-Id: Iba5ebcea0bf0d8e5a31d552977276fc03e536c67
Implements: bp explicit-unscoped
This commit is contained in:
Jamie Lennox 2015-03-31 10:03:20 +11:00
parent 19b3fd648e
commit 91e67141bc
2 changed files with 44 additions and 2 deletions

View File

@ -112,9 +112,13 @@ class Auth(BaseAuth):
is going to expire. (optional) default True
:param bool include_catalog: Include the service catalog in the returned
token. (optional) default True.
:param bool unscoped: Force the return of an unscoped token. This will make
the keystone server return an unscoped token even if
a default_project_id is set for this user.
"""
def __init__(self, auth_url, auth_methods, **kwargs):
self.unscoped = kwargs.pop('unscoped', False)
super(Auth, self).__init__(auth_url=auth_url, **kwargs)
self.auth_methods = auth_methods
@ -138,12 +142,13 @@ class Auth(BaseAuth):
mutual_exclusion = [bool(self.domain_id or self.domain_name),
bool(self.project_id or self.project_name),
bool(self.trust_id)]
bool(self.trust_id),
bool(self.unscoped)]
if sum(mutual_exclusion) > 1:
raise exceptions.AuthorizationFailure(
_('Authentication cannot be scoped to multiple targets. Pick '
'one of: project, domain or trust'))
'one of: project, domain, trust or unscoped'))
if self.domain_id:
body['auth']['scope'] = {'domain': {'id': self.domain_id}}
@ -161,6 +166,8 @@ class Auth(BaseAuth):
scope['project']['domain'] = {'name': self.project_domain_name}
elif self.trust_id:
body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}}
elif self.unscoped:
body['auth']['scope'] = {'unscoped': {}}
# NOTE(jamielennox): we add nocatalog here rather than in token_url
# directly as some federation plugins require the base token_url

View File

@ -496,3 +496,38 @@ class V3IdentityPlugin(utils.TestCase):
self.assertIs(v3.AuthMethod, v3_base.AuthMethod)
self.assertIs(v3.AuthConstructor, v3_base.AuthConstructor)
self.assertIs(v3.Auth, v3_base.Auth)
def test_unscoped_request(self):
token = fixture.V3Token()
self.stub_auth(json=token)
password = uuid.uuid4().hex
a = v3.Password(self.TEST_URL,
user_id=token.user_id,
password=password,
unscoped=True)
s = session.Session()
auth_ref = a.get_access(s)
self.assertFalse(auth_ref.scoped)
body = self.requests_mock.last_request.json()
ident = body['auth']['identity']
self.assertEqual(['password'], ident['methods'])
self.assertEqual(token.user_id, ident['password']['user']['id'])
self.assertEqual(password, ident['password']['user']['password'])
self.assertEqual({}, body['auth']['scope']['unscoped'])
def test_unscoped_with_scope_data(self):
a = v3.Password(self.TEST_URL,
user_id=uuid.uuid4().hex,
password=uuid.uuid4().hex,
unscoped=True,
project_id=uuid.uuid4().hex)
s = session.Session()
self.assertRaises(exceptions.AuthorizationFailure, a.get_auth_ref, s)