Source code for api.kansas_patron

from flask_babel import lazy_gettext as _
import logging
from .authenticator import (
    BasicAuthenticationProvider,
    PatronData,
)
from .config import CannotLoadConfiguration
from core.model import ExternalIntegration
from lxml import etree
from core.util.http import HTTP


[docs]class KansasAuthenticationAPI(BasicAuthenticationProvider): NAME = 'Kansas' DESCRIPTION = _(""" An authentication service for the Kansas State Library. """) DISPLAY_NAME = NAME SETTINGS = [ { "key": ExternalIntegration.URL, "format": "url", "label": _("URL"), "default": "https://ks-kansaslibrary3m.civicplus.com/api/UserDetails", "required": True }, ] + BasicAuthenticationProvider.SETTINGS log = logging.getLogger("Kansas authentication API") def __init__(self, library_id, integration, analytics=None, base_url=None): super(KansasAuthenticationAPI, self).__init__(library_id, integration, analytics) if base_url is None: base_url = integration.url if not base_url: raise CannotLoadConfiguration( "Kansas server url not configured." ) self.base_url = base_url # Begin implementation of BasicAuthenticationProvider abstract # methods.
[docs] def remote_authenticate(self, username, password): # Create XML doc for request authorization_request = self.create_authorize_request(username, password) # Post request to the server response = self.post_request(authorization_request) # Parse response from server authorized, patron_name, library_identifier = self.parse_authorize_response(response.content) if not authorized: return False # Kansas auth gives very little data about the patron. Only name and a library identifier. return PatronData( permanent_id=username, authorization_identifier=username, personal_name=patron_name, library_identifier=library_identifier, complete=True )
# End implementation of BasicAuthenticationProvider abstract methods.
[docs] @staticmethod def create_authorize_request(barcode, pin): # Create the authentication document authorize_request = etree.Element("AuthorizeRequest") user_id = etree.Element("UserID") user_id.text = barcode password = etree.Element("Password") password.text = pin authorize_request.append(user_id) authorize_request.append(password) return etree.tostring(authorize_request, encoding='utf8')
[docs] def parse_authorize_response(self, response): try: authorize_response = etree.fromstring(response) except etree.XMLSyntaxError: self.log.error("Unable to parse response from API. Deny Access. Response: \n%s", response) return False, None, None patron_names = [] for tag in ["FirstName", "LastName"]: element = authorize_response.find(tag) if element is not None and element.text is not None: patron_names.append(element.text) patron_name = ' '.join(patron_names) if len(patron_names) != 0 else None element = authorize_response.find("LibraryID") library_identifier = element.text if element is not None else None element = authorize_response.find('Status') if element is None: self.log.info("Status element not found in response from server. Deny Access.") authorized = True if element is not None and element.text == "1" else False return authorized, patron_name, library_identifier
[docs] def post_request(self, data): """Make an HTTP request. Defined solely so it can be overridden in the mock. """ return HTTP.post_with_timeout( self.base_url, data, headers={"Content-Type": "application/xml"}, allowed_response_codes=['2xx'], )
# Specify which of the classes defined in this module is the # authentication provider. AuthenticationProvider = KansasAuthenticationAPI