Skip to content

Commit 6687bf0

Browse files
committed
Merge branch 'development' into release
2 parents fcb2d40 + de6e6f3 commit 6687bf0

53 files changed

Lines changed: 971 additions & 377 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/Access/ExternalBaseUserProvider.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
namespace BookStack\Access;
44

55
use BookStack\Users\Models\User;
6+
use BookStack\Users\UserRepo;
67
use Illuminate\Contracts\Auth\Authenticatable;
78
use Illuminate\Contracts\Auth\UserProvider;
89

910
class ExternalBaseUserProvider implements UserProvider
1011
{
12+
public function __construct(
13+
protected UserRepo $userRepo,
14+
) {
15+
}
16+
1117
/**
1218
* Retrieve a user by their unique identifier.
1319
*/
@@ -44,9 +50,7 @@ public function updateRememberToken(Authenticatable $user, $token)
4450
*/
4551
public function retrieveByCredentials(array $credentials): ?Authenticatable
4652
{
47-
return User::query()
48-
->where('external_auth_id', $credentials['external_auth_id'])
49-
->first();
53+
return $this->userRepo->getByExternalAuthId($credentials['external_auth_id']);
5054
}
5155

5256
/**

app/Access/Guards/ExternalBaseSessionGuard.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class ExternalBaseSessionGuard implements StatefulGuard
3030
/**
3131
* The user we last attempted to retrieve.
3232
*/
33-
protected Authenticatable|null $lastAttempted;
33+
protected Authenticatable|null $lastAttempted = null;
3434

3535
/**
3636
* The session used by the guard.
@@ -203,7 +203,7 @@ protected function clearUserDataFromStorage(): void
203203
/**
204204
* Get the last user we attempted to authenticate.
205205
*/
206-
public function getLastAttempted(): Authenticatable
206+
public function getLastAttempted(): Authenticatable|null
207207
{
208208
return $this->lastAttempted;
209209
}

app/Access/LoginService.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
use BookStack\Theming\ThemeEvents;
1414
use BookStack\Users\Models\User;
1515
use Exception;
16+
use Illuminate\Contracts\Auth\Authenticatable;
17+
use Illuminate\Support\Facades\Hash;
1618

1719
class LoginService
1820
{
@@ -171,10 +173,24 @@ public function attempt(array $credentials, string $method, bool $remember = fal
171173
} catch (LoginAttemptInvalidUserException $e) {
172174
// Catch and return false for non-login accounts
173175
// so it looks like a normal invalid login.
174-
return false;
176+
$result = false;
175177
}
176178
}
177179

180+
// Perform a dummy hash check to balance out the time of a login with an existing known user
181+
// with that of a user not in the system (which we don't perform a hash check for in the above).
182+
/** @var Authenticatable|null $lastAttempted */
183+
$lastAttempted = auth()->getLastAttempted();
184+
if (!$result && $lastAttempted === null) {
185+
Hash::check($credentials['password'], '$2y$04$A.H9icXH4/lxLd9DHuaYqO/GVBd0OKetxyY0txmNfTAlPLVnTBx3y');
186+
}
187+
188+
// Add some noise to request times on failed login attempts
189+
if (!$result) {
190+
$sleepMs = random_int(0, 250);
191+
usleep($sleepMs * 1000);
192+
}
193+
178194
return $result;
179195
}
180196

