]> BookStack Code Mirror - bookstack/blob - tests/Api/UsersApiTest.php
Chapters API: Added missing book_slug field
[bookstack] / tests / Api / UsersApiTest.php
1 <?php
2
3 namespace Tests\Api;
4
5 use BookStack\Access\Notifications\UserInviteNotification;
6 use BookStack\Activity\ActivityType;
7 use BookStack\Activity\Models\Activity as ActivityModel;
8 use BookStack\Entities\Models\Entity;
9 use BookStack\Facades\Activity;
10 use BookStack\Users\Models\Role;
11 use BookStack\Users\Models\User;
12 use Illuminate\Support\Facades\Hash;
13 use Illuminate\Support\Facades\Notification;
14 use Tests\TestCase;
15
16 class UsersApiTest extends TestCase
17 {
18     use TestsApi;
19
20     protected string $baseEndpoint = '/api/users';
21
22     protected array $endpointMap = [
23         ['get', '/api/users'],
24         ['post', '/api/users'],
25         ['get', '/api/users/1'],
26         ['put', '/api/users/1'],
27         ['delete', '/api/users/1'],
28     ];
29
30     public function test_users_manage_permission_needed_for_all_endpoints()
31     {
32         $this->actingAsApiEditor();
33         foreach ($this->endpointMap as [$method, $uri]) {
34             $resp = $this->json($method, $uri);
35             $resp->assertStatus(403);
36             $resp->assertJson($this->permissionErrorResponse());
37         }
38     }
39
40     public function test_no_endpoints_accessible_in_demo_mode()
41     {
42         config()->set('app.env', 'demo');
43         $this->actingAsApiAdmin();
44
45         foreach ($this->endpointMap as [$method, $uri]) {
46             $resp = $this->json($method, $uri);
47             $resp->assertStatus(403);
48             $resp->assertJson($this->permissionErrorResponse());
49         }
50     }
51
52     public function test_index_endpoint_returns_expected_user()
53     {
54         $this->actingAsApiAdmin();
55         /** @var User $firstUser */
56         $firstUser = User::query()->orderBy('id', 'asc')->first();
57
58         $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
59         $resp->assertJson(['data' => [
60             [
61                 'id'          => $firstUser->id,
62                 'name'        => $firstUser->name,
63                 'slug'        => $firstUser->slug,
64                 'email'       => $firstUser->email,
65                 'profile_url' => $firstUser->getProfileUrl(),
66                 'edit_url'    => $firstUser->getEditUrl(),
67                 'avatar_url'  => $firstUser->getAvatar(),
68             ],
69         ]]);
70     }
71
72     public function test_index_endpoint_has_correct_created_and_last_activity_dates()
73     {
74         $user = $this->users->editor();
75         $user->created_at = now()->subYear();
76         $user->save();
77
78         $this->actingAs($user);
79         Activity::add(ActivityType::AUTH_LOGIN, 'test login activity');
80         /** @var ActivityModel $activity */
81         $activity = ActivityModel::query()->where('user_id', '=', $user->id)->latest()->first();
82
83         $resp = $this->asAdmin()->getJson($this->baseEndpoint . '?filter[id]=3');
84         $resp->assertJson(['data' => [
85             [
86                 'id'          => $user->id,
87                 'created_at' => $user->created_at->toJSON(),
88                 'last_activity_at' => $activity->created_at->toJson(),
89             ],
90         ]]);
91     }
92
93     public function test_create_endpoint()
94     {
95         $this->actingAsApiAdmin();
96         /** @var Role $role */
97         $role = Role::query()->first();
98
99         $resp = $this->postJson($this->baseEndpoint, [
100             'name'        => 'Benny Boris',
101             'email'       => '[email protected]',
102             'password'    => 'mysuperpass',
103             'language'    => 'it',
104             'roles'       => [$role->id],
105             'send_invite' => false,
106         ]);
107
108         $resp->assertStatus(200);
109         $resp->assertJson([
110             'name'             => 'Benny Boris',
111             'email'            => '[email protected]',
112             'external_auth_id' => '',
113             'roles'            => [
114                 [
115                     'id'           => $role->id,
116                     'display_name' => $role->display_name,
117                 ],
118             ],
119         ]);
120         $this->assertDatabaseHas('users', ['email' => '[email protected]']);
121
122         /** @var User $user */
123         $user = User::query()->where('email', '=', '[email protected]')->first();
124         $this->assertActivityExists(ActivityType::USER_CREATE, null, $user->logDescriptor());
125         $this->assertEquals(1, $user->roles()->count());
126         $this->assertEquals('it', setting()->getUser($user, 'language'));
127     }
128
129     public function test_create_with_send_invite()
130     {
131         $this->actingAsApiAdmin();
132         Notification::fake();
133
134         $resp = $this->postJson($this->baseEndpoint, [
135             'name'        => 'Benny Boris',
136             'email'       => '[email protected]',
137             'send_invite' => true,
138         ]);
139
140         $resp->assertStatus(200);
141         /** @var User $user */
142         $user = User::query()->where('email', '=', '[email protected]')->first();
143         Notification::assertSentTo($user, UserInviteNotification::class);
144     }
145
146     public function test_create_with_send_invite_works_with_value_of_1()
147     {
148         $this->actingAsApiAdmin();
149         Notification::fake();
150
151         $resp = $this->postJson($this->baseEndpoint, [
152             'name'        => 'Benny Boris',
153             'email'       => '[email protected]',
154             'send_invite' => '1', // Submissions via x-www-form-urlencoded/form-data may use 1 instead of boolean
155         ]);
156
157         $resp->assertStatus(200);
158         /** @var User $user */
159         $user = User::query()->where('email', '=', '[email protected]')->first();
160         Notification::assertSentTo($user, UserInviteNotification::class);
161     }
162
163     public function test_create_name_and_email_validation()
164     {
165         $this->actingAsApiAdmin();
166         /** @var User $existingUser */
167         $existingUser = User::query()->first();
168
169         $resp = $this->postJson($this->baseEndpoint, [
170             'email' => '[email protected]',
171         ]);
172         $resp->assertStatus(422);
173         $resp->assertJson($this->validationResponse(['name' => ['The name field is required.']]));
174
175         $resp = $this->postJson($this->baseEndpoint, [
176             'name' => 'Benny Boris',
177         ]);
178         $resp->assertStatus(422);
179         $resp->assertJson($this->validationResponse(['email' => ['The email field is required.']]));
180
181         $resp = $this->postJson($this->baseEndpoint, [
182             'email' => $existingUser->email,
183             'name'  => 'Benny Boris',
184         ]);
185         $resp->assertStatus(422);
186         $resp->assertJson($this->validationResponse(['email' => ['The email has already been taken.']]));
187     }
188
189     public function test_read_endpoint()
190     {
191         $this->actingAsApiAdmin();
192         /** @var User $user */
193         $user = User::query()->first();
194         /** @var Role $userRole */
195         $userRole = $user->roles()->first();
196
197         $resp = $this->getJson($this->baseEndpoint . "/{$user->id}");
198
199         $resp->assertStatus(200);
200         $resp->assertJson([
201             'id'               => $user->id,
202             'slug'             => $user->slug,
203             'email'            => $user->email,
204             'external_auth_id' => $user->external_auth_id,
205             'roles'            => [
206                 [
207                     'id'           => $userRole->id,
208                     'display_name' => $userRole->display_name,
209                 ],
210             ],
211         ]);
212     }
213
214     public function test_update_endpoint()
215     {
216         $this->actingAsApiAdmin();
217         /** @var User $user */
218         $user = $this->users->admin();
219         $roles = Role::query()->pluck('id');
220         $resp = $this->putJson($this->baseEndpoint . "/{$user->id}", [
221             'name'             => 'My updated user',
222             'email'            => '[email protected]',
223             'roles'            => $roles,
224             'external_auth_id' => 'btest',
225             'password'         => 'barrytester',
226             'language'         => 'fr',
227         ]);
228
229         $resp->assertStatus(200);
230         $resp->assertJson([
231             'id'               => $user->id,
232             'name'             => 'My updated user',
233             'email'            => '[email protected]',
234             'external_auth_id' => 'btest',
235         ]);
236         $user->refresh();
237         $this->assertEquals('fr', setting()->getUser($user, 'language'));
238         $this->assertEquals(count($roles), $user->roles()->count());
239         $this->assertNotEquals('barrytester', $user->password);
240         $this->assertTrue(Hash::check('barrytester', $user->password));
241     }
242
243     public function test_update_endpoint_does_not_remove_info_if_not_provided()
244     {
245         $this->actingAsApiAdmin();
246         /** @var User $user */
247         $user = $this->users->admin();
248         $roleCount = $user->roles()->count();
249         $resp = $this->putJson($this->baseEndpoint . "/{$user->id}", []);
250
251         $resp->assertStatus(200);
252         $this->assertDatabaseHas('users', [
253             'id'       => $user->id,
254             'name'     => $user->name,
255             'email'    => $user->email,
256             'password' => $user->password,
257         ]);
258         $this->assertEquals($roleCount, $user->roles()->count());
259     }
260
261     public function test_delete_endpoint()
262     {
263         $this->actingAsApiAdmin();
264         /** @var User $user */
265         $user = User::query()->where('id', '!=', $this->users->admin()->id)
266             ->whereNull('system_name')
267             ->first();
268
269         $resp = $this->deleteJson($this->baseEndpoint . "/{$user->id}");
270
271         $resp->assertStatus(204);
272         $this->assertActivityExists('user_delete', null, $user->logDescriptor());
273     }
274
275     public function test_delete_endpoint_with_ownership_migration_user()
276     {
277         $this->actingAsApiAdmin();
278         /** @var User $user */
279         $user = User::query()->where('id', '!=', $this->users->admin()->id)
280             ->whereNull('system_name')
281             ->first();
282         $entityChain = $this->entities->createChainBelongingToUser($user);
283         /** @var User $newOwner */
284         $newOwner = User::query()->where('id', '!=', $user->id)->first();
285
286         /** @var Entity $entity */
287         foreach ($entityChain as $entity) {
288             $this->assertEquals($user->id, $entity->owned_by);
289         }
290
291         $resp = $this->deleteJson($this->baseEndpoint . "/{$user->id}", [
292             'migrate_ownership_id' => $newOwner->id,
293         ]);
294
295         $resp->assertStatus(204);
296         /** @var Entity $entity */
297         foreach ($entityChain as $entity) {
298             $this->assertEquals($newOwner->id, $entity->refresh()->owned_by);
299         }
300     }
301
302     public function test_delete_endpoint_fails_deleting_only_admin()
303     {
304         $this->actingAsApiAdmin();
305         $adminRole = Role::getSystemRole('admin');
306         $adminToDelete = $adminRole->users()->first();
307         $adminRole->users()->where('id', '!=', $adminToDelete->id)->delete();
308
309         $resp = $this->deleteJson($this->baseEndpoint . "/{$adminToDelete->id}");
310
311         $resp->assertStatus(500);
312         $resp->assertJson($this->errorResponse('You cannot delete the only admin', 500));
313     }
314
315     public function test_delete_endpoint_fails_deleting_public_user()
316     {
317         $this->actingAsApiAdmin();
318         /** @var User $publicUser */
319         $publicUser = User::query()->where('system_name', '=', 'public')->first();
320
321         $resp = $this->deleteJson($this->baseEndpoint . "/{$publicUser->id}");
322
323         $resp->assertStatus(500);
324         $resp->assertJson($this->errorResponse('You cannot delete the guest user', 500));
325     }
326 }