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;
8 use Tests\BrowserKitTest;
10 class LdapTest extends BrowserKitTest
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,
38 $this->mockLdap = \Mockery::mock(Ldap::class);
39 $this->app[Ldap::class] = $this->mockLdap;
40 $this->mockUser = factory(User::class)->make();
43 protected function mockEscapes($times = 1)
45 $this->mockLdap->shouldReceive('escape')->times($times)->andReturnUsing(function($val) {
46 return ldap_escape($val);
50 protected function mockExplodes($times = 1)
52 $this->mockLdap->shouldReceive('explodeDn')->times($times)->andReturnUsing(function($dn, $withAttrib) {
53 return ldap_explode_dn($dn, $withAttrib);
57 protected function mockUserLogin()
59 return $this->visit('/login')
61 ->type($this->mockUser->name, '#username')
62 ->type($this->mockUser->password, '#password')
66 public function test_login()
68 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
69 $this->mockLdap->shouldReceive('setVersion')->once();
70 $this->mockLdap->shouldReceive('setOption')->times(2);
71 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
72 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
73 ->andReturn(['count' => 1, 0 => [
74 'uid' => [$this->mockUser->name],
75 'cn' => [$this->mockUser->name],
76 'dn' => ['dc=test' . config('services.ldap.base_dn')]
78 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
79 $this->mockEscapes(2);
81 $this->mockUserLogin()
82 ->seePageIs('/login')->see('Please enter an email to use for this account.');
84 $this->type($this->mockUser->email, '#email')
87 ->see($this->mockUser->name)
88 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name]);
91 public function test_email_domain_restriction_active_on_new_ldap_login()
94 'registration-restrict' => 'testing.com'
97 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
98 $this->mockLdap->shouldReceive('setVersion')->once();
99 $this->mockLdap->shouldReceive('setOption')->times(2);
100 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
101 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
102 ->andReturn(['count' => 1, 0 => [
103 'uid' => [$this->mockUser->name],
104 'cn' => [$this->mockUser->name],
105 'dn' => ['dc=test' . config('services.ldap.base_dn')]
107 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
108 $this->mockEscapes(2);
110 $this->mockUserLogin()
111 ->seePageIs('/login')
112 ->see('Please enter an email to use for this account.');
116 $this->type($email, '#email')
118 ->seePageIs('/login')
119 ->see('That email domain does not have access to this application')
120 ->dontSeeInDatabase('users', ['email' => $email]);
123 public function test_login_works_when_no_uid_provided_by_ldap_server()
125 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
126 $this->mockLdap->shouldReceive('setVersion')->once();
127 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
128 $this->mockLdap->shouldReceive('setOption')->times(1);
129 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
130 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
131 ->andReturn(['count' => 1, 0 => [
132 'cn' => [$this->mockUser->name],
134 'mail' => [$this->mockUser->email]
136 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
137 $this->mockEscapes(1);
139 $this->mockUserLogin()
141 ->see($this->mockUser->name)
142 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
145 public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properly()
147 config()->set(['services.ldap.id_attribute' => 'my_custom_id']);
148 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
149 $this->mockLdap->shouldReceive('setVersion')->once();
150 $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
151 $this->mockLdap->shouldReceive('setOption')->times(1);
152 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
153 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
154 ->andReturn(['count' => 1, 0 => [
155 'cn' => [$this->mockUser->name],
157 'my_custom_id' => ['cooluser456'],
158 'mail' => [$this->mockUser->email]
162 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
163 $this->mockEscapes(1);
165 $this->mockUserLogin()
167 ->see($this->mockUser->name)
168 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
171 public function test_initial_incorrect_credentials()
173 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
174 $this->mockLdap->shouldReceive('setVersion')->once();
175 $this->mockLdap->shouldReceive('setOption')->times(1);
176 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
177 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
178 ->andReturn(['count' => 1, 0 => [
179 'uid' => [$this->mockUser->name],
180 'cn' => [$this->mockUser->name],
181 'dn' => ['dc=test' . config('services.ldap.base_dn')]
183 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true, false);
184 $this->mockEscapes(1);
186 $this->mockUserLogin()
187 ->seePageIs('/login')->see('These credentials do not match our records.')
188 ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
191 public function test_login_not_found_username()
193 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
194 $this->mockLdap->shouldReceive('setVersion')->once();
195 $this->mockLdap->shouldReceive('setOption')->times(1);
196 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
197 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
198 ->andReturn(['count' => 0]);
199 $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true, false);
200 $this->mockEscapes(1);
202 $this->mockUserLogin()
203 ->seePageIs('/login')->see('These credentials do not match our records.')
204 ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
208 public function test_create_user_form()
210 $this->asAdmin()->visit('/settings/users/create')
211 ->dontSee('Password')
212 ->type($this->mockUser->name, '#name')
213 ->type($this->mockUser->email, '#email')
215 ->see('The external auth id field is required.')
216 ->type($this->mockUser->name, '#external_auth_id')
218 ->seePageIs('/settings/users')
219 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
222 public function test_user_edit_form()
224 $editUser = $this->getNormalUser();
225 $this->asAdmin()->visit('/settings/users/' . $editUser->id)
227 ->dontSee('Password')
228 ->type('test_auth_id', '#external_auth_id')
230 ->seePageIs('/settings/users')
231 ->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
234 public function test_registration_disabled()
236 $this->visit('/register')
237 ->seePageIs('/login');
240 public function test_non_admins_cannot_change_auth_id()
242 $testUser = $this->getNormalUser();
243 $this->actingAs($testUser)->visit('/settings/users/' . $testUser->id)
244 ->dontSee('External Authentication');
247 public function test_login_maps_roles_and_retains_existing_roles()
249 $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
250 $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
251 $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
252 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
253 $this->mockUser->attachRole($existingRole);
256 'services.ldap.user_to_groups' => true,
257 'services.ldap.group_attribute' => 'memberOf',
258 'services.ldap.remove_from_groups' => false,
260 $this->mockLdap->shouldReceive('connect')->times(1)->andReturn($this->resourceId);
261 $this->mockLdap->shouldReceive('setVersion')->times(1);
262 $this->mockLdap->shouldReceive('setOption')->times(4);
263 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
264 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
265 ->andReturn(['count' => 1, 0 => [
266 'uid' => [$this->mockUser->name],
267 'cn' => [$this->mockUser->name],
268 'dn' => ['dc=test' . config('services.ldap.base_dn')],
269 'mail' => [$this->mockUser->email],
272 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
273 1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
276 $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
277 $this->mockEscapes(4);
278 $this->mockExplodes(6);
280 $this->mockUserLogin()->seePageIs('/');
282 $user = User::where('email', $this->mockUser->email)->first();
283 $this->seeInDatabase('role_user', [
284 'user_id' => $user->id,
285 'role_id' => $roleToReceive->id
287 $this->seeInDatabase('role_user', [
288 'user_id' => $user->id,
289 'role_id' => $roleToReceive2->id
291 $this->seeInDatabase('role_user', [
292 'user_id' => $user->id,
293 'role_id' => $existingRole->id
297 public function test_login_maps_roles_and_removes_old_roles_if_set()
299 $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
300 $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
301 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
302 $this->mockUser->attachRole($existingRole);
305 'services.ldap.user_to_groups' => true,
306 'services.ldap.group_attribute' => 'memberOf',
307 'services.ldap.remove_from_groups' => true,
309 $this->mockLdap->shouldReceive('connect')->times(1)->andReturn($this->resourceId);
310 $this->mockLdap->shouldReceive('setVersion')->times(1);
311 $this->mockLdap->shouldReceive('setOption')->times(3);
312 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
313 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
314 ->andReturn(['count' => 1, 0 => [
315 'uid' => [$this->mockUser->name],
316 'cn' => [$this->mockUser->name],
317 'dn' => ['dc=test' . config('services.ldap.base_dn')],
318 'mail' => [$this->mockUser->email],
321 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
324 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
325 $this->mockEscapes(3);
326 $this->mockExplodes(2);
328 $this->mockUserLogin()->seePageIs('/');
330 $user = User::where('email', $this->mockUser->email)->first();
331 $this->seeInDatabase('role_user', [
332 'user_id' => $user->id,
333 'role_id' => $roleToReceive->id
335 $this->dontSeeInDatabase('role_user', [
336 'user_id' => $user->id,
337 'role_id' => $existingRole->id
341 public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
343 $role = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
344 $this->asAdmin()->visit('/settings/roles/' . $role->id)
348 public function test_login_maps_roles_using_external_auth_ids_if_set()
350 $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
351 $roleToNotReceive = factory(Role::class)->create(['name' => 'ldaptester-not-receive', 'display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
354 'services.ldap.user_to_groups' => true,
355 'services.ldap.group_attribute' => 'memberOf',
356 'services.ldap.remove_from_groups' => true,
358 $this->mockLdap->shouldReceive('connect')->times(1)->andReturn($this->resourceId);
359 $this->mockLdap->shouldReceive('setVersion')->times(1);
360 $this->mockLdap->shouldReceive('setOption')->times(3);
361 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
362 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
363 ->andReturn(['count' => 1, 0 => [
364 'uid' => [$this->mockUser->name],
365 'cn' => [$this->mockUser->name],
366 'dn' => ['dc=test' . config('services.ldap.base_dn')],
367 'mail' => [$this->mockUser->email],
370 0 => "cn=ex-auth-a,ou=groups,dc=example,dc=com",
373 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
374 $this->mockEscapes(3);
375 $this->mockExplodes(2);
377 $this->mockUserLogin()->seePageIs('/');
379 $user = User::where('email', $this->mockUser->email)->first();
380 $this->seeInDatabase('role_user', [
381 'user_id' => $user->id,
382 'role_id' => $roleToReceive->id
384 $this->dontSeeInDatabase('role_user', [
385 'user_id' => $user->id,
386 'role_id' => $roleToNotReceive->id
390 public function test_login_group_mapping_does_not_conflict_with_default_role()
392 $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
393 $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
394 $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
396 setting()->put('registration-role', $roleToReceive->id);
399 'services.ldap.user_to_groups' => true,
400 'services.ldap.group_attribute' => 'memberOf',
401 'services.ldap.remove_from_groups' => true,
403 $this->mockLdap->shouldReceive('connect')->times(1)->andReturn($this->resourceId);
404 $this->mockLdap->shouldReceive('setVersion')->times(1);
405 $this->mockLdap->shouldReceive('setOption')->times(4);
406 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
407 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
408 ->andReturn(['count' => 1, 0 => [
409 'uid' => [$this->mockUser->name],
410 'cn' => [$this->mockUser->name],
411 'dn' => ['dc=test' . config('services.ldap.base_dn')],
412 'mail' => [$this->mockUser->email],
415 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
416 1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
419 $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
420 $this->mockEscapes(4);
421 $this->mockExplodes(6);
423 $this->mockUserLogin()->seePageIs('/');
425 $user = User::where('email', $this->mockUser->email)->first();
426 $this->seeInDatabase('role_user', [
427 'user_id' => $user->id,
428 'role_id' => $roleToReceive->id
430 $this->seeInDatabase('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->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
443 $this->mockLdap->shouldReceive('setVersion')->once();
444 $this->mockLdap->shouldReceive('setOption')->times(2);
445 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
446 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
447 ->andReturn(['count' => 1, 0 => [
448 'uid' => [$this->mockUser->name],
449 'cn' => [$this->mockUser->name],
450 'dn' => ['dc=test' . config('services.ldap.base_dn')],
451 'displayname' => 'displayNameAttribute'
453 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
454 $this->mockEscapes(2);
456 $this->mockUserLogin()
457 ->seePageIs('/login')->see('Please enter an email to use for this account.');
459 $this->type($this->mockUser->email, '#email')
462 ->see('displayNameAttribute')
463 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => 'displayNameAttribute']);
466 public function test_login_uses_default_display_name_attribute_if_specified_not_present()
469 'services.ldap.display_name_attribute' => 'displayName'
472 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
473 $this->mockLdap->shouldReceive('setVersion')->once();
474 $this->mockLdap->shouldReceive('setOption')->times(2);
475 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
476 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
477 ->andReturn(['count' => 1, 0 => [
478 'uid' => [$this->mockUser->name],
479 'cn' => [$this->mockUser->name],
480 'dn' => ['dc=test' . config('services.ldap.base_dn')]
482 $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
483 $this->mockEscapes(2);
485 $this->mockUserLogin()
486 ->seePageIs('/login')->see('Please enter an email to use for this account.');
488 $this->type($this->mockUser->email, '#email')
491 ->see($this->mockUser->name)
492 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => $this->mockUser->name]);
495 protected function checkLdapReceivesCorrectDetails($serverString, $expectedHost, $expectedPort)
498 'services.ldap.server' => $serverString
502 $this->mockLdap->shouldReceive('setVersion')->once();
503 $this->mockLdap->shouldReceive('setOption')->times(1);
504 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)->andReturn(['count' => 1, 0 => [
505 'uid' => [$this->mockUser->name],
506 'cn' => [$this->mockUser->name],
507 'dn' => ['dc=test' . config('services.ldap.base_dn')]
509 $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
510 $this->mockEscapes(1);
512 $this->mockLdap->shouldReceive('connect')->once()
513 ->with($expectedHost, $expectedPort)->andReturn($this->resourceId);
514 $this->mockUserLogin();
517 public function test_ldap_port_provided_on_host_if_host_is_full_uri()
519 $hostName = 'ldaps://bookstack:8080';
520 $this->checkLdapReceivesCorrectDetails($hostName, $hostName, 389);
523 public function test_ldap_port_parsed_from_server_if_host_is_not_full_uri()
525 $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com:8080', 'ldap.bookstack.com', 8080);
528 public function test_default_ldap_port_used_if_not_in_server_string_and_not_uri()
530 $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com', 'ldap.bookstack.com', 389);
533 public function test_forgot_password_routes_inaccessible()
535 $resp = $this->get('/password/email');
536 $this->assertPermissionError($resp);
538 $resp = $this->post('/password/email');
539 $this->assertPermissionError($resp);
541 $resp = $this->get('/password/reset/abc123');
542 $this->assertPermissionError($resp);
544 $resp = $this->post('/password/reset');
545 $this->assertPermissionError($resp);
548 public function test_user_invite_routes_inaccessible()
550 $resp = $this->get('/register/invite/abc123');
551 $this->assertPermissionError($resp);
553 $resp = $this->post('/register/invite/abc123');
554 $this->assertPermissionError($resp);
557 public function test_user_register_routes_inaccessible()
559 $resp = $this->get('/register');
560 $this->assertPermissionError($resp);
562 $resp = $this->post('/register');
563 $this->assertPermissionError($resp);
566 public function test_dump_user_details_option_works()
568 config()->set(['services.ldap.dump_user_details' => true]);
570 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
571 $this->mockLdap->shouldReceive('setVersion')->once();
572 $this->mockLdap->shouldReceive('setOption')->times(1);
573 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
574 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
575 ->andReturn(['count' => 1, 0 => [
576 'uid' => [$this->mockUser->name],
577 'cn' => [$this->mockUser->name],
578 'dn' => ['dc=test' . config('services.ldap.base_dn')]
580 $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true);
581 $this->mockEscapes(1);
583 $this->post('/login', [
584 'username' => $this->mockUser->name,
585 'password' => $this->mockUser->password,
587 $this->seeJsonStructure([
588 'details_from_ldap' => [],
589 'details_bookstack_parsed' => [],
593 public function test_ldap_attributes_can_be_binary_decoded_if_marked()
595 config()->set(['services.ldap.id_attribute' => 'BIN;uid']);
596 $ldapService = app()->make(LdapService::class);
598 $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
599 $this->mockLdap->shouldReceive('setVersion')->once();
600 $this->mockLdap->shouldReceive('setOption')->times(1);
601 $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
602 ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), ['cn', 'dn', 'uid', 'mail', 'cn'])
603 ->andReturn(['count' => 1, 0 => [
604 'uid' => [hex2bin('FFF8F7')],
605 'cn' => [$this->mockUser->name],
606 'dn' => ['dc=test' . config('services.ldap.base_dn')]
608 $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true);
609 $this->mockEscapes(1);
611 $details = $ldapService->getUserDetails('test');
612 $this->assertEquals('fff8f7', $details['uid']);