]> BookStack Code Mirror - bookstack/blob - tests/Auth/LdapTest.php
Added a default timeout of 60 seconds to dropzone.
[bookstack] / tests / Auth / LdapTest.php
1 <?php namespace Tests;
2 use BookStack\Auth\Role;
3 use BookStack\Auth\Access\Ldap;
4 use BookStack\Auth\User;
5 use Mockery\MockInterface;
6
7 class LdapTest extends BrowserKitTest
8 {
9
10     /**
11      * @var MockInterface
12      */
13     protected $mockLdap;
14
15     protected $mockUser;
16     protected $resourceId = 'resource-test';
17
18     public function setUp()
19     {
20         parent::setUp();
21         if (!defined('LDAP_OPT_REFERRALS')) define('LDAP_OPT_REFERRALS', 1);
22         app('config')->set([
23             'auth.method' => 'ldap',
24             'services.ldap.base_dn' => 'dc=ldap,dc=local',
25             'services.ldap.email_attribute' => 'mail',
26             'services.ldap.user_to_groups' => false,
27             'auth.providers.users.driver' => 'ldap',
28         ]);
29         $this->mockLdap = \Mockery::mock(Ldap::class);
30         $this->app[Ldap::class] = $this->mockLdap;
31         $this->mockUser = factory(User::class)->make();
32     }
33
34     public function test_login()
35     {
36         $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
37         $this->mockLdap->shouldReceive('setVersion')->once();
38         $this->mockLdap->shouldReceive('setOption')->times(4);
39         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
40             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
41             ->andReturn(['count' => 1, 0 => [
42                 'uid' => [$this->mockUser->name],
43                 'cn' => [$this->mockUser->name],
44                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
45             ]]);
46         $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
47
48         $this->visit('/login')
49             ->see('Username')
50             ->type($this->mockUser->name, '#username')
51             ->type($this->mockUser->password, '#password')
52             ->press('Log In')
53             ->seePageIs('/login')->see('Please enter an email to use for this account.');
54
55         $this->type($this->mockUser->email, '#email')
56             ->press('Log In')
57             ->seePageIs('/')
58             ->see($this->mockUser->name)
59             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name]);
60     }
61
62     public function test_login_works_when_no_uid_provided_by_ldap_server()
63     {
64         $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
65         $this->mockLdap->shouldReceive('setVersion')->once();
66         $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
67         $this->mockLdap->shouldReceive('setOption')->times(2);
68         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
69             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
70             ->andReturn(['count' => 1, 0 => [
71                 'cn' => [$this->mockUser->name],
72                 'dn' => $ldapDn,
73                 'mail' => [$this->mockUser->email]
74             ]]);
75         $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true);
76
77         $this->visit('/login')
78             ->see('Username')
79             ->type($this->mockUser->name, '#username')
80             ->type($this->mockUser->password, '#password')
81             ->press('Log In')
82             ->seePageIs('/')
83             ->see($this->mockUser->name)
84             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
85     }
86
87     public function test_initial_incorrect_details()
88     {
89         $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
90         $this->mockLdap->shouldReceive('setVersion')->once();
91         $this->mockLdap->shouldReceive('setOption')->times(2);
92         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
93             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
94             ->andReturn(['count' => 1, 0 => [
95                 'uid' => [$this->mockUser->name],
96                 'cn' => [$this->mockUser->name],
97                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
98             ]]);
99         $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true, true, false);
100
101         $this->visit('/login')
102             ->see('Username')
103             ->type($this->mockUser->name, '#username')
104             ->type($this->mockUser->password, '#password')
105             ->press('Log In')
106             ->seePageIs('/login')->see('These credentials do not match our records.')
107             ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
108     }
109
110     public function test_create_user_form()
111     {
112         $this->asAdmin()->visit('/settings/users/create')
113             ->dontSee('Password')
114             ->type($this->mockUser->name, '#name')
115             ->type($this->mockUser->email, '#email')
116             ->press('Save')
117             ->see('The external auth id field is required.')
118             ->type($this->mockUser->name, '#external_auth_id')
119             ->press('Save')
120             ->seePageIs('/settings/users')
121             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
122     }
123
124     public function test_user_edit_form()
125     {
126         $editUser = $this->getNormalUser();
127         $this->asAdmin()->visit('/settings/users/' . $editUser->id)
128             ->see('Edit User')
129             ->dontSee('Password')
130             ->type('test_auth_id', '#external_auth_id')
131             ->press('Save')
132             ->seePageIs('/settings/users')
133             ->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
134     }
135
136     public function test_registration_disabled()
137     {
138         $this->visit('/register')
139             ->seePageIs('/login');
140     }
141
142     public function test_non_admins_cannot_change_auth_id()
143     {
144         $testUser = $this->getNormalUser();
145         $this->actingAs($testUser)->visit('/settings/users/' . $testUser->id)
146             ->dontSee('External Authentication');
147     }
148
149     public function test_login_maps_roles_and_retains_existsing_roles()
150     {
151         $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
152         $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
153         $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
154         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
155         $this->mockUser->attachRole($existingRole);
156
157         app('config')->set([
158             'services.ldap.user_to_groups' => true,
159             'services.ldap.group_attribute' => 'memberOf',
160             'services.ldap.remove_from_groups' => false,
161         ]);
162         $this->mockLdap->shouldReceive('connect')->times(2)->andReturn($this->resourceId);
163         $this->mockLdap->shouldReceive('setVersion')->times(2);
164         $this->mockLdap->shouldReceive('setOption')->times(5);
165         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(5)
166             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
167             ->andReturn(['count' => 1, 0 => [
168                 'uid' => [$this->mockUser->name],
169                 'cn' => [$this->mockUser->name],
170                 'dn' => ['dc=test' . config('services.ldap.base_dn')],
171                 'mail' => [$this->mockUser->email],
172                 'memberof' => [
173                     'count' => 2,
174                     0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
175                     1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
176                 ]
177             ]]);
178         $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
179
180         $this->visit('/login')
181             ->see('Username')
182             ->type($this->mockUser->name, '#username')
183             ->type($this->mockUser->password, '#password')
184             ->press('Log In')
185             ->seePageIs('/');
186
187         $user = User::where('email', $this->mockUser->email)->first();
188         $this->seeInDatabase('role_user', [
189             'user_id' => $user->id,
190             'role_id' => $roleToReceive->id
191         ]);
192         $this->seeInDatabase('role_user', [
193             'user_id' => $user->id,
194             'role_id' => $roleToReceive2->id
195         ]);
196         $this->seeInDatabase('role_user', [
197             'user_id' => $user->id,
198             'role_id' => $existingRole->id
199         ]);
200     }
201
202     public function test_login_maps_roles_and_removes_old_roles_if_set()
203     {
204         $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
205         $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
206         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
207         $this->mockUser->attachRole($existingRole);
208
209         app('config')->set([
210             'services.ldap.user_to_groups' => true,
211             'services.ldap.group_attribute' => 'memberOf',
212             'services.ldap.remove_from_groups' => true,
213         ]);
214         $this->mockLdap->shouldReceive('connect')->times(2)->andReturn($this->resourceId);
215         $this->mockLdap->shouldReceive('setVersion')->times(2);
216         $this->mockLdap->shouldReceive('setOption')->times(4);
217         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
218             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
219             ->andReturn(['count' => 1, 0 => [
220                 'uid' => [$this->mockUser->name],
221                 'cn' => [$this->mockUser->name],
222                 'dn' => ['dc=test' . config('services.ldap.base_dn')],
223                 'mail' => [$this->mockUser->email],
224                 'memberof' => [
225                     'count' => 1,
226                     0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
227                 ]
228             ]]);
229         $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
230
231         $this->visit('/login')
232             ->see('Username')
233             ->type($this->mockUser->name, '#username')
234             ->type($this->mockUser->password, '#password')
235             ->press('Log In')
236             ->seePageIs('/');
237
238         $user = User::where('email', $this->mockUser->email)->first();
239         $this->seeInDatabase('role_user', [
240             'user_id' => $user->id,
241             'role_id' => $roleToReceive->id
242         ]);
243         $this->dontSeeInDatabase('role_user', [
244             'user_id' => $user->id,
245             'role_id' => $existingRole->id
246         ]);
247     }
248
249     public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
250     {
251         $role = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
252         $this->asAdmin()->visit('/settings/roles/' . $role->id)
253             ->see('ex-auth-a');
254     }
255
256     public function test_login_maps_roles_using_external_auth_ids_if_set()
257     {
258         $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
259         $roleToNotReceive = factory(Role::class)->create(['name' => 'ldaptester-not-receive', 'display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
260
261         app('config')->set([
262             'services.ldap.user_to_groups' => true,
263             'services.ldap.group_attribute' => 'memberOf',
264             'services.ldap.remove_from_groups' => true,
265         ]);
266         $this->mockLdap->shouldReceive('connect')->times(2)->andReturn($this->resourceId);
267         $this->mockLdap->shouldReceive('setVersion')->times(2);
268         $this->mockLdap->shouldReceive('setOption')->times(4);
269         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
270             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
271             ->andReturn(['count' => 1, 0 => [
272                 'uid' => [$this->mockUser->name],
273                 'cn' => [$this->mockUser->name],
274                 'dn' => ['dc=test' . config('services.ldap.base_dn')],
275                 'mail' => [$this->mockUser->email],
276                 'memberof' => [
277                     'count' => 1,
278                     0 => "cn=ex-auth-a,ou=groups,dc=example,dc=com",
279                 ]
280             ]]);
281         $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
282
283         $this->visit('/login')
284             ->see('Username')
285             ->type($this->mockUser->name, '#username')
286             ->type($this->mockUser->password, '#password')
287             ->press('Log In')
288             ->seePageIs('/');
289
290         $user = User::where('email', $this->mockUser->email)->first();
291         $this->seeInDatabase('role_user', [
292             'user_id' => $user->id,
293             'role_id' => $roleToReceive->id
294         ]);
295         $this->dontSeeInDatabase('role_user', [
296             'user_id' => $user->id,
297             'role_id' => $roleToNotReceive->id
298         ]);
299     }
300
301     public function test_login_group_mapping_does_not_conflict_with_default_role()
302     {
303         $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
304         $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
305         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
306
307         setting()->put('registration-role', $roleToReceive->id);
308
309         app('config')->set([
310             'services.ldap.user_to_groups' => true,
311             'services.ldap.group_attribute' => 'memberOf',
312             'services.ldap.remove_from_groups' => true,
313         ]);
314         $this->mockLdap->shouldReceive('connect')->times(2)->andReturn($this->resourceId);
315         $this->mockLdap->shouldReceive('setVersion')->times(2);
316         $this->mockLdap->shouldReceive('setOption')->times(5);
317         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(5)
318             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
319             ->andReturn(['count' => 1, 0 => [
320                 'uid' => [$this->mockUser->name],
321                 'cn' => [$this->mockUser->name],
322                 'dn' => ['dc=test' . config('services.ldap.base_dn')],
323                 'mail' => [$this->mockUser->email],
324                 'memberof' => [
325                     'count' => 2,
326                     0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
327                     1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
328                 ]
329             ]]);
330         $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
331
332         $this->visit('/login')
333             ->see('Username')
334             ->type($this->mockUser->name, '#username')
335             ->type($this->mockUser->password, '#password')
336             ->press('Log In')
337             ->seePageIs('/');
338
339         $user = User::where('email', $this->mockUser->email)->first();
340         $this->seeInDatabase('role_user', [
341             'user_id' => $user->id,
342             'role_id' => $roleToReceive->id
343         ]);
344         $this->seeInDatabase('role_user', [
345             'user_id' => $user->id,
346             'role_id' => $roleToReceive2->id
347         ]);
348     }
349
350 }