def from_config(cls, _db): """Initialize an AuthdataUtility from site configuration. :return: An AuthdataUtility if one is configured; otherwise None. :raise CannotLoadConfiguration: If an AuthdataUtility is incompletely configured. """ integration = Configuration.integration( Configuration.ADOBE_VENDOR_ID_INTEGRATION) if not integration: return None vendor_id = integration.get(Configuration.ADOBE_VENDOR_ID) library_uri = integration.get(cls.LIBRARY_URI_KEY) library = Library.instance(_db) library_short_name = library.library_registry_short_name secret = library.library_registry_shared_secret other_libraries = integration.get(cls.OTHER_LIBRARIES_KEY, {}) if (not vendor_id or not library_uri or not library_short_name or not secret): raise CannotLoadConfiguration( "Adobe Vendor ID configuration is incomplete. %s, %s, library.library_registry_short_name and library.library_registry_shared_secret must all be defined." % (cls.LIBRARY_URI_KEY, Configuration.ADOBE_VENDOR_ID)) if '|' in library_short_name: raise CannotLoadConfiguration( "Library short name cannot contain the pipe character.") return cls(vendor_id, library_uri, library_short_name, secret, other_libraries)
def library_for_request(self, library_short_name): """Look up the library the user is trying to access. Since this is called on pretty much every request, it's also an appropriate time to check whether the site configuration has been changed and needs to be updated. """ self.manager.reload_settings_if_changed() if library_short_name: library = Library.lookup(self._db, short_name=library_short_name) else: library = Library.default(self._db) if not library: return LIBRARY_NOT_FOUND flask.request.library = library return library
def do_run(self, _db=None, cmd_args=None, output=sys.stdout): _db = _db or self._db args = self.parse_command_line(self._db, cmd_args=cmd_args) default_library = Library.default(_db) adobe_integration = ExternalIntegration.lookup( _db, ExternalIntegration.ADOBE_VENDOR_ID, ExternalIntegration.DRM_GOAL, library=default_library ) if not adobe_integration: output.write( "Could not find an Adobe Vendor ID integration for default library %s.\n" % default_library.short_name ) return setting = adobe_integration.setting( AuthdataUtility.OTHER_LIBRARIES_KEY ) other_libraries = setting.json_value chosen_website = args.website_url if not chosen_website: for website in other_libraries.keys(): self.explain(output, other_libraries, website) return if (not args.short_name and not args.secret): self.explain(output, other_libraries, chosen_website) return if not args.short_name or not args.secret: output.write("To configure a library you must provide both --short_name and --secret.\n") return # All three arguments are specified. Set or modify the library's # SCT configuration. if chosen_website in other_libraries: what = "change" else: what = "set" output.write( "About to %s the Short Client Token configuration for %s.\n" % ( what, chosen_website ) ) if chosen_website in other_libraries: output.write("Old configuration:\n") short_name, secret = other_libraries[chosen_website] self.explain(output, other_libraries, chosen_website) other_libraries[chosen_website] = [args.short_name, args.secret] output.write("New configuration:\n") self.explain(output, other_libraries, chosen_website) setting.value = json.dumps(other_libraries) self._db.commit()
def look_up_library_for_role(self, role): """If the role is affiliated with a particular library, as opposed to being sitewide, find the library (and check that it actually exists).""" library = None library_short_name = role.get("library") if library_short_name: library = Library.lookup(self._db, library_short_name) if not library: return LIBRARY_NOT_FOUND.detailed(_("Library \"%(short_name)s\" does not exist.", short_name=library_short_name)) return library
def look_up_library_for_role(self, role): """If the role is affiliated with a particular library, as opposed to being sitewide, find the library (and check that it actually exists).""" library = None library_short_name = role.get("library") if library_short_name: library = Library.lookup(self._db, library_short_name) if not library: return LIBRARY_NOT_FOUND.detailed( _("Library \"%(short_name)s\" does not exist.", short_name=library_short_name)) return library
def _process_library_available_sort_options(self, library: Library) -> None: """Exclude `random` sort option from the library's available sort options. :param library: Library object """ enabled_facets = library.enabled_facets(Facets.ORDER_FACET_GROUP_NAME) self._logger.info( f"Library {library}'s available sort options: {enabled_facets}") if isinstance(enabled_facets, list) and Facets.ORDER_RANDOM in enabled_facets: library.enabled_facets_setting( Facets.ORDER_FACET_GROUP_NAME).value = json.dumps( list(set(enabled_facets) - {Facets.ORDER_RANDOM})) enabled_facets = library.enabled_facets(Facets.ORDER_FACET_GROUP_NAME) self._logger.info( f"Library {library}'s updated available sort options: {enabled_facets}" )
def temp_config(self): """Configure a basic Vendor ID Service setup.""" with temp_config() as config: library_uri = "http://a-library/" secret = "a-secret" vendor_id = "Some Vendor" short_name = "a library" config[Configuration.INTEGRATIONS][ Configuration.ADOBE_VENDOR_ID_INTEGRATION] = { Configuration.ADOBE_VENDOR_ID: vendor_id, AuthdataUtility.LIBRARY_URI_KEY: library_uri, } library = Library.instance(self._db) library.library_registry_short_name = short_name library.library_registry_shared_secret = secret yield config
def _process_library_default_sort_option(self, library: Library) -> None: """Check the library's default sort option and, if it's `random`, replace it. :param library: Library object """ default_facet_setting = library.default_facet_setting( Facets.ORDER_FACET_GROUP_NAME) self._logger.info( f"Library {library}'s default sort option: {default_facet_setting.value if default_facet_setting else None}" ) if default_facet_setting and default_facet_setting.value == Facets.ORDER_RANDOM: default_facet_setting.value = FacetConstants.DEFAULT_FACET.get( Facets.ORDER_FACET_GROUP_NAME) self._logger.info( f"Library {library}'s new default sort option: {default_facet_setting.value}" )
def items_that_need_coverage(self, identifiers=None, **kwargs): qu = super(BibblioCoverageProvider, self).items_that_need_coverage( identifiers=identifiers, **kwargs) data_sources = [DataSource.lookup(self._db, ds) for ds in self.INSTANT_CLASSICS_SOURCES] # Get any identifiers with uncovered editions in the targeted # CustomList and an associated Work to be recommended. edition_entry = aliased(CustomListEntry) edition_list = aliased(CustomList) qu = qu.join(Work.presentation_edition)\ .outerjoin(Work.custom_list_entries)\ .outerjoin(CustomListEntry.customlist)\ .outerjoin(edition_entry, Edition.custom_list_entries)\ .outerjoin(edition_list, edition_entry.customlist)\ .join(Work.license_pools)\ .filter( or_( CustomList.id==self.custom_list.id, edition_list.id==self.custom_list.id ) )\ .options(eagerload(Work.presentation_edition)).distinct() library = Library.default(self._db) qu = library.restrict_to_ready_deliverable_works(qu, Work) qu = qu.filter(LicensePool.superceded==False) if not self.fiction: # Only get nonfiction. This is the default setting. qu = qu.filter(Work.fiction==False) else: qu = qu.filter(or_(Work.fiction==True, Work.fiction==None)) if self.languages: # We only want a particular language. qu = qu.filter(Edition.language.in_(self.languages)) return qu
OPDSImportScript(importer_class, data_source_name, _db=_db) # Create a StandardEbooks Collection. OPDSImportScript(object(), DataSource.STANDARD_EBOOKS, _db=_db, collection_data=dict(url=u'https://standardebooks.org/opds/all')) # Create a Gutenberg Collection. gutenberg, is_new = Collection.by_name_and_protocol( _db, DataSource.GUTENBERG, ExternalIntegration.GUTENBERG ) if not gutenberg.data_source: gutenberg.external_integration.set_setting( Collection.DATA_SOURCE_NAME_SETTING, DataSource.GUTENBERG ) if is_new: library = Library.default(_db) gutenberg.libraries.append(library) logging.info('CREATED Collection for %s: %r' % ( DataSource.GUTENBERG, gutenberg)) _db.commit() # Alright, all the Collections have been created. Let's update the # LicensePools now. base_query = _db.query(LicensePool).filter(LicensePool.collection_id==None) single_collection_sources = [ DataSource.PLYMPTON, DataSource.ELIB, DataSource.UNGLUE_IT, DataSource.STANDARD_EBOOKS, DataSource.GUTENBERG ] for data_source_name in single_collection_sources:
library.collections.append(collection) collection.password = basic_token collection.external_account_id = library_id collection.url = url collection.set_setting("ebook_loan_length", ebook_loan_length) collection.set_setting("eaudio_loan_length", eaudio_loan_length) def convert_content_server(_db, library): config = Configuration.integration("Content Server") if not config: print u"No content server configuration, not creating a Collection for it." return url = config.get('url') collection, ignore = get_one_or_create(_db, Collection, protocol=Collection.OPDS_IMPORT, name="Open Access Content Server") library.collections.append(collection) collection.url = url library = Library.instance(_db) copy_library_registry_information(_db, library) convert_overdrive(_db, library) convert_bibliotheca(_db, library) convert_axis(_db, library) convert_one_click(_db, library) convert_content_server(_db, library) _db.commit()
def library(self): return Library.by_id(self._db, self.library_id)
def initialize_library(cls, _db): """Initialize the Library object with default data.""" library = Library.instance(_db) library.library_registry_short_name = cls.LIBRARY_REGISTRY_SHORT_NAME library.library_registry_shared_secret = cls.LIBRARY_REGISTRY_SHARED_SECRET
for provider in auth_conf.get('providers'): integration = make_patron_auth_integration(_db, provider) module = provider.get('module') if module == 'api.millenium_patron': convert_millenium(_db, integration, provider) elif module == 'api.firstbook': convert_firstbook(_db, integration, provider) elif module == 'api.clever': convert_clever(_db, integration, provider) elif module == 'api.sip': convert_sip(_db, integration, provider) else: log.warn( "I don't know how to convert a provider of type %s. Conversion is probably incomplete." % module) integrations.append(integration) # Add each integration to each library. library = Library.default(_db) for library in _db.query(Library): for integration in integrations: if integration not in library.integrations: library.integrations.append(integration) print "Sitewide bearer token signing secret: %s" % secret_setting.value for library in _db.query(Library): print "\n".join(library.explain(include_secrets=True)) finally: _db.commit() _db.close()
def test_loans_status(self): provider = MockAuthenticationProvider( patrons={"user": "******"}, test_username="******", test_password="******", ) library = Library.instance(self._db) auth = Authenticator(library, provider) class MockPatronActivity(object): def __init__(self, _db, data_source_name): self.source = DataSource.lookup(_db, data_source_name) self.succeed = True def patron_activity(self, patron, pin): if self.succeed: # Simulate a patron with nothing going on. return else: raise ValueError("Doomed to fail!") overdrive = MockPatronActivity(self._db, DataSource.OVERDRIVE) threem = MockPatronActivity(self._db, DataSource.BIBLIOTHECA) axis = MockPatronActivity(self._db, DataSource.AXIS_360) # Test a scenario where all providers succeed. status = ServiceStatus(self._db, auth, overdrive, threem, axis) response = status.loans_status(response=True) for value in response.values(): assert value.startswith('SUCCESS') # Simulate a failure in one of the providers. overdrive.succeed = False response = status.loans_status(response=True) eq_("FAILURE: Doomed to fail!", response['Overdrive patron account']) # Simulate failures on the ILS level. def test_with_broken_basic_auth_provider(value): class BrokenBasicAuthProvider(object): def testing_patron(self, _db): return value auth.basic_auth_provider = BrokenBasicAuthProvider() response = status.loans_status(response=True) eq_({'Patron authentication': 'Could not create patron with configured credentials.'}, response) # Test patron can't authenticate test_with_broken_basic_auth_provider( (None, "password that didn't work") ) # Auth provider is just totally broken. test_with_broken_basic_auth_provider(None) # If the auth process returns a problem detail, the problem # detail is used as the basis for the error message. class ExpiredPatronProvider(object): def testing_patron(self, _db): return EXPIRED_CREDENTIALS, None auth.basic_auth_provider = ExpiredPatronProvider() response = status.loans_status(response=True) eq_({'Patron authentication': EXPIRED_CREDENTIALS.response[0]}, response )