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 class LdapTest extends BrowserKitTest
18 protected $resourceId = 'resource-test';
20 public function setUp(): void
23 if (!defined('LDAP_OPT_REFERRALS')) define('LDAP_OPT_REFERRALS', 1);
25 'auth.method' => 'ldap',
26 'auth.defaults.guard' => 'ldap',
27 'services.ldap.base_dn' => 'dc=ldap,dc=local',
28 'services.ldap.email_attribute' => 'mail',
29 'services.ldap.display_name_attribute' => 'cn',
30 'services.ldap.id_attribute' => 'uid',
31 'services.ldap.user_to_groups' => false,
32 'services.ldap.version' => '3',
33 'services.ldap.user_filter' => '(&(uid=${user}))',
34 'services.ldap.follow_referrals' => false,
35 'services.ldap.tls_insecure' => false,
37 $this->mockLdap = \Mockery::mock(Ldap::class);
38 $this->app[Ldap::class] = $this->mockLdap;
39 $this->mockUser = factory(User::class)->make();
42 protected function mockEscapes($times = 1)
44 $this->mockLdap->shouldReceive('escape')->times($times)->andReturnUsing(function($val) {
45 return ldap_escape($val);
49 protected function mockExplodes($times = 1)
51 $this->mockLdap->shouldReceive('explodeDn')->times($times)->andReturnUsing(function($dn, $withAttrib) {
52 return ldap_explode_dn($dn, $withAttrib);
56 protected function mockUserLogin()
58 return $this->visit('/login')
60 ->type($this->mockUser->name, '#username')
61 ->type($this->mockUser->password, '#password')
65 public function test_login()
67 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
68 $this->mockLdap->shouldReceive('setVersion')->once();
69 $this->mockLdap->shouldReceive('setOption')->times(2);
70 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
71 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
72 ->andReturn(['count' => 1, 0 => [
73 'uid' => [$this->mockUser->name],
74 'cn' => [$this->mockUser->name],
75 'dn' => ['dc=test' . config('services.ldap.base_dn')]
77 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
78 $this->mockEscapes(2);
80 $this->mockUserLogin()
81 ->seePageIs('/login')->see('Please enter an email to use for this account.');
83 $this->type($this->mockUser->email, '#email')
86 ->see($this->mockUser->name)
87 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name]);
90 public function test_email_domain_restriction_active_on_new_ldap_login()
93 'registration-restrict' => 'testing.com'
96 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
97 $this->mockLdap->shouldReceive('setVersion')->once();
98 $this->mockLdap->shouldReceive('setOption')->times(2);
99 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
100 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
101 ->andReturn(['count' => 1, 0 => [
102 'uid' => [$this->mockUser->name],
103 'cn' => [$this->mockUser->name],
104 'dn' => ['dc=test' . config('services.ldap.base_dn')]
106 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
107 $this->mockEscapes(2);
109 $this->mockUserLogin()
110 ->seePageIs('/login')
111 ->see('Please enter an email to use for this account.');
115 $this->type($email, '#email')
117 ->seePageIs('/login')
118 ->see('That email domain does not have access to this application')
119 ->dontSeeInDatabase('users', ['email' => $email]);
122 public function test_login_works_when_no_uid_provided_by_ldap_server()
124 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
125 $this->mockLdap->shouldReceive('setVersion')->once();
126 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
127 $this->mockLdap->shouldReceive('setOption')->times(1);
128 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
129 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
130 ->andReturn(['count' => 1, 0 => [
131 'cn' => [$this->mockUser->name],
133 'mail' => [$this->mockUser->email]
135 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
136 $this->mockEscapes(1);
138 $this->mockUserLogin()
140 ->see($this->mockUser->name)
141 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
144 public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properly()
146 config()->set(['services.ldap.id_attribute' => 'my_custom_id']);
147 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
148 $this->mockLdap->shouldReceive('setVersion')->once();
149 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
150 $this->mockLdap->shouldReceive('setOption')->times(1);
151 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
152 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
153 ->andReturn(['count' => 1, 0 => [
154 'cn' => [$this->mockUser->name],
156 'my_custom_id' => ['cooluser456'],
157 'mail' => [$this->mockUser->email]
161 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
162 $this->mockEscapes(1);
164 $this->mockUserLogin()
166 ->see($this->mockUser->name)
167 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
170 public function test_initial_incorrect_credentials()
172 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
173 $this->mockLdap->shouldReceive('setVersion')->once();
174 $this->mockLdap->shouldReceive('setOption')->times(1);
175 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
176 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
177 ->andReturn(['count' => 1, 0 => [
178 'uid' => [$this->mockUser->name],
179 'cn' => [$this->mockUser->name],
180 'dn' => ['dc=test' . config('services.ldap.base_dn')]
182 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true, false);
183 $this->mockEscapes(1);
185 $this->mockUserLogin()
186 ->seePageIs('/login')->see('These credentials do not match our records.')
187 ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
190 public function test_login_not_found_username()
192 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
193 $this->mockLdap->shouldReceive('setVersion')->once();
194 $this->mockLdap->shouldReceive('setOption')->times(1);
195 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
196 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
197 ->andReturn(['count' => 0]);
198 $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true, false);
199 $this->mockEscapes(1);
201 $this->mockUserLogin()
202 ->seePageIs('/login')->see('These credentials do not match our records.')
203 ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
207 public function test_create_user_form()
209 $this->asAdmin()->visit('/settings/users/create')
210 ->dontSee('Password')
211 ->type($this->mockUser->name, '#name')
212 ->type($this->mockUser->email, '#email')
214 ->see('The external auth id field is required.')
215 ->type($this->mockUser->name, '#external_auth_id')
217 ->seePageIs('/settings/users')
218 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
221 public function test_user_edit_form()
223 $editUser = $this->getNormalUser();
224 $this->asAdmin()->visit('/settings/users/' . $editUser->id)
226 ->dontSee('Password')
227 ->type('test_auth_id', '#external_auth_id')
229 ->seePageIs('/settings/users')
230 ->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
233 public function test_registration_disabled()
235 $this->visit('/register')
236 ->seePageIs('/login');
239 public function test_non_admins_cannot_change_auth_id()
241 $testUser = $this->getNormalUser();
242 $this->actingAs($testUser)->visit('/settings/users/' . $testUser->id)
243 ->dontSee('External Authentication');
246 public function test_login_maps_roles_and_retains_existing_roles()
248 $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
249 $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
250 $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
251 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
252 $this->mockUser->attachRole($existingRole);
255 'services.ldap.user_to_groups' => true,
256 'services.ldap.group_attribute' => 'memberOf',
257 'services.ldap.remove_from_groups' => false,
259 $this->mockLdap->shouldReceive('connect')->times(1)->andReturn($this->resourceId);
260 $this->mockLdap->shouldReceive('setVersion')->times(1);
261 $this->mockLdap->shouldReceive('setOption')->times(4);
262 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
263 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
264 ->andReturn(['count' => 1, 0 => [
265 'uid' => [$this->mockUser->name],
266 'cn' => [$this->mockUser->name],
267 'dn' => ['dc=test' . config('services.ldap.base_dn')],
268 'mail' => [$this->mockUser->email],
271 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
272 1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
275 $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
276 $this->mockEscapes(4);
277 $this->mockExplodes(6);
279 $this->mockUserLogin()->seePageIs('/');
281 $user = User::where('email', $this->mockUser->email)->first();
282 $this->seeInDatabase('role_user', [
283 'user_id' => $user->id,
284 'role_id' => $roleToReceive->id
286 $this->seeInDatabase('role_user', [
287 'user_id' => $user->id,
288 'role_id' => $roleToReceive2->id
290 $this->seeInDatabase('role_user', [
291 'user_id' => $user->id,
292 'role_id' => $existingRole->id
296 public function test_login_maps_roles_and_removes_old_roles_if_set()
298 $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
299 $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
300 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
301 $this->mockUser->attachRole($existingRole);
304 'services.ldap.user_to_groups' => true,
305 'services.ldap.group_attribute' => 'memberOf',
306 'services.ldap.remove_from_groups' => true,
308 $this->mockLdap->shouldReceive('connect')->times(1)->andReturn($this->resourceId);
309 $this->mockLdap->shouldReceive('setVersion')->times(1);
310 $this->mockLdap->shouldReceive('setOption')->times(3);
311 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
312 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
313 ->andReturn(['count' => 1, 0 => [
314 'uid' => [$this->mockUser->name],
315 'cn' => [$this->mockUser->name],
316 'dn' => ['dc=test' . config('services.ldap.base_dn')],
317 'mail' => [$this->mockUser->email],
320 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
323 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
324 $this->mockEscapes(3);
325 $this->mockExplodes(2);
327 $this->mockUserLogin()->seePageIs('/');
329 $user = User::where('email', $this->mockUser->email)->first();
330 $this->seeInDatabase('role_user', [
331 'user_id' => $user->id,
332 'role_id' => $roleToReceive->id
334 $this->dontSeeInDatabase('role_user', [
335 'user_id' => $user->id,
336 'role_id' => $existingRole->id
340 public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
342 $role = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
343 $this->asAdmin()->visit('/settings/roles/' . $role->id)
347 public function test_login_maps_roles_using_external_auth_ids_if_set()
349 $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
350 $roleToNotReceive = factory(Role::class)->create(['name' => 'ldaptester-not-receive', 'display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
353 'services.ldap.user_to_groups' => true,
354 'services.ldap.group_attribute' => 'memberOf',
355 'services.ldap.remove_from_groups' => true,
357 $this->mockLdap->shouldReceive('connect')->times(1)->andReturn($this->resourceId);
358 $this->mockLdap->shouldReceive('setVersion')->times(1);
359 $this->mockLdap->shouldReceive('setOption')->times(3);
360 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
361 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
362 ->andReturn(['count' => 1, 0 => [
363 'uid' => [$this->mockUser->name],
364 'cn' => [$this->mockUser->name],
365 'dn' => ['dc=test' . config('services.ldap.base_dn')],
366 'mail' => [$this->mockUser->email],
369 0 => "cn=ex-auth-a,ou=groups,dc=example,dc=com",
372 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
373 $this->mockEscapes(3);
374 $this->mockExplodes(2);
376 $this->mockUserLogin()->seePageIs('/');
378 $user = User::where('email', $this->mockUser->email)->first();
379 $this->seeInDatabase('role_user', [
380 'user_id' => $user->id,
381 'role_id' => $roleToReceive->id
383 $this->dontSeeInDatabase('role_user', [
384 'user_id' => $user->id,
385 'role_id' => $roleToNotReceive->id
389 public function test_login_group_mapping_does_not_conflict_with_default_role()
391 $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
392 $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
393 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
395 setting()->put('registration-role', $roleToReceive->id);
398 'services.ldap.user_to_groups' => true,
399 'services.ldap.group_attribute' => 'memberOf',
400 'services.ldap.remove_from_groups' => true,
402 $this->mockLdap->shouldReceive('connect')->times(1)->andReturn($this->resourceId);
403 $this->mockLdap->shouldReceive('setVersion')->times(1);
404 $this->mockLdap->shouldReceive('setOption')->times(4);
405 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
406 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
407 ->andReturn(['count' => 1, 0 => [
408 'uid' => [$this->mockUser->name],
409 'cn' => [$this->mockUser->name],
410 'dn' => ['dc=test' . config('services.ldap.base_dn')],
411 'mail' => [$this->mockUser->email],
414 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
415 1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
418 $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
419 $this->mockEscapes(4);
420 $this->mockExplodes(6);
422 $this->mockUserLogin()->seePageIs('/');
424 $user = User::where('email', $this->mockUser->email)->first();
425 $this->seeInDatabase('role_user', [
426 'user_id' => $user->id,
427 'role_id' => $roleToReceive->id
429 $this->seeInDatabase('role_user', [
430 'user_id' => $user->id,
431 'role_id' => $roleToReceive2->id
435 public function test_login_uses_specified_display_name_attribute()
438 'services.ldap.display_name_attribute' => 'displayName'
441 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
442 $this->mockLdap->shouldReceive('setVersion')->once();
443 $this->mockLdap->shouldReceive('setOption')->times(2);
444 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
445 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
446 ->andReturn(['count' => 1, 0 => [
447 'uid' => [$this->mockUser->name],
448 'cn' => [$this->mockUser->name],
449 'dn' => ['dc=test' . config('services.ldap.base_dn')],
450 'displayname' => 'displayNameAttribute'
452 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
453 $this->mockEscapes(2);
455 $this->mockUserLogin()
456 ->seePageIs('/login')->see('Please enter an email to use for this account.');
458 $this->type($this->mockUser->email, '#email')
461 ->see('displayNameAttribute')
462 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => 'displayNameAttribute']);
465 public function test_login_uses_default_display_name_attribute_if_specified_not_present()
468 'services.ldap.display_name_attribute' => 'displayName'
471 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
472 $this->mockLdap->shouldReceive('setVersion')->once();
473 $this->mockLdap->shouldReceive('setOption')->times(2);
474 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
475 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
476 ->andReturn(['count' => 1, 0 => [
477 'uid' => [$this->mockUser->name],
478 'cn' => [$this->mockUser->name],
479 'dn' => ['dc=test' . config('services.ldap.base_dn')]
481 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
482 $this->mockEscapes(2);
484 $this->mockUserLogin()
485 ->seePageIs('/login')->see('Please enter an email to use for this account.');
487 $this->type($this->mockUser->email, '#email')
490 ->see($this->mockUser->name)
491 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => $this->mockUser->name]);
494 protected function checkLdapReceivesCorrectDetails($serverString, $expectedHost, $expectedPort)
497 'services.ldap.server' => $serverString
501 $this->mockLdap->shouldReceive('setVersion')->once();
502 $this->mockLdap->shouldReceive('setOption')->times(1);
503 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)->andReturn(['count' => 1, 0 => [
504 'uid' => [$this->mockUser->name],
505 'cn' => [$this->mockUser->name],
506 'dn' => ['dc=test' . config('services.ldap.base_dn')]
508 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
509 $this->mockEscapes(1);
511 $this->mockLdap->shouldReceive('connect')->once()
512 ->with($expectedHost, $expectedPort)->andReturn($this->resourceId);
513 $this->mockUserLogin();
516 public function test_ldap_port_provided_on_host_if_host_is_full_uri()
518 $hostName = 'ldaps://bookstack:8080';
519 $this->checkLdapReceivesCorrectDetails($hostName, $hostName, 389);
522 public function test_ldap_port_parsed_from_server_if_host_is_not_full_uri()
524 $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com:8080', 'ldap.bookstack.com', 8080);
527 public function test_default_ldap_port_used_if_not_in_server_string_and_not_uri()
529 $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com', 'ldap.bookstack.com', 389);
532 public function test_forgot_password_routes_inaccessible()
534 $resp = $this->get('/password/email');
535 $this->assertPermissionError($resp);
537 $resp = $this->post('/password/email');
538 $this->assertPermissionError($resp);
540 $resp = $this->get('/password/reset/abc123');
541 $this->assertPermissionError($resp);
543 $resp = $this->post('/password/reset');
544 $this->assertPermissionError($resp);
547 public function test_user_invite_routes_inaccessible()
549 $resp = $this->get('/register/invite/abc123');
550 $this->assertPermissionError($resp);
552 $resp = $this->post('/register/invite/abc123');
553 $this->assertPermissionError($resp);
556 public function test_user_register_routes_inaccessible()
558 $resp = $this->get('/register');
559 $this->assertPermissionError($resp);
561 $resp = $this->post('/register');
562 $this->assertPermissionError($resp);
565 public function test_dump_user_details_option_works()
567 config()->set(['services.ldap.dump_user_details' => true]);
569 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
570 $this->mockLdap->shouldReceive('setVersion')->once();
571 $this->mockLdap->shouldReceive('setOption')->times(1);
572 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
573 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
574 ->andReturn(['count' => 1, 0 => [
575 'uid' => [$this->mockUser->name],
576 'cn' => [$this->mockUser->name],
577 'dn' => ['dc=test' . config('services.ldap.base_dn')]
579 $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true);
580 $this->mockEscapes(1);
582 $this->post('/login', [
583 'username' => $this->mockUser->name,
584 'password' => $this->mockUser->password,
586 $this->seeJsonStructure([
587 'details_from_ldap' => [],
588 'details_bookstack_parsed' => [],
592 public function test_ldap_attributes_can_be_binary_decoded_if_marked()
594 config()->set(['services.ldap.id_attribute' => 'BIN;uid']);
595 $ldapService = app()->make(LdapService::class);
597 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
598 $this->mockLdap->shouldReceive('setVersion')->once();
599 $this->mockLdap->shouldReceive('setOption')->times(1);
600 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
601 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), ['cn', 'dn', 'uid', 'mail', 'cn'])
602 ->andReturn(['count' => 1, 0 => [
603 'uid' => [hex2bin('FFF8F7')],
604 'cn' => [$this->mockUser->name],
605 'dn' => ['dc=test' . config('services.ldap.base_dn')]
607 $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true);
608 $this->mockEscapes(1);
610 $details = $ldapService->getUserDetails('test');
611 $this->assertEquals('fff8f7', $details['uid']);