]> BookStack Code Mirror - bookstack/blob - tests/Auth/LdapTest.php
New translations errors.php (Portuguese, Brazilian)
[bookstack] / tests / Auth / LdapTest.php
1 <?php namespace Tests;
2
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
9 class LdapTest extends BrowserKitTest
10 {
11
12     /**
13      * @var MockInterface
14      */
15     protected $mockLdap;
16
17     protected $mockUser;
18     protected $resourceId = 'resource-test';
19
20     public function setUp(): void
21     {
22         parent::setUp();
23         if (!defined('LDAP_OPT_REFERRALS')) define('LDAP_OPT_REFERRALS', 1);
24         config()->set([
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,
36         ]);
37         $this->mockLdap = \Mockery::mock(Ldap::class);
38         $this->app[Ldap::class] = $this->mockLdap;
39         $this->mockUser = factory(User::class)->make();
40     }
41
42     protected function mockEscapes($times = 1)
43     {
44         $this->mockLdap->shouldReceive('escape')->times($times)->andReturnUsing(function($val) {
45             return ldap_escape($val);
46         });
47     }
48
49     protected function mockExplodes($times = 1)
50     {
51         $this->mockLdap->shouldReceive('explodeDn')->times($times)->andReturnUsing(function($dn, $withAttrib) {
52             return ldap_explode_dn($dn, $withAttrib);
53         });
54     }
55
56     protected function mockUserLogin()
57     {
58         return $this->visit('/login')
59             ->see('Username')
60             ->type($this->mockUser->name, '#username')
61             ->type($this->mockUser->password, '#password')
62             ->press('Log In');
63     }
64
65     public function test_login()
66     {
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')]
76             ]]);
77         $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
78         $this->mockEscapes(2);
79
80         $this->mockUserLogin()
81             ->seePageIs('/login')->see('Please enter an email to use for this account.');
82
83         $this->type($this->mockUser->email, '#email')
84             ->press('Log In')
85             ->seePageIs('/')
86             ->see($this->mockUser->name)
87             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name]);
88     }
89
90     public function test_email_domain_restriction_active_on_new_ldap_login()
91     {
92         $this->setSettings([
93             'registration-restrict' => 'testing.com'
94         ]);
95
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')]
105             ]]);
106         $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
107         $this->mockEscapes(2);
108
109         $this->mockUserLogin()
110             ->seePageIs('/login')
111             ->see('Please enter an email to use for this account.');
112
113         $email = '[email protected]';
114
115         $this->type($email, '#email')
116             ->press('Log In')
117             ->seePageIs('/login')
118             ->see('That email domain does not have access to this application')
119             ->dontSeeInDatabase('users', ['email' => $email]);
120     }
121
122     public function test_login_works_when_no_uid_provided_by_ldap_server()
123     {
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],
132                 'dn' => $ldapDn,
133                 'mail' => [$this->mockUser->email]
134             ]]);
135         $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
136         $this->mockEscapes(1);
137
138         $this->mockUserLogin()
139             ->seePageIs('/')
140             ->see($this->mockUser->name)
141             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
142     }
143
144     public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properly()
145     {
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],
155                 'dn' => $ldapDn,
156                 'my_custom_id' => ['cooluser456'],
157                 'mail' => [$this->mockUser->email]
158             ]]);
159
160
161         $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
162         $this->mockEscapes(1);
163
164         $this->mockUserLogin()
165             ->seePageIs('/')
166             ->see($this->mockUser->name)
167             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
168     }
169
170     public function test_initial_incorrect_credentials()
171     {
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')]
181             ]]);
182         $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true, false);
183         $this->mockEscapes(1);
184
185         $this->mockUserLogin()
186             ->seePageIs('/login')->see('These credentials do not match our records.')
187             ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
188     }
189
190     public function test_login_not_found_username()
191     {
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);
200
201         $this->mockUserLogin()
202             ->seePageIs('/login')->see('These credentials do not match our records.')
203             ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
204     }
205
206
207     public function test_create_user_form()
208     {
209         $this->asAdmin()->visit('/settings/users/create')
210             ->dontSee('Password')
211             ->type($this->mockUser->name, '#name')
212             ->type($this->mockUser->email, '#email')
213             ->press('Save')
214             ->see('The external auth id field is required.')
215             ->type($this->mockUser->name, '#external_auth_id')
216             ->press('Save')
217             ->seePageIs('/settings/users')
218             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
219     }
220
221     public function test_user_edit_form()
222     {
223         $editUser = $this->getNormalUser();
224         $this->asAdmin()->visit('/settings/users/' . $editUser->id)
225             ->see('Edit User')
226             ->dontSee('Password')
227             ->type('test_auth_id', '#external_auth_id')
228             ->press('Save')
229             ->seePageIs('/settings/users')
230             ->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
231     }
232
233     public function test_registration_disabled()
234     {
235         $this->visit('/register')
236             ->seePageIs('/login');
237     }
238
239     public function test_non_admins_cannot_change_auth_id()
240     {
241         $testUser = $this->getNormalUser();
242         $this->actingAs($testUser)->visit('/settings/users/' . $testUser->id)
243             ->dontSee('External Authentication');
244     }
245
246     public function test_login_maps_roles_and_retains_existing_roles()
247     {
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);
253
254         app('config')->set([
255             'services.ldap.user_to_groups' => true,
256             'services.ldap.group_attribute' => 'memberOf',
257             'services.ldap.remove_from_groups' => false,
258         ]);
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],
269                 'memberof' => [
270                     'count' => 2,
271                     0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
272                     1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
273                 ]
274             ]]);
275         $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
276         $this->mockEscapes(4);
277         $this->mockExplodes(6);
278
279         $this->mockUserLogin()->seePageIs('/');
280
281         $user = User::where('email', $this->mockUser->email)->first();
282         $this->seeInDatabase('role_user', [
283             'user_id' => $user->id,
284             'role_id' => $roleToReceive->id
285         ]);
286         $this->seeInDatabase('role_user', [
287             'user_id' => $user->id,
288             'role_id' => $roleToReceive2->id
289         ]);
290         $this->seeInDatabase('role_user', [
291             'user_id' => $user->id,
292             'role_id' => $existingRole->id
293         ]);
294     }
295
296     public function test_login_maps_roles_and_removes_old_roles_if_set()
297     {
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);
302
303         app('config')->set([
304             'services.ldap.user_to_groups' => true,
305             'services.ldap.group_attribute' => 'memberOf',
306             'services.ldap.remove_from_groups' => true,
307         ]);
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],
318                 'memberof' => [
319                     'count' => 1,
320                     0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
321                 ]
322             ]]);
323         $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
324         $this->mockEscapes(3);
325         $this->mockExplodes(2);
326
327         $this->mockUserLogin()->seePageIs('/');
328
329         $user = User::where('email', $this->mockUser->email)->first();
330         $this->seeInDatabase('role_user', [
331             'user_id' => $user->id,
332             'role_id' => $roleToReceive->id
333         ]);
334         $this->dontSeeInDatabase('role_user', [
335             'user_id' => $user->id,
336             'role_id' => $existingRole->id
337         ]);
338     }
339
340     public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
341     {
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)
344             ->see('ex-auth-a');
345     }
346
347     public function test_login_maps_roles_using_external_auth_ids_if_set()
348     {
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']);
351
352         app('config')->set([
353             'services.ldap.user_to_groups' => true,
354             'services.ldap.group_attribute' => 'memberOf',
355             'services.ldap.remove_from_groups' => true,
356         ]);
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],
367                 'memberof' => [
368                     'count' => 1,
369                     0 => "cn=ex-auth-a,ou=groups,dc=example,dc=com",
370                 ]
371             ]]);
372         $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
373         $this->mockEscapes(3);
374         $this->mockExplodes(2);
375
376         $this->mockUserLogin()->seePageIs('/');
377
378         $user = User::where('email', $this->mockUser->email)->first();
379         $this->seeInDatabase('role_user', [
380             'user_id' => $user->id,
381             'role_id' => $roleToReceive->id
382         ]);
383         $this->dontSeeInDatabase('role_user', [
384             'user_id' => $user->id,
385             'role_id' => $roleToNotReceive->id
386         ]);
387     }
388
389     public function test_login_group_mapping_does_not_conflict_with_default_role()
390     {
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();
394
395         setting()->put('registration-role', $roleToReceive->id);
396
397         app('config')->set([
398             'services.ldap.user_to_groups' => true,
399             'services.ldap.group_attribute' => 'memberOf',
400             'services.ldap.remove_from_groups' => true,
401         ]);
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],
412                 'memberof' => [
413                     'count' => 2,
414                     0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
415                     1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
416                 ]
417             ]]);
418         $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
419         $this->mockEscapes(4);
420         $this->mockExplodes(6);
421
422         $this->mockUserLogin()->seePageIs('/');
423
424         $user = User::where('email', $this->mockUser->email)->first();
425         $this->seeInDatabase('role_user', [
426             'user_id' => $user->id,
427             'role_id' => $roleToReceive->id
428         ]);
429         $this->seeInDatabase('role_user', [
430             'user_id' => $user->id,
431             'role_id' => $roleToReceive2->id
432         ]);
433     }
434
435     public function test_login_uses_specified_display_name_attribute()
436     {
437         app('config')->set([
438             'services.ldap.display_name_attribute' => 'displayName'
439         ]);
440
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'
451             ]]);
452         $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
453         $this->mockEscapes(2);
454
455         $this->mockUserLogin()
456             ->seePageIs('/login')->see('Please enter an email to use for this account.');
457
458         $this->type($this->mockUser->email, '#email')
459             ->press('Log In')
460             ->seePageIs('/')
461             ->see('displayNameAttribute')
462             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => 'displayNameAttribute']);
463     }
464
465     public function test_login_uses_default_display_name_attribute_if_specified_not_present()
466     {
467         app('config')->set([
468             'services.ldap.display_name_attribute' => 'displayName'
469         ]);
470
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')]
480             ]]);
481         $this->mockLdap->shouldReceive('bind')->times(4)->andReturn(true);
482         $this->mockEscapes(2);
483
484         $this->mockUserLogin()
485             ->seePageIs('/login')->see('Please enter an email to use for this account.');
486
487         $this->type($this->mockUser->email, '#email')
488             ->press('Log In')
489             ->seePageIs('/')
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]);
492     }
493
494     protected function checkLdapReceivesCorrectDetails($serverString, $expectedHost, $expectedPort)
495     {
496         app('config')->set([
497             'services.ldap.server' => $serverString
498         ]);
499
500         // Standard mocks
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')]
507         ]]);
508         $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true);
509         $this->mockEscapes(1);
510
511         $this->mockLdap->shouldReceive('connect')->once()
512             ->with($expectedHost, $expectedPort)->andReturn($this->resourceId);
513         $this->mockUserLogin();
514     }
515
516     public function test_ldap_port_provided_on_host_if_host_is_full_uri()
517     {
518         $hostName = 'ldaps://bookstack:8080';
519         $this->checkLdapReceivesCorrectDetails($hostName, $hostName, 389);
520     }
521
522     public function test_ldap_port_parsed_from_server_if_host_is_not_full_uri()
523     {
524         $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com:8080', 'ldap.bookstack.com', 8080);
525     }
526
527     public function test_default_ldap_port_used_if_not_in_server_string_and_not_uri()
528     {
529         $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com', 'ldap.bookstack.com', 389);
530     }
531
532     public function test_forgot_password_routes_inaccessible()
533     {
534         $resp = $this->get('/password/email');
535         $this->assertPermissionError($resp);
536
537         $resp = $this->post('/password/email');
538         $this->assertPermissionError($resp);
539
540         $resp = $this->get('/password/reset/abc123');
541         $this->assertPermissionError($resp);
542
543         $resp = $this->post('/password/reset');
544         $this->assertPermissionError($resp);
545     }
546
547     public function test_user_invite_routes_inaccessible()
548     {
549         $resp = $this->get('/register/invite/abc123');
550         $this->assertPermissionError($resp);
551
552         $resp = $this->post('/register/invite/abc123');
553         $this->assertPermissionError($resp);
554     }
555
556     public function test_user_register_routes_inaccessible()
557     {
558         $resp = $this->get('/register');
559         $this->assertPermissionError($resp);
560
561         $resp = $this->post('/register');
562         $this->assertPermissionError($resp);
563     }
564
565     public function test_dump_user_details_option_works()
566     {
567         config()->set(['services.ldap.dump_user_details' => true]);
568
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')]
578             ]]);
579         $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true);
580         $this->mockEscapes(1);
581
582         $this->post('/login', [
583             'username' => $this->mockUser->name,
584             'password' => $this->mockUser->password,
585         ]);
586         $this->seeJsonStructure([
587             'details_from_ldap' => [],
588             'details_bookstack_parsed' => [],
589         ]);
590     }
591
592     public function test_ldap_attributes_can_be_binary_decoded_if_marked()
593     {
594         config()->set(['services.ldap.id_attribute' => 'BIN;uid']);
595         $ldapService = app()->make(LdapService::class);
596
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')]
606             ]]);
607         $this->mockLdap->shouldReceive('bind')->times(1)->andReturn(true);
608         $this->mockEscapes(1);
609
610         $details = $ldapService->getUserDetails('test');
611         $this->assertEquals('fff8f7', $details['uid']);
612     }
613 }