app/Access/RegistrationService.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ protected function registrationAllowed(): bool
5252
*/
5353
public function findOrRegister(string $name, string $email, string $externalId): User
5454
{
55-
$user = User::query()
56-
->where('external_auth_id', '=', $externalId)
57-
->first();
55+
$user = $this->userRepo->getByExternalAuthId($externalId);
5856

5957
if (is_null($user)) {
6058
$userData = [

app/App/Providers/AuthServiceProvider.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use BookStack\Access\RegistrationService;
1111
use BookStack\Api\ApiTokenGuard;
1212
use BookStack\Users\Models\User;
13+
use BookStack\Users\UserRepo;
1314
use Illuminate\Support\Facades\Auth;
1415
use Illuminate\Support\ServiceProvider;
1516
use Illuminate\Validation\Rules\Password;
@@ -60,7 +61,7 @@ public function boot(): void
6061
public function register(): void
6162
{
6263
Auth::provider('external-users', function () {
63-
return new ExternalBaseUserProvider();
64+
return new ExternalBaseUserProvider($this->app[UserRepo::class]);
6465
});
6566

6667
// Bind and provide the default system user as a singleton to the app instance when needed.

app/Exceptions/ApiAuthException.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
66

7-
class ApiAuthException extends \Exception implements HttpExceptionInterface
7+
class ApiAuthException extends \Exception implements HttpExceptionInterface, ShowsApiExceptionMessage
88
{
99
protected int $status;
1010

@@ -23,4 +23,9 @@ public function getHeaders(): array
2323
{
2424
return [];
2525
}
26+
27+
public function getMessageForApi(): string
28+
{
29+
return $this->getMessage();
30+
}
2631
}

app/Exceptions/Handler.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,25 @@ protected function renderApiException(Throwable $e): JsonResponse
126126
$headers = $e->getHeaders();
127127
}
128128

129-
if ($e instanceof ModelNotFoundException) {
130-
$code = 404;
131-
}
132-
133129
$responseData = [
134130
'error' => [
135-
'message' => $e->getMessage(),
131+
'message' => 'An error occurred',
136132
],
137133
];
138134

135+
if ($e instanceof ModelNotFoundException) {
136+
$responseData['error']['message'] = 'The requested resource could not be found.';
137+
$code = 404;
138+
}
139+
140+
if ($e instanceof ShowsApiExceptionMessage) {
141+
$responseData['error']['message'] = $e->getMessageForApi();
142+
}
143+
144+
if (app()->hasDebugModeEnabled()) {
145+
$responseData['error']['message'] = $e->getMessage();
146+
}
147+
139148
if ($e instanceof ValidationException) {
140149
$responseData['error']['message'] = 'The given data was invalid.';
141150
$responseData['error']['validation'] = $e->errors();

app/Exceptions/NotifyException.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88

99
/**
1010
* An exception that is thrown to notify the user of something which went wrong.
11-
* Typically these should be translated messages since they will be shown to the end user
12-
* via a pop up notification error message in the UI.
11+
* Typically, these should be translated messages since they will be shown to the end user
12+
* via a pop-up notification error message in the UI.
1313
*
1414
* This exception is not intended to be used for internal system/application errors,
1515
* and therefore will not be logged by the exception handler.
1616
*/
17-
class NotifyException extends Exception implements Responsable, HttpExceptionInterface
17+
class NotifyException extends Exception implements Responsable, HttpExceptionInterface, ShowsApiExceptionMessage
1818
{
1919
public function __construct(
2020
string $message,
@@ -61,4 +61,9 @@ public function toResponse($request)
6161

6262
return redirect($this->redirectLocation);
6363
}
64+
65+
public function getMessageForApi(): string
66+
{
67+
return $this->getMessage();
68+
}
6469
}

app/Exceptions/PermissionsException.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
use Exception;
66

7-
class PermissionsException extends Exception
7+
class PermissionsException extends Exception implements ShowsApiExceptionMessage
88
{
9+
public function getMessageForApi(): string
10+
{
11+
return $this->getMessage();
12+
}
913
}

app/Exceptions/PrettyException.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use Illuminate\Contracts\Support\Responsable;
77
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
88

9-
class PrettyException extends Exception implements Responsable, HttpExceptionInterface
9+
class PrettyException extends Exception implements Responsable, HttpExceptionInterface, ShowsApiExceptionMessage
1010
{
1111
protected ?string $subtitle = null;
1212
protected ?string $details = null;
@@ -56,4 +56,16 @@ public function getHeaders(): array
5656
{
5757
return [];
5858
}
59+
60+
public function getMessageForApi(): string
61+
{
62+
$message = $this->getMessage() . '.';
63+
if ($this->subtitle) {
64+
$message .= " {$this->subtitle}.";
65+
}
66+
if ($this->details) {
67+
$message .= " {$this->details}.";
68+
}
69+
return $message;
70+
}
5971
}

0 commit comments

Comments
 (0)