Comment 1 for bug 1817645

Revision history for this message
Jeff Davis (jdavis-sitka) wrote :

Working branch user/jeffdavis/lp1817645-remoteauth-patron-api has an initial implementation:

https://git.evergreen-ils.org/?p=working/Evergreen.git;a=shortlog;h=refs/heads/user/jeffdavis/lp1817645-remoteauth-patron-api

It introduces a generic RemoteAuth mod_perl handler for processing various types of patron auth requests, and provides a form of "Basic" HTTP Authentication [1] as a reference implementation.

This branch doesn't include templating for responses. I want to add support for EZProxy and possibly PatronAPI, but I'd like the current design and code to be reviewed before I proceed.

How it works:
-------------
1. Client submits a request to a RemoteAuth endpoint containing user credentials and any additional requirements.
2. RemoteAuth loads the handler module for this endpoint.
3. Handler processes the request and authorizes the client.
4. Handler loads this endpoint's configuration from the database.
5. Handler authenticates the user using the credentials provided, and tests whether auth is permitted for this user at this endpoint.
6. Handler returns an appropriate response to RemoteAuth, which passes the response to the client.

Test plan for Basic HTTP Authentication:
----------------------------------------
1. Install the branch on a test server and load concerto data. Basic auth will be enabled by default for localhost access only.
2. Generate base64-encoded credentials for your test patron: echo -n "<username>:<password>" | base64
3. Query the basic auth endpoint: curl -k -s -o /dev/null -I -w "%{http_code}\n" https://localhost/api/basicauth -H "Authorization: Basic <base64-encoded-credentials>"

This will return 200 if patron authentication is successful, and 403 if patron auth fails or is not permitted.

There's also a Perl live test. However, the live test may fail in some environments due to an upstream bug in LWP::Protocol::https that prevents us from skipping certificate verification.[2] The packaged version of that module in Ubuntu 16.04 is affected; installing LWP::Protocol::https version >=6.07 from CPAN resolves the problem.

Apache configuration:
---------------------
To define a new RemoteAuth endpoint, add a new Location directive in your eg_vhost.conf file. The default configuration for Basic auth looks like this:

<Location /api/basicauth>
    SetHandler perl-script
    PerlHandler OpenILS::WWW::RemoteAuth
    Options +ExecCGI

    # access restricted to localhost by default; since this module provides no
    # client authentiation, restricting access by IP or other means is stongly
    # recommended
    Require local

    # remoteauth config name
    PerlSetVar OILSRemoteAuthConfig "Basic"
    # Perl module for processing requests
    PerlSetVar OILSRemoteAuthHandler "OpenILS::WWW::RemoteAuth::Basic"

    # staff username/password for config lookup and patron retrieval
    PerlSetVar OILSRemoteAuthClientUsername "admin"
    PerlSetVar OILSRemoteAuthClientPassword "demo123"
</Location>

The URL path /api/basicauth is our endpoint. External clients send appropriately-constructed requests to this URL and get a response indicating whether auth succeeded (and containing patron account information, depending on how the endpoint is configured). You can specify a different URL if you want.

Since different external services have different requirements for patron auth, each RemoteAuth endpoint handles requests differently. All endpoints use OpenILS::WWW::RemoteAuth as the main mod_perl handler, but specific implementation details are handled by the module specified by the OILSRemoteAuthHandler variable (e.g. OpenILS::WWW::RemoteAuth::Basic for Basic HTTP Authentication endpoints).

The OILSRemoteAuthConfig variable specifies the name of an entry in the config.remoteauth database table (see "Database configuration" below).

OILSRemoteAuthClientUsername and OILSRemoteAuthClientPassword are the username and password of an Evergreen user account that has the permissions necessary to (1) view users at this location and (2) retrieve config entries from the config.remoteauth table. Ideally, these credentials would be provided in the actual request. Where that's not possible, as in the present case, we include them in our Apache config.

When an endpoint doesn't use client-provided authorization credentials, we may wish to restrict access to the endpoint by IP address or other means. By default, Basic auth only allows connections originating from the same server ("Require local"). To make an endpoint publicly accessible, use "Require all granted". To restrict access by IP, use "Require ip <ip-address>".

Database configuration:
-----------------------
In each endpoint's Apache config, the OILSRemoteAuthConfig variable specifies the name of an entry in the config.remoteauth table (see "Apache configuration" above). The config entry tells us the context org unit for requests at this endpoint and defines rules for permitting auth requests. Supported rules include:

- perm: a permission which the user must have in order to be authenticated via RemoteAuth
- restrict_to_org: only allow users belonging to this location to be authenticated, even if the client can retrieve users at other locations
- allow_inactive: allow authentication for inactive users
- allow_expired: allow authentication for expired users
- block_list: authentication is not permitted if the user has a standing penalty at this location with one of these blocks

RemoteAuth uses the actor.permit_remoteauth database function to apply these rules during an authentication request. For example, if allow_expired is false and the user is expired, the database function will respond with 'expired' and RemoteAuth will deny the authentication request.

You can use the same configuration for multiple endpoints. For example, suppose you need separate endpoints for EZProxy and Basic HTTP Authentication, but you want both endpoints to use the same auth rules. In that case, each endpoint gets a separate entry in eg_vhost.conf, but the OILSRemoteAuthConfig variable will be the same for both.

[1]: Basic HTTP Authentication is described in RFC 7617:
https://tools.ietf.org/html/rfc7617

[2]: https://github.com/libwww-perl/libwww-perl/issues/83#issuecomment-405233929