Bug 15113: koha-rebuild-zebra should check USE_INDEXER_DAEMON and skip if enabled
[koha.git] / t / db_dependent / Auth_with_ldap.t
blob4982248c8efd08a66851ddf8583f456b5d6d9e54
1 #!/usr/bin/perl
3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
18 use Modern::Perl;
20 use Test::More tests => 4;
21 use Test::MockModule;
22 use Test::MockObject;
23 use Test::Warn;
25 use C4::Context;
27 my $dbh = C4::Context->dbh;
28 # Start transaction
29 $dbh->{ AutoCommit } = 0;
30 $dbh->{ RaiseError } = 1;
32 # Variables controlling LDAP server config
33 my $update = 0;
34 my $replicate = 0;
35 my $auth_by_bind = 1;
36 my $anonymous_bind = 1;
37 # Variables controlling LDAP behaviour
38 my $desired_authentication_result = 'success';
39 my $desired_connection_result = 'error';
40 my $desired_bind_result = 'error';
41 my $desired_compare_result = 'error';
42 my $desired_search_result = 'error';
43 my $desired_count_result = 1;
44 my $non_anonymous_bind_result = 'error';
45 my $ret;
47 # Mock the context module
48 my $context = new Test::MockModule( 'C4::Context' );
49 $context->mock( 'config', \&mockedC4Config );
51 # Mock the Net::LDAP module
52 my $ldap = new Test::MockModule( 'Net::LDAP' );
54 $ldap->mock( 'new', sub {
55 if ( $desired_connection_result eq 'error' ) {
56 # We were asked to fail the LDAP conexion
57 return;
58 } else {
59 # Return a mocked Net::LDAP object (Test::MockObject)
60 return mock_net_ldap();
62 });
65 # C4::Auth_with_ldap needs several stuff set first ^^^
66 use_ok( 'C4::Auth_with_ldap' );
67 can_ok( 'C4::Auth_with_ldap', qw/
68 checkpw_ldap
69 search_method /);
71 subtest "checkpw_ldap tests" => sub {
73 plan tests => 4;
75 ## Connection fail tests
76 $desired_connection_result = 'error';
77 warning_is { $ret = C4::Auth_with_ldap::checkpw_ldap( $dbh, 'hola', password => 'hey' ) }
78 "LDAP connexion failed",
79 "checkpw_ldap prints correct warning if LDAP conexion fails";
80 is( $ret, 0, "checkpw_ldap returns 0 if LDAP conexion fails");
82 ## Connection success tests
83 $desired_connection_result = 'success';
85 subtest "auth_by_bind = 1 tests" => sub {
87 plan tests => 7;
89 $auth_by_bind = 1;
91 $desired_authentication_result = 'success';
92 $anonymous_bind = 1;
93 $desired_bind_result = 'error';
94 $desired_search_result = 'error';
95 reload_ldap_module();
98 warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap(
99 $dbh, 'hola', password => 'hey' ) }
100 qr/Anonymous LDAP bind failed: LDAP error #1: error_name/,
101 "checkpw_ldap prints correct warning if LDAP anonymous bind fails";
102 is( $ret, 0, "checkpw_ldap returns 0 if LDAP anonymous bind fails");
104 $desired_authentication_result = 'success';
105 $anonymous_bind = 1;
106 $desired_bind_result = 'success';
107 $desired_search_result = 'success';
108 $desired_count_result = 0; # user auth problem
109 $non_anonymous_bind_result = 'success';
110 reload_ldap_module();
111 is ( C4::Auth_with_ldap::checkpw_ldap( $dbh, 'hola', password => 'hey' ),
112 0, "checkpw_ldap returns 0 if user lookup returns 0");
114 $non_anonymous_bind_result = 'error';
115 reload_ldap_module();
117 warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap(
118 $dbh, 'hola', password => 'hey' ) }
119 qr/LDAP bind failed as kohauser hola: LDAP error #1: error_name/,
120 "checkpw_ldap prints correct warning if LDAP bind fails";
121 is ( $ret, -1, "checkpw_ldap returns -1 LDAP bind fails for user (Bug 8148)");
123 # regression tests for bug 12831
124 $desired_authentication_result = 'error';
125 $anonymous_bind = 0;
126 $desired_bind_result = 'error';
127 $desired_search_result = 'success';
128 $desired_count_result = 0; # user auth problem
129 $non_anonymous_bind_result = 'error';
130 reload_ldap_module();
132 warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap(
133 $dbh, 'hola', password => 'hey' ) }
134 qr/LDAP bind failed as kohauser hola: LDAP error #1: error_name/,
135 "checkpw_ldap prints correct warning if LDAP bind fails";
136 is ( $ret, 0, "checkpw_ldap returns 0 LDAP bind fails for user (Bug 12831)");
140 subtest "auth_by_bind = 0 tests" => sub {
142 plan tests => 8;
144 $auth_by_bind = 0;
146 # Anonymous bind
147 $anonymous_bind = 1;
148 $desired_bind_result = 'error';
149 $non_anonymous_bind_result = 'error';
150 reload_ldap_module();
152 warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap(
153 $dbh, 'hola', password => 'hey' ) }
154 qr/LDAP bind failed as ldapuser cn=Manager,dc=metavore,dc=com: LDAP error #1: error_name/,
155 "checkpw_ldap prints correct warning if LDAP bind fails";
156 is ( $ret, 0, "checkpw_ldap returns 0 if bind fails");
158 $anonymous_bind = 1;
159 $desired_bind_result = 'success';
160 $non_anonymous_bind_result = 'success';
161 $desired_compare_result = 'error';
162 reload_ldap_module();
164 warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap(
165 $dbh, 'hola', password => 'hey' ) }
166 qr/LDAP Auth rejected : invalid password for user 'hola'. LDAP error #1: error_name/,
167 "checkpw_ldap prints correct warning if LDAP bind fails";
168 is ( $ret, -1, "checkpw_ldap returns -1 if bind fails (Bug 8148)");
170 # Non-anonymous bind
171 $anonymous_bind = 0;
172 $desired_bind_result = 'success';
173 $non_anonymous_bind_result = 'error';
174 $desired_compare_result = 'dont care';
175 reload_ldap_module();
177 warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap(
178 $dbh, 'hola', password => 'hey' ) }
179 qr/LDAP bind failed as ldapuser cn=Manager,dc=metavore,dc=com: LDAP error #1: error_name/,
180 "checkpw_ldap prints correct warning if LDAP bind fails";
181 is ( $ret, 0, "checkpw_ldap returns 0 if bind fails");
183 $anonymous_bind = 0;
184 $desired_bind_result = 'success';
185 $non_anonymous_bind_result = 'success';
186 $desired_compare_result = 'error';
187 reload_ldap_module();
189 warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap(
190 $dbh, 'hola', password => 'hey' ) }
191 qr/LDAP Auth rejected : invalid password for user 'hola'. LDAP error #1: error_name/,
192 "checkpw_ldap prints correct warning if LDAP bind fails";
193 is ( $ret, -1, "checkpw_ldap returns -1 if bind fails (Bug 8148)");
198 subtest "search_method tests" => sub {
200 plan tests => 5;
202 my $ldap = mock_net_ldap();
204 # Null params tests
205 is( C4::Auth_with_ldap::search_method( $ldap, undef), undef,
206 "search_method returns undef on undefined userid");
207 is( C4::Auth_with_ldap::search_method( undef, "undef"), undef,
208 "search_method returns undef on undefined ldap object");
210 # search ->code and !->code
211 $desired_search_result = 'error';
212 reload_ldap_module();
213 eval { $ret = C4::Auth_with_ldap::search_method( $ldap, "undef"); };
214 like( $@, qr/LDAP search failed to return object : 1/,
215 "search_method prints correct warning when db->search returns error code");
217 $desired_search_result = 'success';
218 $desired_count_result = 2;
219 reload_ldap_module();
220 warning_like { $ret = C4::Auth_with_ldap::search_method( $ldap, '123') }
221 qr/^LDAP Auth rejected \: \(uid\=123\) gets 2 hits/,
222 "search_method prints correct warning when hits count is not 1";
223 is( $ret, 0, "search_method returns 0 when hits count is not 1" );
227 # Function that mocks the call to C4::Context->config(param)
228 sub mockedC4Config {
230 my $param = shift;
232 my %ldap_mapping = (
233 firstname => { is => 'givenname' },
234 surname => { is => 'sn' },
235 address => { is => 'postaladdress' },
236 city => { is => 'l' },
237 zipcode => { is => 'postalcode' },
238 branchcode => { is => 'branch' },
239 userid => { is => 'uid' },
240 password => { is => 'userpassword' },
241 email => { is => 'mail' },
242 categorycode => { is => 'employeetype' },
243 phone => { is => 'telephonenumber' }
246 my %ldap_config = (
247 anonymous_bind => $anonymous_bind,
248 auth_by_bind => $auth_by_bind,
249 base => 'dc=metavore,dc=com',
250 hostname => 'localhost',
251 mapping => \%ldap_mapping,
252 pass => 'metavore',
253 principal_name => '%s@my_domain.com',
254 replicate => $replicate,
255 update => $update,
256 user => 'cn=Manager,dc=metavore,dc=com'
259 return \%ldap_config;
262 # Function that mocks the call to Net::LDAP
263 sub mock_net_ldap {
265 my $mocked_ldap = Test::MockObject->new();
267 $mocked_ldap->mock( 'bind', sub {
269 my @args = @_;
270 my $mocked_message;
272 if ( $#args > 1 ) {
273 # Args passed => non-anonymous bind
274 if ( $non_anonymous_bind_result eq 'error' ) {
275 return mock_net_ldap_message(1,1,'error_name','error_text');
276 } else {
277 return mock_net_ldap_message(0,0,'','');
279 } else {
280 $mocked_message = mock_net_ldap_message(
281 ($desired_bind_result eq 'error' ) ? 1 : 0, # code
282 ($desired_bind_result eq 'error' ) ? 1 : 0, # error
283 ($desired_bind_result eq 'error' ) ? 'error_name' : 0, # error_name
284 ($desired_bind_result eq 'error' ) ? 'error_text' : 0 # error_text
288 return $mocked_message;
291 $mocked_ldap->mock( 'compare', sub {
293 my $mocked_message;
295 if ( $desired_compare_result eq 'error' ) {
296 $mocked_message = mock_net_ldap_message(1,1,'error_name','error_text');
297 } else {
298 # we expect return code 6 for success
299 $mocked_message = mock_net_ldap_message(6,0,'','');
302 return $mocked_message;
305 $mocked_ldap->mock( 'search', sub {
307 return mock_net_ldap_search(
308 ( $desired_count_result ) # count
309 ? $desired_count_result
310 : 1, # default to 1
311 ( $desired_search_result eq 'error' ) # code
313 : 0, # 0 == success
314 ( $desired_search_result eq 'error' ) # error
316 : 0,
317 ( $desired_search_result eq 'error' ) # error_text
318 ? 'error_text'
319 : undef,
320 ( $desired_search_result eq 'error' ) # error_name
321 ? 'error_name'
322 : undef,
323 mock_net_ldap_entry( 'sampledn', 1 ) # shift_entry
328 return $mocked_ldap;
331 sub mock_net_ldap_search {
332 my ( $count, $code, $error, $error_text,
333 $error_name, $shift_entry ) = @_;
335 my $mocked_search = Test::MockObject->new();
336 $mocked_search->mock( 'count', sub { return $count; } );
337 $mocked_search->mock( 'code', sub { return $code; } );
338 $mocked_search->mock( 'error', sub { return $error; } );
339 $mocked_search->mock( 'error_name', sub { return $error_name; } );
340 $mocked_search->mock( 'error_text', sub { return $error_text; } );
341 $mocked_search->mock( 'shift_entry', sub { return $shift_entry; } );
343 return $mocked_search;
346 sub mock_net_ldap_message {
347 my ( $code, $error, $error_name, $error_text ) = @_;
349 my $mocked_message = Test::MockObject->new();
350 $mocked_message->mock( 'code', sub { $code } );
351 $mocked_message->mock( 'error', sub { $error } );
352 $mocked_message->mock( 'error_name', sub { $error_name } );
353 $mocked_message->mock( 'error_text', sub { $error_text } );
355 return $mocked_message;
358 sub mock_net_ldap_entry {
359 my ( $dn, $exists ) = @_;
361 my $mocked_entry = Test::MockObject->new();
362 $mocked_entry->mock( 'dn', sub { return $dn; } );
363 $mocked_entry->mock( 'exists', sub { return $exists } );
365 return $mocked_entry;
368 # TODO: Once we remove the global variables in C4::Auth_with_ldap
369 # we shouldn't need this...
370 # ... Horrible hack
371 sub reload_ldap_module {
372 delete $INC{'C4/Auth_with_ldap.pm'};
373 require C4::Auth_with_ldap;
374 C4::Auth_with_ldap->import;
377 $dbh->rollback;