def test_parse_does_not_raise_exception_when_xml_metadata_does_not_have_display_names( self): # Arrange metadata_parser = SAMLMetadataParser() # Act result = metadata_parser.parse( fixtures.CORRECT_ONE_IDP_METADATA_WITHOUT_DISPLAY_NAMES) # Assert assert isinstance(result, list) eq_(len(result), 1) [result] = result eq_( result, IdentityProviderMetadata( entity_id=fixtures.IDP_1_ENTITY_ID, ui_info=UIInfo(), organization=Organization(), name_id_format=fixtures.NAME_ID_FORMAT_1, sso_service=Service(fixtures.IDP_1_SSO_URL, fixtures.IDP_1_SSO_BINDING), want_authn_requests_signed=False, signing_certificates=[ strip_certificate(fixtures.SIGNING_CERTIFICATE) ], encryption_certificates=[ strip_certificate(fixtures.ENCRYPTION_CERTIFICATE) ]))
from nose.tools import eq_ from parameterized import parameterized from api.saml.configuration import SAMLConfiguration, SAMLOneLoginConfiguration, SAMLConfigurationStorage from api.saml.metadata import ServiceProviderMetadata, UIInfo, Service, NameIDFormat, IdentityProviderMetadata, \ Organization from api.saml.parser import SAMLMetadataParser from tests.saml import fixtures from tests.saml.fixtures import strip_certificate SERVICE_PROVIDER_WITHOUT_CERTIFICATE = ServiceProviderMetadata( fixtures.SP_ENTITY_ID, UIInfo(), Organization(), NameIDFormat.UNSPECIFIED.value, Service(fixtures.SP_ACS_URL, fixtures.SP_ACS_BINDING), ) SERVICE_PROVIDER_WITH_CERTIFICATE = ServiceProviderMetadata( fixtures.SP_ENTITY_ID, UIInfo(), Organization(), NameIDFormat.UNSPECIFIED.value, Service(fixtures.SP_ACS_URL, fixtures.SP_ACS_BINDING), certificate=fixtures.SIGNING_CERTIFICATE, private_key=fixtures.PRIVATE_KEY) IDENTITY_PROVIDERS = [ IdentityProviderMetadata( fixtures.IDP_1_ENTITY_ID, UIInfo(), Organization(), NameIDFormat.UNSPECIFIED.value,
def test_parse_correctly_parses_one_idp_metadata(self): # Arrange metadata_parser = SAMLMetadataParser() # Act result = metadata_parser.parse(fixtures.CORRECT_ONE_IDP_METADATA) # Assert assert isinstance(result, list) eq_(len(result), 1) [result] = result eq_( result, IdentityProviderMetadata( entity_id=fixtures.IDP_1_ENTITY_ID, ui_info=UIInfo([ LocalizableMetadataItem( fixtures.IDP_1_UI_INFO_EN_DISPLAY_NAME, 'en'), LocalizableMetadataItem( fixtures.IDP_1_UI_INFO_ES_DISPLAY_NAME, 'es') ], [ LocalizableMetadataItem(fixtures.IDP_1_UI_INFO_DESCRIPTION, 'en') ], [ LocalizableMetadataItem( fixtures.IDP_1_UI_INFO_INFORMATION_URL, 'en') ], [ LocalizableMetadataItem( fixtures.IDP_1_UI_INFO_PRIVACY_STATEMENT_URL, 'en') ], [LocalizableMetadataItem(fixtures.IDP_1_UI_INFO_LOGO_URL)]), organization=Organization( [ LocalizableMetadataItem( fixtures.IDP_1_ORGANIZATION_EN_ORGANIZATION_NAME, 'en'), LocalizableMetadataItem( fixtures.IDP_1_ORGANIZATION_ES_ORGANIZATION_NAME, 'es') ], [ LocalizableMetadataItem( fixtures. IDP_1_ORGANIZATION_EN_ORGANIZATION_DISPLAY_NAME, 'en'), LocalizableMetadataItem( fixtures. IDP_1_ORGANIZATION_ES_ORGANIZATION_DISPLAY_NAME, 'es') ], [ LocalizableMetadataItem( fixtures.IDP_1_ORGANIZATION_EN_ORGANIZATION_URL, 'en'), LocalizableMetadataItem( fixtures.IDP_1_ORGANIZATION_ES_ORGANIZATION_URL, 'es') ], ), name_id_format=fixtures.NAME_ID_FORMAT_1, sso_service=Service(fixtures.IDP_1_SSO_URL, fixtures.IDP_1_SSO_BINDING), want_authn_requests_signed=False, signing_certificates=[ strip_certificate(fixtures.SIGNING_CERTIFICATE) ], encryption_certificates=[ strip_certificate(fixtures.ENCRYPTION_CERTIFICATE) ]))
def test_parse_correctly_parses_one_sp_metadata(self): # Arrange metadata_parser = SAMLMetadataParser() # Act result = metadata_parser.parse(fixtures.CORRECT_ONE_SP_METADATA) # Assert assert isinstance(result, list) eq_(len(result), 1) [result] = result eq_( result, ServiceProviderMetadata( entity_id=fixtures.SP_ENTITY_ID, ui_info=UIInfo([ LocalizableMetadataItem( fixtures.SP_UI_INFO_EN_DISPLAY_NAME, 'en'), LocalizableMetadataItem( fixtures.SP_UI_INFO_ES_DISPLAY_NAME, 'es') ], [ LocalizableMetadataItem(fixtures.SP_UI_INFO_DESCRIPTION, 'en') ], [ LocalizableMetadataItem( fixtures.SP_UI_INFO_INFORMATION_URL, 'en') ], [ LocalizableMetadataItem( fixtures.SP_UI_INFO_PRIVACY_STATEMENT_URL, 'en') ], [LocalizableMetadataItem(fixtures.SP_UI_INFO_LOGO_URL)]), organization=Organization( [ LocalizableMetadataItem( fixtures.SP_ORGANIZATION_EN_ORGANIZATION_NAME, 'en'), LocalizableMetadataItem( fixtures.SP_ORGANIZATION_ES_ORGANIZATION_NAME, 'es') ], [ LocalizableMetadataItem( fixtures. SP_ORGANIZATION_EN_ORGANIZATION_DISPLAY_NAME, 'en'), LocalizableMetadataItem( fixtures. SP_ORGANIZATION_ES_ORGANIZATION_DISPLAY_NAME, 'es') ], [ LocalizableMetadataItem( fixtures.SP_ORGANIZATION_EN_ORGANIZATION_URL, 'en'), LocalizableMetadataItem( fixtures.SP_ORGANIZATION_ES_ORGANIZATION_URL, 'es') ], ), name_id_format=NameIDFormat.UNSPECIFIED.value, acs_service=Service(fixtures.SP_ACS_URL, fixtures.SP_ACS_BINDING), authn_requests_signed=False, want_assertions_signed=False, certificate=strip_certificate(fixtures.SIGNING_CERTIFICATE)))
def test_parse_correctly_parses_metadata_with_multiple_descriptors(self): # Arrange metadata_parser = SAMLMetadataParser() # Act result = metadata_parser.parse(fixtures.CORRECT_MULTIPLE_IDPS_METADATA) # Assert assert isinstance(result, list) assert len(result) == 2 eq_( result[0], IdentityProviderMetadata( entity_id=fixtures.IDP_1_ENTITY_ID, ui_info=UIInfo([ LocalizableMetadataItem( fixtures.IDP_1_UI_INFO_EN_DISPLAY_NAME, 'en'), LocalizableMetadataItem( fixtures.IDP_1_UI_INFO_ES_DISPLAY_NAME, 'es') ]), organization=Organization( [ LocalizableMetadataItem( fixtures.IDP_1_ORGANIZATION_EN_ORGANIZATION_NAME, 'en'), LocalizableMetadataItem( fixtures.IDP_1_ORGANIZATION_ES_ORGANIZATION_NAME, 'es') ], [ LocalizableMetadataItem( fixtures. IDP_1_ORGANIZATION_EN_ORGANIZATION_DISPLAY_NAME, 'en'), LocalizableMetadataItem( fixtures. IDP_1_ORGANIZATION_ES_ORGANIZATION_DISPLAY_NAME, 'es') ], [ LocalizableMetadataItem( fixtures.IDP_1_ORGANIZATION_EN_ORGANIZATION_URL, 'en'), LocalizableMetadataItem( fixtures.IDP_1_ORGANIZATION_ES_ORGANIZATION_URL, 'es') ], ), name_id_format=fixtures.NAME_ID_FORMAT_1, sso_service=Service(fixtures.IDP_1_SSO_URL, fixtures.IDP_1_SSO_BINDING), want_authn_requests_signed=False, signing_certificates=[ strip_certificate(fixtures.SIGNING_CERTIFICATE) ], encryption_certificates=[ strip_certificate(fixtures.ENCRYPTION_CERTIFICATE) ])) eq_( result[1], IdentityProviderMetadata( entity_id=fixtures.IDP_2_ENTITY_ID, ui_info=UIInfo([ LocalizableMetadataItem( fixtures.IDP_2_UI_INFO_EN_DISPLAY_NAME, 'en'), LocalizableMetadataItem( fixtures.IDP_2_UI_INFO_ES_DISPLAY_NAME, 'es') ]), organization=Organization( [ LocalizableMetadataItem( fixtures.IDP_2_ORGANIZATION_EN_ORGANIZATION_NAME, 'en'), LocalizableMetadataItem( fixtures.IDP_2_ORGANIZATION_ES_ORGANIZATION_NAME, 'es') ], [ LocalizableMetadataItem( fixtures. IDP_2_ORGANIZATION_EN_ORGANIZATION_DISPLAY_NAME, 'en'), LocalizableMetadataItem( fixtures. IDP_2_ORGANIZATION_ES_ORGANIZATION_DISPLAY_NAME, 'es') ], [ LocalizableMetadataItem( fixtures.IDP_2_ORGANIZATION_EN_ORGANIZATION_URL, 'en'), LocalizableMetadataItem( fixtures.IDP_2_ORGANIZATION_ES_ORGANIZATION_URL, 'es') ], ), name_id_format=fixtures.NAME_ID_FORMAT_1, sso_service=Service(fixtures.IDP_2_SSO_URL, fixtures.IDP_2_SSO_BINDING), want_authn_requests_signed=False, signing_certificates=[ strip_certificate(fixtures.SIGNING_CERTIFICATE) ], encryption_certificates=[ strip_certificate(fixtures.ENCRYPTION_CERTIFICATE) ]))
def _parse_sp_metadata( self, provider_node, entity_id, ui_info, organization, required_acs_binding=Binding.HTTP_POST): """Parses SPSSODescriptor node and translates it into a ServiceProvider object :param provider_node: SPSSODescriptor node containing SP metadata :param provider_node: defusedxml.lxml.RestrictedElement :param entity_id: String containing IdP's entityID :type entity_id: string :param ui_info: UIInfo object containing IdP's description :type ui_info: UIInfo :param organization: Organization object containing basic information about an organization responsible for a SAML entity or role :type organization: Organization :param required_acs_binding: Required binding for Assertion Consumer Service (HTTP-Redirect by default) :type required_acs_binding: Binding :return: ServiceProvider containing SP metadata :rtype: ServiceProvider :raise: MetadataParsingError """ authn_requests_signed = provider_node.get('AuthnRequestsSigned', False) want_assertions_signed = provider_node.get('WantAssertionsSigned', False) name_id_format = self._parse_name_id_format(provider_node) acs_service = None acs_service_nodes = OneLogin_Saml2_Utils.query( provider_node, "./md:AssertionConsumerService[@Binding='%s']" % required_acs_binding.value ) if len(acs_service_nodes) > 0: acs_service_node = self._select_default_or_first_indexed_element(acs_service_nodes) acs_url = acs_service_node.get('Location', None) acs_service = Service(acs_url, required_acs_binding) else: raise SAMLMetadataParsingError(_('Missing {0} AssertionConsumerService'.format(required_acs_binding.value))) certificate_nodes = OneLogin_Saml2_Utils.query( provider_node, './md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate') certificates = self._parse_certificates(certificate_nodes) if len(certificates) > 1: raise SAMLMetadataParsingError( _('There are more than 1 SP certificates'.format(required_acs_binding.value))) certificate = next(iter(certificates)) if certificates else None sp = ServiceProviderMetadata( entity_id, ui_info, organization, name_id_format, acs_service, authn_requests_signed, want_assertions_signed, certificate) return sp
def _parse_idp_metadata( self, provider_node, entity_id, ui_info, organization, required_sso_binding=Binding.HTTP_REDIRECT, required_slo_binding=Binding.HTTP_REDIRECT): """Parses IDPSSODescriptor node and translates it into an IdentityProviderMetadata object :param provider_node: IDPSSODescriptor node containing IdP metadata :param provider_node: defusedxml.lxml.RestrictedElement :param entity_id: String containing IdP's entityID :type entity_id: string :param ui_info: UIInfo object containing IdP's description :type ui_info: UIInfo :param organization: Organization object containing basic information about an organization responsible for a SAML entity or role :type organization: Organization :param required_sso_binding: Required binding for Single Sign-On profile (HTTP-Redirect by default) :type required_sso_binding: Binding :param required_slo_binding: Required binding for Single Sing-Out profile (HTTP-Redirect by default) :type required_slo_binding: Binding :return: IdentityProviderMetadata containing IdP metadata :rtype: IdentityProviderMetadata :raise: MetadataParsingError """ want_authn_requests_signed = provider_node.get('WantAuthnRequestsSigned', False) name_id_format = self._parse_name_id_format(provider_node) sso_service = None sso_nodes = OneLogin_Saml2_Utils.query( provider_node, "./md:SingleSignOnService[@Binding='%s']" % required_sso_binding.value ) if len(sso_nodes) > 0: sso_node = self._select_default_or_first_indexed_element(sso_nodes) sso_url = sso_node.get('Location', None) sso_service = Service(sso_url, required_sso_binding) else: raise SAMLMetadataParsingError( _('Missing {0} SingleSignOnService service declaration'.format(required_sso_binding.value))) slo_service = None slo_nodes = OneLogin_Saml2_Utils.query( provider_node, "./md:SingleLogoutService[@Binding='%s']" % required_slo_binding.value ) if len(slo_nodes) > 0: slo_node = self._select_default_or_first_indexed_element(slo_nodes) slo_url = slo_node.get('Location', None) slo_service = Service(slo_url, required_slo_binding) signing_certificate_nodes = OneLogin_Saml2_Utils.query( provider_node, './md:KeyDescriptor[not(contains(@use, "encryption"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate') signing_certificates = self._parse_certificates(signing_certificate_nodes) encryption_certificate_nodes = OneLogin_Saml2_Utils.query( provider_node, './md:KeyDescriptor[not(contains(@use, "signing"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate') encryption_certificates = self._parse_certificates(encryption_certificate_nodes) idp = IdentityProviderMetadata( entity_id, ui_info, organization, name_id_format, sso_service, slo_service, want_authn_requests_signed, signing_certificates, encryption_certificates) return idp