예제 #1
0
    def test_estimate_language_collection_for_library(self):

        library = self._default_library

        # We thought we'd have big collections.
        old_settings = {
            Configuration.LARGE_COLLECTION_LANGUAGES : ["spa", "fre"],
            Configuration.SMALL_COLLECTION_LANGUAGES : ["chi"],
            Configuration.TINY_COLLECTION_LANGUAGES : ["rus"],
        }

        for key, value in old_settings.items():
            ConfigurationSetting.for_library(
                key, library).value = json.dumps(value)

        # But there's nothing in our database, so when we call
        # Configuration.estimate_language_collections_for_library...
        Configuration.estimate_language_collections_for_library(library)

        # ...it gets reset to the default.
        eq_(["eng"], ConfigurationSetting.for_library(
            Configuration.LARGE_COLLECTION_LANGUAGES, library
        ).json_value)

        eq_([], ConfigurationSetting.for_library(
            Configuration.SMALL_COLLECTION_LANGUAGES, library
        ).json_value)

        eq_([], ConfigurationSetting.for_library(
            Configuration.TINY_COLLECTION_LANGUAGES, library
        ).json_value)
예제 #2
0
    def test_key_pair(self):
        # Test the ability to create, replace, or look up a
        # public/private key pair in a ConfigurationSetting.
        setting = ConfigurationSetting.sitewide(
            self._db, Configuration.KEY_PAIR
        )
        setting.value = "nonsense"

        # If you pass in a ConfigurationSetting that is missing its
        # value, or whose value is not a public key pair, a new key
        # pair is created.
        public_key, private_key = Configuration.key_pair(setting)
        assert 'BEGIN PUBLIC KEY' in public_key
        assert 'BEGIN RSA PRIVATE KEY' in private_key
        eq_([public_key, private_key], setting.json_value)

        setting.value = None
        public_key, private_key = Configuration.key_pair(setting)
        assert 'BEGIN PUBLIC KEY' in public_key
        assert 'BEGIN RSA PRIVATE KEY' in private_key
        eq_([public_key, private_key], setting.json_value)

        # If the setting has a good value already, the key pair is
        # returned as is.
        new_public, new_private = Configuration.key_pair(setting)
        eq_(new_public, public_key)
        eq_(new_private, private_key)
예제 #3
0
    def __init__(self):

        providers = []
        if Configuration.integration('3M'):
            providers.append(BibliothecaBibliographicCoverageProvider)
        if Configuration.integration('Overdrive'):
            providers.append(OverdriveBibliographicCoverageProvider)
        if Configuration.integration('Axis 360'):
            providers.append(Axis360BibliographicCoverageProvider)

        if not providers:
            raise Exception("No licensed book sources configured, nothing to get coverage from!")
        super(BibliographicCoverageProvidersScript, self).__init__(providers)
예제 #4
0
파일: scripts.py 프로젝트: dguo/circulation
    def __init__(self):

        providers = []
        if Configuration.integration('3M'):
            providers.append(ThreeMBibliographicCoverageProvider)
        if Configuration.integration('Overdrive'):
            providers.append(OverdriveBibliographicCoverageProvider)
        if Configuration.integration('Axis 360'):
            providers.append(Axis360BibliographicCoverageProvider)

        if not providers:
            raise Exception("No licensed book sources configured, nothing to get coverage from!")
        super(BibliographicCoverageProvidersScript, self).__init__(providers)
예제 #5
0
    def filter_facets(self, values, group_name):
        allowable = Configuration.enabled_facets(group_name)
        default = Configuration.default_facet(group_name)
        if not values:
            return [default]

        filtered = []
        for v in values:
            if v in allowable:
                filtered.append(v)
            else:
                self.log.warn('Ignoring unrecognized value "%s"', v)
        return filtered
