Example #1
0
    def test_get_service_provider_returns_correct_value(self):
        # Arrange
        service_provider_metadata = fixtures.CORRECT_XML_WITH_ONE_SP

        metadata_parser = SAMLMetadataParser()
        metadata_parser.parse = MagicMock(side_effect=metadata_parser.parse)

        configuration_storage = ConfigurationStorage(
            self._saml_integration_association)
        configuration_storage.load = MagicMock(
            side_effect=configuration_storage.load)

        saml_configuration_factory = SAMLConfigurationFactory(metadata_parser)

        with saml_configuration_factory.create(
                configuration_storage, self._db,
                SAMLConfiguration) as configuration:
            configuration.service_provider_xml_metadata = service_provider_metadata

            # Act
            service_provider = configuration.get_service_provider(self._db)

            # Assert
            assert True == isinstance(service_provider,
                                      SAMLServiceProviderMetadata)
            assert fixtures.SP_ENTITY_ID == service_provider.entity_id

            configuration_storage.load.assert_has_calls([
                call(self._db,
                     SAMLConfiguration.service_provider_xml_metadata.key),
                call(self._db,
                     SAMLConfiguration.service_provider_private_key.key),
            ])
            metadata_parser.parse.assert_called_once_with(
                service_provider_metadata)
Example #2
0
    def test_parse_does_not_raise_exception_when_xml_metadata_does_not_have_display_names(
        self, ):
        # Arrange
        metadata_parser = SAMLMetadataParser()

        # Act
        parsing_results = metadata_parser.parse(
            fixtures.CORRECT_XML_WITH_ONE_IDP_METADATA_WITHOUT_DISPLAY_NAMES)

        # Assert
        assert 1 == len(parsing_results)

        [parsing_result] = parsing_results
        assert True == isinstance(parsing_result, SAMLMetadataParsingResult)
        assert True == isinstance(parsing_result.provider,
                                  SAMLIdentityProviderMetadata)
        assert True == isinstance(parsing_result.xml_node,
                                  onelogin.saml2.xmlparser.RestrictedElement)
        assert (SAMLIdentityProviderMetadata(
            entity_id=fixtures.IDP_1_ENTITY_ID,
            ui_info=SAMLUIInfo(),
            organization=SAMLOrganization(),
            name_id_format=fixtures.NAME_ID_FORMAT_1,
            sso_service=SAMLService(fixtures.IDP_1_SSO_URL,
                                    fixtures.IDP_1_SSO_BINDING),
            want_authn_requests_signed=False,
            signing_certificates=[
                fixtures.strip_certificate(fixtures.SIGNING_CERTIFICATE)
            ],
            encryption_certificates=[
                fixtures.strip_certificate(fixtures.ENCRYPTION_CERTIFICATE)
            ],
        ) == parsing_result.provider)
Example #3
0
    def test_parse_raises_exception_when_xml_metadata_has_incorrect_format(
            self):
        # Arrange
        metadata_parser = SAMLMetadataParser()

        # Act
        with pytest.raises(SAMLMetadataParsingError):
            metadata_parser.parse(fixtures.INCORRECT_XML)
Example #4
0
    def test_parse_raises_exception_when_sp_metadata_does_not_contain_acs_service(
            self):
        # Arrange
        metadata_parser = SAMLMetadataParser()

        # Act
        with pytest.raises(SAMLMetadataParsingError):
            metadata_parser.parse(
                fixtures.INCORRECT_XML_WITH_ONE_SP_METADATA_WITHOUT_ACS_SERVICE
            )
Example #5
0
    def test_parse_raises_exception_when_idp_metadata_contains_sso_service_with_wrong_binding(
        self, ):
        # Arrange
        metadata_parser = SAMLMetadataParser()

        # Act
        with pytest.raises(SAMLMetadataParsingError):
            metadata_parser.parse(
                fixtures.
                INCORRECT_XML_WITH_ONE_IDP_METADATA_WITH_SSO_SERVICE_WITH_WRONG_BINDING
            )
