From 1628b77a792ae0d1f9b79eb52713cfd93653d68e Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Sun, 29 Mar 2015 02:55:01 -0400 Subject: [PATCH] Add support to create ECP assertion based on a token A user should be able to exchange their token for an ECP wrapped SAML assertion. implements bp generate-saml-assertions Change-Id: Ic9c20aebc5cd91650576ad050c09779df54f1d94 --- .../tests/unit/v3/saml2_fixtures.py | 30 +++++++++++++++-- .../tests/unit/v3/test_auth_saml2.py | 25 ++++++++++++++ keystoneclient/v3/contrib/federation/saml.py | 33 ++++++++++++++++--- 3 files changed, 82 insertions(+), 6 deletions(-) diff --git a/keystoneclient/tests/unit/v3/saml2_fixtures.py b/keystoneclient/tests/unit/v3/saml2_fixtures.py index 513082df6..f428a87ac 100644 --- a/keystoneclient/tests/unit/v3/saml2_fixtures.py +++ b/keystoneclient/tests/unit/v3/saml2_fixtures.py @@ -170,8 +170,9 @@ DOMAINS = { } } -TOKEN_BASED_SAML = """ - +SAML_ENCODING = "" + +TOKEN_SAML_RESPONSE = """ """ + +TOKEN_BASED_SAML = ''.join([SAML_ENCODING, TOKEN_SAML_RESPONSE]) + +ECP_ENVELOPE = """ + + + + ss:mem:1ddfe8b0f58341a5a840d2e8717b0737 + + + + {0} + + +""".format(TOKEN_SAML_RESPONSE) + +TOKEN_BASED_ECP = ''.join([SAML_ENCODING, ECP_ENVELOPE]) diff --git a/keystoneclient/tests/unit/v3/test_auth_saml2.py b/keystoneclient/tests/unit/v3/test_auth_saml2.py index f77a163f6..3b79b5860 100644 --- a/keystoneclient/tests/unit/v3/test_auth_saml2.py +++ b/keystoneclient/tests/unit/v3/test_auth_saml2.py @@ -631,6 +631,8 @@ class SAMLGenerationTests(utils.TestCase): self.manager = self.client.federation.saml self.SAML2_FULL_URL = ''.join([self.TEST_URL, saml_manager.SAML2_ENDPOINT]) + self.ECP_FULL_URL = ''.join([self.TEST_URL, + saml_manager.ECP_ENDPOINT]) def test_saml_create(self): """Test that a token can be exchanged for a SAML assertion.""" @@ -654,3 +656,26 @@ class SAMLGenerationTests(utils.TestCase): self.assertEqual(service_provider_id, req_json['auth']['scope']['service_provider']['id']) self.assertRequestHeaderEqual('Content-Type', 'application/json') + + def test_ecp_create(self): + """Test that a token can be exchanged for an ECP wrapped assertion.""" + + token_id = uuid.uuid4().hex + service_provider_id = uuid.uuid4().hex + + # Mock returned text for '/auth/OS-FEDERATION/saml2/ecp + self.requests_mock.post(self.ECP_FULL_URL, + text=saml2_fixtures.TOKEN_BASED_ECP) + + text = self.manager.create_ecp_assertion(service_provider_id, + token_id) + + # Ensure returned text is correct + self.assertEqual(saml2_fixtures.TOKEN_BASED_ECP, text) + + # Ensure request headers and body are correct + req_json = self.requests_mock.last_request.json() + self.assertEqual(token_id, req_json['auth']['identity']['token']['id']) + self.assertEqual(service_provider_id, + req_json['auth']['scope']['service_provider']['id']) + self.assertRequestHeaderEqual('Content-Type', 'application/json') diff --git a/keystoneclient/v3/contrib/federation/saml.py b/keystoneclient/v3/contrib/federation/saml.py index c3cd28663..b7583546a 100644 --- a/keystoneclient/v3/contrib/federation/saml.py +++ b/keystoneclient/v3/contrib/federation/saml.py @@ -14,6 +14,7 @@ from keystoneclient import base SAML2_ENDPOINT = '/auth/OS-FEDERATION/saml2' +ECP_ENDPOINT = '/auth/OS-FEDERATION/saml2/ecp' class SamlManager(base.Manager): @@ -34,6 +35,33 @@ class SamlManager(base.Manager): :rtype: string """ + headers, body = self._create_common_request(service_provider, token_id) + resp, body = self.client.post(SAML2_ENDPOINT, json=body, + headers=headers) + return resp.text + + def create_ecp_assertion(self, service_provider, token_id): + """Create an ECP wrapped SAML assertion from a token. + + Equivalent Identity API call: + POST /auth/OS-FEDERATION/saml2/ecp + + :param service_provider: Service Provider resource. + :type service_provider: string + :param token_id: Token to transform to SAML assertion. + :type token_id: string + + :returns: SAML representation of token_id, wrapped in ECP envelope + :rtype: string + """ + + headers, body = self._create_common_request(service_provider, token_id) + resp, body = self.client.post(ECP_ENDPOINT, json=body, + headers=headers) + return resp.text + + def _create_common_request(self, service_provider, token_id): + headers = {'Content-Type': 'application/json'} body = { 'auth': { 'identity': { @@ -50,7 +78,4 @@ class SamlManager(base.Manager): } } - headers = {'Content-Type': 'application/json'} - resp, body = self.client.post(SAML2_ENDPOINT, json=body, - headers=headers) - return resp.text + return (headers, body)