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.
19 OpenILS::Application::AuthProxy - Negotiator for proxy-style authentication
23 Dan Wells, dbw2@calvin.edu
27 package OpenILS::Application::AuthProxy;
31 use OpenILS::Application;
32 use base qw/OpenILS::Application/;
33 use OpenSRF::Utils::Logger qw(:logger);
34 use OpenSRF::Utils::SettingsClient;
35 use OpenILS::Application::AppUtils;
36 use OpenILS::Utils::Fieldmapper;
38 use UNIVERSAL::require;
39 use Digest::MD5 qw/md5_hex/;
40 my $U = 'OpenILS::Application::AppUtils';
42 # NOTE: code assumes throughout that '0' is never a valid username, barcode,
43 # or password; some logic will need to be tweaked to support it if needed.
46 my %authenticators_by_name;
47 my $enabled = 'false';
50 my $conf = OpenSRF::Utils::SettingsClient->new;
51 my @pfx = ( "apps", "open-ils.auth_proxy", "app_settings" );
53 $enabled = $conf->config_value( @pfx, 'enabled' );
55 my $auth_configs = $conf->config_value( @pfx, 'authenticators', 'authenticator' );
56 $auth_configs = [$auth_configs] if ref($auth_configs) eq 'HASH';
58 if ( !@$auth_configs ) {
59 $logger->error("AuthProxy: authenticators list not found!");
61 foreach my $auth_config (@$auth_configs) {
63 if ($auth_config->{'name'} eq 'native') {
64 $auth_handler = 'OpenILS::Application::AuthProxy::Native';
66 $auth_handler = $auth_config->{module};
67 next unless $auth_handler;
69 $logger->debug("Attempting to load AuthProxy handler: $auth_handler");
72 $logger->error("Unable to load AuthProxy handler [$auth_handler]: $@");
77 &_make_option_array($auth_config, 'login_types', 'type');
78 &_make_option_array($auth_config, 'org_units', 'unit');
80 my $authenticator = $auth_handler->new($auth_config);
81 push @authenticators, $authenticator;
82 $authenticators_by_name{$authenticator->name} = $authenticator;
83 $logger->debug("Successfully loaded AuthProxy handler: $auth_handler");
85 $logger->debug("AuthProxy: authenticators loaded");
89 # helper function to simplify the config structure
90 sub _make_option_array {
91 my ($auth_config, $container_name, $node_name) = @_;
93 if (exists $auth_config->{$container_name}
94 and ref $auth_config->{$container_name} eq 'HASH') {
95 my $nodes = $auth_config->{$container_name}{$node_name};
97 if (ref $nodes ne 'ARRAY') {
98 $auth_config->{$container_name} = [$nodes];
100 $auth_config->{$container_name} = $nodes;
103 delete $auth_config->{$container_name};
106 delete $auth_config->{$container_name};
112 __PACKAGE__->register_method(
114 api_name => "open-ils.auth_proxy.enabled",
119 desc => q/Check if AuthProxy is enabled/,
121 desc => "True if enabled, false if not",
127 return (!$enabled or $enabled eq 'false') ? 0 : 1;
130 __PACKAGE__->register_method(
132 api_name => "open-ils.auth_proxy.login",
137 desc => q/Basic single-factor login method/,
139 {name=> "args", desc => q/A hash of arguments. Valid keys and their meanings:
140 username := Username to authenticate.
141 barcode := Barcode of user to authenticate (currently supported by 'native' only!)
142 password := Password for verifying the user.
143 type := Type of login being attempted (Staff Client, OPAC, etc.).
149 desc => "Authentication seed or failure event",
155 my ( $self, $conn, $args ) = @_;
157 return OpenILS::Event->new( 'LOGIN_FAILED' )
158 unless (&enabled() and ($args->{'username'} or $args->{'barcode'}));
161 my $authenticated = 0;
164 # if they specify an authenticator by name, only try that one
165 if ($args->{'name'}) {
166 $auths = [$authenticators_by_name{$args->{'name'}}];
168 $auths = \@authenticators;
171 foreach my $authenticator (@$auths) {
172 # skip authenticators specified for a different login type
174 if ($authenticator->login_types and $args->{'type'}) {
175 next unless grep(/^(all|$args->{'type'})$/, @{$authenticator->{'login_types'}});
177 if ($authenticator->org_units and $args->{'org'}) {
178 next unless grep(/^(all|$args->{'org'})$/, @{$authenticator->{'org_units'}});
182 # treat native specially
183 if ($authenticator->name eq 'native') {
184 $event = &_do_login($args);
186 $event = $authenticator->authenticate($args);
188 my $code = $U->event_code($event);
190 push @error_events, $event;
191 } elsif (defined $code) { # code is '0', i.e. SUCCESS
192 if (exists $event->{'payload'}) { # we have a complete native login
194 } else { # do a 'forced' login
195 return &_do_login($args, 1);
200 # if we got this far, we failed
201 # TODO: send back some form of collected error events
202 return OpenILS::Event->new( 'LOGIN_FAILED' );
207 my $authenticated = shift;
209 my $seeder = $args->{'username'} ? $args->{'username'} : $args->{'barcode'};
211 OpenSRF::AppSession->create("open-ils.auth")
212 ->request( 'open-ils.auth.authenticate.init', $seeder )->gather(1);
214 return OpenILS::Event->new( 'LOGIN_FAILED' )
217 my $real_password = $args->{'password'};
218 # if we have already authenticated, look up the password needed to finish
219 if ($authenticated) {
220 # barcode-based login is supported only for 'native' logins
221 return OpenILS::Event->new( 'LOGIN_FAILED' ) if !$args->{'username'};
222 my $user = $U->cstorereq(
223 "open-ils.cstore.direct.actor.user.search.atomic",
224 { usrname => $args->{'username'} }
226 $args->{'password'} = md5_hex( $seed . $user->[0]->passwd );
228 $args->{'password'} = md5_hex( $seed . md5_hex($real_password) );
230 my $response = OpenSRF::AppSession->create("open-ils.auth")->request(
231 'open-ils.auth.authenticate.complete',
234 $args->{'password'} = $real_password;
236 return OpenILS::Event->new( 'LOGIN_FAILED' )
242 __PACKAGE__->register_method(
243 method => "authenticators",
244 api_name => "open-ils.auth_proxy.authenticators",
249 desc => q/Get a list of viable authenticators/,
251 {name=> "args", desc => q/A hash of arguments. Valid keys and their meanings:
252 type := Type of login being attempted (Staff Client, OPAC, etc.).
258 desc => "List of viable authenticators",
264 my ( $self, $conn, $args ) = @_;
268 foreach my $authenticator (@authenticators) {
269 # skip authenticators specified for a different login type
271 if ($authenticator->login_types and $args->{'type'}) {
272 next unless grep(/^(all|$args->{'type'})$/, @{$authenticator->login_types});
274 if ($authenticator->org_units and $args->{'org'}) {
275 next unless grep(/^(all|$args->{'org'})$/, @{$authenticator->org_units});
278 push @viable_auths, $authenticator->name;
281 return \@viable_auths;
285 # --------------------------------------------------------------------------
286 # Stub package for 'native' authenticator
287 # --------------------------------------------------------------------------
288 package OpenILS::Application::AuthProxy::Native;
289 use strict; use warnings;
290 use base 'OpenILS::Application::AuthProxy::AuthBase';