Example #6
0
    def test_get_identity_providers_returns_non_federated_idps(self):
        # Arrange
        identity_providers_metadata = fixtures.CORRECT_XML_WITH_MULTIPLE_IDPS

        metadata_parser = SAMLMetadataParser()
        metadata_parser.parse = MagicMock(side_effect=metadata_parser.parse)

        configuration_storage = ConfigurationStorage(
            self._saml_integration_association)
        configuration_storage.load = MagicMock(
            side_effect=configuration_storage.load)

        saml_configuration_factory = SAMLConfigurationFactory(metadata_parser)

        with saml_configuration_factory.create(
                configuration_storage, self._db,
                SAMLConfiguration) as configuration:
            configuration.non_federated_identity_provider_xml_metadata = (
                identity_providers_metadata)

            # Act
            identity_providers = configuration.get_identity_providers(self._db)

            # Assert
            assert 2 == len(identity_providers)

            assert True == isinstance(identity_providers[0],
                                      SAMLIdentityProviderMetadata)
            assert fixtures.IDP_1_ENTITY_ID == identity_providers[0].entity_id

            assert True == isinstance(identity_providers[1],
                                      SAMLIdentityProviderMetadata)
            assert fixtures.IDP_2_ENTITY_ID == identity_providers[1].entity_id

            configuration_storage.load.assert_has_calls([
                call(
                    self._db,
                    SAMLConfiguration.
                    non_federated_identity_provider_xml_metadata.key,
                ),
                call(
                    self._db,
                    SAMLConfiguration.federated_identity_provider_entity_ids.
                    key,
                ),
            ])
            metadata_parser.parse.assert_called_once_with(
                identity_providers_metadata)
Example #7
0
    def __init__(self, library, integration, analytics=None):
        """Initializes a new instance of SAMLAuthenticationProvider class

        :param library: Patrons authenticated through this provider
            are associated with this Library. Don't store this object!
            It's associated with a scoped database session. Just pull
            normal Python objects out of it.
        :type library: Library

        :param integration: The ExternalIntegration that
            configures this AuthenticationProvider. Don't store this
            object! It's associated with a scoped database session. Just
            pull normal Python objects out of it.
        :type integration: ExternalIntegration
        """
        super(BaseSAMLAuthenticationProvider,
              self).__init__(library, integration, analytics)

        self._logger = logging.getLogger(__name__)
        self._configuration_storage = ConfigurationStorage(self)
        self._configuration_factory = SAMLConfigurationFactory(
            SAMLMetadataParser())
        self._authentication_manager_factory = SAMLAuthenticationManagerFactory(
        )

        db = Session.object_session(library)

        with self.get_configuration(db) as configuration:
            self._patron_id_use_name_id = ConfigurationMetadata.to_bool(
                configuration.patron_id_use_name_id)
            self._patron_id_attributes = configuration.patron_id_attributes
            self._patron_id_regular_expression = (
                configuration.patron_id_regular_expression)
Example #8
0
def validator_factory():
    metadata_parser = SAMLMetadataParser()
    parser = DSLParser()
    visitor = DSLEvaluationVisitor()
    evaluator = DSLEvaluator(parser, visitor)
    subject_filter = SAMLSubjectFilter(evaluator)

    return SAMLSettingsValidator(metadata_parser, subject_filter)
