Merge "Add openstack_project_domain to assertion"

This commit is contained in:
Jenkins 2015-04-30 17:39:51 +00:00 committed by Gerrit Code Review
commit f029195f25
4 changed files with 45 additions and 9 deletions

View File

@ -346,9 +346,12 @@ class Auth(auth_controllers.Auth):
raise exception.ForbiddenAction(action=action)
project = token_ref.project_name
# NOTE(rodrigods): the domain name is necessary in order to distinguish
# between projects with the same name in different domains.
domain = token_ref.project_domain_name
generator = keystone_idp.SAMLGenerator()
response = generator.samlize_token(issuer, sp_url, subject, roles,
project)
project, domain)
return (response, service_provider)
def _build_response_headers(self, service_provider):

View File

@ -44,7 +44,7 @@ class SAMLGenerator(object):
self.assertion_id = uuid.uuid4().hex
def samlize_token(self, issuer, recipient, user, roles, project,
expires_in=None):
project_domain_name, expires_in=None):
"""Convert Keystone attributes to a SAML assertion.
:param issuer: URL of the issuing party
@ -57,6 +57,8 @@ class SAMLGenerator(object):
:type roles: list
:param project: Project name
:type project: string
:param project_domain_name: Project Domain name
:type project_domain_name: string
:param expires_in: Sets how long the assertion is valid for, in seconds
:type expires_in: int
@ -67,8 +69,8 @@ class SAMLGenerator(object):
status = self._create_status()
saml_issuer = self._create_issuer(issuer)
subject = self._create_subject(user, expiration_time, recipient)
attribute_statement = self._create_attribute_statement(user, roles,
project)
attribute_statement = self._create_attribute_statement(
user, roles, project, project_domain_name)
authn_statement = self._create_authn_statement(issuer, expiration_time)
signature = self._create_signature()
@ -153,7 +155,8 @@ class SAMLGenerator(object):
subject.name_id = name_id
return subject
def _create_attribute_statement(self, user, roles, project):
def _create_attribute_statement(self, user, roles, project,
project_domain_name):
"""Create an object that represents a SAML AttributeStatement.
<ns0:AttributeStatement>
@ -171,6 +174,10 @@ class SAMLGenerator(object):
<ns0:AttributeValue
xsi:type="xs:string">development</ns0:AttributeValue>
</ns0:Attribute>
<ns0:Attribute Name="openstack_project_domain">
<ns0:AttributeValue
xsi:type="xs:string">Default</ns0:AttributeValue>
</ns0:Attribute>
</ns0:AttributeStatement>
:return: XML <AttributeStatement> object
@ -199,10 +206,18 @@ class SAMLGenerator(object):
project_value.set_text(project)
project_attribute.attribute_value = project_value
openstack_project_domain = 'openstack_project_domain'
project_domain_attribute = saml.Attribute()
project_domain_attribute.name = openstack_project_domain
project_domain_value = saml.AttributeValue()
project_domain_value.set_text(project_domain_name)
project_domain_attribute.attribute_value = project_domain_value
attribute_statement = saml.AttributeStatement()
attribute_statement.attribute.append(user_attribute)
attribute_statement.attribute.append(roles_attribute)
attribute_statement.attribute.append(project_attribute)
attribute_statement.attribute.append(project_domain_attribute)
return attribute_statement
def _create_authn_statement(self, issuer, expiration_time):

View File

@ -59,5 +59,8 @@ UHeBXxQq/GmfBv3l+V5ObQ+EHKnyDodLHCk=</ns1:X509Certificate>
<ns0:Attribute Name="openstack_project" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<ns0:AttributeValue xsi:type="xs:string">development</ns0:AttributeValue>
</ns0:Attribute>
<ns0:Attribute Name="openstack_project_domain" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<ns0:AttributeValue xsi:type="xs:string">Default</ns0:AttributeValue>
</ns0:Attribute>
</ns0:AttributeStatement>
</ns0:Assertion>