예제 #6
0
파일: env.py 프로젝트: fjros/ebury
def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    from api.config import Configuration

    api_config = Configuration()
    alembic_config = config.get_section(config.config_ini_section)
    alembic_config['sqlalchemy.url'] = api_config.database_url

    connectable = engine_from_config(
        alembic_config,
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()
def copy_library_registry_information(_db, library):
    config = Configuration.integration("Adobe Vendor ID")
    if not config:
        print u"No Adobe Vendor ID configuration, not setting short name or secret."
    library.short_name = config.get("library_short_name")
    library.library_registry_short_name = config.get("library_short_name")
    library.library_registry_shared_secret = config.get("authdata_secret")
예제 #8
0
파일: scripts.py 프로젝트: dguo/circulation
 def __init__(self, metadata_web_app_url=None):
     self.metadata_url = (
         metadata_web_app_url or Configuration.integration_url(
             Configuration.METADATA_WRANGLER_INTEGRATION
         )
     )
     self.lookup = SimplifiedOPDSLookup(self.metadata_url)
예제 #9
0
def admin_view(collection=None, book=None, **kwargs):
    setting_up = (app.manager.admin_sign_in_controller.auth == None)
    if not setting_up:
        admin = app.manager.admin_sign_in_controller.authenticated_admin_from_request()
        csrf_token = app.manager.admin_sign_in_controller.get_csrf_token()
        if isinstance(admin, ProblemDetail) or csrf_token is None or isinstance(csrf_token, ProblemDetail):
            redirect_url = flask.request.url
            if (collection):
                quoted_collection = urllib.quote(collection)
                redirect_url = redirect_url.replace(
                    quoted_collection,
                    quoted_collection.replace("/", "%2F"))
            if (book):
                quoted_book = urllib.quote(book)
                redirect_url = redirect_url.replace(
                    quoted_book,
                    quoted_book.replace("/", "%2F"))
            return redirect(app.manager.url_for('admin_sign_in', redirect=redirect_url))
    else:
        csrf_token = None
    show_circ_events_download = (
        "core.local_analytics_provider" in (Configuration.policy("analytics") or [])
    )
    return flask.render_template_string(
        admin_template,
        csrf_token=csrf_token,
        home_url=app.manager.url_for('acquisition_groups'),
        show_circ_events_download=show_circ_events_download,
        setting_up=setting_up,
    )
예제 #10
0
    def assert_borrowing_privileges(cls, patron):
        """Raise an exception unless the patron currently has borrowing
        privileges.

        :raise AuthorizationExpired: If the patron's authorization has 
        expired.

        :raise OutstandingFines: If the patron has too many outstanding
        fines.
        """
        now = datetime.datetime.utcnow()
        if not cls.authorization_is_active(patron):
            # The patron's card has expired.
            raise AuthorizationExpired()

        if patron.fines:
            max_fines = Configuration.max_outstanding_fines(patron.library)
            if patron.fines >= max_fines.amount:
                raise OutstandingFines()

        from api.authenticator import PatronData
        if patron.block_reason is not None:
            if patron.block_reason is PatronData.EXCESSIVE_FINES:
                # The authentication mechanism itself may know that
                # the patron has outstanding fines, even if the circulation
                # manager is not configured to make that deduction.
                raise OutstandingFines()
            raise AuthorizationBlocked()
def copy_library_registry_information(_db, library):
    config = Configuration.integration("Adobe Vendor ID")
    if not config:
        print u"No Adobe Vendor ID configuration, not setting short name or secret."
        return
    library.short_name = config.get("library_short_name")
    library.library_registry_short_name = config.get("library_short_name")
    library.library_registry_shared_secret = config.get("authdata_secret")
예제 #12
0
def setup_admin_controllers(manager):
    """Set up all the controllers that will be used by the admin parts of the web app."""
    if not manager.testing:
        try:
            manager.config = Configuration.load()
        except CannotLoadConfiguration, e:
            self.log.error("Could not load configuration file: %s" % e)
            sys.exit()
    def do_run(self):
        integration = Configuration.integration(
            Configuration.ELASTICSEARCH_INTEGRATION, )
        old_index = integration.get(Configuration.ELASTICSEARCH_INDEX_KEY, )
        new_index = old_index + "-v2"
        alias = old_index + "-current"

        search_index_client = ExternalSearchIndex(works_index=new_index)
        search_index_client.indices.put_alias(index=new_index, name=alias)
예제 #14
0
파일: scripts.py 프로젝트: dguo/circulation
 def __init__(self):
     os.environ['AUTOINITIALIZE'] = "False"
     from api.app import app
     del os.environ['AUTOINITIALIZE']
     app.manager = CirculationManager(self._db)
     self.app = app
     self.base_url = Configuration.integration_url(
         Configuration.CIRCULATION_MANAGER_INTEGRATION, required=True
     )
예제 #15
0
파일: backend.py 프로젝트: fjros/ebury
class RateBackendBinding:
    """Return concrete implementation of the `RateBackend` abstract class
    """

    config = Configuration()

    @classmethod
    def get(cls) -> RateBackend:
        return FixerRateBackend(cls.config.fixer_access_key)
예제 #16
0
 def __init__(self, _db=None, testing=False):
     super(LaneSweeperScript, self).__init__(_db)
     os.environ['AUTOINITIALIZE'] = "False"
     from api.app import app
     del os.environ['AUTOINITIALIZE']
     app.manager = CirculationManager(self._db, testing=testing)
     self.app = app
     self.base_url = Configuration.integration_url(
         Configuration.CIRCULATION_MANAGER_INTEGRATION, required=True)
예제 #17
0
    def test_cipher(self):
        """Test the cipher() helper method."""

        # Generate a public/private key pair.
        key = RSA.generate(2048)
        cipher = PKCS1_OAEP.new(key)
        public = key.publickey().exportKey()
        private = key.exportKey()

        # Pass the public key into cipher() to get something that can
        # encrypt.
        encryptor = Configuration.cipher(public)
        encrypted = encryptor.encrypt("some text")

        # Pass the private key into cipher() to get something that can
        # decrypt.
        decryptor = Configuration.cipher(private)
        decrypted = decryptor.decrypt(encrypted)
        eq_("some text", decrypted)
예제 #18
0
파일: scripts.py 프로젝트: dguo/circulation
    def do_generate(self, lane):
        feeds = []
        annotator = self.app.manager.annotator(lane)
        if isinstance(lane, Lane):
            languages = lane.language_key
            lane_name = None
        else:
            languages = None
            lane_name = None

        url = self.app.manager.cdn_url_for(
            "feed", languages=lane.languages, lane_name=lane_name
        )

        order_facets = Configuration.enabled_facets(
            Facets.ORDER_FACET_GROUP_NAME
        )
        availability = Configuration.default_facet(
            Facets.AVAILABILITY_FACET_GROUP_NAME
        )
        collection = Configuration.default_facet(
            Facets.COLLECTION_FACET_GROUP_NAME
        )        

        for sort_order in order_facets:
            pagination = Pagination.default()
            facets = Facets(
                collection=collection, availability=availability,
                order=sort_order, order_ascending=True
            )
            title = lane.display_name
            for pagenum in (0, 2):
                feeds.append(
                    AcquisitionFeed.page(
                        self._db, title, url, lane, annotator, 
                        facets=facets, pagination=pagination,
                        force_refresh=True
                    )
                )
                pagination = pagination.next_page
        return feeds
예제 #19
0
파일: scripts.py 프로젝트: dguo/circulation
    def run(self):
        languages = Configuration.localization_languages()
        for language in languages:
            base_path = "translations/%s/LC_MESSAGES" % language
            if not os.path.exists(base_path):
                logging.warn("No translations for configured language %s" % language)
                continue

            os.system("rm %(path)s/messages.po" % dict(path=base_path))
            os.system("cat %(path)s/*.po > %(path)s/messages.po" % dict(path=base_path))
        
        os.system("pybabel compile -f -d translations")
예제 #20
0
    def run(self):
        languages = Configuration.localization_languages()
        for language in languages:
            base_path = "translations/%s/LC_MESSAGES" % language
            if not os.path.exists(base_path):
                logging.warn("No translations for configured language %s" % language)
                continue

            os.system("rm %(path)s/messages.po" % dict(path=base_path))
            os.system("cat %(path)s/*.po > %(path)s/messages.po" % dict(path=base_path))
        
        os.system("pybabel compile -f -d translations")
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
예제 #22
0
    def do_generate(self, lane):
        feeds = []
        annotator = self.app.manager.annotator(lane)
        if isinstance(lane, Lane) and lane.parent:
            languages = lane.language_key
            lane_name = lane.name
        else:
            languages = None
            lane_name = None

        url = self.app.manager.cdn_url_for("feed",
                                           languages=lane.languages,
                                           lane_name=lane_name)

        order_facets = Configuration.enabled_facets(
            Facets.ORDER_FACET_GROUP_NAME)
        availability = Configuration.default_facet(
            Facets.AVAILABILITY_FACET_GROUP_NAME)
        collection = Configuration.default_facet(
            Facets.COLLECTION_FACET_GROUP_NAME)

        for sort_order in self.orders:
            for availability in self.availabilities:
                for collection in self.collections:
                    pagination = Pagination.default()
                    facets = Facets(collection=collection,
                                    availability=availability,
                                    order=sort_order,
                                    order_ascending=True)
                    title = lane.display_name
                    for pagenum in range(0, self.pages):
                        yield AcquisitionFeed.page(self._db,
                                                   title,
                                                   url,
                                                   lane,
                                                   annotator,
                                                   facets=facets,
                                                   pagination=pagination,
                                                   force_refresh=True)
                        pagination = pagination.next_page
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"
    )
    collection.external_integration.setting("data_source").value = DataSource.OA_CONTENT_SERVER
    library.collections.append(collection)