Example #9
0
    def test_validate(
        self,
        _,
        sp_xml_metadata,
        idp_xml_metadata,
        patron_id_regular_expression,
        expected_validation_result,
    ):
        """Ensure that SAMLSettingsValidator correctly validates the input data.

        :param sp_xml_metadata: SP SAML metadata
        :type sp_xml_metadata: str

        :param idp_xml_metadata: IdP SAML metadata
        :type idp_xml_metadata: str

        :param patron_id_regular_expression: Regular expression used to extract a unique patron ID from SAML attributes
        :type patron_id_regular_expression: str

        :param expected_validation_result: Expected result: ProblemDetail object if validation must fail, None otherwise
        :type expected_validation_result: Optional[ProblemDetail]
        """
        # Arrange
        submitted_form_data = MultiDict()

        if sp_xml_metadata is not None:
            submitted_form_data.add(
                SAMLConfiguration.service_provider_xml_metadata.key,
                sp_xml_metadata)
        if idp_xml_metadata is not None:
            submitted_form_data.add(
                SAMLConfiguration.non_federated_identity_provider_xml_metadata.
                key,
                idp_xml_metadata,
            )
        if patron_id_regular_expression is not None:
            submitted_form_data.add(
                SAMLConfiguration.patron_id_regular_expression.key,
                patron_id_regular_expression,
            )

        submitted_form = {"form": submitted_form_data}
        metadata_parser = SAMLMetadataParser()
        parser = DSLParser()
        visitor = DSLEvaluationVisitor()
        evaluator = DSLEvaluator(parser, visitor)
        subject_filter = SAMLSubjectFilter(evaluator)
        validator = SAMLSettingsValidator(metadata_parser, subject_filter)

        # Act
        settings = list(SAMLWebSSOAuthenticationProvider.SETTINGS)
        result = validator.validate(settings, submitted_form)

        # Assert
        if isinstance(result, ProblemDetail):
            assert expected_validation_result.response == result.response
        else:
            assert expected_validation_result == result
    def setup_method(self, _db=None, set_up_circulation_manager=True):
        super(TestSAMLWebSSOAuthenticationProvider, self).setup_method()

        metadata_parser = SAMLMetadataParser()

        self._external_integration_association = create_autospec(
            spec=HasExternalIntegration)
        self._external_integration_association.external_integration = MagicMock(
            return_value=self._integration)

        self._configuration_storage = ConfigurationStorage(
            self._external_integration_association)
        self._configuration_factory = SAMLConfigurationFactory(metadata_parser)
Example #11
0
    def test_load(self):
        # Arrange
        federation_type = incommon.FEDERATION_TYPE
        federation_idp_metadata_service_url = incommon.IDP_METADATA_SERVICE_URL
        xml_metadata = fixtures.CORRECT_XML_WITH_MULTIPLE_IDPS

        metadata_loader = create_autospec(spec=SAMLMetadataLoader)
        metadata_validator = create_autospec(
            spec=SAMLFederatedMetadataValidator)
        metadata_parser = SAMLMetadataParser()
        idp_loader = SAMLFederatedIdentityProviderLoader(
            metadata_loader, metadata_validator, metadata_parser)
        saml_federation = SAMLFederation(federation_type,
                                         federation_idp_metadata_service_url)

        metadata_loader.load_idp_metadata = MagicMock(
            return_value=xml_metadata)
        metadata_parser.parse = MagicMock(side_effect=metadata_parser.parse)

        # Act
        idps = idp_loader.load(saml_federation)

        # Assert
        assert 2 == len(idps)

        assert fixtures.IDP_1_ENTITY_ID == idps[0].entity_id
        assert fixtures.IDP_1_UI_INFO_EN_DISPLAY_NAME == idps[0].display_name
        assert saml_federation == idps[0].federation

        assert fixtures.IDP_2_ENTITY_ID == idps[1].entity_id
        assert fixtures.IDP_2_UI_INFO_EN_DISPLAY_NAME == idps[1].display_name
        assert saml_federation == idps[1].federation

        metadata_loader.load_idp_metadata.assert_called_once_with(
            federation_idp_metadata_service_url)
        metadata_parser.parse.assert_called_once_with(xml_metadata)
Example #12
0
    def __init__(self, circulation_manager, authenticator):
        """Initializes a new instance of SAMLController class

        :param circulation_manager: Circulation Manager
        :type circulation_manager: CirculationManager

        :param authenticator: Authenticator object used to route requests to the appropriate LibraryAuthenticator
        :type authenticator: Authenticator
        """
        self._circulation_manager = circulation_manager
        self._authenticator = authenticator

        self._logger = logging.getLogger(__name__)
        self._configuration_factory = SAMLConfigurationFactory(
            SAMLMetadataParser())
