View | Details | Raw Unified | Return to bug 29894
Collapse All | Expand All

(-)a/Koha/Auth/TwoFactorAuth.pm (+45 lines)
Lines 19-28 use Modern::Perl; Link Here
19
use Imager::QRCode;
19
use Imager::QRCode;
20
use MIME::Base64 qw( encode_base64 );
20
use MIME::Base64 qw( encode_base64 );
21
21
22
use C4::Letters;
22
use Koha::Exceptions;
23
use Koha::Exceptions;
24
use Koha::Exceptions::Patron;
23
25
24
use base qw( Auth::GoogleAuth );
26
use base qw( Auth::GoogleAuth );
25
27
28
use constant CONFIRM_NOTICE_REG => '2FA_REGISTER';
29
use constant CONFIRM_NOTICE_DEREG => '2FA_DEREGISTER';
30
26
=head1 NAME
31
=head1 NAME
27
32
28
Koha::Auth::TwoFactorAuth- Koha class deal with Two factor authentication
33
Koha::Auth::TwoFactorAuth- Koha class deal with Two factor authentication
Lines 33-39 use Koha::Auth::TwoFactorAuth; Link Here
33
38
34
my $secret = Koha::AuthUtils::generate_salt( 'weak', 16 );
39
my $secret = Koha::AuthUtils::generate_salt( 'weak', 16 );
35
my $auth = Koha::Auth::TwoFactorAuth->new({ patron => $patron, secret => $secret });
40
my $auth = Koha::Auth::TwoFactorAuth->new({ patron => $patron, secret => $secret });
41
my $image_src = $auth->qr_code;
36
my $ok = $auth->verify( $pin_code, 1 );
42
my $ok = $auth->verify( $pin_code, 1 );
43
$auth->send_confirm_notice({ patron => $patron });
37
44
38
It's based on Auth::GoogleAuth
45
It's based on Auth::GoogleAuth
39
46
Lines 111-114 sub qr_code { Link Here
111
    return "data:image/png;base64,". encode_base64( $data, q{} ); # does not contain newlines
118
    return "data:image/png;base64,". encode_base64( $data, q{} ); # does not contain newlines
112
}
119
}
113
120
121
=head3 send_confirm_notice
122
123
    $auth->send_confirm_notice({ patron => $p, deregister => 1 });
124
125
    Send a notice to confirm (de)registering 2FA.
126
    Parameter patron is mandatory.
127
    If there is no deregister param, a register notice is sent.
128
    If the patron has no email address, we throw an exception.