예제 #24
0
def get_app() -> falcon.API:
    """WSGI entry point
    """

    session_manager = SessionManagerMiddleware()
    api = falcon.API(middleware=[session_manager])

    config = Configuration()
    api.add_route('{}/currencies'.format(config.base_uri), CurrenciesController())
    api.add_route('{}/rates'.format(config.base_uri), RatesController())
    api.add_route('{}/trades'.format(config.base_uri), TradesController())

    return api
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")
    collection.external_integration.setting(
        "data_source").value = DataSource.OA_CONTENT_SERVER
    library.collections.append(collection)
예제 #26
0
    def run(self):
        inp = self.open()
        tag_fields = {
            'tags': Subject.NYPL_APPEAL,
        }

        integ = Configuration.integration(
            Configuration.STAFF_PICKS_INTEGRATION)
        fields = integ.get(Configuration.LIST_FIELDS, {})

        importer = CustomListFromCSV(DataSource.LIBRARY_STAFF,
                                     CustomList.STAFF_PICKS_NAME, **fields)
        reader = csv.DictReader(inp, dialect='excel-tab')
        importer.to_customlist(self._db, reader)
        self._db.commit()
    def do_run(self):
        integration = Configuration.integration(
            Configuration.ELASTICSEARCH_INTEGRATION,
        )
        old_index = integration.get(
            Configuration.ELASTICSEARCH_INDEX_KEY,
        )
        new_index = old_index + "-v2"
        alias = old_index + "-current"

        search_index_client = ExternalSearchIndex(works_index=new_index)
        search_index_client.indices.put_alias(
            index=new_index,
            name=alias
        )
예제 #28
0
파일: scripts.py 프로젝트: dguo/circulation
    def run(self):
        inp = self.open()
        tag_fields = {
            'tags': Subject.NYPL_APPEAL,
        }

        integ = Configuration.integration(Configuration.STAFF_PICKS_INTEGRATION)
        fields = integ.get(Configuration.LIST_FIELDS, {})

        importer = CustomListFromCSV(
            DataSource.LIBRARY_STAFF, CustomList.STAFF_PICKS_NAME,
            **fields
        )
        reader = csv.DictReader(inp)
        importer.to_customlist(self._db, reader)
        self._db.commit()
def convert_bibliotheca(_db, library):
    config = Configuration.integration('3M')
    if not config:
        print u"No Bibliotheca configuration, not creating a Collection for it."
        return
    print u"Creating Collection object for Bibliotheca collection."
    username = config.get('account_id')
    password = config.get('account_key')
    library_id = config.get('library_id')
    collection, ignore = get_one_or_create(_db,
                                           Collection,
                                           protocol=Collection.BIBLIOTHECA,
                                           name="Bibliotheca")
    library.collections.append(collection)
    collection.external_integration.username = username
    collection.external_integration.password = password
    collection.external_account_id = library_id