Example #13
0
    def test_parse_correctly_parses_one_idp_metadata(self):
        # Arrange
        metadata_parser = SAMLMetadataParser()

        # Act
        parsing_results = metadata_parser.parse(
            fixtures.CORRECT_XML_WITH_IDP_1)

        # Assert
        assert 1 == len(parsing_results)

        [parsing_result] = parsing_results
        assert True == isinstance(parsing_result, SAMLMetadataParsingResult)
        assert True == isinstance(parsing_result.provider,
                                  SAMLIdentityProviderMetadata)
        assert True == isinstance(parsing_result.xml_node,
                                  onelogin.saml2.xmlparser.RestrictedElement)
        assert (SAMLIdentityProviderMetadata(
            entity_id=fixtures.IDP_1_ENTITY_ID,
            ui_info=SAMLUIInfo(
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_UI_INFO_EN_DISPLAY_NAME, "en"),
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_UI_INFO_ES_DISPLAY_NAME, "es"),
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_UI_INFO_DESCRIPTION, "en")
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_UI_INFO_INFORMATION_URL, "en")
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_UI_INFO_PRIVACY_STATEMENT_URL, "en")
                ],
                [
                    SAMLLocalizedMetadataItem(fixtures.IDP_1_UI_INFO_LOGO_URL,
                                              "en")
                ],
            ),
            organization=SAMLOrganization(
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_ORGANIZATION_EN_ORGANIZATION_NAME,
                        "en"),
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_ORGANIZATION_ES_ORGANIZATION_NAME,
                        "es"),
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.
                        IDP_1_ORGANIZATION_EN_ORGANIZATION_DISPLAY_NAME,
                        "en",
                    ),
                    SAMLLocalizedMetadataItem(
                        fixtures.
                        IDP_1_ORGANIZATION_ES_ORGANIZATION_DISPLAY_NAME,
                        "es",
                    ),
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_ORGANIZATION_EN_ORGANIZATION_URL, "en"),
                    SAMLLocalizedMetadataItem(
                        fixtures.IDP_1_ORGANIZATION_ES_ORGANIZATION_URL, "es"),
                ],
            ),
            name_id_format=fixtures.NAME_ID_FORMAT_1,
            sso_service=SAMLService(fixtures.IDP_1_SSO_URL,
                                    fixtures.IDP_1_SSO_BINDING),
            want_authn_requests_signed=False,
            signing_certificates=[
                fixtures.strip_certificate(fixtures.SIGNING_CERTIFICATE)
            ],
            encryption_certificates=[
                fixtures.strip_certificate(fixtures.ENCRYPTION_CERTIFICATE)
            ],
        ) == parsing_result.provider)
Example #14
0
    def test_parse_correctly_parses_one_sp_metadata(self):
        # Arrange
        metadata_parser = SAMLMetadataParser()

        # Act
        parsing_results = metadata_parser.parse(
            fixtures.CORRECT_XML_WITH_ONE_SP)

        # Assert
        assert 1 == len(parsing_results)

        [parsing_result] = parsing_results
        assert True == isinstance(parsing_result, SAMLMetadataParsingResult)
        assert True == isinstance(parsing_result.provider,
                                  SAMLServiceProviderMetadata)
        assert True == isinstance(parsing_result.xml_node,
                                  onelogin.saml2.xmlparser.RestrictedElement)

        assert (SAMLServiceProviderMetadata(
            entity_id=fixtures.SP_ENTITY_ID,
            ui_info=SAMLUIInfo(
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_UI_INFO_EN_DISPLAY_NAME, "en"),
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_UI_INFO_ES_DISPLAY_NAME, "es"),
                ],
                [
                    SAMLLocalizedMetadataItem(fixtures.SP_UI_INFO_DESCRIPTION,
                                              "en")
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_UI_INFO_INFORMATION_URL, "en")
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_UI_INFO_PRIVACY_STATEMENT_URL, "en")
                ],
                [SAMLLocalizedMetadataItem(fixtures.SP_UI_INFO_LOGO_URL)],
            ),
            organization=SAMLOrganization(
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_ORGANIZATION_EN_ORGANIZATION_NAME, "en"),
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_ORGANIZATION_ES_ORGANIZATION_NAME, "es"),
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_ORGANIZATION_EN_ORGANIZATION_DISPLAY_NAME,
                        "en"),
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_ORGANIZATION_ES_ORGANIZATION_DISPLAY_NAME,
                        "es"),
                ],
                [
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_ORGANIZATION_EN_ORGANIZATION_URL, "en"),
                    SAMLLocalizedMetadataItem(
                        fixtures.SP_ORGANIZATION_ES_ORGANIZATION_URL, "es"),
                ],
            ),
            name_id_format=SAMLNameIDFormat.UNSPECIFIED.value,
            acs_service=SAMLService(fixtures.SP_ACS_URL,
                                    fixtures.SP_ACS_BINDING),
            authn_requests_signed=False,
            want_assertions_signed=False,
            certificate=fixtures.strip_certificate(
                fixtures.SIGNING_CERTIFICATE),
        ) == parsing_result.provider)
