Skip to content

Commit a256d7e

Browse files
Merge pull request #61615 from nextcloud/fix/lostpassword-throttle-reset-form
fix(core): Throttle lost-password reset form when link is disabled
2 parents 65736e5 + f9e274d commit a256d7e

2 files changed

Lines changed: 41 additions & 20 deletions

File tree

core/Controller/LostController.php

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,19 @@ public function resetform(string $token, string $userId): TemplateResponse {
9090
try {
9191
$this->checkPasswordResetToken($token, $userId);
9292
} catch (Exception $e) {
93-
if ($this->config->getSystemValue('lost_password_link', '') !== 'disabled'
94-
|| ($e instanceof InvalidTokenException
95-
&& !in_array($e->getCode(), [InvalidTokenException::TOKEN_NOT_FOUND, InvalidTokenException::USER_UNKNOWN]))
96-
) {
97-
$response = new TemplateResponse(
98-
'core', 'error', [
99-
'errors' => [['error' => $e->getMessage()]]
100-
],
101-
TemplateResponse::RENDER_AS_GUEST
102-
);
103-
$response->throttle();
104-
return $response;
93+
if ($this->config->getSystemValue('lost_password_link', '') === 'disabled') {
94+
$message = $this->l10n->t('Password reset is disabled');
95+
} else {
96+
$message = $e->getMessage();
10597
}
106-
return new TemplateResponse('core', 'error', [
107-
'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
108-
],
98+
$response = new TemplateResponse(
99+
'core', 'error', [
100+
'errors' => [['error' => $message]]
101+
],
109102
TemplateResponse::RENDER_AS_GUEST
110103
);
104+
$response->throttle();
105+
return $response;
111106
}
112107
$this->initialState->provideInitialState('resetPasswordUser', $userId);
113108
$this->initialState->provideInitialState('resetPasswordTarget',

tests/Core/Controller/LostControllerTest.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class LostControllerTest extends TestCase {
5353
private $defaults;
5454
/** @var IConfig | MockObject */
5555
private $config;
56+
private string $lostPasswordLink = '';
5657
/** @var IMailer | MockObject */
5758
private $mailer;
5859
/** @var IManager|MockObject */
@@ -93,11 +94,13 @@ protected function setUp(): void {
9394
$this->config = $this->createMock(IConfig::class);
9495
$this->config->expects($this->any())
9596
->method('getSystemValue')
96-
->willReturnMap([
97-
['secret', null, 'SECRET'],
98-
['secret', '', 'SECRET'],
99-
['lost_password_link', '', ''],
100-
]);
97+
->willReturnCallback(function (string $key, $default = '') {
98+
return match ($key) {
99+
'secret' => 'SECRET',
100+
'lost_password_link' => $this->lostPasswordLink,
101+
default => $default,
102+
};
103+
});
101104
$this->l10n = $this->createMock(IL10N::class);
102105
$this->l10n
103106
->expects($this->any())
@@ -162,6 +165,29 @@ public function testResetFormTokenError(): void {
162165
$this->assertEquals($expectedResponse, $response);
163166
}
164167

168+
public function testResetFormTokenErrorWithDisabledLink(): void {
169+
$this->lostPasswordLink = 'disabled';
170+
$this->userManager->method('get')
171+
->with('ValidTokenUser')
172+
->willReturn($this->existingUser);
173+
$this->verificationToken->expects($this->once())
174+
->method('check')
175+
->with('12345:MySecretToken', $this->existingUser, 'lostpassword', 'test@example.com')
176+
->willThrowException(new InvalidTokenException(InvalidTokenException::TOKEN_NOT_FOUND));
177+
178+
$response = $this->lostController->resetform('12345:MySecretToken', 'ValidTokenUser');
179+
$expectedResponse = new TemplateResponse('core',
180+
'error',
181+
[
182+
'errors' => [
183+
['error' => 'Password reset is disabled'],
184+
]
185+
],
186+
'guest');
187+
$expectedResponse->throttle();
188+
$this->assertEquals($expectedResponse, $response);
189+
}
190+
165191
public function testResetFormValidToken(): void {
166192
$this->userManager->method('get')
167193
->with('ValidTokenUser')

0 commit comments

Comments
 (0)