예제 #30
0
    def has_excess_fines(cls, patron):
        """Does this patron have fines in excess of the maximum fine amount set for their library?

        :param a Patron:
        :return: A boolean
        """
        if not patron.fines:
            return False

        if isinstance(patron.fines, Money):
            patron_fines = patron.fines
        else:
            patron_fines = MoneyUtility.parse(patron.fines)
        actual_fines = patron_fines.amount
        max_fines = Configuration.max_outstanding_fines(patron.library)
        if max_fines is not None and actual_fines > max_fines.amount:
            return True
        return False
def convert_bibliotheca(_db, library):
    config = Configuration.integration('3M')
    if not config:
        print u"No Bibliotheca configuration, not creating a Collection for it."
        return
    print u"Creating Collection object for Bibliotheca collection."
    username = config.get('account_id')
    password = config.get('account_key')
    library_id = config.get('library_id')
    collection, ignore = get_one_or_create(
        _db, Collection,
        protocol=Collection.BIBLIOTHECA,
        name="Bibliotheca"
    )
    library.collections.append(collection)
    collection.external_integration.username = username
    collection.external_integration.password = password
    collection.external_account_id = library_id
def convert_overdrive(_db, library):
    config = Configuration.integration('Overdrive')
    if not config:
        print u"No Overdrive configuration, not creating a Collection for it."
    print u"Creating Collection object for Overdrive collection."
    username = config.get('client_key')
    password = config.get('client_secret')
    library_id = config.get('library_id')
    website_id = config.get('website_id')

    collection, ignore = get_one_or_create(_db,
                                           Collection,
                                           protocol=Collection.OVERDRIVE,
                                           name="Overdrive")
    library.collections.append(collection)
    collection.username = username
    collection.password = password
    collection.external_account_id = library_id
    collection.set_setting("website_id", website_id)
예제 #33
0
    def _create_registration_payload(self, url_for, stage):
        """Collect the key-value pairs to be sent when kicking off the
        registration protocol.

        :param url_for: An implementation of Flask url_for.
        :param state: The registrant's opinion about what stage this
           registration should be in.
        :return: A dictionary suitable for passing into requests.post.
        """
        auth_document_url = url_for("authentication_document",
                                    library_short_name=self.library.short_name)
        payload = dict(url=auth_document_url, stage=stage)

        # Find the email address the administrator should use if they notice
        # a problem with the way the library is using an integration.
        contact = Configuration.configuration_contact_uri(self.library)
        if contact:
            payload['contact'] = contact
        return payload
예제 #34
0
    def open(self):
        if len(sys.argv) > 1:
            return open(sys.argv[1])

        url = Configuration.integration_url(
            Configuration.STAFF_PICKS_INTEGRATION, True
        )
        if not url.startswith('https://') or url.startswith('http://'):
            url = self.DEFAULT_URL_TEMPLATE % url
        self.log.info("Retrieving %s", url)
        representation, cached = Representation.get(
            self._db, url, do_get=Representation.browser_http_get,
            accept="text/csv", max_age=timedelta(days=1))
        if representation.status_code != 200:
            raise ValueError("Unexpected status code %s" % 
                             representation.status_code)
        if not representation.media_type.startswith("text/csv"):
            raise ValueError("Unexpected media type %s" % 
                             representation.media_type)
        return StringIO(representation.content)
예제 #35
0
파일: scripts.py 프로젝트: dguo/circulation
    def open(self):
        if len(sys.argv) > 1:
            return open(sys.argv[1])

        url = Configuration.integration_url(
            Configuration.STAFF_PICKS_INTEGRATION, True
        )
        if not url.startswith('https://') or url.startswith('http://'):
            url = self.DEFAULT_URL_TEMPLATE % url
        self.log.info("Retrieving %s", url)
        representation, cached = Representation.get(
            self._db, url, do_get=Representation.browser_http_get,
            accept="text/csv", max_age=timedelta(days=1))
        if representation.status_code != 200:
            raise ValueError("Unexpected status code %s" % 
                             representation.status_code)
        if not representation.media_type.startswith("text/csv"):
            raise ValueError("Unexpected media type %s" % 
                             representation.media_type)
        return StringIO(representation.content)
def convert_overdrive(_db, library):
    config = Configuration.integration('Overdrive')
    if not config:
        print u"No Overdrive configuration, not creating a Collection for it."
        return
    print u"Creating Collection object for Overdrive collection."
    username = config.get('client_key')
    password = config.get('client_secret')
    library_id = config.get('library_id')
    website_id = config.get('website_id')

    collection, ignore = get_one_or_create(
        _db, Collection,
        protocol=Collection.OVERDRIVE,
        name="Overdrive"
    )
    library.collections.append(collection)
    collection.external_integration.username = username
    collection.external_integration.password = password
    collection.external_account_id = library_id
    collection.external_integration.set_setting("website_id", website_id)
예제 #37
0
    def _create_registration_payload(self, url_for, stage):
        """Collect the key-value pairs to be sent when kicking off the
        registration protocol.

        :param url_for: An implementation of Flask url_for.
        :param state: The registrant's opinion about what stage this
           registration should be in.
        :return: A dictionary suitable for passing into requests.post.
        """
        auth_document_url = url_for(
            "authentication_document",
            library_short_name=self.library.short_name
        )
        payload = dict(url=auth_document_url, stage=stage)

        # Find the email address the administrator should use if they notice
        # a problem with the way the library is using an integration.
        contact = Configuration.configuration_contact_uri(self.library)
        if contact:
            payload['contact'] = contact
        return payload
