]> BookStack Code Mirror - bookstack/blob - app/Users/Controllers/UserApiController.php
PHP: Addressed 8.4 deprecations within app itself
[bookstack] / app / Users / Controllers / UserApiController.php
1 <?php
2
3 namespace BookStack\Users\Controllers;
4
5 use BookStack\Exceptions\UserUpdateException;
6 use BookStack\Http\ApiController;
7 use BookStack\Users\Models\User;
8 use BookStack\Users\UserRepo;
9 use Closure;
10 use Illuminate\Http\Request;
11 use Illuminate\Support\Facades\DB;
12 use Illuminate\Validation\Rules\Password;
13 use Illuminate\Validation\Rules\Unique;
14
15 class UserApiController extends ApiController
16 {
17     protected UserRepo $userRepo;
18
19     protected array $fieldsToExpose = [
20         'email', 'created_at', 'updated_at', 'last_activity_at', 'external_auth_id',
21     ];
22
23     public function __construct(UserRepo $userRepo)
24     {
25         $this->userRepo = $userRepo;
26
27         // Checks for all endpoints in this controller
28         $this->middleware(function ($request, $next) {
29             $this->checkPermission('users-manage');
30             $this->preventAccessInDemoMode();
31
32             return $next($request);
33         });
34     }
35
36     protected function rules(?int $userId = null): array
37     {
38         return [
39             'create' => [
40                 'name'  => ['required', 'string', 'min:1', 'max:100'],
41                 'email' => [
42                     'required', 'string', 'email', 'min:2', new Unique('users', 'email'),
43                 ],
44                 'external_auth_id' => ['string'],
45                 'language'         => ['string', 'max:15', 'alpha_dash'],
46                 'password'         => ['string', Password::default()],
47                 'roles'            => ['array'],
48                 'roles.*'          => ['integer'],
49                 'send_invite'      => ['boolean'],
50             ],
51             'update' => [
52                 'name'  => ['string', 'min:1', 'max:100'],
53                 'email' => [
54                     'string',
55                     'email',
56                     'min:2',
57                     (new Unique('users', 'email'))->ignore($userId),
58                 ],
59                 'external_auth_id' => ['string'],
60                 'language'         => ['string', 'max:15', 'alpha_dash'],
61                 'password'         => ['string', Password::default()],
62                 'roles'            => ['array'],
63                 'roles.*'          => ['integer'],
64             ],
65             'delete' => [
66                 'migrate_ownership_id' => ['integer', 'exists:users,id'],
67             ],
68         ];
69     }
70
71     /**
72      * Get a listing of users in the system.
73      * Requires permission to manage users.
74      */
75     public function list()
76     {
77         $users = User::query()->select(['users.*'])
78             ->scopes('withLastActivityAt')
79             ->with(['avatar']);
80
81         return $this->apiListingResponse($users, [
82             'id', 'name', 'slug', 'email', 'external_auth_id',
83             'created_at', 'updated_at', 'last_activity_at',
84         ], [Closure::fromCallable([$this, 'listFormatter'])]);
85     }
86
87     /**
88      * Create a new user in the system.
89      * Requires permission to manage users.
90      */
91     public function create(Request $request)
92     {
93         $data = $this->validate($request, $this->rules()['create']);
94         $sendInvite = boolval($data['send_invite'] ?? false) === true;
95
96         $user = null;
97         DB::transaction(function () use ($data, $sendInvite, &$user) {
98             $user = $this->userRepo->create($data, $sendInvite);
99         });
100
101         $this->singleFormatter($user);
102
103         return response()->json($user);
104     }
105
106     /**
107      * View the details of a single user.
108      * Requires permission to manage users.
109      */
110     public function read(string $id)
111     {
112         $user = $this->userRepo->getById($id);
113         $this->singleFormatter($user);
114
115         return response()->json($user);
116     }
117
118     /**
119      * Update an existing user in the system.
120      * Requires permission to manage users.
121      *
122      * @throws UserUpdateException
123      */
124     public function update(Request $request, string $id)
125     {
126         $data = $this->validate($request, $this->rules($id)['update']);
127         $user = $this->userRepo->getById($id);
128         $this->userRepo->update($user, $data, userCan('users-manage'));
129         $this->singleFormatter($user);
130
131         return response()->json($user);
132     }
133
134     /**
135      * Delete a user from the system.
136      * Can optionally accept a user id via `migrate_ownership_id` to indicate
137      * who should be the new owner of their related content.
138      * Requires permission to manage users.
139      */
140     public function delete(Request $request, string $id)
141     {
142         $user = $this->userRepo->getById($id);
143         $newOwnerId = $request->get('migrate_ownership_id', null);
144
145         $this->userRepo->destroy($user, $newOwnerId);
146
147         return response('', 204);
148     }
149
150     /**
151      * Format the given user model for single-result display.
152      */
153     protected function singleFormatter(User $user)
154     {
155         $this->listFormatter($user);
156         $user->load('roles:id,display_name');
157         $user->makeVisible(['roles']);
158     }
159
160     /**
161      * Format the given user model for a listing multi-result display.
162      */
163     protected function listFormatter(User $user)
164     {
165         $user->makeVisible($this->fieldsToExpose);
166         $user->setAttribute('profile_url', $user->getProfileUrl());
167         $user->setAttribute('edit_url', $user->getEditUrl());
168         $user->setAttribute('avatar_url', $user->getAvatar());
169     }
170 }