Merge "Routes for Keystone-IdP metadata endpoint"
This commit is contained in:
commit
8081eafffd
|
@ -1350,6 +1350,11 @@
|
|||
# administrative billing, and other (string value)
|
||||
#idp_contact_type=other
|
||||
|
||||
# Path to the Identity Provider Metadata file. This file
|
||||
# should be generated with the keystone-manage
|
||||
# saml_idp_metadata command. (string value)
|
||||
#idp_metadata_path=/etc/keystone/saml2_idp_metadata.xml
|
||||
|
||||
|
||||
[signing]
|
||||
|
||||
|
|
|
@ -864,6 +864,11 @@ FILE_OPTIONS = {
|
|||
help='Contact type. Allowed values are: '
|
||||
'technical, support, administrative '
|
||||
'billing, and other'),
|
||||
cfg.StrOpt('idp_metadata_path',
|
||||
default='/etc/keystone/saml2_idp_metadata.xml',
|
||||
help='Path to the Identity Provider Metadata file. '
|
||||
'This file should be generated with the '
|
||||
'keystone-manage saml_idp_metadata command.'),
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ from keystone import config
|
|||
from keystone.contrib.federation import idp as keystone_idp
|
||||
from keystone.contrib.federation import schema
|
||||
from keystone.contrib.federation import utils
|
||||
from keystone import exception
|
||||
from keystone.models import token_model
|
||||
|
||||
|
||||
|
@ -332,3 +333,18 @@ class ProjectV3(controller.V3Controller):
|
|||
projects = self.assignment_api.list_projects_for_groups(
|
||||
auth_context['group_ids'])
|
||||
return ProjectV3.wrap_collection(context, projects)
|
||||
|
||||
|
||||
class SAMLMetadataV3(_ControllerBase):
|
||||
member_name = 'metadata'
|
||||
|
||||
def get_metadata(self, context):
|
||||
metadata_path = CONF.federation.idp_metadata_path
|
||||
try:
|
||||
with open(metadata_path, 'r') as metadata_handler:
|
||||
metadata = metadata_handler.read()
|
||||
except IOError as e:
|
||||
# Raise HTTP 500 in case Metadata file cannot be read.
|
||||
raise exception.MetadataFileError(reason=e)
|
||||
return wsgi.render_response(body=metadata, status=('200', 'OK'),
|
||||
headers=[('Content-Type', 'text/xml')])
|
||||
|
|
|
@ -67,8 +67,12 @@ class FederationExtension(wsgi.V3ExtensionRouter):
|
|||
POST /OS-FEDERATION/identity_providers/$identity_provider/
|
||||
protocols/$protocol/auth
|
||||
|
||||
|
||||
POST /auth/OS-FEDERATION/saml2
|
||||
|
||||
GET /OS-FEDERATION/saml2/metadata
|
||||
|
||||
|
||||
"""
|
||||
def _construct_url(self, suffix):
|
||||
return "/OS-FEDERATION/%s" % suffix
|
||||
|
@ -83,6 +87,7 @@ class FederationExtension(wsgi.V3ExtensionRouter):
|
|||
mapping_controller = controllers.MappingController()
|
||||
project_controller = controllers.ProjectV3()
|
||||
domain_controller = controllers.DomainV3()
|
||||
saml_metadata_controller = controllers.SAMLMetadataV3()
|
||||
|
||||
# Identity Provider CRUD operations
|
||||
|
||||
|
@ -176,3 +181,10 @@ class FederationExtension(wsgi.V3ExtensionRouter):
|
|||
path='/auth' + self._construct_url('saml2'),
|
||||
post_action='create_saml_assertion',
|
||||
rel=build_resource_relation(resource_name='saml2'))
|
||||
|
||||
# Keystone-Identity-Provider metadata endpoint
|
||||
self._add_resource(
|
||||
mapper, saml_metadata_controller,
|
||||
path=self._construct_url('saml2/metadata'),
|
||||
get_action='get_metadata',
|
||||
rel=build_resource_relation(resource_name='metadata'))
|
||||
|
|
|
@ -375,6 +375,10 @@ class MappedGroupNotFound(UnexpectedError):
|
|||
"%(mapping_id)s was not found in the backend.")
|
||||
|
||||
|
||||
class MetadataFileError(UnexpectedError):
|
||||
message_format = _("Error while reading metadata file, %(reason)s")
|
||||
|
||||
|
||||
class NotImplemented(Error):
|
||||
message_format = _("The action you have requested has not"
|
||||
" been implemented.")
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ns0:EntityDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns1="http://www.w3.org/2000/09/xmldsig#" entityID="k2k.com/v3/OS-FEDERATION/idp" validUntil="2014-08-19T21:24:17.411289Z">
|
||||
<ns0:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<ns0:KeyDescriptor use="signing">
|
||||
<ns1:KeyInfo>
|
||||
<ns1:X509Data>
|
||||
<ns1:X509Certificate>MIIDpTCCAo0CAREwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0xMzA3MDkxNjI1MDBaGA8yMDcyMDEwMTE2MjUwMFowgY8xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3JnMREwDwYDVQQDEwhLZXlzdG9uZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTC6IdNd9Cg1DshcrT5gRVRF36nEmjSA9QWdik7B925PK70U4F6j4pz/5JL7plIo/8rJ4jJz9ccE7m0iA+IuABtEhEwXkG9rj47Oy0J4ZyDGSh2K1Bl78PA9zxXSzysUTSjBKdAh29dPYbJY7cgZJ0uC3AtfVceYiAOIi14SdFeZ0LZLDXBuLaqUmSMrmKwJ9wAMOCb/jbBP9/3Ycd0GYjlvrSBU4Bqb8/NHasyO4DpPN68OAoyD5r5jUtV8QZN03UjIsoux8e0lrL6+MVtJo0OfWvlSrlzS5HKSryY+uqqQEuxtZKpJM2MV85ujvjc8eDSChh2shhDjBem3FIlHKUCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAed9fHgdJrk+gZcO5gsqq6uURfDOuYD66GsSdZw4BqHjYAcnyWq2da+iw7Uxkqu7iLf2k4+Hu3xjDFrce479OwZkSnbXmqB7XspTGOuM8MgT7jB/ypKTOZ6qaZKSWK1Hta995hMrVVlhUNBLh0MPGqoVWYA4d7mblujgH9vp+4mpCciJagHks8K5FBmI+pobB+uFdSYDoRzX9LTpStspK4e3IoY8baILuGcdKimRNBv6ItG4hMrntAe1/nWMJyUu5rDTGf2V/vAaS0S/faJBwQSz1o38QHMTWHNspfwIdX3yMqI9u7/vYlz3rLy5WdBdUgZrZ3/VLmJTiJVZu5Owq4Q==
|
||||
</ns1:X509Certificate>
|
||||
</ns1:X509Data>
|
||||
</ns1:KeyInfo>
|
||||
</ns0:KeyDescriptor>
|
||||
</ns0:IDPSSODescriptor>
|
||||
<ns0:Organization>
|
||||
<ns0:OrganizationName xml:lang="en">openstack</ns0:OrganizationName>
|
||||
<ns0:OrganizationDisplayName xml:lang="en">openstack</ns0:OrganizationDisplayName>
|
||||
<ns0:OrganizationURL xml:lang="en">openstack</ns0:OrganizationURL>
|
||||
</ns0:Organization>
|
||||
<ns0:ContactPerson contactType="technical">
|
||||
<ns0:Company>openstack</ns0:Company>
|
||||
<ns0:GivenName>first</ns0:GivenName>
|
||||
<ns0:SurName>lastname</ns0:SurName>
|
||||
<ns0:EmailAddress>admin@example.com</ns0:EmailAddress>
|
||||
<ns0:TelephoneNumber>555-555-5555</ns0:TelephoneNumber>
|
||||
</ns0:ContactPerson>
|
||||
</ns0:EntityDescriptor>
|
|
@ -1652,6 +1652,11 @@ def _is_xmlsec1_installed():
|
|||
return not bool(p.wait())
|
||||
|
||||
|
||||
def _load_xml(filename):
|
||||
with open(os.path.join(XMLDIR, filename), 'r') as xml:
|
||||
return xml.read()
|
||||
|
||||
|
||||
class SAMLGenerationTests(FederationTests):
|
||||
|
||||
ISSUER = 'https://acme.com/FIM/sps/openstack/saml20'
|
||||
|
@ -1664,11 +1669,7 @@ class SAMLGenerationTests(FederationTests):
|
|||
def setUp(self):
|
||||
super(SAMLGenerationTests, self).setUp()
|
||||
self.signed_assertion = saml2.create_class_from_xml_string(
|
||||
saml.Assertion, self._load_xml('signed_saml2_assertion.xml'))
|
||||
|
||||
def _load_xml(self, filename):
|
||||
with open(os.path.join(XMLDIR, filename), 'r') as xml:
|
||||
return xml.read()
|
||||
saml.Assertion, _load_xml('signed_saml2_assertion.xml'))
|
||||
|
||||
def test_samlize_token_values(self):
|
||||
"""Test the SAML generator produces a SAML object.
|
||||
|
@ -1901,6 +1902,8 @@ class SAMLGenerationTests(FederationTests):
|
|||
class IdPMetadataGenerationTests(FederationTests):
|
||||
"""A class for testing Identity Provider Metadata generation."""
|
||||
|
||||
METADATA_URL = '/OS-FEDERATION/saml2/metadata'
|
||||
|
||||
def setUp(self):
|
||||
super(IdPMetadataGenerationTests, self).setUp()
|
||||
self.generator = keystone_idp.MetadataGenerator()
|
||||
|
@ -2011,3 +2014,15 @@ class IdPMetadataGenerationTests(FederationTests):
|
|||
idp_entity_id=None)
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.generator.generate_metadata)
|
||||
|
||||
def test_get_metadata_with_no_metadata_file_configured(self):
|
||||
self.get(self.METADATA_URL, expected_status=500)
|
||||
|
||||
def test_get_metadata(self):
|
||||
CONF.federation.idp_metadata_path = XMLDIR + '/idp_saml2_metadata.xml'
|
||||
r = self.get(self.METADATA_URL, response_content_type='text/xml',
|
||||
expected_status=200)
|
||||
self.assertEqual('text/xml', r.headers.get('Content-Type'))
|
||||
|
||||
reference_file = _load_xml('idp_saml2_metadata.xml')
|
||||
self.assertEqual(reference_file, r.result)
|
||||
|
|
Loading…
Reference in New Issue