def convert_one_click(_db, library):
    config = Configuration.integration('OneClick')
    if not config:
        print u"No OneClick configuration, not creating a Collection for it."
    print u"Creating Collection object for OneClick collection."
    basic_token = config.get('basic_token')
    library_id = config.get('library_id')
    url = config.get('url')
    ebook_loan_length = config.get('ebook_loan_length')
    eaudio_loan_length = config.get('eaudio_loan_length')

    collection, ignore = get_one_or_create(_db,
                                           Collection,
                                           protocol=Collection.ONE_CLICK,
                                           name="OneClick")
    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_axis(_db, library):
    config = Configuration.integration('Axis 360')
    if not config:
        print u"No Axis 360 configuration, not creating a Collection for it."
        return
    print u"Creating Collection object for Axis 360 collection."
    username = config.get('username')
    password = config.get('password')
    library_id = config.get('library_id')
    # This is not technically a URL, it's u"production" or u"staging",
    # but it's converted into a URL internally.
    url = config.get('server')
    collection, ignore = get_one_or_create(_db,
                                           Collection,
                                           protocol=Collection.AXIS_360,
                                           name="Axis 360")
    library.collections.append(collection)
    collection.external_integration.username = username
    collection.external_integration.password = password
    collection.external_account_id = library_id
    collection.external_integration.url = url
def convert_axis(_db, library):
    config = Configuration.integration('Axis 360')
    if not config:
        print u"No Axis 360 configuration, not creating a Collection for it."
        return
    print u"Creating Collection object for Axis 360 collection."
    username = config.get('username')
    password = config.get('password')
    library_id = config.get('library_id')
    # This is not technically a URL, it's u"production" or u"staging",
    # but it's converted into a URL internally.
    url = config.get('server')
    collection, ignore = get_one_or_create(
        _db, Collection,
        protocol=Collection.AXIS_360,
        name="Axis 360"
    )
    library.collections.append(collection)
    collection.external_integration.username = username
    collection.external_integration.password = password
    collection.external_account_id = library_id
    collection.external_integration.url = url
예제 #41
0
    def assert_borrowing_privileges(cls, patron):
        """Raise an exception unless the patron currently has borrowing
        privileges.

        :raise AuthorizationExpired: If the patron's authorization has 
        expired.

        :raise OutstandingFines: If the patron has too many outstanding
        fines.
        """
        now = datetime.datetime.utcnow()
        if not cls.authorization_is_active(patron):
            # The patron's card has expired.
            raise AuthorizationExpired()

        if patron.fines:
            max_fines = Configuration.max_outstanding_fines()
            if patron.fines >= max_fines.amount:
                raise OutstandingFines()

        if patron.block_reason is not None:
            raise AuthorizationBlocked()
def convert_one_click(_db, library):
    config = Configuration.integration('OneClick')
    if not config:
        print u"No OneClick configuration, not creating a Collection for it."
        return
    print u"Creating Collection object for OneClick collection."
    basic_token = config.get('basic_token')
    library_id = config.get('library_id')
    url = config.get('url')
    ebook_loan_length = config.get('ebook_loan_length')
    eaudio_loan_length = config.get('eaudio_loan_length')

    collection, ignore = get_one_or_create(
        _db, Collection,
        protocol=Collection.ONECLICK,
        name="OneClick"
    )
    library.collections.append(collection)
    collection.external_integration.password = basic_token
    collection.external_account_id = library_id
    collection.external_integration.url = url
    collection.external_integration.set_setting("ebook_loan_length", ebook_loan_length)
    collection.external_integration.set_setting("eaudio_loan_length", eaudio_loan_length)
예제 #43
0
 def setUpClass(cls):
     cls.engine = sqlalchemy.create_engine(Configuration().database_url)
예제 #44
0
    def push(self, stage, url_for, catalog_url=None, do_get=HTTP.debuggable_get,
             do_post=HTTP.debuggable_post):
        """Attempt to register a library with a RemoteRegistry.

        NOTE: This method is designed to be used in a
        controller. Other callers may use this method, but they must be
        able to render a ProblemDetail when there's a failure.

        NOTE: The application server must be running when this method
        is called, because part of the OPDS Directory Registration
        Protocol is the remote server retrieving the library's
        Authentication For OPDS document.

        :param stage: Either TESTING_STAGE or PRODUCTION_STAGE
        :param url_for: Flask url_for() or equivalent, used to generate URLs
            for the application server.
        :param do_get: Mockable method to make a GET request.
        :param do_post: Mockable method to make a POST request.

        :return: A ProblemDetail if there was a problem; otherwise True.
        """
        # Assume that the registration will fail.
        #
        # TODO: If a registration has previously succeeded, failure to
        # re-register probably means a maintenance of the status quo,
        # not a change of success to failure. But we don't have any way
        # of being sure.
        self.status_field.value = self.FAILURE_STATUS

        if stage not in self.VALID_REGISTRATION_STAGES:
            return INVALID_INPUT.detailed(
                _("%r is not a valid registration stage") % stage
            )

        # Verify that a public/private key pair exists for this library.
        # This key pair is created during initialization of the
        # LibraryAuthenticator, so this should always be present.
        #
        # We can't just create the key pair here because the process
        # of pushing a registration involves the other site making a
        # request to the circulation manager. This means the key pair
        # needs to be committed to the database _before_ the push
        # attempt starts.
        key_pair = ConfigurationSetting.for_library(
            Configuration.KEY_PAIR, self.library).json_value
        if not key_pair:
            # TODO: We could create the key pair _here_. The database
            # session will be committed at the end of this request,
            # so the push attempt would succeed if repeated.
            return SHARED_SECRET_DECRYPTION_ERROR.detailed(
                _("Library %(library)s has no key pair set.",
                  library=self.library.short_name)
            )
        public_key, private_key = key_pair
        cipher = Configuration.cipher(private_key)

        # Before we can start the registration protocol, we must fetch
        # the remote catalog's URL and extract the link to the
        # registration resource that kicks off the protocol.
        catalog_url = catalog_url or self.integration.url
        response = do_get(catalog_url)
        if isinstance(response, ProblemDetail):
            return response

        result = self._extract_catalog_information(response)
        if isinstance(result, ProblemDetail):
            return result
        register_url, vendor_id = result

        # Store the vendor id as a ConfigurationSetting on the integration
        # -- it'll be the same value for all libraries.
        if vendor_id:
            ConfigurationSetting.for_externalintegration(
                AuthdataUtility.VENDOR_ID_KEY, self.integration
            ).value = vendor_id

        # Build the document we'll be sending to the registration URL.
        payload = self._create_registration_payload(url_for, stage)

        if isinstance(payload, ProblemDetail):
            return payload

        headers = self._create_registration_headers()
        if isinstance(headers, ProblemDetail):
            return headers

        # Send the document.
        response = self._send_registration_request(
            register_url, headers, payload, do_post
        )

        if isinstance(response, ProblemDetail):
            return response
        catalog = json.loads(response.content)

        # Process the result.
        return self._process_registration_result(catalog, cipher, stage)
