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