1 <?php namespace Tests\Auth;
3 use BookStack\Auth\Access\LdapService;
4 use BookStack\Auth\Role;
5 use BookStack\Auth\Access\Ldap;
6 use BookStack\Auth\User;
7 use Mockery\MockInterface;
9 use Tests\TestResponse;
11 class LdapTest extends TestCase
19 protected $resourceId = 'resource-test';
21 public function setUp(): void
24 if (!defined('LDAP_OPT_REFERRALS')) define('LDAP_OPT_REFERRALS', 1);
26 'auth.method' => 'ldap',
27 'auth.defaults.guard' => 'ldap',
28 'services.ldap.base_dn' => 'dc=ldap,dc=local',
29 'services.ldap.email_attribute' => 'mail',
30 'services.ldap.display_name_attribute' => 'cn',
31 'services.ldap.id_attribute' => 'uid',
32 'services.ldap.user_to_groups' => false,
33 'services.ldap.version' => '3',
34 'services.ldap.user_filter' => '(&(uid=${user}))',
35 'services.ldap.follow_referrals' => false,
36 'services.ldap.tls_insecure' => false,
37 'services.ldap.thumbnail_attribute' => null,
39 $this->mockLdap = \Mockery::mock(Ldap::class);
40 $this->app[Ldap::class] = $this->mockLdap;
41 $this->mockUser = factory(User::class)->make();
44 protected function runFailedAuthLogin()
46 $this->commonLdapMocks(1, 1, 1, 1, 1);
47 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
48 ->andReturn(['count' => 0]);
49 $this->post('/login', ['username' => 'timmyjenkins', 'password' => 'cattreedog']);
52 protected function mockEscapes($times = 1)
54 $this->mockLdap->shouldReceive('escape')->times($times)->andReturnUsing(function ($val) {
55 return ldap_escape($val);
59 protected function mockExplodes($times = 1)
61 $this->mockLdap->shouldReceive('explodeDn')->times($times)->andReturnUsing(function ($dn, $withAttrib) {
62 return ldap_explode_dn($dn, $withAttrib);
66 protected function mockUserLogin(?string $email = null): TestResponse
68 return $this->post('/login', [
69 'username' => $this->mockUser->name,
70 'password' => $this->mockUser->password,
71 ] + ($email ? ['email' => $email] : []));
75 * Set LDAP method mocks for things we commonly call without altering.
77 protected function commonLdapMocks(int $connects = 1, int $versions = 1, int $options = 2, int $binds = 4, int $escapes = 2, int $explodes = 0)
79 $this->mockLdap->shouldReceive('connect')->times($connects)->andReturn($this->resourceId);
80 $this->mockLdap->shouldReceive('setVersion')->times($versions);
81 $this->mockLdap->shouldReceive('setOption')->times($options);
82 $this->mockLdap->shouldReceive('bind')->times($binds)->andReturn(true);
83 $this->mockEscapes($escapes);
84 $this->mockExplodes($explodes);
87 public function test_login()
89 $this->commonLdapMocks(1, 1, 2, 4, 2);
90 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
91 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
92 ->andReturn(['count' => 1, 0 => [
93 'uid' => [$this->mockUser->name],
94 'cn' => [$this->mockUser->name],
95 'dn' => ['dc=test' . config('services.ldap.base_dn')]
98 $resp = $this->mockUserLogin();
99 $resp->assertRedirect('/login');
100 $resp = $this->followRedirects($resp);
101 $resp->assertSee('Please enter an email to use for this account.');
102 $resp->assertSee($this->mockUser->name);
104 $resp = $this->followingRedirects()->mockUserLogin($this->mockUser->email);
105 $resp->assertElementExists('#home-default');
106 $resp->assertSee($this->mockUser->name);
107 $this->assertDatabaseHas('users', [
108 'email' => $this->mockUser->email,
109 'email_confirmed' => false,
110 'external_auth_id' => $this->mockUser->name
114 public function test_email_domain_restriction_active_on_new_ldap_login()
117 'registration-restrict' => 'testing.com'
120 $this->commonLdapMocks(1, 1, 2, 4, 2);
121 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
122 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
123 ->andReturn(['count' => 1, 0 => [
124 'uid' => [$this->mockUser->name],
125 'cn' => [$this->mockUser->name],
126 'dn' => ['dc=test' . config('services.ldap.base_dn')]
129 $resp = $this->mockUserLogin();
130 $resp->assertRedirect('/login');
131 $this->followRedirects($resp)->assertSee('Please enter an email to use for this account.');
135 $resp = $this->mockUserLogin($email);
136 $resp->assertRedirect('/login');
137 $this->followRedirects($resp)->assertSee('That email domain does not have access to this application');
139 $this->assertDatabaseMissing('users', ['email' => $email]);
142 public function test_login_works_when_no_uid_provided_by_ldap_server()
144 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
146 $this->commonLdapMocks(1, 1, 1, 2, 1);
147 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
148 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
149 ->andReturn(['count' => 1, 0 => [
150 'cn' => [$this->mockUser->name],
152 'mail' => [$this->mockUser->email]
155 $resp = $this->mockUserLogin();
156 $resp->assertRedirect('/');
157 $this->followRedirects($resp)->assertSee($this->mockUser->name);
158 $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
161 public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properly()
163 config()->set(['services.ldap.id_attribute' => 'my_custom_id']);
165 $this->commonLdapMocks(1, 1, 1, 2, 1);
166 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
167 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
168 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
169 ->andReturn(['count' => 1, 0 => [
170 'cn' => [$this->mockUser->name],
172 'my_custom_id' => ['cooluser456'],
173 'mail' => [$this->mockUser->email]
177 $resp = $this->mockUserLogin();
178 $resp->assertRedirect('/');
179 $this->followRedirects($resp)->assertSee($this->mockUser->name);
180 $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
183 public function test_initial_incorrect_credentials()
185 $this->commonLdapMocks(1, 1, 1, 0, 1);
186 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
187 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
188 ->andReturn(['count' => 1, 0 => [
189 'uid' => [$this->mockUser->name],
190 'cn' => [$this->mockUser->name],
191 'dn' => ['dc=test' . config('services.ldap.base_dn')]
193 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true, false);
195 $resp = $this->mockUserLogin();
196 $resp->assertRedirect('/login');
197 $this->followRedirects($resp)->assertSee('These credentials do not match our records.');
198 $this->assertDatabaseMissing('users', ['external_auth_id' => $this->mockUser->name]);
201 public function test_login_not_found_username()
203 $this->commonLdapMocks(1, 1, 1, 1, 1);
204 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
205 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
206 ->andReturn(['count' => 0]);
208 $resp = $this->mockUserLogin();
209 $resp->assertRedirect('/login');
210 $this->followRedirects($resp)->assertSee('These credentials do not match our records.');
211 $this->assertDatabaseMissing('users', ['external_auth_id' => $this->mockUser->name]);
214 public function test_create_user_form()
216 $userForm = $this->asAdmin()->get('/settings/users/create');
217 $userForm->assertDontSee('Password');
219 $save = $this->post('/settings/users/create', [
220 'name' => $this->mockUser->name,
221 'email' => $this->mockUser->email,
223 $save->assertSessionHasErrors(['external_auth_id' => 'The external auth id field is required.']);
225 $save = $this->post('/settings/users/create', [
226 'name' => $this->mockUser->name,
227 'email' => $this->mockUser->email,
228 'external_auth_id' => $this->mockUser->name,
230 $save->assertRedirect('/settings/users');
231 $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
234 public function test_user_edit_form()
236 $editUser = $this->getNormalUser();
237 $editPage = $this->asAdmin()->get("/settings/users/{$editUser->id}");
238 $editPage->assertSee('Edit User');
239 $editPage->assertDontSee('Password');
241 $update = $this->put("/settings/users/{$editUser->id}", [
242 'name' => $editUser->name,
243 'email' => $editUser->email,
244 'external_auth_id' => 'test_auth_id',
246 $update->assertRedirect('/settings/users');
247 $this->assertDatabaseHas('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
250 public function test_registration_disabled()
252 $this->followingRedirects()->get('/register')->assertElementContains('#content', 'Log In');
255 public function test_non_admins_cannot_change_auth_id()
257 $testUser = $this->getNormalUser();
258 $this->actingAs($testUser)
259 ->get('/settings/users/' . $testUser->id)
260 ->assertDontSee('External Authentication');
263 public function test_login_maps_roles_and_retains_existing_roles()
265 $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
266 $roleToReceive2 = factory(Role::class)->create(['display_name' => 'LdapTester Second']);
267 $existingRole = factory(Role::class)->create(['display_name' => 'ldaptester-existing']);
268 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
269 $this->mockUser->attachRole($existingRole);
272 'services.ldap.user_to_groups' => true,
273 'services.ldap.group_attribute' => 'memberOf',
274 'services.ldap.remove_from_groups' => false,
277 $this->commonLdapMocks(1, 1, 4, 5, 4, 6);
278 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
279 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
280 ->andReturn(['count' => 1, 0 => [
281 'uid' => [$this->mockUser->name],
282 'cn' => [$this->mockUser->name],
283 'dn' => ['dc=test' . config('services.ldap.base_dn')],
284 'mail' => [$this->mockUser->email],
287 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
288 1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
292 $this->mockUserLogin()->assertRedirect('/');
294 $user = User::where('email', $this->mockUser->email)->first();
295 $this->assertDatabaseHas('role_user', [
296 'user_id' => $user->id,
297 'role_id' => $roleToReceive->id
299 $this->assertDatabaseHas('role_user', [
300 'user_id' => $user->id,
301 'role_id' => $roleToReceive2->id
303 $this->assertDatabaseHas('role_user', [
304 'user_id' => $user->id,
305 'role_id' => $existingRole->id
309 public function test_login_maps_roles_and_removes_old_roles_if_set()
311 $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
312 $existingRole = factory(Role::class)->create(['display_name' => 'ldaptester-existing']);
313 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
314 $this->mockUser->attachRole($existingRole);
317 'services.ldap.user_to_groups' => true,
318 'services.ldap.group_attribute' => 'memberOf',
319 'services.ldap.remove_from_groups' => true,
322 $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
323 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
324 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
325 ->andReturn(['count' => 1, 0 => [
326 'uid' => [$this->mockUser->name],
327 'cn' => [$this->mockUser->name],
328 'dn' => ['dc=test' . config('services.ldap.base_dn')],
329 'mail' => [$this->mockUser->email],
332 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
336 $this->mockUserLogin()->assertRedirect('/');
338 $user = User::query()->where('email', $this->mockUser->email)->first();
339 $this->assertDatabaseHas('role_user', [
340 'user_id' => $user->id,
341 'role_id' => $roleToReceive->id
343 $this->assertDatabaseMissing('role_user', [
344 'user_id' => $user->id,
345 'role_id' => $existingRole->id
349 public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
351 $role = factory(Role::class)->create(['display_name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
352 $this->asAdmin()->get('/settings/roles/' . $role->id)
353 ->assertSee('ex-auth-a');
356 public function test_login_maps_roles_using_external_auth_ids_if_set()
358 $roleToReceive = factory(Role::class)->create(['display_name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
359 $roleToNotReceive = factory(Role::class)->create(['display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
362 'services.ldap.user_to_groups' => true,
363 'services.ldap.group_attribute' => 'memberOf',
364 'services.ldap.remove_from_groups' => true,
367 $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
368 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
369 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
370 ->andReturn(['count' => 1, 0 => [
371 'uid' => [$this->mockUser->name],
372 'cn' => [$this->mockUser->name],
373 'dn' => ['dc=test' . config('services.ldap.base_dn')],
374 'mail' => [$this->mockUser->email],
377 0 => "cn=ex-auth-a,ou=groups,dc=example,dc=com",
381 $this->mockUserLogin()->assertRedirect('/');
383 $user = User::query()->where('email', $this->mockUser->email)->first();
384 $this->assertDatabaseHas('role_user', [
385 'user_id' => $user->id,
386 'role_id' => $roleToReceive->id
388 $this->assertDatabaseMissing('role_user', [
389 'user_id' => $user->id,
390 'role_id' => $roleToNotReceive->id
394 public function test_login_group_mapping_does_not_conflict_with_default_role()
396 $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
397 $roleToReceive2 = factory(Role::class)->create(['display_name' => 'LdapTester Second']);
398 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
400 setting()->put('registration-role', $roleToReceive->id);
403 'services.ldap.user_to_groups' => true,
404 'services.ldap.group_attribute' => 'memberOf',
405 'services.ldap.remove_from_groups' => true,
408 $this->commonLdapMocks(1, 1, 4, 5, 4, 6);
409 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
410 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
411 ->andReturn(['count' => 1, 0 => [
412 'uid' => [$this->mockUser->name],
413 'cn' => [$this->mockUser->name],
414 'dn' => ['dc=test' . config('services.ldap.base_dn')],
415 'mail' => [$this->mockUser->email],
418 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
419 1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
423 $this->mockUserLogin()->assertRedirect('/');
425 $user = User::query()->where('email', $this->mockUser->email)->first();
426 $this->assertDatabaseHas('role_user', [
427 'user_id' => $user->id,
428 'role_id' => $roleToReceive->id
430 $this->assertDatabaseHas('role_user', [
431 'user_id' => $user->id,
432 'role_id' => $roleToReceive2->id
436 public function test_login_uses_specified_display_name_attribute()
439 'services.ldap.display_name_attribute' => 'displayName'
442 $this->commonLdapMocks(1, 1, 2, 4, 2);
443 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
444 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
445 ->andReturn(['count' => 1, 0 => [
446 'uid' => [$this->mockUser->name],
447 'cn' => [$this->mockUser->name],
448 'dn' => ['dc=test' . config('services.ldap.base_dn')],
449 'displayname' => 'displayNameAttribute'
452 $this->mockUserLogin()->assertRedirect('/login');
453 $this->get('/login')->assertSee('Please enter an email to use for this account.');
455 $resp = $this->mockUserLogin($this->mockUser->email);
456 $resp->assertRedirect('/');
457 $this->get('/')->assertSee('displayNameAttribute');
458 $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => 'displayNameAttribute']);
461 public function test_login_uses_default_display_name_attribute_if_specified_not_present()
464 'services.ldap.display_name_attribute' => 'displayName'
467 $this->commonLdapMocks(1, 1, 2, 4, 2);
468 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
469 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
470 ->andReturn(['count' => 1, 0 => [
471 'uid' => [$this->mockUser->name],
472 'cn' => [$this->mockUser->name],
473 'dn' => ['dc=test' . config('services.ldap.base_dn')]
476 $this->mockUserLogin()->assertRedirect('/login');
477 $this->get('/login')->assertSee('Please enter an email to use for this account.');
479 $resp = $this->mockUserLogin($this->mockUser->email);
480 $resp->assertRedirect('/');
481 $this->get('/')->assertSee($this->mockUser->name);
482 $this->assertDatabaseHas('users', [
483 'email' => $this->mockUser->email,
484 'email_confirmed' => false,
485 'external_auth_id' => $this->mockUser->name,
486 'name' => $this->mockUser->name
490 protected function checkLdapReceivesCorrectDetails($serverString, $expectedHost, $expectedPort)
493 'services.ldap.server' => $serverString
497 $this->commonLdapMocks(0, 1, 1, 2, 1);
498 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)->andReturn(['count' => 1, 0 => [
499 'uid' => [$this->mockUser->name],
500 'cn' => [$this->mockUser->name],
501 'dn' => ['dc=test' . config('services.ldap.base_dn')]
504 $this->mockLdap->shouldReceive('connect')->once()
505 ->with($expectedHost, $expectedPort)->andReturn($this->resourceId);
506 $this->mockUserLogin();
509 public function test_ldap_port_provided_on_host_if_host_is_full_uri()
511 $hostName = 'ldaps://bookstack:8080';
512 $this->checkLdapReceivesCorrectDetails($hostName, $hostName, 389);
515 public function test_ldap_port_parsed_from_server_if_host_is_not_full_uri()
517 $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com:8080', 'ldap.bookstack.com', 8080);
520 public function test_default_ldap_port_used_if_not_in_server_string_and_not_uri()
522 $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com', 'ldap.bookstack.com', 389);
525 public function test_forgot_password_routes_inaccessible()
527 $resp = $this->get('/password/email');
528 $this->assertPermissionError($resp);
530 $resp = $this->post('/password/email');
531 $this->assertPermissionError($resp);
533 $resp = $this->get('/password/reset/abc123');
534 $this->assertPermissionError($resp);
536 $resp = $this->post('/password/reset');
537 $this->assertPermissionError($resp);
540 public function test_user_invite_routes_inaccessible()
542 $resp = $this->get('/register/invite/abc123');
543 $this->assertPermissionError($resp);
545 $resp = $this->post('/register/invite/abc123');
546 $this->assertPermissionError($resp);
549 public function test_user_register_routes_inaccessible()
551 $resp = $this->get('/register');
552 $this->assertPermissionError($resp);
554 $resp = $this->post('/register');
555 $this->assertPermissionError($resp);
558 public function test_dump_user_details_option_works()
560 config()->set(['services.ldap.dump_user_details' => true]);
562 $this->commonLdapMocks(1, 1, 1, 1, 1);
563 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
564 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
565 ->andReturn(['count' => 1, 0 => [
566 'uid' => [$this->mockUser->name],
567 'cn' => [$this->mockUser->name],
568 'dn' => ['dc=test' . config('services.ldap.base_dn')]
571 $resp = $this->post('/login', [
572 'username' => $this->mockUser->name,
573 'password' => $this->mockUser->password,
575 $resp->assertJsonStructure([
576 'details_from_ldap' => [],
577 'details_bookstack_parsed' => [],
581 public function test_start_tls_called_if_option_set()
583 config()->set(['services.ldap.start_tls' => true]);
584 $this->mockLdap->shouldReceive('startTls')->once()->andReturn(true);
585 $this->runFailedAuthLogin();
588 public function test_connection_fails_if_tls_fails()
590 config()->set(['services.ldap.start_tls' => true]);
591 $this->mockLdap->shouldReceive('startTls')->once()->andReturn(false);
592 $this->commonLdapMocks(1, 1, 0, 0, 0);
593 $resp = $this->post('/login', ['username' => 'timmyjenkins', 'password' => 'cattreedog']);
594 $resp->assertStatus(500);
597 public function test_ldap_attributes_can_be_binary_decoded_if_marked()
599 config()->set(['services.ldap.id_attribute' => 'BIN;uid']);
600 $ldapService = app()->make(LdapService::class);
601 $this->commonLdapMocks(1, 1, 1, 1, 1);
602 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
603 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), ['cn', 'dn', 'uid', 'mail', 'cn'])
604 ->andReturn(['count' => 1, 0 => [
605 'uid' => [hex2bin('FFF8F7')],
606 'cn' => [$this->mockUser->name],
607 'dn' => ['dc=test' . config('services.ldap.base_dn')]
610 $details = $ldapService->getUserDetails('test');
611 $this->assertEquals('fff8f7', $details['uid']);
614 public function test_new_ldap_user_login_with_already_used_email_address_shows_error_message_to_user()
616 $this->commonLdapMocks(1, 1, 2, 4, 2);
617 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
618 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
619 ->andReturn(['count' => 1, 0 => [
620 'uid' => [$this->mockUser->name],
621 'cn' => [$this->mockUser->name],
622 'dn' => ['dc=test' . config('services.ldap.base_dn')],
624 ]], ['count' => 1, 0 => [
627 'dn' => ['dc=bscott' . config('services.ldap.base_dn')],
632 $this->mockUserLogin()->assertRedirect('/');
636 $resp = $this->followingRedirects()->post('/login', ['username' => 'bscott', 'password' => 'pass']);
637 $resp->assertSee('A user with the email
[email protected] already exists but with different credentials');
640 public function test_login_with_email_confirmation_required_maps_groups_but_shows_confirmation_screen()
642 $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
643 $user = factory(User::class)->make();
644 setting()->put('registration-confirmation', 'true');
647 'services.ldap.user_to_groups' => true,
648 'services.ldap.group_attribute' => 'memberOf',
649 'services.ldap.remove_from_groups' => true,
652 $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
653 $this->mockLdap->shouldReceive('searchAndGetEntries')
655 ->andReturn(['count' => 1, 0 => [
656 'uid' => [$user->name],
657 'cn' => [$user->name],
658 'dn' => ['dc=test' . config('services.ldap.base_dn')],
659 'mail' => [$user->email],
662 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
666 $this->followingRedirects()->mockUserLogin()->assertSee('Thanks for registering!');
667 $this->assertDatabaseHas('users', [
668 'email' => $user->email,
669 'email_confirmed' => false,
672 $user = User::query()->where('email', '=', $user->email)->first();
673 $this->assertDatabaseHas('role_user', [
674 'user_id' => $user->id,
675 'role_id' => $roleToReceive->id
678 $homePage = $this->get('/');
679 $homePage->assertRedirect('/register/confirm/awaiting');
682 public function test_failed_logins_are_logged_when_message_configured()
684 $log = $this->withTestLogger();
685 config()->set(['logging.failed_login.message' => 'Failed login for %u']);
686 $this->runFailedAuthLogin();
687 $this->assertTrue($log->hasWarningThatContains('Failed login for timmyjenkins'));
690 public function test_thumbnail_attribute_used_as_user_avatar_if_configured()
692 config()->set(['services.ldap.thumbnail_attribute' => 'jpegPhoto']);
694 $this->commonLdapMocks(1, 1, 1, 2, 1);
695 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
696 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
697 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
698 ->andReturn(['count' => 1, 0 => [
699 'cn' => [$this->mockUser->name],
701 'jpegphoto' => [base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8Q
702 EBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=')],
703 'mail' => [$this->mockUser->email]
706 $this->mockUserLogin()
707 ->assertRedirect('/');
709 $user = User::query()->where('email', '=', $this->mockUser->email)->first();
710 $this->assertNotNull($user->avatar);
711 $this->assertEquals('8c90748342f19b195b9c6b4eff742ded', md5_file(public_path($user->avatar->path)));