UserManagerController.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <?php
  2. namespace App\Api\v1\Controllers;
  3. use App\Api\v1\Requests\UserManagerPromoteRequest;
  4. use App\Api\v1\Requests\UserManagerStoreRequest;
  5. use App\Api\v1\Resources\UserAuthentication;
  6. use App\Api\v1\Resources\UserManagerResource;
  7. use App\Http\Controllers\Controller;
  8. use App\Models\User;
  9. use Illuminate\Http\Request;
  10. use Illuminate\Support\Facades\Hash;
  11. use Illuminate\Support\Facades\Log;
  12. use Illuminate\Support\Facades\Password;
  13. use Laravel\Passport\TokenRepository;
  14. class UserManagerController extends Controller
  15. {
  16. /**
  17. * Display all users.
  18. *
  19. * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
  20. */
  21. public function index(Request $request)
  22. {
  23. return UserManagerResource::collection(User::all());
  24. }
  25. /**
  26. * Get a user
  27. *
  28. * @return \App\Api\v1\Resources\UserManagerResource
  29. */
  30. public function show(User $user)
  31. {
  32. $this->authorize('view', $user);
  33. return new UserManagerResource($user);
  34. }
  35. /**
  36. * Reset user's password
  37. *
  38. * @return \Illuminate\Http\JsonResponse
  39. */
  40. public function resetPassword(Request $request, User $user)
  41. {
  42. Log::info(sprintf('Password reset for User ID #%s requested by User ID #%s', $user->id, $request->user()->id));
  43. $this->authorize('update', $user);
  44. $credentials = [
  45. 'token' => $this->broker()->createToken($user),
  46. 'email' => $user->email,
  47. 'password' => $user->password,
  48. ];
  49. $response = $this->broker()->reset(
  50. $credentials, function ($user) {
  51. $user->resetPassword();
  52. $user->save();
  53. }
  54. );
  55. if ($response == Password::PASSWORD_RESET) {
  56. Log::info(sprintf('Temporary password set for User ID #%s', $user->id));
  57. $response = $this->broker()->sendResetLink(
  58. ['email' => $credentials['email']]
  59. );
  60. } else {
  61. return response()->json([
  62. 'message' => 'bad request',
  63. 'reason' => is_string($response) ? __($response) : __('errors.no_pwd_reset_for_this_user_type'),
  64. ], 400);
  65. }
  66. return $response == Password::RESET_LINK_SENT
  67. ? new UserManagerResource($user)
  68. : response()->json([
  69. 'message' => 'bad request',
  70. 'reason' => __($response),
  71. ], 400);
  72. }
  73. /**
  74. * Store a newly created user in storage.
  75. *
  76. * @return \Illuminate\Http\JsonResponse
  77. */
  78. public function store(UserManagerStoreRequest $request)
  79. {
  80. $this->authorize('create', User::class);
  81. $validated = $request->validated();
  82. $user = User::create([
  83. 'name' => $validated['name'],
  84. 'email' => $validated['email'],
  85. 'password' => Hash::make($validated['password']),
  86. ]);
  87. Log::info(sprintf('User ID #%s created by user ID #%s', $user->id, $request->user()->id));
  88. if ($validated['is_admin']) {
  89. if ($user->promoteToAdministrator()) {
  90. $user->save();
  91. Log::notice(sprintf('User ID #%s set as administrator at creation by user ID #%s', $user->id, $request->user()->id));
  92. }
  93. }
  94. $user->refresh();
  95. return (new UserManagerResource($user))
  96. ->response()
  97. ->setStatusCode(201);
  98. }
  99. /**
  100. * Purge user's PATs.
  101. *
  102. * @return \Illuminate\Http\JsonResponse
  103. */
  104. public function revokePATs(Request $request, User $user, TokenRepository $tokenRepository)
  105. {
  106. Log::info(sprintf('Deletion of all personal access tokens for User ID #%s requested by User ID #%s', $user->id, $request->user()->id));
  107. $this->authorize('update', $user);
  108. $tokens = $tokenRepository->forUser($user->getAuthIdentifier());
  109. $tokens->load('client')->filter(function ($token) {
  110. return $token->client->personal_access_client && ! $token->revoked; /** @phpstan-ignore-line */
  111. })->each(function ($token) {
  112. $token->revoke(); /** @phpstan-ignore-line */
  113. });
  114. Log::info(sprintf('All personal access tokens for User ID #%s have been revoked', $user->id));
  115. return response()->json(null, 204);
  116. }
  117. /**
  118. * Purge user's webauthn credentials.
  119. *
  120. * @return \Illuminate\Http\JsonResponse
  121. */
  122. public function revokeWebauthnCredentials(Request $request, User $user)
  123. {
  124. Log::info(sprintf('Deletion of all security devices for User ID #%s requested by User ID #%s', $user->id, $request->user()->id));
  125. $this->authorize('update', $user);
  126. $user->flushCredentials();
  127. // WebauthnOnly user options need to be reset to prevent impossible login when
  128. // no more registered device exists.
  129. // See #110
  130. if (blank($user->webAuthnCredentials()->WhereEnabled()->get())) {
  131. $user['preferences->useWebauthnOnly'] = false;
  132. $user->save();
  133. Log::notice(sprintf('No more Webauthn credential for user ID #%s, useWebauthnOnly user preference reset to false', $user->id));
  134. }
  135. Log::info(sprintf('All security devices for User ID #%s have been revoked', $user->id));
  136. return response()->json(null, 204);
  137. }
  138. /**
  139. * Remove the specified user from storage.
  140. *
  141. * @return \Illuminate\Http\JsonResponse
  142. */
  143. public function destroy(Request $request, User $user)
  144. {
  145. $this->authorize('delete', $user);
  146. // This will delete the user and all its 2FAs & Groups thanks to the onCascadeDelete constrains.
  147. // Deletion will not be done (and returns False) if the user is the only existing admin (see UserObserver clas)
  148. return $user->delete() === false
  149. ? response()->json([
  150. 'message' => __('errors.cannot_delete_the_only_admin'),
  151. ], 403)
  152. : response()->json(null, 204);
  153. }
  154. /**
  155. * Promote (or demote) a user
  156. *
  157. * @return \App\Api\v1\Resources\UserManagerResource|\Illuminate\Http\JsonResponse
  158. */
  159. public function promote(UserManagerPromoteRequest $request, User $user)
  160. {
  161. $this->authorize('promote', $user);
  162. if ($user->promoteToAdministrator($request->validated('is_admin'))) {
  163. $user->save();
  164. Log::info(sprintf('User ID #%s set is_admin=%s for User ID #%s', $request->user()->id, $user->isAdministrator(), $user->id));
  165. return new UserManagerResource($user);
  166. }
  167. return response()->json([
  168. 'message' => __('errors.cannot_demote_the_only_admin'),
  169. ], 403);
  170. }
  171. /**
  172. * Get the user's authentication logs
  173. *
  174. * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
  175. */
  176. public function authentications(Request $request, User $user)
  177. {
  178. $this->authorize('view', $user);
  179. $validated = $this->validate($request, [
  180. 'period' => 'sometimes|numeric',
  181. 'limit' => 'sometimes|numeric',
  182. ]);
  183. $authentications = $request->has('period') ? $user->authenticationsByPeriod($validated['period']) : $user->authentications;
  184. $authentications = $request->has('limit') ? $authentications->take($validated['limit']) : $authentications;
  185. return UserAuthentication::collection($authentications);
  186. }
  187. /**
  188. * Get the broker to be used during password reset.
  189. *
  190. * @return \Illuminate\Contracts\Auth\PasswordBroker|\Illuminate\Auth\Passwords\PasswordBroker
  191. */
  192. protected function broker()
  193. {
  194. return Password::broker();
  195. }
  196. }