api.lcp package

Submodules

api.lcp.collection module

class api.lcp.collection.LCPAPI(db, collection)[source]

Bases: BaseCirculationAPI, HasExternalIntegration

Implements LCP workflow

DESCRIPTION = 'Manually imported collection protected using Readium LCP DRM'
NAME = 'LCP'
SERVICE_NAME = 'LCP'
SETTINGS = [{'key': 'lcpserver_url', 'label': l'LCP License Server's URL', 'description': l'URL of the LCP License Server', 'type': None, 'required': True, 'default': None, 'options': None, 'category': None}, {'key': 'lcpserver_user', 'label': l'LCP License Server's user', 'description': l'Name of the user used to connect to the LCP License Server', 'type': None, 'required': True, 'default': None, 'options': None, 'category': None}, {'key': 'lcpserver_password', 'label': l'LCP License Server's password', 'description': l'Password of the user used to connect to the LCP License Server', 'type': None, 'required': True, 'default': None, 'options': None, 'category': None}, {'key': 'lcpserver_input_directory', 'label': l'LCP License Server's input directory', 'description': l'Full path to the directory containing encrypted books. This directory should be the same as lcpencrypt's output directory', 'type': None, 'required': True, 'default': None, 'options': None, 'category': None}, {'key': 'lcpserver_page_size', 'label': l'LCP License Server's page size', 'description': l'Number of licences returned by the server', 'type': 'number', 'required': False, 'default': 100, 'options': None, 'category': None}, {'key': 'provider_name', 'label': l'LCP service provider's identifier', 'description': l'URI that identifies the provider in an unambiguous way', 'type': None, 'required': True, 'default': None, 'options': None, 'category': None}, {'key': 'passphrase_hint', 'label': l'Passphrase hint', 'description': l'Hint proposed to the user for selecting their passphrase', 'type': None, 'required': False, 'default': 'If you do not remember your passphrase, please contact your administrator', 'options': None, 'category': None}, {'key': 'encryption_algorithm', 'label': l'Passphrase encryption algorithm', 'description': l'Algorithm used for encrypting the passphrase', 'type': 'select', 'required': False, 'default': 'http://www.w3.org/2001/04/xmlenc#sha256', 'options': [{'key': 'http://www.w3.org/2001/04/xmlenc#sha256', 'label': 'SHA256'}, {'key': 'http://www.w3.org/2001/04/xmlenc#sha512', 'label': 'SHA512'}], 'category': None}, {'key': 'max_printable_pages', 'label': l'Maximum number or printable pages', 'description': l'Maximum number of pages that can be printed over the lifetime of the license', 'type': 'number', 'required': False, 'default': None, 'options': None, 'category': None}, {'key': 'max_copiable_pages', 'label': l'Maximum number or copiable characters', 'description': l'Maximum number of characters that can be copied to the clipboard', 'type': 'number', 'required': False, 'default': None, 'options': None, 'category': None}, {'key': 'lcpencrypt_location', 'label': l'lcpencrypt's location', 'description': l'Full path to the local lcpencrypt binary. The default value is /go/bin/lcpencrypt', 'type': None, 'required': False, 'default': '/go/bin/lcpencrypt', 'options': None, 'category': None}, {'key': 'lcpencrypt_output_directory', 'label': l'lcpencrypt's output directory', 'description': l'Full path to the directory where lcpencrypt stores encrypted content. If not set encrypted books will be stored in lcpencrypt's working directory', 'type': None, 'required': False, 'default': None, 'options': None, 'category': None}]
checkout(patron, pin, licensepool, internal_format)[source]

Checks out a book on behalf of a patron

Parameters:
  • patron (Patron) – A Patron object for the patron who wants to check out the book

  • pin (string) – The patron’s alleged password

  • licensepool (LicensePool) – Contains lending info as well as link to parent Identifier

  • internal_format (Any) – Represents the patron’s desired book format.

Returns:

a LoanInfo object

Return type:

LoanInfo

property collection

Returns an associated Collection object

Returns:

Associated Collection object

Return type:

core.model.collection.Collection

external_integration(db)[source]

Returns an external integration associated with this object

Parameters:

db (sqlalchemy.orm.session.Session) – Database session

Returns:

External integration associated with this object

Return type:

core.model.configuration.ExternalIntegration

fulfill(patron, pin, licensepool, internal_format=None, part=None, fulfill_part_url=None)[source]

Get the actual resource file to the patron.

Parameters:
  • patron (Patron) – A Patron object for the patron who wants to check out the book

  • pin (string) – The patron’s alleged password

  • licensepool (LicensePool) – Contains lending info as well as link to parent Identifier

  • internal_format – A vendor-specific name indicating the format requested by the patron

  • part (Any) – A vendor-specific identifier indicating that the patron wants to fulfill one specific part of the book (e.g. one chapter of an audiobook), not the whole thing

  • fulfill_part_url (Any) – A function that takes one argument (a vendor-specific part identifier) and returns the URL to use when fulfilling that part

Returns:

a FulfillmentInfo object

Return type:

FulfillmentInfo

internal_format(delivery_mechanism)[source]

Look up the internal format for this delivery mechanism or raise an exception.

Parameters:

delivery_mechanism (LicensePoolDeliveryMechanism) – A LicensePoolDeliveryMechanism

patron_activity(patron, pin)[source]

Returns patron’s loans

Parameters:
  • patron (Patron) – A Patron object for the patron who wants to check out the book

  • pin (string) – The patron’s alleged password

Returns:

List of patron’s loans

Return type:

List[LoanInfo]

class api.lcp.collection.LCPFulfilmentInfo(identifier, collection, data_source_name, identifier_type, content_link=None, content_type=None, content=None, content_expires=None)[source]

Bases: FulfillmentInfo

Sends LCP licenses as fulfilment info

property as_response

Returns LCP license as a Flask response

Returns:

LCP license as a Flask response

Return type:

Response

api.lcp.controller module

class api.lcp.controller.LCPController(manager)[source]

Bases: CirculationManagerController

Contains API endpoints related to LCP workflow

get_lcp_license(collection_name, license_id)[source]

Returns an LCP license with the specified ID

Parameters:
  • collection_name (string) – Name of the collection

  • license_id (string) – License ID

Returns:

Flask response containing the LCP license with the specified ID

Return type:

string

get_lcp_passphrase()[source]

Returns an LCP passphrase for the authenticated patron

Returns:

Flask response containing the LCP passphrase for the authenticated patron

Return type:

Response

api.lcp.encrypt module

class api.lcp.encrypt.LCPEncryptionConfiguration(configuration_storage, db)[source]

Bases: ConfigurationGrouping

Contains different settings required by LCPEncryptor

DEFAULT_LCPENCRYPT_DOCKER_IMAGE = 'readium/lcpencrypt'
DEFAULT_LCPENCRYPT_LOCATION = '/go/bin/lcpencrypt'
lcpencrypt_location

Contains configuration metadata

lcpencrypt_output_directory

Contains configuration metadata

exception api.lcp.encrypt.LCPEncryptionException(message=None, inner_exception=None)[source]

Bases: BaseError

Raised in the case of any errors occurring during LCP encryption process

class api.lcp.encrypt.LCPEncryptionResult(content_id, content_encryption_key, protected_content_location, protected_content_disposition, protected_content_type, protected_content_length, protected_content_sha256)[source]

Bases: object

Represents an output sent by lcpencrypt

CONTENT_ENCRYPTION_KEY = 'content-encryption-key'
CONTENT_ID = 'content-id'
PROTECTED_CONTENT_DISPOSITION = 'protected-content-disposition'
PROTECTED_CONTENT_LENGTH = 'protected-content-length'
PROTECTED_CONTENT_LOCATION = 'protected-content-location'
PROTECTED_CONTENT_SHA256 = 'protected-content-sha256'
PROTECTED_CONTENT_TYPE = 'protected-content-type'
property content_encryption_key

Returns a content identifier

Returns:

Content identifier

Return type:

Optional[string]

property content_id

Returns a content encryption key

Returns:

Content encryption key

Return type:

Optional[string]

classmethod from_dict(result_dict)[source]

Creates an LCPEncryptorResult object from a Python dictionary

Parameters:

result_dict (Dict) – Python dictionary containing an lcpencrypt output

Returns:

LCPEncryptorResult object

Return type:

LCPEncryptionResult

property protected_content_disposition

Returns a file name of the encrypted content

Returns:

File name of the encrypted content

Return type:

Optional[string]

property protected_content_length

Returns a size of the encrypted content

Returns:

Size of the encrypted content

Return type:

Optional[string]

property protected_content_location

Returns a complete file path of the encrypted content

Returns:

Complete file path of the encrypted content

Return type:

Optional[string]

property protected_content_sha256

Returns a hash of the encrypted content

Returns:

Hash of the encrypted content

Return type:

Optional[string]

property protected_content_type

Returns a media type of the encrypted content

Returns:

Media type of the encrypted content

Return type:

Optional[string]

class api.lcp.encrypt.LCPEncryptor(configuration_storage, configuration_factory)[source]

Bases: object

Wrapper around lcpencrypt tool containing logic to run it locally and in a Docker container

OUTPUT_REGEX = re.compile('(\\{.+\\})?(.+)', re.DOTALL)
class Parameters(file_path, identifier, configuration)[source]

Bases: object

Parses input parameters for lcpencrypt

property content_id

Returns content ID

Returns:

Content ID

Return type:

string

property input_file_path

Returns path of the input file

Returns:

Path of the input file

Return type:

string

property lcpencrypt_location

Returns location of lcpencrypt binary

Returns:

Location of lcpencrypt binary

Return type:

string

property output_file_path

Returns path of the output file

Returns:

Path of the output file

Return type:

string

to_array()[source]

Returns parameters in an array

Returns:

Parameters in an array

Return type:

List

encrypt(db, file_path, identifier)[source]

Encrypts a book

Parameters:
  • db (sqlalchemy.orm.session.Session) – Database session

  • file_path (string) – File path to the book to be encrypted

  • identifier (string) – Book’s identifier

Returns:

Encryption result

Return type:

LCPEncryptionResult

class api.lcp.encrypt.LCPEncryptorResultJSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)[source]

Bases: JSONEncoder

Serializes LCPEncryptorResult as a JSON object

default(result)[source]

Serializers a Subject object to JSON

Parameters:

result (LCPEncryptionResult) – LCPEncryptorResult object

Returns:

String containing JSON representation of the LCPEncryptorResult object

Return type:

string

api.lcp.factory module

class api.lcp.factory.LCPServerFactory[source]

Bases: object

Creates a new instance of LCPServer

create(integration_association)[source]

Creates a new instance of LCPServer

Parameters:

integration_association (core.model.configuration.HasExternalIntegration) – Association with an external integration

Returns:

New instance of LCPServer

Return type:

LCPServer

api.lcp.hash module

class api.lcp.hash.Hasher(hashing_algorithm)[source]

Bases: object

Base class for all implementations of different hashing algorithms

abstract hash(value)[source]
class api.lcp.hash.HasherFactory[source]

Bases: object

create(hashing_algorithm)[source]
class api.lcp.hash.HashingAlgorithm(value)[source]

Bases: Enum

An enumeration.

SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256'
SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512'
exception api.lcp.hash.HashingError(message=None, inner_exception=None)[source]

Bases: BaseError

Raised in the case of errors occurred during hashing

class api.lcp.hash.UniversalHasher(hashing_algorithm)[source]

Bases: Hasher

hash(value)[source]

api.lcp.importer module

class api.lcp.importer.LCPImporter(lcp_encryptor, lcp_server)[source]

Bases: object

Class implementing LCP import workflow

import_book(db, file_path, identifier)[source]

Encrypts a book and sends a notification to the LCP server

Parameters:
  • db (sqlalchemy.orm.session.Session) – Database session

  • file_path (string) – File path to the book to be encrypted

  • identifier (string) – Book’s identifier

Returns:

Encryption result

Return type:

LCPEncryptionResult

api.lcp.mirror module

class api.lcp.mirror.LCPMirror(integration)[source]

Bases: MinIOUploader, HasExternalIntegrationPerCollection

Implements LCP import workflow: 1. Encrypts unencrypted books using lcpencrypt 2. Sends encrypted books to the LCP License Server 3. LCP License Server generates license metadata and uploads encrypted books to the encrypted_repository

NAME = 'LCP'
SETTINGS = [{'key': 'username', 'label': l'Access Key', 'description': '', 'type': None, 'required': False, 'default': None, 'options': None, 'category': None, 'format': None}, {'key': 'password', 'label': l'Secret Key', 'description': l'If the <em>Access Key</em> and <em>Secret Key</em> are not given here credentials will be used as outlined in the <a href="https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#configuring-credentials">Boto3 documenation</a>. If <em>Access Key</em> is given, <em>Secrent Key</em> must also be given.', 'type': None, 'required': False, 'default': None, 'options': None, 'category': None, 'format': None}, {'key': 'protected_content_bucket', 'label': l'Protected Access Content Bucket', 'description': l'Self-hosted books will be uploaded to this S3 bucket. <p>The bucket must already exist&mdash;it will not be created automatically.</p>', 'type': None, 'required': False, 'default': None, 'options': None, 'category': None, 'format': None}, {'key': 's3_region', 'label': l'S3 region', 'description': l'S3 region which will be used for storing the content.', 'type': 'select', 'required': False, 'default': 'us-east-1', 'options': [{'key': 'af-south-1', 'label': 'af-south-1'}, {'key': 'ap-east-1', 'label': 'ap-east-1'}, {'key': 'ap-northeast-1', 'label': 'ap-northeast-1'}, {'key': 'ap-northeast-2', 'label': 'ap-northeast-2'}, {'key': 'ap-northeast-3', 'label': 'ap-northeast-3'}, {'key': 'ap-south-1', 'label': 'ap-south-1'}, {'key': 'ap-southeast-1', 'label': 'ap-southeast-1'}, {'key': 'ap-southeast-2', 'label': 'ap-southeast-2'}, {'key': 'ap-southeast-3', 'label': 'ap-southeast-3'}, {'key': 'ca-central-1', 'label': 'ca-central-1'}, {'key': 'eu-central-1', 'label': 'eu-central-1'}, {'key': 'eu-north-1', 'label': 'eu-north-1'}, {'key': 'eu-south-1', 'label': 'eu-south-1'}, {'key': 'eu-west-1', 'label': 'eu-west-1'}, {'key': 'eu-west-2', 'label': 'eu-west-2'}, {'key': 'eu-west-3', 'label': 'eu-west-3'}, {'key': 'me-south-1', 'label': 'me-south-1'}, {'key': 'sa-east-1', 'label': 'sa-east-1'}, {'key': 'us-east-1', 'label': 'us-east-1'}, {'key': 'us-east-2', 'label': 'us-east-2'}, {'key': 'us-west-1', 'label': 'us-west-1'}, {'key': 'us-west-2', 'label': 'us-west-2'}], 'category': None, 'format': None}, {'key': 's3_addressing_style', 'label': l'S3 addressing style', 'description': l'Buckets created after September 30, 2020, will support only virtual hosted-style requests. Path-style requests will continue to be supported for buckets created on or before this date. For more information, see <a href="https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/">Amazon S3 Path Deprecation Plan - The Rest of the Story</a>.', 'type': 'select', 'required': False, 'default': 'us-east-1', 'options': [{'key': 'virtual', 'label': l'Virtual'}, {'key': 'path', 'label': l'Path'}, {'key': 'auto', 'label': l'Auto'}], 'category': None, 'format': None}, {'key': 's3_presigned_url_expiration', 'label': l'S3 presigned URL expiration', 'description': l'Time in seconds for the presigned URL to remain valid', 'type': 'number', 'required': False, 'default': 3600, 'options': None, 'category': None, 'format': None}, {'key': 'bucket_name_transform', 'label': l'URL format', 'description': l'A file mirrored to S3 is available at <code>http://{bucket}.s3.{region}.amazonaws.com/{filename}</code>. If you've set up your DNS so that http://[bucket]/ or https://[bucket]/ points to the appropriate S3 bucket, you can configure this S3 integration to shorten the URLs. <p>If you haven't set up your S3 buckets, don't change this from the default -- you'll get URLs that don't work.</p>', 'type': 'select', 'required': False, 'default': 'identity', 'options': [{'key': 'identity', 'label': l'S3 Default: https://{bucket}.s3.{region}.amazonaws.com/{file}'}, {'key': 'https', 'label': l'HTTPS: https://{bucket}/{file}'}, {'key': 'http', 'label': l'HTTP: http://{bucket}/{file}'}], 'category': None, 'format': None}, {'key': 'ENDPOINT_URL', 'label': l'Endpoint URL', 'description': l'S3 endpoint URL', 'type': None, 'required': False, 'default': None, 'options': None, 'category': None, 'format': None}]
book_url(identifier, extension='.epub', open_access=False, data_source=None, title=None)[source]

Returns the path to the hosted EPUB file for the given identifier.

collection_external_integration(collection)[source]

Returns an external integration associated with the collection

Parameters:

collection (core.model.Collection) – Collection

Returns:

External integration associated with the collection

Return type:

core.model.configuration.ExternalIntegration

cover_image_root(bucket, data_source, scaled_size=None)[source]

The root URL to the S3 location of cover images for the given data source.

cover_image_url(data_source, identifier, filename, scaled_size=None)[source]

The path to the hosted cover image for the given identifier.

do_upload(representation)[source]
marc_file_root(bucket, library)[source]
marc_file_url(library, lane, end_time, start_time=None)[source]

The path to the hosted MARC file for the given library, lane, and date range.

mirror_one(representation, mirror_to, collection=None)[source]

Uploads an encrypted book to the encrypted_repository via LCP License Server

Parameters:
class api.lcp.mirror.LCPMirrorConfiguration(configuration_storage, db)[source]

Bases: S3UploaderConfiguration

endpoint_url

Contains configuration metadata

api.lcp.server module

class api.lcp.server.LCPServer(configuration_storage, configuration_factory, hasher_factory, credential_factory)[source]

Bases: object

Wrapper around LCP License Server’s API

add_content(db, encrypted_content)[source]

Notifies LCP License Server about new encrypted content

Parameters:
  • db (sqlalchemy.orm.session.Session) – Database session

  • encrypted_content (LCPEncryptionResult) – LCPEncryptionResult object containing information about encrypted content

generate_license(db, content_id, patron, license_start, license_end)[source]

Generates a new LCP license

Parameters:
  • db (sqlalchemy.orm.session.Session) – Database session

  • content_id (string) – Unique content ID

  • patron (Patron) – Patron object

  • license_start (datetime.datetime) – Unique patron ID

  • license_start – Date and time when the license begins

  • license_end (datetime.datetime) – Date and time when the license ends

Returns:

LCP license

Return type:

Dict

get_license(db, license_id, patron)[source]

Returns an existing license

Parameters:
  • db (sqlalchemy.orm.session.Session) – Database session

  • license_id (int) – License’s ID

  • patron (Patron) – Patron object

Returns:

Existing license

Return type:

string

class api.lcp.server.LCPServerConfiguration(configuration_storage, db)[source]

Bases: ConfigurationGrouping

Contains LCP License Server’s settings

DEFAULT_ENCRYPTION_ALGORITHM = 'http://www.w3.org/2001/04/xmlenc#sha256'
DEFAULT_PAGE_SIZE = 100
DEFAULT_PASSPHRASE_HINT = 'If you do not remember your passphrase, please contact your administrator'
encryption_algorithm

Contains configuration metadata

lcpserver_input_directory

Contains configuration metadata

lcpserver_page_size

Contains configuration metadata

lcpserver_password

Contains configuration metadata

lcpserver_url

Contains configuration metadata

lcpserver_user

Contains configuration metadata

max_copiable_pages

Contains configuration metadata

max_printable_pages

Contains configuration metadata

passphrase_hint

Contains configuration metadata

provider_name

Contains configuration metadata

api.lcp.utils module

api.lcp.utils.bind_method(instance, func, as_name=None)[source]

Bind the function func to instance, with either provided name as_name or the existing name of func. The provided func should accept the instance as the first argument, i.e. “self”.

api.lcp.utils.format_datetime(datetime_value)[source]

Converts a datetime value into a string using the format which Go understands

Parameters:

datetime_value (datetime.datetime) – Datetime value

Returns:

String representation of the datetime value

Return type:

string

api.lcp.utils.get_target_extension(input_extension)[source]

Module contents