Scope unscoped saml2 tokens.

Scope unscoped federation token.
The plugin mimics standard v3.Token behaviour.

Change-Id: I81f30a7c893be15e715c57bd43035b12d8435f58
Implements: blueprint add-saml2-cli-authentication
This commit is contained in:
Marek Denis
2014-06-12 13:39:42 +02:00
parent c9ae9d1fa2
commit c758a2fc50
3 changed files with 149 additions and 1 deletions

View File

@@ -141,7 +141,7 @@ class Saml2UnscopedToken(v3.AuthConstructor):
def _first(self, _list):
if len(_list) != 1:
raise IndexError("Only single element list can be flatten")
raise IndexError("Only single element is acceptable")
return _list[0]
def _prepare_idp_saml2_request(self, saml2_authn_request):
@@ -409,3 +409,27 @@ class Saml2UnscopedToken(v3.AuthConstructor):
token, token_json = self._get_unscoped_token(session, **kwargs)
return access.AccessInfoV3(token,
**token_json)
class Saml2ScopedTokenMethod(v3.TokenMethod):
_method_name = 'saml2'
def get_auth_data(self, session, auth, headers, **kwargs):
"""Build and return request body for token scoping step."""
t = super(Saml2ScopedTokenMethod, self).get_auth_data(
session, auth, headers, **kwargs)
_token_method, token = t
return self._method_name, token
class Saml2ScopedToken(v3.Token):
"""Class for scoping unscoped saml2 token."""
_auth_method_class = Saml2ScopedTokenMethod
def __init__(self, auth_url, token, **kwargs):
super(Saml2ScopedToken, self).__init__(auth_url, token, **kwargs)
if not (self.project_id or self.domain_id):
raise exceptions.ValidationError(
'Neither project nor domain specified')

View File

@@ -119,3 +119,50 @@ UNSCOPED_TOKEN = {
}
}
}
PROJECTS = {
"projects": [
{
"domain_id": "37ef61",
"enabled": 'true',
"id": "12d706",
"links": {
"self": "http://identity:35357/v3/projects/12d706"
},
"name": "a project name"
},
{
"domain_id": "37ef61",
"enabled": 'true',
"id": "9ca0eb",
"links": {
"self": "http://identity:35357/v3/projects/9ca0eb"
},
"name": "another project"
}
],
"links": {
"self": "http://identity:35357/v3/OS-FEDERATION/projects",
"previous": 'null',
"next": 'null'
}
}
DOMAINS = {
"domains": [
{
"description": "desc of domain",
"enabled": 'true',
"id": "37ef61",
"links": {
"self": "http://identity:35357/v3/domains/37ef61"
},
"name": "my domain"
}
],
"links": {
"self": "http://identity:35357/v3/OS-FEDERATION/domains",
"previous": 'null',
"next": 'null'
}
}

View File

@@ -23,6 +23,7 @@ from keystoneclient.openstack.common.fixture import config
from keystoneclient.openstack.common import jsonutils
from keystoneclient import session
from keystoneclient.tests.auth import utils as auth_utils
from keystoneclient.tests.v3 import client_fixtures
from keystoneclient.tests.v3 import saml2_fixtures
from keystoneclient.tests.v3 import utils
@@ -309,3 +310,79 @@ class AuthenticateviaSAML2Tests(auth_utils.TestCase, utils.TestCase):
response = self.saml2plugin.get_auth_ref(self.session)
self.assertEqual(saml2_fixtures.UNSCOPED_TOKEN_HEADER,
response.auth_token)
class ScopeFederationTokenTests(AuthenticateviaSAML2Tests):
def setUp(self):
super(ScopeFederationTokenTests, self).setUp()
self.PROJECT_SCOPED_TOKEN_JSON = client_fixtures.project_scoped_token()
self.PROJECT_SCOPED_TOKEN_JSON['methods'] = ['saml2']
# for better readibility
self.TEST_TENANT_ID = self.PROJECT_SCOPED_TOKEN_JSON.project_id
self.TEST_TENANT_NAME = self.PROJECT_SCOPED_TOKEN_JSON.project_name
self.DOMAIN_SCOPED_TOKEN_JSON = client_fixtures.domain_scoped_token()
self.DOMAIN_SCOPED_TOKEN_JSON['methods'] = ['saml2']
#for better readibility
self.TEST_DOMAIN_ID = self.DOMAIN_SCOPED_TOKEN_JSON.domain_id
self.TEST_DOMAIN_NAME = self.DOMAIN_SCOPED_TOKEN_JSON.domain_name
self.saml2_scope_plugin = saml2.Saml2ScopedToken(
self.TEST_URL, saml2_fixtures.UNSCOPED_TOKEN_HEADER,
project_id=self.TEST_TENANT_ID)
@httpretty.activate
def test_scope_saml2_token_to_project(self):
self.simple_http('POST', self.TEST_URL + '/auth/tokens',
body=jsonutils.dumps(self.PROJECT_SCOPED_TOKEN_JSON),
content_type='application/json',
headers=client_fixtures.AUTH_RESPONSE_HEADERS)
token = self.saml2_scope_plugin.get_auth_ref(self.session)
self.assertTrue(token.project_scoped, "Received token is not scoped")
self.assertEqual(client_fixtures.AUTH_SUBJECT_TOKEN, token.auth_token)
self.assertEqual(self.TEST_TENANT_ID, token.project_id)
self.assertEqual(self.TEST_TENANT_NAME, token.project_name)
@httpretty.activate
def test_scope_saml2_token_to_invalid_project(self):
self.simple_http('POST', self.TEST_URL + '/auth/tokens', status=401)
self.saml2_scope_plugin.project_id = uuid.uuid4().hex
self.saml2_scope_plugin.project_name = None
self.assertRaises(exceptions.Unauthorized,
self.saml2_scope_plugin.get_auth_ref,
self.session)
@httpretty.activate
def test_scope_saml2_token_to_invalid_domain(self):
self.simple_http('POST', self.TEST_URL + '/auth/tokens', status=401)
self.saml2_scope_plugin.project_id = None
self.saml2_scope_plugin.project_name = None
self.saml2_scope_plugin.domain_id = uuid.uuid4().hex
self.saml2_scope_plugin.domain_name = None
self.assertRaises(exceptions.Unauthorized,
self.saml2_scope_plugin.get_auth_ref,
self.session)
@httpretty.activate
def test_scope_saml2_token_to_domain(self):
self.simple_http('POST', self.TEST_URL + '/auth/tokens',
body=jsonutils.dumps(self.DOMAIN_SCOPED_TOKEN_JSON),
content_type='application/json',
headers=client_fixtures.AUTH_RESPONSE_HEADERS)
token = self.saml2_scope_plugin.get_auth_ref(self.session)
self.assertTrue(token.domain_scoped, "Received token is not scoped")
self.assertEqual(client_fixtures.AUTH_SUBJECT_TOKEN, token.auth_token)
self.assertEqual(self.TEST_DOMAIN_ID, token.domain_id)
self.assertEqual(self.TEST_DOMAIN_NAME, token.domain_name)
def test_dont_set_project_nor_domain(self):
self.saml2_scope_plugin.project_id = None
self.saml2_scope_plugin.domain_id = None
self.assertRaises(exceptions.ValidationError,
saml2.Saml2ScopedToken,
self.TEST_URL, client_fixtures.AUTH_SUBJECT_TOKEN)