예제 #45
0
    def test_push(self):
        """Test the other methods orchestrated by the push() method.
        """
        class Mock(Registration):
            def _extract_catalog_information(self, response):
                self.initial_catalog_response = response
                return "register_url", "vendor_id"

            def _create_registration_payload(self, url_for, stage):
                self.payload_ingredients = (url_for, stage)
                return dict(payload="this is it")

            def _create_registration_headers(self):
                self._create_registration_headers_called = True
                return dict(Header="Value")

            def _send_registration_request(self, register_url, headers,
                                           payload, do_post):
                self._send_registration_request_called_with = (register_url,
                                                               headers,
                                                               payload,
                                                               do_post)
                return MockRequestsResponse(200,
                                            content=json.dumps("you did it!"))

            def _process_registration_result(self, catalog, encryptor, stage):
                self._process_registration_result_called_with = (catalog,
                                                                 encryptor,
                                                                 stage)
                return "all done!"

            def mock_do_get(self, url):
                self.do_get_called_with = url
                return "A fake catalog"

        # If there is no preexisting key pair set up for the library,
        # registration fails. (This normally won't happen because the
        # key pair is set up when the LibraryAuthenticator is
        # initialized.
        library = self._default_library
        registration = Mock(self.registry, library)
        stage = Registration.TESTING_STAGE
        url_for = object()
        catalog_url = "http://catalog/"
        do_post = object()

        def push():
            return registration.push(stage, url_for, catalog_url,
                                     registration.mock_do_get, do_post)

        result = push()
        expect = "Library %s has no key pair set." % library.short_name
        eq_(expect, result.detail)

        # When a key pair is present, registration is kicked off, and
        # in this case it succeeds.
        key_pair_setting = ConfigurationSetting.for_library(
            Configuration.KEY_PAIR, library)
        public_key, private_key = Configuration.key_pair(key_pair_setting)
        result = registration.push(stage, url_for, catalog_url,
                                   registration.mock_do_get, do_post)
        eq_("all done!", result)

        # But there were many steps towards this result.

        # First, do_get was called on the catalog URL.
        eq_(catalog_url, registration.do_get_called_with)

        # Then, the catalog was passed into _extract_catalog_information.
        eq_("A fake catalog", registration.initial_catalog_response)

        # _extract_catalog_information returned a registration URL and
        # a vendor ID. The registration URL was used later on...
        #
        # The vendor ID was set as a ConfigurationSetting on
        # the ExternalIntegration associated with this registry.
        eq_(
            "vendor_id",
            ConfigurationSetting.for_externalintegration(
                AuthdataUtility.VENDOR_ID_KEY, self.integration).value)

        # _create_registration_payload was called to create the body
        # of the registration request.
        eq_((url_for, stage), registration.payload_ingredients)

        # _create_registration_headers was called to create the headers
        # sent along with the request.
        eq_(True, registration._create_registration_headers_called)

        # Then _send_registration_request was called, POSTing the
        # payload to "register_url", the registration URL we got earlier.
        results = registration._send_registration_request_called_with
        eq_(("register_url", {
            "Header": "Value"
        }, dict(payload="this is it"), do_post), results)

        # Finally, the return value of that method was loaded as JSON
        # and passed into _process_registration_result, along with
        # a cipher created from the private key. (That cipher would be used
        # to decrypt anything the foreign site signed using this site's
        # public key.)
        results = registration._process_registration_result_called_with
        message, cipher, actual_stage = results
        eq_("you did it!", message)
        eq_(cipher._key.exportKey(), private_key)
        eq_(actual_stage, stage)

        # If a nonexistent stage is provided a ProblemDetail is the result.
        result = registration.push("no such stage", url_for, catalog_url,
                                   registration.mock_do_get, do_post)
        eq_(INVALID_INPUT.uri, result.uri)
        eq_("'no such stage' is not a valid registration stage", result.detail)

        # Now in reverse order, let's replace the mocked methods so
        # that they return ProblemDetail documents. This tests that if
        # there is a failure at any stage, the ProblemDetail is
        # propagated.

        # The push() function will no longer push anything, so rename it.
        cause_problem = push

        def fail(*args, **kwargs):
            return INVALID_REGISTRATION.detailed(
                "could not process registration result")

        registration._process_registration_result = fail
        problem = cause_problem()
        eq_("could not process registration result", problem.detail)

        def fail(*args, **kwargs):
            return INVALID_REGISTRATION.detailed(
                "could not send registration request")

        registration._send_registration_request = fail
        problem = cause_problem()
        eq_("could not send registration request", problem.detail)

        def fail(*args, **kwargs):
            return INVALID_REGISTRATION.detailed(
                "could not create registration payload")

        registration._create_registration_payload = fail
        problem = cause_problem()
        eq_("could not create registration payload", problem.detail)

        def fail(*args, **kwargs):
            return INVALID_REGISTRATION.detailed(
                "could not extract catalog information")

        registration._extract_catalog_information = fail
        problem = cause_problem()
        eq_("could not extract catalog information", problem.detail)
