3 namespace BookStack\Api;
5 use BookStack\Activity\ActivityType;
6 use BookStack\Http\Controller;
7 use BookStack\Permissions\Permission;
8 use BookStack\Users\Models\User;
9 use Illuminate\Http\Request;
10 use Illuminate\Support\Facades\Hash;
11 use Illuminate\Support\Str;
13 class UserApiTokenController extends Controller
16 * Show the form to create a new API token.
18 public function create(Request $request, int $userId)
20 $this->checkPermission(Permission::AccessApi);
21 $this->checkPermissionOrCurrentUser(Permission::UsersManage, $userId);
22 $this->updateContext($request);
24 $user = User::query()->findOrFail($userId);
26 $this->setPageTitle(trans('settings.user_api_token_create'));
28 return view('users.api-tokens.create', [
30 'back' => $this->getRedirectPath($user),
35 * Store a new API token in the system.
37 public function store(Request $request, int $userId)
39 $this->checkPermission(Permission::AccessApi);
40 $this->checkPermissionOrCurrentUser(Permission::UsersManage, $userId);
42 $this->validate($request, [
43 'name' => ['required', 'max:250'],
44 'expires_at' => ['date_format:Y-m-d'],
47 $user = User::query()->findOrFail($userId);
48 $secret = Str::random(32);
50 $token = (new ApiToken())->forceFill([
51 'name' => $request->get('name'),
52 'token_id' => Str::random(32),
53 'secret' => Hash::make($secret),
54 'user_id' => $user->id,
55 'expires_at' => $request->get('expires_at') ?: ApiToken::defaultExpiry(),
58 while (ApiToken::query()->where('token_id', '=', $token->token_id)->exists()) {
59 $token->token_id = Str::random(32);
64 session()->flash('api-token-secret:' . $token->id, $secret);
65 $this->logActivity(ActivityType::API_TOKEN_CREATE, $token);
67 return redirect($token->getUrl());
71 * Show the details for a user API token, with access to edit.
73 public function edit(Request $request, int $userId, int $tokenId)
75 $this->updateContext($request);
77 [$user, $token] = $this->checkPermissionAndFetchUserToken($userId, $tokenId);
78 $secret = session()->pull('api-token-secret:' . $token->id, null);
80 $this->setPageTitle(trans('settings.user_api_token'));
82 return view('users.api-tokens.edit', [
87 'back' => $this->getRedirectPath($user),
92 * Update the API token.
94 public function update(Request $request, int $userId, int $tokenId)
96 $this->validate($request, [
97 'name' => ['required', 'max:250'],
98 'expires_at' => ['date_format:Y-m-d'],
101 [$user, $token] = $this->checkPermissionAndFetchUserToken($userId, $tokenId);
103 'name' => $request->get('name'),
104 'expires_at' => $request->get('expires_at') ?: ApiToken::defaultExpiry(),
107 $this->logActivity(ActivityType::API_TOKEN_UPDATE, $token);
109 return redirect($token->getUrl());
113 * Show the delete view for this token.
115 public function delete(int $userId, int $tokenId)
117 [$user, $token] = $this->checkPermissionAndFetchUserToken($userId, $tokenId);
119 $this->setPageTitle(trans('settings.user_api_token_delete'));
121 return view('users.api-tokens.delete', [
128 * Destroy a token from the system.
130 public function destroy(int $userId, int $tokenId)
132 [$user, $token] = $this->checkPermissionAndFetchUserToken($userId, $tokenId);
135 $this->logActivity(ActivityType::API_TOKEN_DELETE, $token);
137 return redirect($this->getRedirectPath($user));
141 * Check the permission for the current user and return an array
142 * where the first item is the user in context and the second item is their
143 * API token in context.
145 protected function checkPermissionAndFetchUserToken(int $userId, int $tokenId): array
147 $this->checkPermissionOr(Permission::UsersManage, function () use ($userId) {
148 return $userId === user()->id && userCan(Permission::AccessApi);
151 $user = User::query()->findOrFail($userId);
152 $token = ApiToken::query()->where('user_id', '=', $user->id)->where('id', '=', $tokenId)->firstOrFail();
154 return [$user, $token];
158 * Update the context for where the user is coming from to manage API tokens.
159 * (Track of location for correct return redirects)
161 protected function updateContext(Request $request): void
163 $context = $request->query('context');
165 session()->put('api-token-context', $context);
170 * Get the redirect path for the current api token editing session.
171 * Attempts to recall the context of where the user is editing from.
173 protected function getRedirectPath(User $relatedUser): string
175 $context = session()->get('api-token-context');
176 if ($context === 'settings' || user()->id !== $relatedUser->id) {
177 return $relatedUser->getEditUrl('#api_tokens');
180 return url('/my-account/auth#api_tokens');