129
130
=cut
131
132
sub send_confirm_notice {
133
    my ( $self, $params ) = @_;
134
    my $patron = $params->{patron};
135
    my $deregister = $params->{deregister};
136
137
    Koha::Exceptions::MissingParameter->throw("Mandatory patron parameter missing")
138
        unless $patron && ref($patron) eq 'Koha::Patron';
139
    Koha::Exceptions::Patron::MissingEmailAddress->throw
140
        if !$patron->notice_email_address;
141
142
    my $letter = C4::Letters::GetPreparedLetter (
143
        module => 'members', # called patrons on interface
144
        letter_code => $deregister ? CONFIRM_NOTICE_DEREG : CONFIRM_NOTICE_REG,
145
        branchcode => $patron->branchcode,
146
        lang => $patron->lang,
147
        tables => {
148
            'branches'    => $patron->branchcode,
149
            'borrowers'   => $patron->id,
150
        },
151
    );
152
    C4::Letters::EnqueueLetter({
153
        letter         => $letter,
154
        borrowernumber => $patron->id,
155
        message_transport_type => 'email',
156
    }) or warn "Couldnt enqueue 2FA notice for patron ". $patron->id;
157
}
158
114
1;
159
1;
(-)a/Koha/Exceptions/Patron.pm (+3 lines)
Lines 6-11 use Exception::Class ( Link Here
6
    'Koha::Exceptions::Patron' => {
6
    'Koha::Exceptions::Patron' => {
7
        description => "Something went wrong!"
7
        description => "Something went wrong!"
8
    },
8
    },
9
    'Koha::Exceptions::Patron::MissingEmailAddress' => {
10
        description => "Patron has no email address",
11
    },
9
    'Koha::Exceptions::Patron::FailedDelete' => {
12
    'Koha::Exceptions::Patron::FailedDelete' => {
10
        isa         => 'Koha::Exceptions::Patron',
13
        isa         => 'Koha::Exceptions::Patron',
11
        description => "Deleting patron failed"
14
        description => "Deleting patron failed"
(-)a/members/two_factor_auth.pl (+7 lines)
Lines 72-77 if ( $op eq 'register-2FA' ) { Link Here
72
        $logged_in_user->secret($secret32);
72
        $logged_in_user->secret($secret32);
73
        $logged_in_user->auth_method('two-factor')->store;
73
        $logged_in_user->auth_method('two-factor')->store;
74
        $op = 'registered';
74
        $op = 'registered';
75
        if( $logged_in_user->notice_email_address ) {
76
            $auth->send_confirm_notice({ patron => $logged_in_user });
77
        }
75
    }
78
    }
76
    else {
79
    else {
77
        $template->param( invalid_pin => 1, );
80
        $template->param( invalid_pin => 1, );
Lines 97-104 if ( $op eq 'enable-2FA' ) { Link Here
97
elsif ( $op eq 'disable-2FA' ) {
100
elsif ( $op eq 'disable-2FA' ) {
98
    output_and_exit( $cgi, $cookie, $template, 'wrong_csrf_token' )
101
    output_and_exit( $cgi, $cookie, $template, 'wrong_csrf_token' )
99
        unless Koha::Token->new->check_csrf($csrf_pars);
102
        unless Koha::Token->new->check_csrf($csrf_pars);
103
    my $auth = Koha::Auth::TwoFactorAuth->new({ patron => $logged_in_user });
100
    $logged_in_user->secret(undef);
104
    $logged_in_user->secret(undef);
101
    $logged_in_user->auth_method('password')->store;
105
    $logged_in_user->auth_method('password')->store;
106
    if( $logged_in_user->notice_email_address ) {
107
        $auth->send_confirm_notice({ patron => $logged_in_user, deregister => 1 });
108
    }
102
}
109
}
103
110
104
$template->param(
111
$template->param(
(-)a/t/db_dependent/Koha/Auth/TwoFactorAuth.t (-2 / +37 lines)
Lines 1-15 Link Here
1
use Modern::Perl;
1
use Modern::Perl;
2
use Test::More tests => 2;
2
use Test::More tests => 3;
3
use Test::Exception;
3
use Test::Exception;
4
use Test::MockModule;
4
5
5
use t::lib::Mocks;
6
use t::lib::Mocks;
6
use t::lib::TestBuilder;
7
use t::lib::TestBuilder;
7
8
8
use Koha::Database;
9
use Koha::Database;
9
use Koha::Auth::TwoFactorAuth;
10
use Koha::Auth::TwoFactorAuth;
11
use Koha::Exceptions;
12
use Koha::Exceptions::Patron;
13
use Koha::Notice::Messages;
10
14
11
our $schema = Koha::Database->new->schema;
15
our $schema = Koha::Database->new->schema;
12
our $builder = t::lib::TestBuilder->new;
16
our $builder = t::lib::TestBuilder->new;
17
our $mocked_stuffer =  Test::MockModule->new('Email::Stuffer');
18
$mocked_stuffer->mock( 'send_or_die', sub { warn 'I do not send mails now'; } );
13
19
14
subtest 'new' => sub {
20
subtest 'new' => sub {
15
    plan tests => 10;
21
    plan tests => 10;
Lines 79-81 subtest 'qr_code' => sub { Link Here
79
85
80
    $schema->storage->txn_rollback;
86
    $schema->storage->txn_rollback;
81
};
87
};
82
- 
88
89
subtest 'send_confirm_notice' => sub {
90
    plan tests => 4;
91
    $schema->storage->txn_begin;
92
93
    t::lib::Mocks::mock_preference('TwoFactorAuthentication', 1);
94
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
95
    $patron->secret('you2wont2guess2it'); # this is base32 btw
96
    $patron->auth_method('two-factor');
97
    $patron->store;
98
    my $auth = Koha::Auth::TwoFactorAuth->new({ patron => $patron });
99
100
    # Trivial tests: no patron, no email
101
    throws_ok { $auth->send_confirm_notice; }
102
        'Koha::Exceptions::MissingParameter',
103
        'Croaked on missing patron';
104
    $patron->set({ email => undef, emailpro => undef, B_email => undef });
105
    throws_ok { $auth->send_confirm_notice({ patron => $patron }) }
106
        'Koha::Exceptions::Patron::MissingEmailAddress',
107
        'Croaked on missing email';
108
109
    $patron->email('noreply@doof.nl')->store;
110
    $auth->send_confirm_notice({ patron => $patron });
111
    is( Koha::Notice::Messages->search({ borrowernumber => $patron->id, letter_code => '2FA_REGISTER' })->count, 1, 'Found message' );
112
    $auth->send_confirm_notice({ patron => $patron, deregister => 1 });
113
    is( Koha::Notice::Messages->search({ borrowernumber => $patron->id, letter_code => '2FA_DEREGISTER' })->count, 1, 'Found message' );
114
115
    $schema->storage->txn_rollback;
116
    $mocked_stuffer->unmock;
117
};

Return to bug 29894