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

(-)a/t/Auth_with_shibboleth.t (-144 / +63 lines)
Lines 1-5 Link Here
1
#!/usr/bin/perl
1
#!/usr/bin/perl
2
2
3
# Copyright 2014, 2023 Koha development team
4
#
3
# This file is part of Koha.
5
# This file is part of Koha.
4
#
6
#
5
# Koha is free software; you can redistribute it and/or modify it
7
# Koha is free software; you can redistribute it and/or modify it
Lines 18-111 Link Here
18
use Modern::Perl;
20
use Modern::Perl;
19
use utf8;
21
use utf8;
20
22
21
use Test::More tests => 18;
23
use Test::More tests => 5;
22
use Test::MockModule;
24
use Test::MockModule;
23
use Test::Warn;
25
use Test::Warn;
24
use CGI qw(-utf8 );
26
use CGI qw(-utf8 );
25
use File::Temp qw(tempdir);
27
use File::Temp qw(tempdir);
26
28
29
use t::lib::Mocks;
27
use t::lib::Mocks::Logger;
30
use t::lib::Mocks::Logger;
28
use t::lib::TestBuilder;
31
use t::lib::TestBuilder;
29
32
30
use C4::Context;
33
use C4::Auth_with_shibboleth qw( shib_ok login_shib_url get_login_shib checkpw_shib );
31
use Koha::Database;
34
use Koha::Database;
32
35
33
my $schema = Koha::Database->new->schema;
36
my $schema = Koha::Database->new->schema;
34
$schema->storage->txn_begin;
37
$schema->storage->txn_begin;
35
my $builder = t::lib::TestBuilder->new;
38
my $builder = t::lib::TestBuilder->new;
39
my $logger = t::lib::Mocks::Logger->new();
36
40
37
# Mock Variables
41
# Mock variables
38
my $matchpoint = 'userid';
42
my $shibboleth;
39
my $autocreate = 0;
43
change_config({});
40
my $welcome    = 0;
44
my $interface = 'opac';
41
my $sync       = 0;
42
my %mapping    = (
43
    'userid'       => { 'is' => 'uid' },
44
    'surname'      => { 'is' => 'sn' },
45
    'dateexpiry'   => { 'is' => 'exp' },
46
    'categorycode' => { 'is' => 'cat' },
47
    'address'      => { 'is' => 'add' },
48
    'city'         => { 'is' => 'city' },
49
    'emailpro'     => { 'is' => 'emailpro' },
50
);
51
$ENV{'uid'}      = "test1234";
52
$ENV{'sn'}       = undef;
53
$ENV{'exp'}      = undef;
54
$ENV{'cat'}      = undef;
55
$ENV{'add'}      = undef;
56
$ENV{'city'}     = undef;
57
$ENV{'emailpro'} = undef;
58
59
# Setup Mocks
60
## Mock Context
61
my $context = Test::MockModule->new('C4::Context');
62
63
### Mock ->config
64
$context->mock( 'config', \&mockedConfig );
65
45
66
### Mock ->preference
46
# Mock few preferences
67
my $OPACBaseURL = "testopac.com";
47
t::lib::Mocks::mock_preference('OPACBaseURL', 'testopac.com' );
68
my $staffClientBaseURL = "teststaff.com";
48
t::lib::Mocks::mock_preference('StaffClientBaseURL', 'teststaff.com' );
69
$context->mock( 'preference', \&mockedPref );
49
t::lib::Mocks::mock_preference( 'EmailFieldPrimary', 'OFF' );
50
t::lib::Mocks::mock_preference( 'EmailFieldPrecedence', 'emailpro' );
70
51
71
### Mock ->tz
52
# Mock Context: config, tz and interface
53
my $context = Test::MockModule->new('C4::Context');
54
$context->mock( 'config', sub { return $shibboleth; } ); # easier than caching by Mocks::mock_config
72
$context->mock( 'timezone', sub { return 'local'; } );
55
$context->mock( 'timezone', sub { return 'local'; } );
56
$context->mock( 'interface', sub { return $interface; } );
73
57
74
### Mock ->interface
58
# Mock Letters: GetPreparedLetter, EnqueueLetter and SendQueuedMessages
75
my $interface = 'opac';
59
# We want to test the params
76
$context->mock( 'interface', \&mockedInterface );
77
78
### Mock Letters
79
my $mocked_letters = Test::MockModule->new('C4::Letters');
60
my $mocked_letters = Test::MockModule->new('C4::Letters');
80
# we want to test the params
81
$mocked_letters->mock( 'GetPreparedLetter', sub {
61
$mocked_letters->mock( 'GetPreparedLetter', sub {
82
    warn "GetPreparedLetter called";
62
    warn "GetPreparedLetter called";
83
    return 1;
63
    return 1;
84
});
64
});
85
# we don't care about EnqueueLetter for now
86
$mocked_letters->mock( 'EnqueueLetter', sub {
65
$mocked_letters->mock( 'EnqueueLetter', sub {
87
    warn "EnqueueLetter called";
66
    warn "EnqueueLetter called";
88
    # return a 'message_id'
67
    # return a 'message_id'
89
    return 42;
68
    return 42;
90
});
69
});
91
# we don't care about EnqueueLetter for now
92
$mocked_letters->mock( 'SendQueuedMessages', sub {
70
$mocked_letters->mock( 'SendQueuedMessages', sub {
93
    my $params = shift;
71
    my $params = shift;
94
    warn "SendQueuedMessages called with message_id: $params->{message_id}";
72
    warn "SendQueuedMessages called with message_id: $params->{message_id}";
95
    return 1;
73
    return 1;
96
});
74
});
97
75
98
# Tests
76
# Start testing ----------------------------------------------------------------
99
##############################################################
100
77
101
my $logger = t::lib::Mocks::Logger->new();
102
103
# Can module load
104
use C4::Auth_with_shibboleth qw( shib_ok login_shib_url get_login_shib checkpw_shib );
105
require_ok('C4::Auth_with_shibboleth');
106
107
# Subroutine tests
108
## shib_ok
109
subtest "shib_ok tests" => sub {
78
subtest "shib_ok tests" => sub {
110
    plan tests => 5;
79
    plan tests => 5;
111
    my $result;
80
    my $result;
Lines 114-126 subtest "shib_ok tests" => sub { Link Here
114
    is( shib_ok(), '1', "good config" );
83
    is( shib_ok(), '1', "good config" );
115
84
116
    # bad config, no debug
85
    # bad config, no debug
117
    $matchpoint = undef;
86
    delete $shibboleth->{matchpoint};
118
    warnings_are { $result = shib_ok() }
87
    warnings_are { $result = shib_ok() }
119
    [ { carped => 'shibboleth matchpoint not defined' }, ],
88
    [ { carped => 'shibboleth matchpoint not defined' }, ],
120
      "undefined matchpoint = fatal config, warning given";
89
      "undefined matchpoint = fatal config, warning given";
121
    is( $result, '0', "bad config" );
90
    is( $result, '0', "bad config" );
122
91
123
    $matchpoint = 'email';
92
    change_config({ matchpoint => 'email' });
124
    warnings_are { $result = shib_ok() }
93
    warnings_are { $result = shib_ok() }
125
    [ { carped => 'shibboleth matchpoint not mapped' }, ],
94
    [ { carped => 'shibboleth matchpoint not mapped' }, ],
126
      "unmapped matchpoint = fatal config, warning given";
95
      "unmapped matchpoint = fatal config, warning given";
Lines 128-134 subtest "shib_ok tests" => sub { Link Here
128
97
129
    # add test for undefined shibboleth block
98
    # add test for undefined shibboleth block
130
    $logger->clear;
99
    $logger->clear;
131
    reset_config();
100
    change_config({});
132
};
101
};
133
102
134
subtest "login_shib_url tests" => sub {
103
subtest "login_shib_url tests" => sub {
Lines 175-203 subtest "login_shib_url tests" => sub { Link Here
175
    close $fh_read;
144
    close $fh_read;
176
};
145
};
177
146
178
## get_login_shib
179
subtest "get_login_shib tests" => sub {
147
subtest "get_login_shib tests" => sub {
180
181
    plan tests => 3;
148
    plan tests => 3;
182
149
183
    my $login;
150
    my $login = get_login_shib();
184
185
    $login = get_login_shib();
186
187
    $logger->debug_is("koha borrower field to match: userid", "borrower match field debug info")
151
    $logger->debug_is("koha borrower field to match: userid", "borrower match field debug info")
188
           ->debug_is("shibboleth attribute to match: uid",   "shib match attribute debug info")
152
           ->debug_is("shibboleth attribute to match: uid",   "shib match attribute debug info")
189
           ->clear();
153
           ->clear();
190
191
    is( $login, "test1234", "good config, attribute value returned" );
154
    is( $login, "test1234", "good config, attribute value returned" );
192
};
155
};
193
156
194
## checkpw_shib
195
subtest "checkpw_shib tests" => sub {
157
subtest "checkpw_shib tests" => sub {
196
    plan tests => 33;
158
    plan tests => 33;
197
159
198
    my $shib_login;
199
    my ( $retval, $retcard, $retuserid );
200
201
    # Test borrower data
160
    # Test borrower data
202
    my $test_borrowers = [
161
    my $test_borrowers = [
203
        { cardnumber => 'testcardnumber', userid => 'test1234', surname => 'renvoize', address => 'myaddress', city => 'johnston', email => undef },
162
        { cardnumber => 'testcardnumber', userid => 'test1234', surname => 'renvoize', address => 'myaddress', city => 'johnston', email => undef },
Lines 209-217 subtest "checkpw_shib tests" => sub { Link Here
209
    my $library = $builder->build_object({ class => 'Koha::Libraries' });
168
    my $library = $builder->build_object({ class => 'Koha::Libraries' });
210
169
211
    # good user
170
    # good user
212
    $shib_login = "test1234";
171
    my $shib_login = "test1234";
213
    ( $retval, $retcard, $retuserid ) = checkpw_shib($shib_login);
172
    my ( $retval, $retcard, $retuserid ) = checkpw_shib($shib_login);
214
215
    is( $logger->count(), 2,          "Two debugging entries");
173
    is( $logger->count(), 2,          "Two debugging entries");
216
    is( $retval,    "1",              "user authenticated" );
174
    is( $retval,    "1",              "user authenticated" );
217
    is( $retcard,   "testcardnumber", "expected cardnumber returned" );
175
    is( $retcard,   "testcardnumber", "expected cardnumber returned" );
Lines 229-236 subtest "checkpw_shib tests" => sub { Link Here
229
           ->clear();
187
           ->clear();
230
188
231
    # duplicated matchpoint
189
    # duplicated matchpoint
232
    $matchpoint = 'email';
190
    change_config({ matchpoint => 'email', mapping => { email => { is => 'email' }} });
233
    $mapping{'email'} = { is => 'email' };
234
    $shib_login = 'kid@clamp.io';
191
    $shib_login = 'kid@clamp.io';
235
    ( $retval, $retcard, $retuserid ) = checkpw_shib($shib_login);
192
    ( $retval, $retcard, $retuserid ) = checkpw_shib($shib_login);
236
    is( $retval, "0", "user not authenticated if duplicated matchpoint" );
193
    is( $retval, "0", "user not authenticated if duplicated matchpoint" );
Lines 244-254 subtest "checkpw_shib tests" => sub { Link Here
244
           ->warn_is('There are several users with email of kid@clamp.io, matchpoints must be unique', "duplicated matchpoint warned with debug")
201
           ->warn_is('There are several users with email of kid@clamp.io, matchpoints must be unique', "duplicated matchpoint warned with debug")
245
           ->clear();
202
           ->clear();
246
203
247
    reset_config();
248
249
    # autocreate user (welcome)
204
    # autocreate user (welcome)
250
    $autocreate      = 1;
205
    change_config({ autocreate => 1, welcome => 1 });
251
    $welcome         = 1;
252
    $shib_login      = 'test4321';
206
    $shib_login      = 'test4321';
253
    $ENV{'uid'}      = 'test4321';
207
    $ENV{'uid'}      = 'test4321';
254
    $ENV{'sn'}       = "pika";
208
    $ENV{'sn'}       = "pika";
Lines 280-290 subtest "checkpw_shib tests" => sub { Link Here
280
    is_deeply( [ map { $rec->$_ } qw/surname dateexpiry address city/ ],
234
    is_deeply( [ map { $rec->$_ } qw/surname dateexpiry address city/ ],
281
        [qw/pika 2017-01-01 Address City/],
235
        [qw/pika 2017-01-01 Address City/],
282
        'Found $new_user surname' );
236
        'Found $new_user surname' );
283
    $autocreate = 0;
284
    $welcome    = 0;
285
237
286
    # sync user
238
    # sync user
287
    $sync = 1;
239
    $shibboleth->{sync} = 1;
288
    $ENV{'city'} = 'AnotherCity';
240
    $ENV{'city'} = 'AnotherCity';
289
    ( $retval, $retcard, $retuserid ) = checkpw_shib($shib_login);
241
    ( $retval, $retcard, $retuserid ) = checkpw_shib($shib_login);
290
    $logger->debug_is("koha borrower field to match: userid", "borrower match field debug info")
242
    $logger->debug_is("koha borrower field to match: userid", "borrower match field debug info")
Lines 298-304 subtest "checkpw_shib tests" => sub { Link Here
298
    is_deeply( [ map { $rec->$_ } qw/surname dateexpiry address city/ ],
250
    is_deeply( [ map { $rec->$_ } qw/surname dateexpiry address city/ ],
299
        [qw/pika 2017-01-01 Address AnotherCity/],
251
        [qw/pika 2017-01-01 Address AnotherCity/],
300
        'Found $sync_user synced city' );
252
        'Found $sync_user synced city' );
301
    $sync = 0;
253
    change_config({ sync => 0 });
302
254
303
    # good user
255
    # good user
304
    $shib_login = "test1234";
256
    $shib_login = "test1234";
Lines 317-425 subtest "checkpw_shib tests" => sub { Link Here
317
    $logger->info_is("There are several users with userid of martin, matchpoints must be unique", "Duplicated matchpoint warned to info");
269
    $logger->info_is("There are several users with userid of martin, matchpoints must be unique", "Duplicated matchpoint warned to info");
318
};
270
};
319
271
320
## _get_uri - opac
272
subtest 'get_uri' => sub {
321
$OPACBaseURL = "testopac.com";
273
plan tests => 13;
274
# Tests for OPAC
275
t::lib::Mocks::mock_preference('OPACBaseURL', 'testopac.com' );
322
is( C4::Auth_with_shibboleth::_get_uri(),
276
is( C4::Auth_with_shibboleth::_get_uri(),
323
    "https://testopac.com", "https opac uri returned" );
277
    "https://testopac.com", "https opac uri returned" );
324
278
325
$logger->clear;
279
$logger->clear;
326
280
327
$OPACBaseURL = "http://testopac.com";
281
t::lib::Mocks::mock_preference('OPACBaseURL', 'http://testopac.com' );
328
my $result = C4::Auth_with_shibboleth::_get_uri();
282
my $result = C4::Auth_with_shibboleth::_get_uri();
329
is( $result, "https://testopac.com", "https opac uri returned" );
283
is( $result, "https://testopac.com", "https opac uri returned" );
330
$logger->warn_is("Shibboleth requires OPACBaseURL/staffClientBaseURL to use the https protocol!", "Improper protocol logged to warn")
284
$logger->warn_is("Shibboleth requires OPACBaseURL/staffClientBaseURL to use the https protocol!", "Improper protocol logged to warn")
331
       ->clear();
285
       ->clear();
332
286
333
$OPACBaseURL = "https://testopac.com";
287
t::lib::Mocks::mock_preference('OPACBaseURL', 'https://testopac.com' );
334
is( C4::Auth_with_shibboleth::_get_uri(),
288
is( C4::Auth_with_shibboleth::_get_uri(),
335
    "https://testopac.com", "https opac uri returned" );
289
    "https://testopac.com", "https opac uri returned" );
336
290
337
$logger->clear();
291
$logger->clear();
338
292
339
$OPACBaseURL = undef;
293
t::lib::Mocks::mock_preference('OPACBaseURL', undef );
340
$result = C4::Auth_with_shibboleth::_get_uri();
294
$result = C4::Auth_with_shibboleth::_get_uri();
341
is( $result, "https://", "https $interface uri returned" );
295
is( $result, "https://", "https $interface uri returned" );
342
296
343
$logger->warn_is("Syspref staffClientBaseURL or OPACBaseURL not set!", "undefined OPACBaseURL - received expected warning")
297
$logger->warn_is("Syspref staffClientBaseURL or OPACBaseURL not set!", "undefined OPACBaseURL - received expected warning")
344
       ->clear();
298
       ->clear();
345
299
346
## _get_uri - intranet
300
# Tests for staff client
347
$interface = 'intranet';
301
$interface = 'intranet';
348
$staffClientBaseURL = "teststaff.com";
302
t::lib::Mocks::mock_preference('StaffClientBaseURL', 'teststaff.com' );
349
is( C4::Auth_with_shibboleth::_get_uri(),
303
is( C4::Auth_with_shibboleth::_get_uri(),
350
    "https://teststaff.com", "https $interface uri returned" );
304
    "https://teststaff.com", "https $interface uri returned" );
351
305
352
353
$logger->clear;
306
$logger->clear;
354
307
355
$staffClientBaseURL = "http://teststaff.com";
308
t::lib::Mocks::mock_preference('StaffClientBaseURL', 'http://teststaff.com' );
356
$result = C4::Auth_with_shibboleth::_get_uri();
309
$result = C4::Auth_with_shibboleth::_get_uri();
357
is( $result, "https://teststaff.com", "https $interface uri returned" );
310
is( $result, "https://teststaff.com", "https $interface uri returned" );
358
$logger->warn_is("Shibboleth requires OPACBaseURL/staffClientBaseURL to use the https protocol!")
311
$logger->warn_is("Shibboleth requires OPACBaseURL/staffClientBaseURL to use the https protocol!", 'check protocol warn')
359
       ->clear;
312
       ->clear;
360
313
361
$staffClientBaseURL = "https://teststaff.com";
314
t::lib::Mocks::mock_preference('StaffClientBaseURL', 'https://teststaff.com' );
362
is( C4::Auth_with_shibboleth::_get_uri(),
315
is( C4::Auth_with_shibboleth::_get_uri(),
363
    "https://teststaff.com", "https $interface uri returned" );
316
    "https://teststaff.com", "https $interface uri returned" );
364
is( $logger->count(), 0, 'No logging' );
317
is( $logger->count(), 0, 'No logging' );
365
318
366
$staffClientBaseURL = undef;
319
t::lib::Mocks::mock_preference('StaffClientBaseURL', undef );
367
$result = C4::Auth_with_shibboleth::_get_uri();
320
$result = C4::Auth_with_shibboleth::_get_uri();
368
is( $result, "https://", "https $interface uri returned" );
321
is( $result, "https://", "https $interface uri returned" );
369
$logger->warn_is("Syspref staffClientBaseURL or OPACBaseURL not set!", "undefined staffClientBaseURL - received expected warning")
322
$logger->warn_is("Syspref staffClientBaseURL or OPACBaseURL not set!", "undefined staffClientBaseURL - received expected warning")
370
       ->clear;
323
       ->clear;
324
};
325
$schema->storage->txn_rollback;
371
326
372
## _get_shib_config
327
# Internal helper function
373
# Internal helper function, covered in tests above
374
375
sub mockedConfig {
376
    my $param = shift;
377
378
    my %shibboleth = (
379
        'autocreate' => $autocreate,
380
        'welcome'    => $welcome,
381
        'sync'       => $sync,
382
        'matchpoint' => $matchpoint,
383
        'mapping'    => \%mapping
384
    );
385
386
    return \%shibboleth;
387
}
388
389
sub mockedPref {
390
    my $param = $_[1];
391
    my $return;
392
393
    if ( $param eq 'OPACBaseURL' ) {
394
        $return = $OPACBaseURL;
395
    }
396
397
    if ( $param eq 'staffClientBaseURL' ) {
398
        $return = $staffClientBaseURL;
399
    }
400
401
    if ( $param eq 'EmailFieldPrimary' ) {
402
        $return = 'OFF';
403
    }
404
405
    if ( $param eq 'EmailFieldPrecedence' ) {
406
        $return = 'emailpro';
407
    }
408
409
    return $return;
410
}
411
328
412
sub mockedInterface {
329
sub change_config {
413
    return $interface;
330
    my $params = shift;
414
}
415
331
416
## Convenience method to reset config
332
    my %mapping = (
417
sub reset_config {
418
    $matchpoint = 'userid';
419
    $autocreate = 0;
420
    $welcome = 0;
421
    $sync = 0;
422
    %mapping = (
423
        'userid'       => { 'is' => 'uid' },
333
        'userid'       => { 'is' => 'uid' },
424
        'surname'      => { 'is' => 'sn' },
334
        'surname'      => { 'is' => 'sn' },
425
        'dateexpiry'   => { 'is' => 'exp' },
335
        'dateexpiry'   => { 'is' => 'exp' },
Lines 429-434 sub reset_config { Link Here
429
        'emailpro'     => { 'is' => 'emailpro' },
339
        'emailpro'     => { 'is' => 'emailpro' },
430
        'branchcode'   => { 'is' => 'branchcode' },
340
        'branchcode'   => { 'is' => 'branchcode' },
431
    );
341
    );
342
    if( exists $params->{mapping} ) {
343
        $mapping{$_} = $params->{mapping}->{$_} for keys %{$params->{mapping}};
344
    }
345
    $shibboleth = {
346
        autocreate => $params->{autocreate} // 0,
347
        welcome    => $params->{welcome} // 0,
348
        sync       => $params->{sync} // 0,
349
        matchpoint => $params->{matchpoint} // 'userid',
350
        mapping    => \%mapping,
351
    };
352
353
    # Change environment too
432
    $ENV{'uid'}      = "test1234";
354
    $ENV{'uid'}      = "test1234";
433
    $ENV{'sn'}       = undef;
355
    $ENV{'sn'}       = undef;
434
    $ENV{'exp'}      = undef;
356
    $ENV{'exp'}      = undef;
Lines 436-441 sub reset_config { Link Here
436
    $ENV{'add'}      = undef;
358
    $ENV{'add'}      = undef;
437
    $ENV{'city'}     = undef;
359
    $ENV{'city'}     = undef;
438
    $ENV{'emailpro'} = undef;
360
    $ENV{'emailpro'} = undef;
439
440
    return 1;
441
}
361
}
442
- 

Return to bug 33778