From fa7a72ae7579a45c51bea258e6df25f4dea2d5e3 Mon Sep 17 00:00:00 2001 From: Alex Arnaud Date: Thu, 1 Mar 2018 08:43:41 +0000 Subject: [PATCH] Bug 20340 - Ability to add and use authentication plugins Plugins must be placed in and enabled with AuthenticationModule system preference. Test plan: - Apply this patch and update your database, - set UseKohaPlugins to enabled, - define a pluginsdir in your koha-conf.xml, - put a plugin in this directory. - I.e, take the one provided in tests directory: - t/Koha/Plugin/TestAuth.pm, - login/pass is test/test - You should have: /your/plugins/dir/Koha/Plugin/TestAuth.pm, - replace default by your plugin in AuthenticationModule system preference, - test authentication with and without the plugin. --- C4/Auth.pm | 32 ++++++++++++ Koha/Plugins.pm | 1 + admin/preferences.pl | 12 +++++ .../add-AuthenticationModule-syspref.sql | 2 + installer/data/mysql/sysprefs.sql | 3 +- .../prog/en/modules/admin/preferences/admin.pref | 6 +++ t/Koha/Plugin/TestAuth.pm | 60 ++++++++++++++++++++++ t/db_dependent/Auth.t | 16 +++++- 8 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/add-AuthenticationModule-syspref.sql create mode 100644 t/Koha/Plugin/TestAuth.pm diff --git a/C4/Auth.pm b/C4/Auth.pm index c3ad351..1e76a5f 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -36,6 +36,7 @@ use Koha::AuthUtils qw(get_script_name hash_password); use Koha::Library::Groups; use Koha::Libraries; use Koha::Patrons; +use Koha::Plugins; use POSIX qw/strftime/; use List::MoreUtils qw/ any /; use Encode qw( encode is_utf8); @@ -1765,6 +1766,10 @@ sub checkpw { my ( $dbh, $userid, $password, $query, $type, $no_set_userenv ) = @_; $type = 'opac' unless $type; + if ( my $module = enabled_auth_module() ) { + return $module->checkpw(@_); + } + my @return; my $patron = Koha::Patrons->find({ userid => $userid }); my $check_internal_as_fallback = 0; @@ -2088,6 +2093,33 @@ sub getborrowernumber { return 0; } +=head2 enabled_auth_module + + my $module = enabled_auth_module(); + + Instantiate and return the module (object) enable for + authentication (AuthenticationModule syspref). + +=cut + +sub enabled_auth_module { + my $module = C4::Context->preference('AuthenticationModule'); + + return if $module eq 'default'; + + my @plugins = Koha::Plugins->new()->GetPlugins({ + method => 'checkpw', + }); + + foreach my $plugin (@plugins) { + if ( $plugin->{class} eq $module ) { + return $plugin; + } + } + + return; +} + END { } # module clean-up code here (global destructor) 1; __END__ diff --git a/Koha/Plugins.pm b/Koha/Plugins.pm index c0aaa9c..a67c2e3 100644 --- a/Koha/Plugins.pm +++ b/Koha/Plugins.pm @@ -82,6 +82,7 @@ sub GetPlugins { # Limit results by method or metadata next if $method && !$plugin->can($method); my $plugin_metadata = $plugin->get_metadata; + next if $plugin_metadata and %$req_metadata and any { !$plugin_metadata->{$_} || $plugin_metadata->{$_} ne $req_metadata->{$_} } keys %$req_metadata; diff --git a/admin/preferences.pl b/admin/preferences.pl index 1e12b1e..763a6df 100755 --- a/admin/preferences.pl +++ b/admin/preferences.pl @@ -29,6 +29,7 @@ use C4::Log; use C4::Output; use C4::Templates; use Koha::Acquisition::Currencies; +use Koha::Plugins; use File::Spec; use IO::File; use YAML::Syck qw(); @@ -92,6 +93,17 @@ sub _get_chunk { $options{'choices'} = { map { $_ => $_ } getallthemes( 'opac' ) } } elsif ( $options{'choices'} eq 'staff-templates' ) { $options{'choices'} = { map { $_ => $_ } getallthemes( 'intranet' ) } + } elsif ( $options{'choices'} eq 'auth-plugins' ) { + my $choices = {'default' => 'default Koha'}; + my @plugins = Koha::Plugins->new()->GetPlugins({ + method => 'checkpw', + }); + + foreach my $plugin ( @plugins ) { + my $metadata = $plugin->get_metadata; + $choices->{ $metadata->{class} } = $metadata->{name}; + } + $options{'choices'} = $choices; } else { die 'Unrecognized source of preference values: ' . $options{'choices'}; } diff --git a/installer/data/mysql/atomicupdate/add-AuthenticationModule-syspref.sql b/installer/data/mysql/atomicupdate/add-AuthenticationModule-syspref.sql new file mode 100644 index 0000000..4ca38f2 --- /dev/null +++ b/installer/data/mysql/atomicupdate/add-AuthenticationModule-syspref.sql @@ -0,0 +1,2 @@ +INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES +('AuthenticationModule','default','','Define the current authentication module','Plugin'); diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 76bb2cc..4f95d7d 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -603,5 +603,6 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'), ('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'), ('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'), -('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo') +('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo'), +('AuthenticationModule','default','','Define the current authentication module','Plugin') ; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref index 2e8dce7..1b5d37b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref @@ -111,6 +111,12 @@ Administration: choices: yes: "Yes" no: "No" + Authentication: + - + - Use + - pref: AuthenticationModule + choices: auth-plugins + - module for authentication. CAS authentication: - - "Use CAS for login authentication: " diff --git a/t/Koha/Plugin/TestAuth.pm b/t/Koha/Plugin/TestAuth.pm new file mode 100644 index 0000000..49f93a6 --- /dev/null +++ b/t/Koha/Plugin/TestAuth.pm @@ -0,0 +1,60 @@ +package Koha::Plugin::TestAuth; + +use Modern::Perl; + +use base 'Koha::Plugins::Base'; + +our $VERSION = '1.0'; +our $metadata = { + name => 'Authentication test plugin', + author => 'Alex Arnaud ', + description => 'Plugin for testing authentication plugin API', + date_authored => '2018-02-28', + date_updated => '2018-02-28', + minimum_version => '17.05', + maximum_version => undef, + type => 'authentication', + version => $VERSION, +}; + +=head1 METHODS + +=head2 new + + Koha::Plugin::TestAuth->new(); + + Create new object + +=cut + +sub new { + my ($class, $args) = @_; + + $args->{metadata} = $metadata; + $args->{metadata}->{class} = $class; + + my $self = $class->SUPER::new($args); + + return $self; +} + +=head2 checkpw + + ($result, cardnumber, $userid) = + checkpw($self, $dbh, $userid, $password, $query, $type, $no_set_userenv); + + Authenticate a patron using login/password. + +=cut + +sub checkpw { + my ( $self, $dbh, $userid, $password, $query, $type, $no_set_userenv ) = @_; + + if ( $userid = 'test' && $password eq 'test' ) { + return (1, 'test', 'test'); + } + + return (0, '', ''); +} + +1; diff --git a/t/db_dependent/Auth.t b/t/db_dependent/Auth.t index f1d1ddd..4242a73 100644 --- a/t/db_dependent/Auth.t +++ b/t/db_dependent/Auth.t @@ -10,7 +10,7 @@ use CGI qw ( -utf8 ); use Test::MockObject; use Test::MockModule; use List::MoreUtils qw/all any none/; -use Test::More tests => 21; +use Test::More tests => 27; use Test::Warn; use t::lib::Mocks; use t::lib::TestBuilder; @@ -20,8 +20,11 @@ use C4::Members; use Koha::AuthUtils qw/hash_password/; use Koha::Database; use Koha::Patrons; +use File::Basename; BEGIN { + push( @INC, dirname(__FILE__) . '/..' ); + use_ok('C4::Auth'); } @@ -32,6 +35,7 @@ my $dbh = C4::Context->dbh; # FIXME: SessionStorage defaults to mysql, but it seems to break transaction # handling t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); +t::lib::Mocks::mock_preference( 'AuthenticationModule', 'default' ); $schema->storage->txn_begin; @@ -88,6 +92,16 @@ my $hash2 = hash_password('password'); is( C4::Context->userenv->{branch}, $library->{branchcode}, 'Userenv branch is preserved if no_set_userenv is true' ); ok( checkpw( $dbh, $patron->{userid}, 'password', undef, undef, 0 ), 'checkpw still returns true' ); isnt( C4::Context->userenv->{branch}, $library->{branchcode}, 'Userenv branch is overwritten if no_set_userenv is false' ); + + t::lib::Mocks::mock_preference( 'AuthenticationModule', 'Koha::Plugin::TestAuth' ); + my @result = checkpw( $dbh, $patron->{userid}, 'password', undef, undef, 1 ); + is( $result[0], 0, 'With TestAuth plugin, checkpw returns 0' ); + is( $result[1], '', 'With TestAuth plugin, checkpw returns empty cardnumber' ); + is( $result[2], '', 'With TestAuth plugin, checkpw returns empty userid' ); + @result = checkpw( $dbh, 'test', 'test', undef, undef, 1 ); + is( $result[0], 1, 'With TestAuth plugin, checkpw returns 1' ); + is( $result[1], 'test', 'With TestAuth plugin, checkpw returns test cardnumber' ); + is( $result[2], 'test', 'With TestAuth plugin, checkpw returns test userid' ); } # get_template_and_user tests -- 2.7.4