View File

@ -3018,6 +3018,7 @@ class SAMLGenerationTests(FederationTests):
SUBJECT = 'test_user'
ROLES = ['admin', 'member']
PROJECT = 'development'
DOMAIN = 'Default'
SAML_GENERATION_ROUTE = '/auth/OS-FEDERATION/saml2'
ECP_GENERATION_ROUTE = '/auth/OS-FEDERATION/saml2/ecp'
ASSERTION_VERSION = "2.0"
@ -3056,7 +3057,7 @@ class SAMLGenerationTests(FederationTests):
generator = keystone_idp.SAMLGenerator()
response = generator.samlize_token(self.ISSUER, self.RECIPIENT,
self.SUBJECT, self.ROLES,
self.PROJECT)
self.PROJECT, self.DOMAIN)
assertion = response.assertion
self.assertIsNotNone(assertion)
@ -3076,6 +3077,11 @@ class SAMLGenerationTests(FederationTests):
self.assertEqual(self.PROJECT,
project_attribute.attribute_value[0].text)
project_domain_attribute = (
assertion.attribute_statement[0].attribute[3])
self.assertEqual(self.DOMAIN,
project_domain_attribute.attribute_value[0].text)
def test_verify_assertion_object(self):
"""Test that the Assertion object is built properly.
@ -3088,7 +3094,7 @@ class SAMLGenerationTests(FederationTests):
generator = keystone_idp.SAMLGenerator()
response = generator.samlize_token(self.ISSUER, self.RECIPIENT,
self.SUBJECT, self.ROLES,
self.PROJECT)
self.PROJECT, self.DOMAIN)
assertion = response.assertion
self.assertEqual(self.ASSERTION_VERSION, assertion.version)
@ -3105,7 +3111,7 @@ class SAMLGenerationTests(FederationTests):
generator = keystone_idp.SAMLGenerator()
response = generator.samlize_token(self.ISSUER, self.RECIPIENT,
self.SUBJECT, self.ROLES,
self.PROJECT)
self.PROJECT, self.DOMAIN)
saml_str = response.to_string()
response = etree.fromstring(saml_str)
@ -3125,6 +3131,9 @@ class SAMLGenerationTests(FederationTests):
project_attribute = assertion[4][2]
self.assertEqual(self.PROJECT, project_attribute[0].text)
project_domain_attribute = assertion[4][3]
self.assertEqual(self.DOMAIN, project_domain_attribute[0].text)
def test_assertion_using_explicit_namespace_prefixes(self):
def mocked_subprocess_check_output(*popenargs, **kwargs):
# the last option is the assertion file to be signed
@ -3140,7 +3149,7 @@ class SAMLGenerationTests(FederationTests):
generator = keystone_idp.SAMLGenerator()
response = generator.samlize_token(self.ISSUER, self.RECIPIENT,
self.SUBJECT, self.ROLES,
self.PROJECT)
self.PROJECT, self.DOMAIN)
assertion_xml = response.assertion.to_string()
# make sure we have the proper tag and prefix for the assertion
# namespace
@ -3273,6 +3282,9 @@ class SAMLGenerationTests(FederationTests):
project_attribute = assertion[4][2]
self.assertIsInstance(project_attribute[0].text, str)
project_domain_attribute = assertion[4][3]
self.assertIsInstance(project_domain_attribute[0].text, str)
def test_invalid_scope_body(self):
"""Test that missing the scope in request body raises an exception.
@ -3382,6 +3394,9 @@ class SAMLGenerationTests(FederationTests):
project_attribute = assertion[4][2]
self.assertIsInstance(project_attribute[0].text, str)
project_domain_attribute = assertion[4][3]
self.assertIsInstance(project_domain_attribute[0].text, str)
class IdPMetadataGenerationTests(FederationTests):
"""A class for testing Identity Provider Metadata generation."""