Skip to content

Commit d947f9d

Browse files
committed
Allow overriding the $resetRequestLifetime when generating a token
1 parent 81ad118 commit d947f9d

File tree

3 files changed

+81
-8
lines changed

3 files changed

+81
-8
lines changed

src/ResetPasswordHelper.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,19 @@ public function __construct(ResetPasswordTokenGenerator $generator, ResetPasswor
6060
*
6161
* @throws TooManyPasswordRequestsException
6262
*/
63-
public function generateResetToken(object $user): ResetPasswordToken
63+
public function generateResetToken(object $user, ?int $resetRequestLifetime = null): ResetPasswordToken
6464
{
6565
$this->resetPasswordCleaner->handleGarbageCollection();
6666

6767
if ($availableAt = $this->hasUserHitThrottling($user)) {
6868
throw new TooManyPasswordRequestsException($availableAt);
6969
}
7070

71-
$expiresAt = new \DateTimeImmutable(sprintf('+%d seconds', $this->resetRequestLifetime));
71+
$resetRequestLifetime = $resetRequestLifetime ?: $this->resetRequestLifetime;
7272

73-
$generatedAt = ($expiresAt->getTimestamp() - $this->resetRequestLifetime);
73+
$expiresAt = new \DateTimeImmutable(sprintf('+%d seconds', $resetRequestLifetime));
74+
75+
$generatedAt = ($expiresAt->getTimestamp() - $resetRequestLifetime);
7476

7577
$tokenComponents = $this->tokenGenerator->createToken($expiresAt, $this->repository->getUserIdentifier($user));
7678

@@ -164,11 +166,12 @@ public function getTokenLifetime(): int
164166
*
165167
* This method should not be used when timing attacks are a concern.
166168
*/
167-
public function generateFakeResetToken(): ResetPasswordToken
169+
public function generateFakeResetToken(?int $resetRequestLifetime = null): ResetPasswordToken
168170
{
169-
$expiresAt = new \DateTimeImmutable(sprintf('+%d seconds', $this->resetRequestLifetime));
171+
$resetRequestLifetime = $resetRequestLifetime ?: $this->resetRequestLifetime;
172+
$expiresAt = new \DateTimeImmutable(sprintf('+%d seconds', $resetRequestLifetime));
170173

171-
$generatedAt = ($expiresAt->getTimestamp() - $this->resetRequestLifetime);
174+
$generatedAt = ($expiresAt->getTimestamp() - $resetRequestLifetime);
172175

173176
return new ResetPasswordToken('fake-token', $expiresAt, $generatedAt);
174177
}

src/ResetPasswordHelperInterface.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* @author Jesse Rushlow <[email protected]>
1717
* @author Ryan Weaver <[email protected]>
1818
*
19-
* @method ResetPasswordToken generateFakeResetToken() Generates a fake ResetPasswordToken.
19+
* @method ResetPasswordToken generateFakeResetToken(?int $resetRequestLifetime = null) Generates a fake ResetPasswordToken.
2020
*/
2121
interface ResetPasswordHelperInterface
2222
{
@@ -28,9 +28,11 @@ interface ResetPasswordHelperInterface
2828
* and removeResetRequest() can eventually invalidate it by removing it
2929
* from storage.
3030
*
31+
* @param ?int $resetRequestLifetime Override the default (to be added to interface in 2.0)
32+
*
3133
* @throws ResetPasswordExceptionInterface
3234
*/
33-
public function generateResetToken(object $user): ResetPasswordToken;
35+
public function generateResetToken(object $user/*, ?int $resetRequestLifetime = null*/): ResetPasswordToken;
3436

3537
/**
3638
* Validate a reset request and fetch the user from persistence.

tests/UnitTests/ResetPasswordHelperTest.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,74 @@ public function testExpiresAtUsesCurrentTimeZone(): void
332332
self::assertSame(date_default_timezone_get(), $expiresAt->getTimezone()->getName());
333333
}
334334

335+
public function testExpiresAtUsingDefault(): void
336+
{
337+
$helper = new ResetPasswordHelper(
338+
$this->mockTokenGenerator,
339+
$this->mockCleaner,
340+
$this->mockRepo,
341+
60,
342+
99999999
343+
);
344+
345+
$token = $helper->generateResetToken(new \stdClass());
346+
$expiresAt = $token->getExpiresAt();
347+
348+
self::assertGreaterThan(new \DateTimeImmutable('+55 seconds'), $expiresAt);
349+
self::assertLessThan(new \DateTimeImmutable('+65 seconds'), $expiresAt);
350+
}
351+
352+
public function testExpiresAtUsingOverride(): void
353+
{
354+
$helper = new ResetPasswordHelper(
355+
$this->mockTokenGenerator,
356+
$this->mockCleaner,
357+
$this->mockRepo,
358+
60,
359+
99999999
360+
);
361+
362+
$token = $helper->generateResetToken(new \stdClass(), 30);
363+
$expiresAt = $token->getExpiresAt();
364+
365+
self::assertGreaterThan(new \DateTimeImmutable('+25 seconds'), $expiresAt);
366+
self::assertLessThan(new \DateTimeImmutable('+35 seconds'), $expiresAt);
367+
}
368+
369+
public function testFakeTokenExpiresAtUsingDefault(): void
370+
{
371+
$helper = new ResetPasswordHelper(
372+
$this->mockTokenGenerator,
373+
$this->mockCleaner,
374+
$this->mockRepo,
375+
60,
376+
99999999
377+
);
378+
379+
$token = $helper->generateFakeResetToken();
380+
$expiresAt = $token->getExpiresAt();
381+
382+
self::assertGreaterThan(new \DateTimeImmutable('+55 seconds'), $expiresAt);
383+
self::assertLessThan(new \DateTimeImmutable('+65 seconds'), $expiresAt);
384+
}
385+
386+
public function testFakeTokenExpiresAtUsingOverride(): void
387+
{
388+
$helper = new ResetPasswordHelper(
389+
$this->mockTokenGenerator,
390+
$this->mockCleaner,
391+
$this->mockRepo,
392+
60,
393+
99999999
394+
);
395+
396+
$token = $helper->generateFakeResetToken(30);
397+
$expiresAt = $token->getExpiresAt();
398+
399+
self::assertGreaterThan(new \DateTimeImmutable('+25 seconds'), $expiresAt);
400+
self::assertLessThan(new \DateTimeImmutable('+35 seconds'), $expiresAt);
401+
}
402+
335403
private function getPasswordResetHelper(): ResetPasswordHelper
336404
{
337405
return new ResetPasswordHelper(

0 commit comments

Comments
 (0)