1 # Copyright (C) 2019 BC Libraries Cooperative
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ======================================================================
18 # - RemoteAuth handler for HTTP basic access authorization (RFC 7617)
19 # - patron credentials are Bas64-encoded in Authorization header
20 # - no client authorization - restricting access by IP or other methods
21 # is strongly recommended!
22 # ======================================================================
24 package OpenILS::WWW::RemoteAuth::Basic;
25 use strict; use warnings;
26 use OpenILS::WWW::RemoteAuth;
27 use base "OpenILS::WWW::RemoteAuth";
29 use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN AUTH_REQUIRED HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST);
31 use OpenSRF::EX qw(:try);
32 use OpenSRF::Utils::Logger qw/$logger/;
33 use OpenILS::Utils::CStoreEditor qw/:funcs/;
34 use OpenSRF::Utils::JSON;
37 my( $class, $args ) = @_;
39 $class = ref $class || $class;
40 return bless($args, $class);
43 # here's our main method; it controls the various steps of the auth flow,
44 # prepares the response content, and returns an HTTP status code
47 my ($authtoken, $editor, $config);
51 my $client_user = $r->dir_config('OILSRemoteAuthClientUsername');
52 my $client_pw = $r->dir_config('OILSRemoteAuthClientPassword');
53 $authtoken = $self->do_client_auth($client_user, $client_pw);
55 $logger->error("RemoteAuth Basic failed on client auth: @_");
56 return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
58 return $self->client_not_authorized unless $authtoken;
62 $editor = new_editor( authtoken => $authtoken );
63 $config = $self->load_config($editor, $r);
65 $logger->error("RemoteAuth Basic failed on load config: @_");
66 return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
68 return $self->backend_error unless $config;
70 # extract patron id/password from Authorization request header
71 my $auth_header = $r->headers_in->get('Authorization');
72 unless (defined $auth_header && $auth_header =~ /^Basic /) {
73 # include WWW-Authenticate header on 401 responses, per RFC 7617
74 my $name = $config->name;
75 $r->err_headers_out->add('WWW-Authenticate' => "Basic realm=\"$name\"");
76 return Apache2::Const::AUTH_REQUIRED;
78 $auth_header =~ s/^Basic //;
79 my ($id, $password) = split(/:/, decode_base64($auth_header), 2);
82 my $stat = $self->do_patron_auth($editor, $config, $id, $password);
83 return $stat unless $stat == Apache2::Const::OK;
85 # XXX RFC 7617 doesn't require any particular content in the body of the
86 # response. The response content could be made configurable, but for now,
87 # let's respond with a simple JSON message containing the username/barcode
88 # used to authenticate the user: it's a predictable response, it doesn't
89 # require us to retrieve any additional patron information, and it's
90 # compatible with the Apereo CAS server's requirements for remote REST
91 # authentication, as documented here:
92 # https://apereo.github.io/cas/5.0.x/installation/Rest-Authentication.html
94 my $response_content = { id => $id };
95 $r->content_type('application/json');
96 $r->print( OpenSRF::Utils::JSON->perl2JSON($response_content) );
97 return Apache2::Const::OK;
101 # ... and here are all our util methods:
105 return Apache2::Const::OK;
108 # generic backend error
110 return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
113 # client error (e.g. missing params)
115 return Apache2::Const::HTTP_BAD_REQUEST;
119 sub client_not_authorized {
120 return Apache2::Const::AUTH_REQUIRED;
123 # patron auth failed (bad password etc)
124 sub patron_not_authenticated {
125 return Apache2::Const::FORBIDDEN;
128 # patron does not exist or is inactive/deleted
129 sub patron_not_found {
130 return Apache2::Const::FORBIDDEN;
133 # patron is barred or has blocking penalties
134 sub patron_is_blocked {
135 return Apache2::Const::FORBIDDEN;
139 sub patron_is_expired {
140 return Apache2::Const::FORBIDDEN;