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)