2 use BookStack\Auth\Role;
3 use BookStack\Auth\Access\Ldap;
4 use BookStack\Auth\User;
5 use Mockery\MockInterface;
7 class LdapTest extends BrowserKitTest
16 protected $resourceId = 'resource-test';
18 public function setUp()
21 if (!defined('LDAP_OPT_REFERRALS')) define('LDAP_OPT_REFERRALS', 1);
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',
29 $this->mockLdap = \Mockery::mock(Ldap::class);
30 $this->app[Ldap::class] = $this->mockLdap;
31 $this->mockUser = factory(User::class)->make();
34 public function test_login()
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')]
46 $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
48 $this->visit('/login')
50 ->type($this->mockUser->name, '#username')
51 ->type($this->mockUser->password, '#password')
53 ->seePageIs('/login')->see('Please enter an email to use for this account.');
55 $this->type($this->mockUser->email, '#email')
58 ->see($this->mockUser->name)
59 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name]);
62 public function test_login_works_when_no_uid_provided_by_ldap_server()
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],
73 'mail' => [$this->mockUser->email]
75 $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true);
77 $this->visit('/login')
79 ->type($this->mockUser->name, '#username')
80 ->type($this->mockUser->password, '#password')
83 ->see($this->mockUser->name)
84 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
87 public function test_initial_incorrect_details()
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')]
99 $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true, true, false);
101 $this->visit('/login')
103 ->type($this->mockUser->name, '#username')
104 ->type($this->mockUser->password, '#password')
106 ->seePageIs('/login')->see('These credentials do not match our records.')
107 ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
110 public function test_create_user_form()
112 $this->asAdmin()->visit('/settings/users/create')
113 ->dontSee('Password')
114 ->type($this->mockUser->name, '#name')
115 ->type($this->mockUser->email, '#email')
117 ->see('The external auth id field is required.')
118 ->type($this->mockUser->name, '#external_auth_id')
120 ->seePageIs('/settings/users')
121 ->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
124 public function test_user_edit_form()
126 $editUser = $this->getNormalUser();
127 $this->asAdmin()->visit('/settings/users/' . $editUser->id)
129 ->dontSee('Password')
130 ->type('test_auth_id', '#external_auth_id')
132 ->seePageIs('/settings/users')
133 ->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
136 public function test_registration_disabled()
138 $this->visit('/register')
139 ->seePageIs('/login');
142 public function test_non_admins_cannot_change_auth_id()
144 $testUser = $this->getNormalUser();
145 $this->actingAs($testUser)->visit('/settings/users/' . $testUser->id)
146 ->dontSee('External Authentication');
149 public function test_login_maps_roles_and_retains_existsing_roles()
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);
158 'services.ldap.user_to_groups' => true,
159 'services.ldap.group_attribute' => 'memberOf',
160 'services.ldap.remove_from_groups' => false,
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],
174 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
175 1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
178 $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
180 $this->visit('/login')
182 ->type($this->mockUser->name, '#username')
183 ->type($this->mockUser->password, '#password')
187 $user = User::where('email', $this->mockUser->email)->first();
188 $this->seeInDatabase('role_user', [
189 'user_id' => $user->id,
190 'role_id' => $roleToReceive->id
192 $this->seeInDatabase('role_user', [
193 'user_id' => $user->id,
194 'role_id' => $roleToReceive2->id
196 $this->seeInDatabase('role_user', [
197 'user_id' => $user->id,
198 'role_id' => $existingRole->id
202 public function test_login_maps_roles_and_removes_old_roles_if_set()
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);
210 'services.ldap.user_to_groups' => true,
211 'services.ldap.group_attribute' => 'memberOf',
212 'services.ldap.remove_from_groups' => true,
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],
226 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
229 $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
231 $this->visit('/login')
233 ->type($this->mockUser->name, '#username')
234 ->type($this->mockUser->password, '#password')
238 $user = User::where('email', $this->mockUser->email)->first();
239 $this->seeInDatabase('role_user', [
240 'user_id' => $user->id,
241 'role_id' => $roleToReceive->id
243 $this->dontSeeInDatabase('role_user', [
244 'user_id' => $user->id,
245 'role_id' => $existingRole->id
249 public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
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)
256 public function test_login_maps_roles_using_external_auth_ids_if_set()
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']);
262 'services.ldap.user_to_groups' => true,
263 'services.ldap.group_attribute' => 'memberOf',
264 'services.ldap.remove_from_groups' => true,
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],
278 0 => "cn=ex-auth-a,ou=groups,dc=example,dc=com",
281 $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
283 $this->visit('/login')
285 ->type($this->mockUser->name, '#username')
286 ->type($this->mockUser->password, '#password')
290 $user = User::where('email', $this->mockUser->email)->first();
291 $this->seeInDatabase('role_user', [
292 'user_id' => $user->id,
293 'role_id' => $roleToReceive->id
295 $this->dontSeeInDatabase('role_user', [
296 'user_id' => $user->id,
297 'role_id' => $roleToNotReceive->id
301 public function test_login_group_mapping_does_not_conflict_with_default_role()
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();
307 setting()->put('registration-role', $roleToReceive->id);
310 'services.ldap.user_to_groups' => true,
311 'services.ldap.group_attribute' => 'memberOf',
312 'services.ldap.remove_from_groups' => true,
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],
326 0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
327 1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
330 $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
332 $this->visit('/login')
334 ->type($this->mockUser->name, '#username')
335 ->type($this->mockUser->password, '#password')
339 $user = User::where('email', $this->mockUser->email)->first();
340 $this->seeInDatabase('role_user', [
341 'user_id' => $user->id,
342 'role_id' => $roleToReceive->id
344 $this->seeInDatabase('role_user', [
345 'user_id' => $user->id,
346 'role_id' => $roleToReceive2->id