Exemple #1
0
    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)
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #5
0
 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}"
        )
Exemple #7
0
    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)
Exemple #13
0
 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
        )