5 use BookStack\Access\Ldap;
6 use BookStack\Access\LdapService;
7 use BookStack\Users\Models\Role;
8 use BookStack\Users\Models\User;
9 use Illuminate\Testing\TestResponse;
10 use Mockery\MockInterface;
13 class LdapTest extends TestCase
15 protected MockInterface $mockLdap;
17 protected User $mockUser;
18 protected string $resourceId = 'resource-test';
20 protected function setUp(): void
23 if (!defined('LDAP_OPT_REFERRALS')) {
24 define('LDAP_OPT_REFERRALS', 1);
27 'auth.method' => 'ldap',
28 'auth.defaults.guard' => 'ldap',
29 'services.ldap.base_dn' => 'dc=ldap,dc=local',
30 'services.ldap.email_attribute' => 'mail',
31 'services.ldap.display_name_attribute' => 'cn',
32 'services.ldap.id_attribute' => 'uid',
33 'services.ldap.user_to_groups' => false,
34 'services.ldap.version' => '3',
35 'services.ldap.user_filter' => '(&(uid=${user}))',
36 'services.ldap.follow_referrals' => false,
37 'services.ldap.tls_insecure' => false,
38 'services.ldap.thumbnail_attribute' => null,
40 $this->mockLdap = $this->mock(Ldap::class);
41 $this->mockUser = User::factory()->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 $this->withHtml($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.');
134 $resp = $this->mockUserLogin($email);
135 $resp->assertRedirect('/login');
136 $this->followRedirects($resp)->assertSee('That email domain does not have access to this application');
138 $this->assertDatabaseMissing('users', ['email' => $email]);
141 public function test_login_works_when_no_uid_provided_by_ldap_server()
143 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
145 $this->commonLdapMocks(1, 1, 1, 2, 1);
146 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
147 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
148 ->andReturn(['count' => 1, 0 => [
149 'cn' => [$this->mockUser->name],
151 'mail' => [$this->mockUser->email],
154 $resp = $this->mockUserLogin();
155 $resp->assertRedirect('/');
156 $this->followRedirects($resp)->assertSee($this->mockUser->name);
157 $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
160 public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properly()
162 config()->set(['services.ldap.id_attribute' => 'my_custom_id']);
164 $this->commonLdapMocks(1, 1, 1, 2, 1);
165 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
166 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
167 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
168 ->andReturn(['count' => 1, 0 => [
169 'cn' => [$this->mockUser->name],
171 'my_custom_id' => ['cooluser456'],
172 'mail' => [$this->mockUser->email],
175 $resp = $this->mockUserLogin();
176 $resp->assertRedirect('/');
177 $this->followRedirects($resp)->assertSee($this->mockUser->name);
178 $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
181 public function test_initial_incorrect_credentials()
183 $this->commonLdapMocks(1, 1, 1, 0, 1);
184 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
185 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
186 ->andReturn(['count' => 1, 0 => [
187 'uid' => [$this->mockUser->name],
188 'cn' => [$this->mockUser->name],
189 'dn' => 'dc=test' . config('services.ldap.base_dn'),
191 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true, false);
193 $resp = $this->mockUserLogin();
194 $resp->assertRedirect('/login');
195 $this->followRedirects($resp)->assertSee('These credentials do not match our records.');
196 $this->assertDatabaseMissing('users', ['external_auth_id' => $this->mockUser->name]);
199 public function test_login_not_found_username()
201 $this->commonLdapMocks(1, 1, 1, 1, 1);
202 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
203 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
204 ->andReturn(['count' => 0]);
206 $resp = $this->mockUserLogin();
207 $resp->assertRedirect('/login');
208 $this->followRedirects($resp)->assertSee('These credentials do not match our records.');
209 $this->assertDatabaseMissing('users', ['external_auth_id' => $this->mockUser->name]);
212 public function test_create_user_form()
214 $userForm = $this->asAdmin()->get('/settings/users/create');
215 $userForm->assertDontSee('Password');
217 $save = $this->post('/settings/users/create', [
218 'name' => $this->mockUser->name,
219 'email' => $this->mockUser->email,
221 $save->assertSessionHasErrors(['external_auth_id' => 'The external auth id field is required.']);
223 $save = $this->post('/settings/users/create', [
224 'name' => $this->mockUser->name,
225 'email' => $this->mockUser->email,
226 'external_auth_id' => $this->mockUser->name,
228 $save->assertRedirect('/settings/users');
229 $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
232 public function test_user_edit_form()
234 $editUser = $this->users->viewer();
235 $editPage = $this->asAdmin()->get("/settings/users/{$editUser->id}");
236 $editPage->assertSee('Edit User');
237 $editPage->assertDontSee('Password');
239 $update = $this->put("/settings/users/{$editUser->id}", [
240 'name' => $editUser->name,
241 'email' => $editUser->email,
242 'external_auth_id' => 'test_auth_id',
244 $update->assertRedirect('/settings/users');
245 $this->assertDatabaseHas('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
248 public function test_registration_disabled()
250 $resp = $this->followingRedirects()->get('/register');
251 $this->withHtml($resp)->assertElementContains('#content', 'Log In');
254 public function test_non_admins_cannot_change_auth_id()
256 $testUser = $this->users->viewer();
257 $this->actingAs($testUser)
258 ->get('/settings/users/' . $testUser->id)
259 ->assertDontSee('External Authentication');
262 public function test_login_maps_roles_and_retains_existing_roles()
264 $roleToReceive = Role::factory()->create(['display_name' => 'LdapTester']);
265 $roleToReceive2 = Role::factory()->create(['display_name' => 'LdapTester Second']);
266 $existingRole = Role::factory()->create(['display_name' => 'ldaptester-existing']);
267 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
268 $this->mockUser->attachRole($existingRole);
271 'services.ldap.user_to_groups' => true,
272 'services.ldap.group_attribute' => 'memberOf',
273 'services.ldap.remove_from_groups' => false,
276 $this->commonLdapMocks(1, 1, 4, 5, 4, 6);
277 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
278 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
279 ->andReturn(['count' => 1, 0 => [
280 'uid' => [$this->mockUser->name],
281 'cn' => [$this->mockUser->name],
282 'dn' => 'dc=test' . config('services.ldap.base_dn'),
283 'mail' => [$this->mockUser->email],
286 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com',
287 1 => 'cn=ldaptester-second,ou=groups,dc=example,dc=com',
291 $this->mockUserLogin()->assertRedirect('/');
293 $user = User::where('email', $this->mockUser->email)->first();
294 $this->assertDatabaseHas('role_user', [
295 'user_id' => $user->id,
296 'role_id' => $roleToReceive->id,
298 $this->assertDatabaseHas('role_user', [
299 'user_id' => $user->id,
300 'role_id' => $roleToReceive2->id,
302 $this->assertDatabaseHas('role_user', [
303 'user_id' => $user->id,
304 'role_id' => $existingRole->id,
308 public function test_login_maps_roles_and_removes_old_roles_if_set()
310 $roleToReceive = Role::factory()->create(['display_name' => 'LdapTester']);
311 $existingRole = Role::factory()->create(['display_name' => 'ldaptester-existing']);
312 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
313 $this->mockUser->attachRole($existingRole);
316 'services.ldap.user_to_groups' => true,
317 'services.ldap.group_attribute' => 'memberOf',
318 'services.ldap.remove_from_groups' => true,
321 $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
322 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
323 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
324 ->andReturn(['count' => 1, 0 => [
325 'uid' => [$this->mockUser->name],
326 'cn' => [$this->mockUser->name],
327 'dn' => 'dc=test' . config('services.ldap.base_dn'),
328 'mail' => [$this->mockUser->email],
331 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com',
335 $this->mockUserLogin()->assertRedirect('/');
337 $user = User::query()->where('email', $this->mockUser->email)->first();
338 $this->assertDatabaseHas('role_user', [
339 'user_id' => $user->id,
340 'role_id' => $roleToReceive->id,
342 $this->assertDatabaseMissing('role_user', [
343 'user_id' => $user->id,
344 'role_id' => $existingRole->id,
348 public function test_dump_user_groups_shows_group_related_details_as_json()
351 'services.ldap.user_to_groups' => true,
352 'services.ldap.group_attribute' => 'memberOf',
353 'services.ldap.remove_from_groups' => true,
354 'services.ldap.dump_user_groups' => true,
357 $userResp = ['count' => 1, 0 => [
358 'uid' => [$this->mockUser->name],
359 'cn' => [$this->mockUser->name],
360 'dn' => 'dc=test,' . config('services.ldap.base_dn'),
361 'mail' => [$this->mockUser->email],
363 $this->commonLdapMocks(1, 1, 4, 5, 4, 2);
364 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
365 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
366 ->andReturn($userResp, ['count' => 1,
368 'dn' => 'dc=test,' . config('services.ldap.base_dn'),
371 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com',
377 'dn' => 'cn=ldaptester,ou=groups,dc=example,dc=com',
380 0 => 'cn=monsters,ou=groups,dc=example,dc=com',
385 $resp = $this->mockUserLogin();
387 'details_from_ldap' => [
388 'dn' => 'dc=test,' . config('services.ldap.base_dn'),
390 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com',
394 'parsed_direct_user_groups' => [
397 'parsed_recursive_user_groups' => [
404 public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
406 $role = Role::factory()->create(['display_name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
407 $this->asAdmin()->get('/settings/roles/' . $role->id)
408 ->assertSee('ex-auth-a');
411 public function test_login_maps_roles_using_external_auth_ids_if_set()
413 $roleToReceive = Role::factory()->create(['display_name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
414 $roleToNotReceive = Role::factory()->create(['display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
417 'services.ldap.user_to_groups' => true,
418 'services.ldap.group_attribute' => 'memberOf',
419 'services.ldap.remove_from_groups' => true,
422 $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
423 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
424 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
425 ->andReturn(['count' => 1, 0 => [
426 'uid' => [$this->mockUser->name],
427 'cn' => [$this->mockUser->name],
428 'dn' => 'dc=test' . config('services.ldap.base_dn'),
429 'mail' => [$this->mockUser->email],
432 0 => 'cn=ex-auth-a,ou=groups,dc=example,dc=com',
436 $this->mockUserLogin()->assertRedirect('/');
438 $user = User::query()->where('email', $this->mockUser->email)->first();
439 $this->assertDatabaseHas('role_user', [
440 'user_id' => $user->id,
441 'role_id' => $roleToReceive->id,
443 $this->assertDatabaseMissing('role_user', [
444 'user_id' => $user->id,
445 'role_id' => $roleToNotReceive->id,
449 public function test_login_group_mapping_does_not_conflict_with_default_role()
451 $roleToReceive = Role::factory()->create(['display_name' => 'LdapTester']);
452 $roleToReceive2 = Role::factory()->create(['display_name' => 'LdapTester Second']);
453 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
455 setting()->put('registration-role', $roleToReceive->id);
458 'services.ldap.user_to_groups' => true,
459 'services.ldap.group_attribute' => 'memberOf',
460 'services.ldap.remove_from_groups' => true,
463 $this->commonLdapMocks(1, 1, 4, 5, 4, 6);
464 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
465 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
466 ->andReturn(['count' => 1, 0 => [
467 'uid' => [$this->mockUser->name],
468 'cn' => [$this->mockUser->name],
469 'dn' => 'dc=test' . config('services.ldap.base_dn'),
470 'mail' => [$this->mockUser->email],
473 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com',
474 1 => 'cn=ldaptester-second,ou=groups,dc=example,dc=com',
478 $this->mockUserLogin()->assertRedirect('/');
480 $user = User::query()->where('email', $this->mockUser->email)->first();
481 $this->assertDatabaseHas('role_user', [
482 'user_id' => $user->id,
483 'role_id' => $roleToReceive->id,
485 $this->assertDatabaseHas('role_user', [
486 'user_id' => $user->id,
487 'role_id' => $roleToReceive2->id,
491 public function test_login_uses_specified_display_name_attribute()
494 'services.ldap.display_name_attribute' => 'displayName',
497 $this->commonLdapMocks(1, 1, 2, 4, 2);
498 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
499 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
500 ->andReturn(['count' => 1, 0 => [
501 'uid' => [$this->mockUser->name],
502 'cn' => [$this->mockUser->name],
503 'dn' => 'dc=test' . config('services.ldap.base_dn'),
504 'displayname' => 'displayNameAttribute',
507 $this->mockUserLogin()->assertRedirect('/login');
508 $this->get('/login')->assertSee('Please enter an email to use for this account.');
510 $resp = $this->mockUserLogin($this->mockUser->email);
511 $resp->assertRedirect('/');
512 $this->get('/')->assertSee('displayNameAttribute');
513 $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => 'displayNameAttribute']);
516 public function test_login_uses_default_display_name_attribute_if_specified_not_present()
519 'services.ldap.display_name_attribute' => 'displayName',
522 $this->commonLdapMocks(1, 1, 2, 4, 2);
523 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
524 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
525 ->andReturn(['count' => 1, 0 => [
526 'uid' => [$this->mockUser->name],
527 'cn' => [$this->mockUser->name],
528 'dn' => 'dc=test' . config('services.ldap.base_dn'),
531 $this->mockUserLogin()->assertRedirect('/login');
532 $this->get('/login')->assertSee('Please enter an email to use for this account.');
534 $resp = $this->mockUserLogin($this->mockUser->email);
535 $resp->assertRedirect('/');
536 $this->get('/')->assertSee($this->mockUser->name);
537 $this->assertDatabaseHas('users', [
538 'email' => $this->mockUser->email,
539 'email_confirmed' => false,
540 'external_auth_id' => $this->mockUser->name,
541 'name' => $this->mockUser->name,
545 protected function checkLdapReceivesCorrectDetails($serverString, $expectedHostString): void
547 app('config')->set(['services.ldap.server' => $serverString]);
549 $this->mockLdap->shouldReceive('connect')
551 ->with($expectedHostString)
554 $this->mockUserLogin();
557 public function test_ldap_receives_correct_connect_host_from_config()
559 $expectedResultByInput = [
560 'ldaps://bookstack:8080' => 'ldaps://bookstack:8080',
561 'ldap.bookstack.com:8080' => 'ldap://ldap.bookstack.com:8080',
562 'ldap.bookstack.com' => 'ldap://ldap.bookstack.com',
563 'ldaps://ldap.bookstack.com' => 'ldaps://ldap.bookstack.com',
564 'ldaps://ldap.bookstack.com ldap://a.b.com' => 'ldaps://ldap.bookstack.com ldap://a.b.com',
567 foreach ($expectedResultByInput as $input => $expectedResult) {
568 $this->checkLdapReceivesCorrectDetails($input, $expectedResult);
569 $this->refreshApplication();
574 public function test_forgot_password_routes_inaccessible()
576 $resp = $this->get('/password/email');
577 $this->assertPermissionError($resp);
579 $resp = $this->post('/password/email');
580 $this->assertPermissionError($resp);
582 $resp = $this->get('/password/reset/abc123');
583 $this->assertPermissionError($resp);
585 $resp = $this->post('/password/reset');
586 $this->assertPermissionError($resp);
589 public function test_user_invite_routes_inaccessible()
591 $resp = $this->get('/register/invite/abc123');
592 $this->assertPermissionError($resp);
594 $resp = $this->post('/register/invite/abc123');
595 $this->assertPermissionError($resp);
598 public function test_user_register_routes_inaccessible()
600 $resp = $this->get('/register');
601 $this->assertPermissionError($resp);
603 $resp = $this->post('/register');
604 $this->assertPermissionError($resp);
607 public function test_dump_user_details_option_works()
609 config()->set(['services.ldap.dump_user_details' => true, 'services.ldap.thumbnail_attribute' => 'jpegphoto']);
611 $this->commonLdapMocks(1, 1, 1, 1, 1);
612 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
613 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
614 ->andReturn(['count' => 1, 0 => [
615 'uid' => [$this->mockUser->name],
616 'cn' => [$this->mockUser->name],
617 // Test dumping binary data for avatar responses
618 'jpegphoto' => base64_decode('/9j/4AAQSkZJRg=='),
619 'dn' => 'dc=test' . config('services.ldap.base_dn'),
622 $resp = $this->post('/login', [
623 'username' => $this->mockUser->name,
624 'password' => $this->mockUser->password,
626 $resp->assertJsonStructure([
627 'details_from_ldap' => [],
628 'details_bookstack_parsed' => [],
632 public function test_start_tls_called_if_option_set()
634 config()->set(['services.ldap.start_tls' => true]);
635 $this->mockLdap->shouldReceive('startTls')->once()->andReturn(true);
636 $this->runFailedAuthLogin();
639 public function test_connection_fails_if_tls_fails()
641 config()->set(['services.ldap.start_tls' => true]);
642 $this->mockLdap->shouldReceive('startTls')->once()->andReturn(false);
643 $this->commonLdapMocks(1, 1, 0, 0, 0);
644 $resp = $this->post('/login', ['username' => 'timmyjenkins', 'password' => 'cattreedog']);
645 $resp->assertStatus(500);
648 public function test_ldap_attributes_can_be_binary_decoded_if_marked()
650 config()->set(['services.ldap.id_attribute' => 'BIN;uid']);
651 $ldapService = app()->make(LdapService::class);
652 $this->commonLdapMocks(1, 1, 1, 1, 1);
653 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
654 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), ['cn', 'dn', 'uid', 'mail', 'cn'])
655 ->andReturn(['count' => 1, 0 => [
656 'uid' => [hex2bin('FFF8F7')],
657 'cn' => [$this->mockUser->name],
658 'dn' => 'dc=test' . config('services.ldap.base_dn'),
661 $details = $ldapService->getUserDetails('test');
662 $this->assertEquals('fff8f7', $details['uid']);
665 public function test_new_ldap_user_login_with_already_used_email_address_shows_error_message_to_user()
667 $this->commonLdapMocks(1, 1, 2, 4, 2);
668 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
669 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
670 ->andReturn(['count' => 1, 0 => [
671 'uid' => [$this->mockUser->name],
672 'cn' => [$this->mockUser->name],
673 'dn' => 'dc=test' . config('services.ldap.base_dn'),
675 ]], ['count' => 1, 0 => [
678 'dn' => 'dc=bscott' . config('services.ldap.base_dn'),
683 $this->mockUserLogin()->assertRedirect('/');
687 $resp = $this->followingRedirects()->post('/login', ['username' => 'bscott', 'password' => 'pass']);
688 $resp->assertSee('A user with the email
[email protected] already exists but with different credentials');
691 public function test_login_with_email_confirmation_required_maps_groups_but_shows_confirmation_screen()
693 $roleToReceive = Role::factory()->create(['display_name' => 'LdapTester']);
694 $user = User::factory()->make();
695 setting()->put('registration-confirmation', 'true');
698 'services.ldap.user_to_groups' => true,
699 'services.ldap.group_attribute' => 'memberOf',
700 'services.ldap.remove_from_groups' => true,
703 $this->commonLdapMocks(1, 1, 6, 8, 6, 4);
704 $this->mockLdap->shouldReceive('searchAndGetEntries')
706 ->andReturn(['count' => 1, 0 => [
707 'uid' => [$user->name],
708 'cn' => [$user->name],
709 'dn' => 'dc=test' . config('services.ldap.base_dn'),
710 'mail' => [$user->email],
713 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com',
717 $login = $this->followingRedirects()->mockUserLogin();
718 $login->assertSee('Thanks for registering!');
719 $this->assertDatabaseHas('users', [
720 'email' => $user->email,
721 'email_confirmed' => false,
724 $user = User::query()->where('email', '=', $user->email)->first();
725 $this->assertDatabaseHas('role_user', [
726 'user_id' => $user->id,
727 'role_id' => $roleToReceive->id,
730 $this->assertNull(auth()->user());
732 $homePage = $this->get('/');
733 $homePage->assertRedirect('/login');
735 $login = $this->followingRedirects()->mockUserLogin();
736 $login->assertSee('Email Address Not Confirmed');
739 public function test_failed_logins_are_logged_when_message_configured()
741 $log = $this->withTestLogger();
742 config()->set(['logging.failed_login.message' => 'Failed login for %u']);
743 $this->runFailedAuthLogin();
744 $this->assertTrue($log->hasWarningThatContains('Failed login for timmyjenkins'));
747 public function test_thumbnail_attribute_used_as_user_avatar_if_configured()
749 config()->set(['services.ldap.thumbnail_attribute' => 'jpegPhoto']);
751 $this->commonLdapMocks(1, 1, 1, 2, 1);
752 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
753 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
754 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
755 ->andReturn(['count' => 1, 0 => [
756 'cn' => [$this->mockUser->name],
758 'jpegphoto' => [base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8Q
759 EBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=')],
760 'mail' => [$this->mockUser->email],
763 $this->mockUserLogin()
764 ->assertRedirect('/');
766 $user = User::query()->where('email', '=', $this->mockUser->email)->first();
767 $this->assertNotNull($user->avatar);
768 $this->assertEquals('8c90748342f19b195b9c6b4eff742ded', md5_file(public_path($user->avatar->path)));