예제 #46
0
"""
API module for the application
"""

from flask import Flask
from api.config import Configuration
from api.metrics import Metrics
from api.request_logger import add_request_logging

app = Flask(__name__)
add_request_logging(app)
configuration = Configuration()
metrics = Metrics(app, configuration)

@app.route('/health')
def health():
    """
    Health endpoint which returns the configuration
    """
    return configuration.config
예제 #47
0
파일: scripts.py 프로젝트: dguo/circulation
 def load_config(self):
     if not Configuration.instance:
         Configuration.load()
    def test_sitewide_registration_post_success(self):
        # A service to register with
        metadata_wrangler_service = self._external_integration(
            ExternalIntegration.METADATA_WRANGLER,
            goal=ExternalIntegration.METADATA_GOAL, url=self._url
        )

        # The service knows this site's public key, and is going
        # to use it to encrypt a shared secret.
        public_key, private_key = self.manager.sitewide_key_pair
        encryptor = Configuration.cipher(public_key)

        # A catalog with registration url
        register_link_type = self.manager.admin_settings_controller.METADATA_SERVICE_URI_TYPE
        registration_url = self._url
        catalog = dict(
            id = metadata_wrangler_service.url,
            links = [
                dict(rel='collection-add', href=self._url, type='collection'),
                dict(rel='register', href=registration_url, type=register_link_type),
                dict(rel='collection-remove', href=self._url, type='collection'),
            ]
        )
        headers = { 'Content-Type' : 'application/opds+json' }
        self.responses.append(
            MockRequestsResponse(200, content=json.dumps(catalog), headers=headers)
        )

        # A registration document with an encrypted secret
        shared_secret = os.urandom(24).encode('hex')
        encrypted_secret = base64.b64encode(encryptor.encrypt(shared_secret))
        registration = dict(
            id = metadata_wrangler_service.url,
            metadata = dict(shared_secret=encrypted_secret)
        )
        self.responses.insert(0, MockRequestsResponse(200, content=json.dumps(registration)))

        with self.request_context_with_admin('/', method='POST'):
            flask.request.form = MultiDict([
                ('integration_id', metadata_wrangler_service.id),
            ])
            response = self.manager.admin_metadata_services_controller.process_sitewide_registration(
                metadata_wrangler_service, do_get=self.do_request,
                do_post=self.do_request
            )
        eq_(None, response)

        # We made two requests: a GET to get the service document from
        # the metadata wrangler, and a POST to the registration
        # service, with the entity-body containing a callback URL and
        # a JWT.
        metadata_wrangler_service_request, registration_request = self.requests
        url, i1, i2 = metadata_wrangler_service_request
        eq_(metadata_wrangler_service.url, url)

        url, [document], ignore = registration_request
        eq_(url, registration_url)
        for k in 'url', 'jwt':
            assert k in document

        # The end result is that our ExternalIntegration for the metadata
        # wrangler has been updated with a (decrypted) shared secret.
        eq_(shared_secret, metadata_wrangler_service.password)
#!/usr/bin/env python
"""Create a -current alias for the index being used"""

import os
import sys
from pdb import set_trace
bin_dir = os.path.split(__file__)[0]
package_dir = os.path.join(bin_dir, "..")
sys.path.append(os.path.abspath(package_dir))

from api.config import Configuration as C
from core.external_search import ExternalSearchIndex

C.load()
config_index = C.integration(C.ELASTICSEARCH_INTEGRATION).get(C.ELASTICSEARCH_INDEX_KEY)
if not config_index:
    print "No action taken. Elasticsearch not configured."
    sys.exit()

search = ExternalSearchIndex()
update_required_text = (
    "\n\tConfiguration update required for given alias \"%s\".\n"
    "\t============================================\n"
    "\tReplace Elasticsearch configuration \"works_index\" value with alias.\n"
    "\te.g. \"works_index\" : \"%s\" ===> \"works_index\" : \"%s\"\n\n"
)

