From 84881509d71348a935420d86f3a7b25a51964379 Mon Sep 17 00:00:00 2001 From: Marcel de Rooy Date: Fri, 21 Jan 2022 07:58:45 +0000 Subject: [PATCH] Bug 29894: Send a confirmation notice Content-Type: text/plain; charset=utf-8 When registering or deregistering, send a confirmation. Test plan: Register or deregister with patron having email address. Verify that you got a confirmation mail. Run t/db_dependent/Koha/Auth/TwoFactorAuth.t Signed-off-by: Marcel de Rooy --- Koha/Auth/TwoFactorAuth.pm | 45 ++++++++++++++++++++++++ Koha/Exceptions/Patron.pm | 3 ++ members/two_factor_auth.pl | 7 ++++ t/db_dependent/Koha/Auth/TwoFactorAuth.t | 38 +++++++++++++++++++- 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/Koha/Auth/TwoFactorAuth.pm b/Koha/Auth/TwoFactorAuth.pm index efa9b9d829..75288a8f12 100644 --- a/Koha/Auth/TwoFactorAuth.pm +++ b/Koha/Auth/TwoFactorAuth.pm @@ -19,10 +19,15 @@ use Modern::Perl; use GD::Barcode; use MIME::Base64 qw( encode_base64 ); +use C4::Letters; use Koha::Exceptions; +use Koha::Exceptions::Patron; use base qw( Auth::GoogleAuth ); +use constant CONFIRM_NOTICE_REG => '2FA_REGISTER'; +use constant CONFIRM_NOTICE_DEREG => '2FA_DEREGISTER'; + =head1 NAME Koha::Auth::TwoFactorAuth- Koha class deal with Two factor authentication @@ -33,7 +38,9 @@ use Koha::Auth::TwoFactorAuth; my $secret = Koha::AuthUtils::generate_salt( 'weak', 16 ); my $auth = Koha::Auth::TwoFactorAuth->new({ patron => $patron, secret => $secret }); +my $image_src = $auth->qr_code; my $ok = $auth->verify( $pin_code, 1 ); +$auth->send_confirm_notice({ patron => $patron }); It's based on Auth::GoogleAuth @@ -101,4 +108,42 @@ sub qr_code { return "data:image/png;base64,". encode_base64( $data, q{} ); # does not contain newlines } +=head3 send_confirm_notice + + $auth->send_confirm_notice({ patron => $p, deregister => 1 }); + + Send a notice to confirm (de)registering 2FA. + Parameter patron is mandatory. + If there is no deregister param, a register notice is sent. + If the patron has no email address, we throw an exception. + +=cut + +sub send_confirm_notice { + my ( $self, $params ) = @_; + my $patron = $params->{patron}; + my $deregister = $params->{deregister}; + + Koha::Exceptions::MissingParameter->throw("Mandatory patron parameter missing") + unless $patron && ref($patron) eq 'Koha::Patron'; + Koha::Exceptions::Patron::MissingEmailAddress->throw + if !$patron->notice_email_address; + + my $letter = C4::Letters::GetPreparedLetter ( + module => 'members', # called patrons on interface + letter_code => $deregister ? CONFIRM_NOTICE_DEREG : CONFIRM_NOTICE_REG, + branchcode => $patron->branchcode, + lang => $patron->lang, + tables => { + 'branches' => $patron->branchcode, + 'borrowers' => $patron->id, + }, + ); + C4::Letters::EnqueueLetter({ + letter => $letter, + borrowernumber => $patron->id, + message_transport_type => 'email', + }) or warn "Couldnt enqueue 2FA notice for patron ". $patron->id; +} + 1; diff --git a/Koha/Exceptions/Patron.pm b/Koha/Exceptions/Patron.pm index 017340a340..596f28c94c 100644 --- a/Koha/Exceptions/Patron.pm +++ b/Koha/Exceptions/Patron.pm @@ -8,6 +8,9 @@ use Exception::Class ( 'Koha::Exceptions::Patron' => { isa => 'Koha::Exception', }, + 'Koha::Exceptions::Patron::MissingEmailAddress' => { + description => "Patron has no email address", + }, 'Koha::Exceptions::Patron::FailedDelete' => { isa => 'Koha::Exceptions::Patron', description => "Deleting patron failed" diff --git a/members/two_factor_auth.pl b/members/two_factor_auth.pl index 7b8a1a434b..6d5847e0ac 100755 --- a/members/two_factor_auth.pl +++ b/members/two_factor_auth.pl @@ -72,6 +72,9 @@ if ( $op eq 'register-2FA' ) { $logged_in_user->secret($secret32); $logged_in_user->auth_method('two-factor')->store; $op = 'registered'; + if( $logged_in_user->notice_email_address ) { + $auth->send_confirm_notice({ patron => $logged_in_user }); + } } else { $template->param( invalid_pin => 1, ); @@ -97,8 +100,12 @@ if ( $op eq 'enable-2FA' ) { elsif ( $op eq 'disable-2FA' ) { output_and_exit( $cgi, $cookie, $template, 'wrong_csrf_token' ) unless Koha::Token->new->check_csrf($csrf_pars); + my $auth = Koha::Auth::TwoFactorAuth->new({ patron => $logged_in_user }); $logged_in_user->secret(undef); $logged_in_user->auth_method('password')->store; + if( $logged_in_user->notice_email_address ) { + $auth->send_confirm_notice({ patron => $logged_in_user, deregister => 1 }); + } } $template->param( diff --git a/t/db_dependent/Koha/Auth/TwoFactorAuth.t b/t/db_dependent/Koha/Auth/TwoFactorAuth.t index 2bd0e9128b..4185e5cde0 100755 --- a/t/db_dependent/Koha/Auth/TwoFactorAuth.t +++ b/t/db_dependent/Koha/Auth/TwoFactorAuth.t @@ -1,15 +1,21 @@ use Modern::Perl; -use Test::More tests => 2; +use Test::More tests => 3; use Test::Exception; +use Test::MockModule; use t::lib::Mocks; use t::lib::TestBuilder; use Koha::Database; use Koha::Auth::TwoFactorAuth; +use Koha::Exceptions; +use Koha::Exceptions::Patron; +use Koha::Notice::Messages; our $schema = Koha::Database->new->schema; our $builder = t::lib::TestBuilder->new; +our $mocked_stuffer = Test::MockModule->new('Email::Stuffer'); +$mocked_stuffer->mock( 'send_or_die', sub { warn 'I do not send mails now'; } ); subtest 'new' => sub { plan tests => 10; @@ -79,3 +85,33 @@ subtest 'qr_code' => sub { $schema->storage->txn_rollback; }; + +subtest 'send_confirm_notice' => sub { + plan tests => 4; + $schema->storage->txn_begin; + + t::lib::Mocks::mock_preference('TwoFactorAuthentication', 1); + my $patron = $builder->build_object({ class => 'Koha::Patrons' }); + $patron->secret('you2wont2guess2it'); # this is base32 btw + $patron->auth_method('two-factor'); + $patron->store; + my $auth = Koha::Auth::TwoFactorAuth->new({ patron => $patron }); + + # Trivial tests: no patron, no email + throws_ok { $auth->send_confirm_notice; } + 'Koha::Exceptions::MissingParameter', + 'Croaked on missing patron'; + $patron->set({ email => undef, emailpro => undef, B_email => undef }); + throws_ok { $auth->send_confirm_notice({ patron => $patron }) } + 'Koha::Exceptions::Patron::MissingEmailAddress', + 'Croaked on missing email'; + + $patron->email('noreply@doof.nl')->store; + $auth->send_confirm_notice({ patron => $patron }); + is( Koha::Notice::Messages->search({ borrowernumber => $patron->id, letter_code => '2FA_REGISTER' })->count, 1, 'Found message' ); + $auth->send_confirm_notice({ patron => $patron, deregister => 1 }); + is( Koha::Notice::Messages->search({ borrowernumber => $patron->id, letter_code => '2FA_DEREGISTER' })->count, 1, 'Found message' ); + + $schema->storage->txn_rollback; + $mocked_stuffer->unmock; +}; -- 2.20.1