mirror of
https://github.com/nextcloud/server.git
synced 2026-07-10 18:23:14 -04:00
fix(core): Throttle lost-password reset form when link is disabled
When password reset links were disabled, the reset form did not rate-limit failed token attempts, leaving that endpoint open to unthrottled token guessing. Failed attempts on the form are now always throttled, consistent with the other reset steps. Signed-off-by: nfebe <fenn25.fn@gmail.com>
This commit is contained in:
parent
39d2dc253e
commit
f9e274dfe3
2 changed files with 41 additions and 20 deletions
|
|
@ -90,24 +90,19 @@ class LostController extends Controller {
|
|||
try {
|
||||
$this->checkPasswordResetToken($token, $userId);
|
||||
} catch (Exception $e) {
|
||||
if ($this->config->getSystemValue('lost_password_link', '') !== 'disabled'
|
||||
|| ($e instanceof InvalidTokenException
|
||||
&& !in_array($e->getCode(), [InvalidTokenException::TOKEN_NOT_FOUND, InvalidTokenException::USER_UNKNOWN]))
|
||||
) {
|
||||
$response = new TemplateResponse(
|
||||
'core', 'error', [
|
||||
'errors' => [['error' => $e->getMessage()]]
|
||||
],
|
||||
TemplateResponse::RENDER_AS_GUEST
|
||||
);
|
||||
$response->throttle();
|
||||
return $response;
|
||||
if ($this->config->getSystemValue('lost_password_link', '') === 'disabled') {
|
||||
$message = $this->l10n->t('Password reset is disabled');
|
||||
} else {
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return new TemplateResponse('core', 'error', [
|
||||
'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
|
||||
],
|
||||
$response = new TemplateResponse(
|
||||
'core', 'error', [
|
||||
'errors' => [['error' => $message]]
|
||||
],
|
||||
TemplateResponse::RENDER_AS_GUEST
|
||||
);
|
||||
$response->throttle();
|
||||
return $response;
|
||||
}
|
||||
$this->initialState->provideInitialState('resetPasswordUser', $userId);
|
||||
$this->initialState->provideInitialState('resetPasswordTarget',
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class LostControllerTest extends TestCase {
|
|||
private $defaults;
|
||||
/** @var IConfig | MockObject */
|
||||
private $config;
|
||||
private string $lostPasswordLink = '';
|
||||
/** @var IMailer | MockObject */
|
||||
private $mailer;
|
||||
/** @var IManager|MockObject */
|
||||
|
|
@ -93,11 +94,13 @@ class LostControllerTest extends TestCase {
|
|||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->config->expects($this->any())
|
||||
->method('getSystemValue')
|
||||
->willReturnMap([
|
||||
['secret', null, 'SECRET'],
|
||||
['secret', '', 'SECRET'],
|
||||
['lost_password_link', '', ''],
|
||||
]);
|
||||
->willReturnCallback(function (string $key, $default = '') {
|
||||
return match ($key) {
|
||||
'secret' => 'SECRET',
|
||||
'lost_password_link' => $this->lostPasswordLink,
|
||||
default => $default,
|
||||
};
|
||||
});
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->l10n
|
||||
->expects($this->any())
|
||||
|
|
@ -162,6 +165,29 @@ class LostControllerTest extends TestCase {
|
|||
$this->assertEquals($expectedResponse, $response);
|
||||
}
|
||||
|
||||
public function testResetFormTokenErrorWithDisabledLink(): void {
|
||||
$this->lostPasswordLink = 'disabled';
|
||||
$this->userManager->method('get')
|
||||
->with('ValidTokenUser')
|
||||
->willReturn($this->existingUser);
|
||||
$this->verificationToken->expects($this->once())
|
||||
->method('check')
|
||||
->with('12345:MySecretToken', $this->existingUser, 'lostpassword', 'test@example.com')
|
||||
->willThrowException(new InvalidTokenException(InvalidTokenException::TOKEN_NOT_FOUND));
|
||||
|
||||
$response = $this->lostController->resetform('12345:MySecretToken', 'ValidTokenUser');
|
||||
$expectedResponse = new TemplateResponse('core',
|
||||
'error',
|
||||
[
|
||||
'errors' => [
|
||||
['error' => 'Password reset is disabled'],
|
||||
]
|
||||
],
|
||||
'guest');
|
||||
$expectedResponse->throttle();
|
||||
$this->assertEquals($expectedResponse, $response);
|
||||
}
|
||||
|
||||
public function testResetFormValidToken(): void {
|
||||
$this->userManager->method('get')
|
||||
->with('ValidTokenUser')
|
||||
|
|
|
|||
Loading…
Reference in a new issue