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