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)
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)
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)
def test_import_audiobook(self, importer, mock_get, datasource, db): """Ensure that ODL2Importer2 correctly processes and imports a feed with an audiobook.""" license = self.get_data("license-audiobook.json") feed = self.get_data("feed-audiobook.json") mock_get.add(license) configuration_storage = ConfigurationStorage(importer) configuration_factory = ConfigurationFactory() with configuration_factory.create( configuration_storage, db, ODL2APIConfiguration ) as configuration: configuration.skipped_license_formats = json.dumps(["text/html"]) imported_editions, pools, works, failures = importer.import_from_feed(feed) # Make sure we imported one edition and it is an audiobook assert isinstance(imported_editions, list) assert 1 == len(imported_editions) [edition] = imported_editions assert isinstance(edition, Edition) assert edition.primary_identifier.identifier == "9780792766919" assert edition.primary_identifier.type == "ISBN" assert EditionConstants.AUDIO_MEDIUM == edition.medium # Make sure that license pools have correct configuration assert isinstance(pools, list) assert 1 == len(pools) [license_pool] = pools assert not license_pool.open_access assert 1 == license_pool.licenses_owned assert 1 == license_pool.licenses_available assert 2 == len(license_pool.delivery_mechanisms) lcp_delivery_mechanism = ( self._get_delivery_mechanism_by_drm_scheme_and_content_type( license_pool.delivery_mechanisms, MediaTypes.AUDIOBOOK_PACKAGE_LCP_MEDIA_TYPE, DeliveryMechanism.LCP_DRM, ) ) assert lcp_delivery_mechanism is not None feedbooks_delivery_mechanism = ( self._get_delivery_mechanism_by_drm_scheme_and_content_type( license_pool.delivery_mechanisms, MediaTypes.AUDIOBOOK_MANIFEST_MEDIA_TYPE, DeliveryMechanism.FEEDBOOKS_AUDIOBOOK_DRM, ) ) assert feedbooks_delivery_mechanism is not None
def setup_method(self, mock_search=True): super(TestLCPAPI, self).setup_method() self._lcp_collection = self._collection( protocol=ExternalIntegration.LCP) self._integration = self._lcp_collection.external_integration integration_association = create_autospec(spec=HasExternalIntegration) integration_association.external_integration = MagicMock( return_value=self._integration) self._configuration_storage = ConfigurationStorage( integration_association) self._configuration_factory = ConfigurationFactory()
def setup_method(self, mock_search=True): super(TestProQuestAPIClient, self).setup_method() self._proquest_collection = self._collection( protocol=ExternalIntegration.PROQUEST) self._integration = self._proquest_collection.external_integration integration_owner = create_autospec(spec=HasExternalIntegration) integration_owner.external_integration = MagicMock( return_value=self._integration) self._configuration_storage = ConfigurationStorage(integration_owner) self._configuration_factory = ConfigurationFactory() self._client = ProQuestAPIClient(self._configuration_storage, self._configuration_factory)
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)
def _create_lcp_server(self): """Creates a new instance of LCPServer :return: New instance of LCPServer :rtype: LCPServer """ configuration_storage = ConfigurationStorage(self) configuration_factory = ConfigurationFactory() hasher_factory = HasherFactory() credential_factory = LCPCredentialFactory() lcp_server = LCPServer(configuration_storage, configuration_factory, hasher_factory, credential_factory) return lcp_server
def create(self, integration_association): """Create a new instance of ProQuestAPIClientFactory. :param integration_association: Association with an external integration :type integration_association: core.model.configuration.HasExternalIntegration :return: New instance of ProQuestAPIClient :rtype: ProQuestAPIClient """ configuration_storage = ConfigurationStorage(integration_association) configuration_factory = ConfigurationFactory() client = ProQuestAPIClient(configuration_storage, configuration_factory) return client
def test_local_lcpencrypt( self, _, file_path, lcpencrypt_output, expected_result, expected_exception=None, create_file=True, ): # Arrange integration_owner = create_autospec(spec=HasExternalIntegration) integration_owner.external_integration = MagicMock( return_value=self._integration) configuration_storage = ConfigurationStorage(integration_owner) configuration_factory = ConfigurationFactory() encryptor = LCPEncryptor(configuration_storage, configuration_factory) identifier = Identifier(identifier=fixtures.BOOK_IDENTIFIER) with configuration_factory.create( configuration_storage, self._db, LCPEncryptionConfiguration) as configuration: configuration.lcpencrypt_location = ( LCPEncryptionConfiguration.DEFAULT_LCPENCRYPT_LOCATION) with Patcher() as patcher: patcher.fs.create_file( LCPEncryptionConfiguration.DEFAULT_LCPENCRYPT_LOCATION) if create_file: patcher.fs.create_file(file_path) with patch("subprocess.check_output" ) as subprocess_check_output_mock: subprocess_check_output_mock.return_value = lcpencrypt_output if expected_exception: with pytest.raises(expected_exception.__class__ ) as exception_metadata: encryptor.encrypt(self._db, file_path, identifier.identifier) # Assert assert exception_metadata.value == expected_exception else: # Assert result = encryptor.encrypt(self._db, file_path, identifier.identifier) assert result == expected_result
def setup(self, mock_search=True): super(TestLCPServer, self).setup() self._lcp_collection = self._collection( protocol=ExternalIntegration.LCP) self._integration = self._lcp_collection.external_integration integration_owner = create_autospec(spec=HasExternalIntegration) integration_owner.external_integration = MagicMock( return_value=self._integration) self._configuration_storage = ConfigurationStorage(integration_owner) self._configuration_factory = ConfigurationFactory() self._hasher_factory = HasherFactory() self._credential_factory = LCPCredentialFactory() self._lcp_server = LCPServer(self._configuration_storage, self._configuration_factory, self._hasher_factory, self._credential_factory)
def create(self, integration_association): """Creates a new instance of LCPServer :param integration_association: Association with an external integration :type integration_association: core.model.configuration.HasExternalIntegration :return: New instance of LCPServer :rtype: LCPServer """ configuration_storage = ConfigurationStorage(integration_association) configuration_factory = ConfigurationFactory() hasher_factory = HasherFactory() credential_factory = LCPCredentialFactory() lcp_server = LCPServer(configuration_storage, configuration_factory, hasher_factory, credential_factory) return lcp_server
def create(self, integration_owner): """ Creates a new instance of SAMLAuthenticationManager class :param integration_owner: External integration owner :type integration_owner: api.saml.configuration.ExternalIntegrationOwner :return: SAML authentication manager :rtype: SAMLAuthenticationManager """ configuration_storage = ConfigurationStorage(integration_owner) configuration = SAMLConfiguration(configuration_storage, SAMLMetadataParser()) onelogin_configuration = SAMLOneLoginConfiguration(configuration) subject_parser = SAMLSubjectParser() authentication_manager = SAMLAuthenticationManager(onelogin_configuration, subject_parser) return authentication_manager
def test_configuration_metadata_correctly_recognize_bool_values( self, _, value, expected_result): """Ensure that ConfigurationMetadata.to_bool correctly translates different values into boolean (True/False). :param _: Name of the test case :type _: str :param value: Configuration setting's value :type value: Any :param expected_result: Expected boolean result :type expected_result: bool """ # Arrange external_integration = self._external_integration("test") external_integration_association = create_autospec( spec=HasExternalIntegration) external_integration_association.external_integration = MagicMock( return_value=external_integration) configuration_storage = ConfigurationStorage( external_integration_association) configuration = ConfigurationWithBooleanProperty( configuration_storage, self._db) # We set a new value using ConfigurationMetadata.__set__ configuration.boolean_setting = value # Act # We read the existing value using ConfigurationMetadata.__get__ result = ConfigurationMetadata.to_bool(configuration.boolean_setting) # Assert assert expected_result == result
def __init__( self, db, collection, parser=None, data_source_name=None, identifier_mapping=None, http_get=None, metadata_client=None, content_modifier=None, map_from_collection=None, mirrors=None, ): """Initialize a new instance of ProQuestOPDS2Importer class. :param db: Database session :type db: sqlalchemy.orm.session.Session :param collection: Circulation Manager's collection. LicensePools created by this OPDS2Import class will be associated with the given Collection. If this is None, no LicensePools will be created -- only Editions. :type collection: Collection :param parser: Feed parser :type parser: RWPMManifestParser :param data_source_name: Name of the source of this OPDS feed. All Editions created by this import will be associated with this DataSource. If there is no DataSource with this name, one will be created. NOTE: If `collection` is provided, its .data_source will take precedence over any value provided here. This is only for use when you are importing OPDS metadata without any particular Collection in mind. :type data_source_name: str :param identifier_mapping: Dictionary used for mapping external identifiers into a set of internal ones :type identifier_mapping: Dict :param metadata_client: A SimplifiedOPDSLookup object that is used to fill in missing metadata :type metadata_client: SimplifiedOPDSLookup :param content_modifier: A function that may modify-in-place representations (such as images and EPUB documents) as they come in from the network. :type content_modifier: Callable :param map_from_collection: Identifier mapping :type map_from_collection: Dict :param mirrors: A dictionary of different MirrorUploader objects for different purposes :type mirrors: Dict[MirrorUploader] """ super(ProQuestOPDS2Importer, self).__init__( db, collection, parser if parser else RWPMManifestParser(OPDS2FeedParserFactory()), data_source_name, identifier_mapping, http_get, metadata_client, content_modifier, map_from_collection, mirrors, ) self._logger = logging.getLogger(__name__) self._configuration_storage = ConfigurationStorage(self) self._configuration_factory = ConfigurationFactory() factory = ProQuestAPIClientFactory() self._api_client = factory.create(self) self._credential_manager = ProQuestCredentialManager()
def test_import(self, importer, mock_get, datasource, db): """Ensure that ODL2Importer2 correctly processes and imports the ODL feed encoded using OPDS 2.x. NOTE: `freeze_time` decorator is required to treat the licenses in the ODL feed as non-expired. """ # Arrange moby_dick_license = LicenseInfoHelper( license=LicenseHelper( identifier="urn:uuid:f7847120-fc6f-11e3-8158-56847afe9799", concurrency=10, checkouts=30, expires="2016-04-25T12:25:21+02:00", ), left=30, available=10, ) mock_get.add(moby_dick_license) feed = self.get_data("feed.json") configuration_storage = ConfigurationStorage(importer) configuration_factory = ConfigurationFactory() with configuration_factory.create( configuration_storage, db, ODL2APIConfiguration ) as configuration: configuration.skipped_license_formats = json.dumps(["text/html"]) # Act imported_editions, pools, works, failures = importer.import_from_feed(feed) # Assert # 1. Make sure that there is a single edition only assert isinstance(imported_editions, list) assert 1 == len(imported_editions) [moby_dick_edition] = imported_editions assert isinstance(moby_dick_edition, Edition) assert moby_dick_edition.primary_identifier.identifier == "978-3-16-148410-0" assert moby_dick_edition.primary_identifier.type == "ISBN" assert u"Moby-Dick" == moby_dick_edition.title assert u"eng" == moby_dick_edition.language assert u"eng" == moby_dick_edition.language assert EditionConstants.BOOK_MEDIUM == moby_dick_edition.medium assert u"Herman Melville" == moby_dick_edition.author assert 1 == len(moby_dick_edition.author_contributors) [moby_dick_author] = moby_dick_edition.author_contributors assert isinstance(moby_dick_author, Contributor) assert u"Herman Melville" == moby_dick_author.display_name assert u"Melville, Herman" == moby_dick_author.sort_name assert 1 == len(moby_dick_author.contributions) [moby_dick_author_author_contribution] = moby_dick_author.contributions assert isinstance(moby_dick_author_author_contribution, Contribution) assert moby_dick_author == moby_dick_author_author_contribution.contributor assert moby_dick_edition == moby_dick_author_author_contribution.edition assert Contributor.AUTHOR_ROLE == moby_dick_author_author_contribution.role assert datasource == moby_dick_edition.data_source assert u"Test Publisher" == moby_dick_edition.publisher assert datetime.date(2015, 9, 29) == moby_dick_edition.published assert u"http://example.org/cover.jpg" == moby_dick_edition.cover_full_url assert ( u"http://example.org/cover-small.jpg" == moby_dick_edition.cover_thumbnail_url ) # 2. Make sure that license pools have correct configuration assert isinstance(pools, list) assert 1 == len(pools) [moby_dick_license_pool] = pools assert isinstance(moby_dick_license_pool, LicensePool) assert moby_dick_license_pool.identifier.identifier == "978-3-16-148410-0" assert moby_dick_license_pool.identifier.type == "ISBN" assert not moby_dick_license_pool.open_access assert 30 == moby_dick_license_pool.licenses_owned assert 10 == moby_dick_license_pool.licenses_available assert 2 == len(moby_dick_license_pool.delivery_mechanisms) moby_dick_epub_adobe_drm_delivery_mechanism = ( self._get_delivery_mechanism_by_drm_scheme_and_content_type( moby_dick_license_pool.delivery_mechanisms, MediaTypes.EPUB_MEDIA_TYPE, DeliveryMechanism.ADOBE_DRM, ) ) assert moby_dick_epub_adobe_drm_delivery_mechanism is not None moby_dick_epub_lcp_drm_delivery_mechanism = ( self._get_delivery_mechanism_by_drm_scheme_and_content_type( moby_dick_license_pool.delivery_mechanisms, MediaTypes.EPUB_MEDIA_TYPE, DeliveryMechanism.LCP_DRM, ) ) assert moby_dick_epub_lcp_drm_delivery_mechanism is not None assert 1 == len(moby_dick_license_pool.licenses) [moby_dick_license] = moby_dick_license_pool.licenses assert ( "urn:uuid:f7847120-fc6f-11e3-8158-56847afe9799" == moby_dick_license.identifier ) assert ( "http://www.example.com/get{?id,checkout_id,expires,patron_id,passphrase,hint,hint_url,notification_url}" == moby_dick_license.checkout_url ) assert "http://www.example.com/status/294024" == moby_dick_license.status_url assert ( datetime.datetime(2016, 4, 25, 10, 25, 21, tzinfo=datetime.timezone.utc) == moby_dick_license.expires ) assert 30 == moby_dick_license.checkouts_left assert 10 == moby_dick_license.checkouts_available # 3. Make sure that work objects contain all the required metadata assert isinstance(works, list) assert 1 == len(works) [moby_dick_work] = works assert isinstance(moby_dick_work, Work) assert moby_dick_edition == moby_dick_work.presentation_edition assert 1 == len(moby_dick_work.license_pools) assert moby_dick_license_pool == moby_dick_work.license_pools[0] # 4. Make sure that the failure is covered assert 1 == len(failures) huck_finn_failures = failures["9781234567897"] assert 1 == len(huck_finn_failures) [huck_finn_failure] = huck_finn_failures assert isinstance(huck_finn_failure, CoverageFailure) assert "9781234567897" == huck_finn_failure.obj.identifier huck_finn_semantic_error = ( ODL_PUBLICATION_MUST_CONTAIN_EITHER_LICENSES_OR_OA_ACQUISITION_LINK_ERROR( node=ODLPublication( metadata=PresentationMetadata(identifier="urn:isbn:9781234567897") ), node_property=None, ) ) assert str(huck_finn_semantic_error) == huck_finn_failure.exception
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), ])