misplaced_alias_text = (
    "\n\tExpected Elasticsearch alias \"%s\" is being used with\n"
    "\tindex \"%s\" instead of configured index \"%s\".\n"
    "\t============================================\n"
예제 #50
0
    def test_push(self):
        """Test the other methods orchestrated by the push() method.
        """

        class Mock(Registration):

            def _extract_catalog_information(self, response):
                self.initial_catalog_response = response
                return "register_url", "vendor_id"

            def _create_registration_payload(self, url_for, stage):
                self.payload_ingredients = (url_for, stage)
                return dict(payload="this is it")

            def _create_registration_headers(self):
                self._create_registration_headers_called = True
                return dict(Header="Value")

            def _send_registration_request(
                    self, register_url, headers, payload, do_post
            ):
                self._send_registration_request_called_with = (
                    register_url, headers, payload, do_post
                )
                return MockRequestsResponse(
                    200, content=json.dumps("you did it!")
                )

            def _process_registration_result(self, catalog, encryptor, stage):
                self._process_registration_result_called_with = (
                    catalog, encryptor, stage
                )
                return "all done!"

            def mock_do_get(self, url):
                self.do_get_called_with = url
                return "A fake catalog"

        # If there is no preexisting key pair set up for the library,
        # registration fails. (This normally won't happen because the
        # key pair is set up when the LibraryAuthenticator is
        # initialized.
        library = self._default_library
        registration = Mock(self.registry, library)
        stage = Registration.TESTING_STAGE
        url_for = object()
        catalog_url = "http://catalog/"
        do_post = object()
        def push():
            return registration.push(
                stage, url_for, catalog_url, registration.mock_do_get, do_post
            )

        result = push()
        expect = "Library %s has no key pair set." % library.short_name
        eq_(expect, result.detail)

        # When a key pair is present, registration is kicked off, and
        # in this case it succeeds.
        key_pair_setting = ConfigurationSetting.for_library(
            Configuration.KEY_PAIR, library
        )
        public_key, private_key = Configuration.key_pair(key_pair_setting)
        result = registration.push(
            stage, url_for, catalog_url, registration.mock_do_get, do_post
        )
        eq_("all done!", result)

        # But there were many steps towards this result.

        # First, do_get was called on the catalog URL.
        eq_(catalog_url, registration.do_get_called_with)

        # Then, the catalog was passed into _extract_catalog_information.
        eq_("A fake catalog", registration.initial_catalog_response)

        # _extract_catalog_information returned a registration URL and
        # a vendor ID. The registration URL was used later on...
        #
        # The vendor ID was set as a ConfigurationSetting on
        # the ExternalIntegration associated with this registry.
        eq_(
            "vendor_id",
            ConfigurationSetting.for_externalintegration(
                AuthdataUtility.VENDOR_ID_KEY, self.integration
            ).value
        )

        # _create_registration_payload was called to create the body
        # of the registration request.
        eq_((url_for, stage), registration.payload_ingredients)

        # _create_registration_headers was called to create the headers
        # sent along with the request.
        eq_(True, registration._create_registration_headers_called)

        # Then _send_registration_request was called, POSTing the
        # payload to "register_url", the registration URL we got earlier.
        results = registration._send_registration_request_called_with
        eq_(
            ("register_url", {"Header": "Value"}, dict(payload="this is it"),
             do_post),
            results
        )

        # Finally, the return value of that method was loaded as JSON
        # and passed into _process_registration_result, along with
        # a cipher created from the private key. (That cipher would be used
        # to decrypt anything the foreign site signed using this site's
        # public key.)
        results = registration._process_registration_result_called_with
        message, cipher, actual_stage = results
        eq_("you did it!", message)
        eq_(cipher._key.exportKey(), private_key)
        eq_(actual_stage, stage)

        # If a nonexistent stage is provided a ProblemDetail is the result.
        result = registration.push(
            "no such stage", url_for, catalog_url, registration.mock_do_get,
            do_post
        )
        eq_(INVALID_INPUT.uri, result.uri)
        eq_("'no such stage' is not a valid registration stage",
            result.detail)

        # Now in reverse order, let's replace the mocked methods so
        # that they return ProblemDetail documents. This tests that if
        # there is a failure at any stage, the ProblemDetail is
        # propagated.

        # The push() function will no longer push anything, so rename it.
        cause_problem = push

        def fail(*args, **kwargs):
            return INVALID_REGISTRATION.detailed(
                "could not process registration result"
            )
        registration._process_registration_result = fail
        problem = cause_problem()
        eq_("could not process registration result", problem.detail)

        def fail(*args, **kwargs):
            return INVALID_REGISTRATION.detailed(
                "could not send registration request"
            )
        registration._send_registration_request = fail
        problem = cause_problem()
        eq_("could not send registration request", problem.detail)

        def fail(*args, **kwargs):
            return INVALID_REGISTRATION.detailed(
                "could not create registration payload"
            )
        registration._create_registration_payload = fail
        problem = cause_problem()
        eq_("could not create registration payload", problem.detail)

        def fail(*args, **kwargs):
            return INVALID_REGISTRATION.detailed(
                "could not extract catalog information"
            )
        registration._extract_catalog_information = fail
        problem = cause_problem()
        eq_("could not extract catalog information", problem.detail)
    ExternalIntegration as EI,
    Library,
    get_one_or_create,
    production_session,
)

from api.adobe_vendor_id import AuthdataUtility
from api.config import Configuration

log = logging.getLogger(name="Circulation manager configuration import")

def log_import(integration_or_setting):
    log.info("CREATED: %r" % integration_or_setting)

try:
    Configuration.load()
    _db = production_session()
    LIBRARIES = _db.query(Library).all()

    # Import Circulation Manager base url.
    circ_manager_conf = Configuration.integration('Circulation Manager')
    if circ_manager_conf:
        url = circ_manager_conf.get('url')
        if url:
            setting = ConfigurationSetting.sitewide(_db, Configuration.BASE_URL_KEY)
            setting.value = unicode(url)
            log_import(setting)

    # Import Metadata Wrangler configuration.
    metadata_wrangler_conf = Configuration.integration('Metadata Wrangler')