Example #15
0
    def test_get_identity_providers_returns_both_non_federated_and_federated_idps(
            self):
        # Arrange
        non_federated_identity_providers_metadata = (
            fixtures.CORRECT_XML_WITH_MULTIPLE_IDPS)

        federated_identity_provider_entity_ids = json.dumps(
            [fixtures.IDP_1_ENTITY_ID, fixtures.IDP_2_ENTITY_ID])

        metadata_parser = SAMLMetadataParser()
        metadata_parser.parse = MagicMock(side_effect=metadata_parser.parse)

        configuration_storage = ConfigurationStorage(
            self._saml_integration_association)
        configuration_storage.load = MagicMock(
            side_effect=configuration_storage.load)

        saml_configuration_factory = SAMLConfigurationFactory(metadata_parser)

        federation = SAMLFederation("Test federation", "http://localhost")
        federated_idp_1 = SAMLFederatedIdentityProvider(
            federation,
            fixtures.IDP_1_ENTITY_ID,
            fixtures.IDP_1_UI_INFO_EN_DISPLAY_NAME,
            fixtures.CORRECT_XML_WITH_IDP_1,
        )
        federated_idp_2 = SAMLFederatedIdentityProvider(
            federation,
            fixtures.IDP_2_ENTITY_ID,
            fixtures.IDP_2_UI_INFO_EN_DISPLAY_NAME,
            fixtures.CORRECT_XML_WITH_IDP_2,
        )

        self._db.add_all([federation, federated_idp_1, federated_idp_2])

        with saml_configuration_factory.create(
                configuration_storage, self._db,
                SAMLConfiguration) as configuration:
            configuration.non_federated_identity_provider_xml_metadata = (
                non_federated_identity_providers_metadata)
            configuration.federated_identity_provider_entity_ids = (
                federated_identity_provider_entity_ids)

            # Act
            identity_providers = configuration.get_identity_providers(self._db)

            # Assert
            assert 4 == len(identity_providers)
            assert True == isinstance(identity_providers[0],
                                      SAMLIdentityProviderMetadata)
            assert fixtures.IDP_1_ENTITY_ID == identity_providers[0].entity_id

            assert True == isinstance(identity_providers[1],
                                      SAMLIdentityProviderMetadata)
            assert fixtures.IDP_2_ENTITY_ID == identity_providers[1].entity_id

            assert True == isinstance(identity_providers[2],
                                      SAMLIdentityProviderMetadata)
            assert fixtures.IDP_1_ENTITY_ID == identity_providers[2].entity_id

            assert True == isinstance(identity_providers[3],
                                      SAMLIdentityProviderMetadata)
            assert fixtures.IDP_2_ENTITY_ID == identity_providers[3].entity_id

            configuration_storage.load.assert_has_calls([
                call(
                    self._db,
                    SAMLConfiguration.
                    non_federated_identity_provider_xml_metadata.key,
                ),
                call(
                    self._db,
                    SAMLConfiguration.federated_identity_provider_entity_ids.
                    key,
                ),
            ])
            metadata_parser.parse.assert_has_calls([
                call(non_federated_identity_providers_metadata),
                call(federated_idp_1.xml_metadata),
                call(federated_idp_2.xml_metadata),
            ])