diff --git a/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php b/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php index e4e5729faef9..fe5f54b79765 100755 --- a/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php +++ b/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php @@ -50,7 +50,7 @@ class DatabaseTokenRepository implements TokenRepositoryInterface * * @var int */ - protected $timeout; + protected $throttle; /** * Create a new token repository instance. @@ -60,18 +60,19 @@ class DatabaseTokenRepository implements TokenRepositoryInterface * @param string $table * @param string $hashKey * @param int $expires - * @param int $timeout + * @param int $throttle * @return void */ public function __construct(ConnectionInterface $connection, HasherContract $hasher, - $table, $hashKey, $expires = 60, $timeout = 60) + $table, $hashKey, $expires = 60, + $throttle = 60) { $this->table = $table; $this->hasher = $hasher; $this->hashKey = $hashKey; $this->expires = $expires * 60; - $this->timeout = $timeout; $this->connection = $connection; + $this->throttle = $throttle; } /** @@ -149,12 +150,12 @@ protected function tokenExpired($createdAt) } /** - * Determine if a token record exists and was recently created. + * Determine if the given user recently created a password reset token. * - * @param \Illuminate\Contracts\Auth\CanResetPassword $user + * @param \Illuminate\Contracts\Auth\CanResetPassword $user * @return bool */ - public function recentlyCreated(CanResetPasswordContract $user) + public function recentlyCreatedToken(CanResetPasswordContract $user) { $record = (array) $this->getTable()->where( 'email', $user->getEmailForPasswordReset() @@ -166,12 +167,18 @@ public function recentlyCreated(CanResetPasswordContract $user) /** * Determine if the token was recently created. * - * @param string $createdAt + * @param string $createdAt * @return bool */ protected function tokenRecentlyCreated($createdAt) { - return Carbon::parse($createdAt)->addSeconds($this->timeout)->isFuture(); + if ($this->throttle <= 0) { + return false; + } + + return Carbon::parse($createdAt)->addSeconds( + $this->throttle + )->isFuture(); } /** diff --git a/src/Illuminate/Auth/Passwords/PasswordBroker.php b/src/Illuminate/Auth/Passwords/PasswordBroker.php index 968096cc3f4a..66d4d2d3372c 100755 --- a/src/Illuminate/Auth/Passwords/PasswordBroker.php +++ b/src/Illuminate/Auth/Passwords/PasswordBroker.php @@ -55,14 +55,9 @@ public function sendResetLink(array $credentials) return static::INVALID_USER; } - // Before 7.x we have to check the existence of a new method. - // In 7.x, this code must be removed. - if (method_exists($this->tokens, 'recentlyCreated')) { - // An attacker can make a lot of password reset requests, - // which will lead to spam in user's mailbox. - if ($this->tokens->recentlyCreated($user)) { - return static::RESEND_TIMEOUT; - } + if (method_exists($this->tokens, 'recentlyCreatedToken') && + $this->tokens->recentlyCreatedToken($user)) { + return static::RESET_THROTTLED; } // Once we have the reset token, we are ready to send the message out to this diff --git a/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php b/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php index 39e9d0073f07..ac2d95c3b7c6 100644 --- a/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php +++ b/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php @@ -96,9 +96,7 @@ protected function createTokenRepository(array $config) $config['table'], $key, $config['expire'], - // Before 7.x this element in the configuration may not exist. - // In 7.x, this check must be removed. - $config['timeout'] ?? 0 + $config['throttle'] ?? 0 ); } diff --git a/src/Illuminate/Contracts/Auth/PasswordBroker.php b/src/Illuminate/Contracts/Auth/PasswordBroker.php index 3d9a7e257701..944a249bea80 100644 --- a/src/Illuminate/Contracts/Auth/PasswordBroker.php +++ b/src/Illuminate/Contracts/Auth/PasswordBroker.php @@ -35,11 +35,11 @@ interface PasswordBroker const INVALID_TOKEN = 'passwords.token'; /** - * Constant representing the wait before password reset link resending. + * Constant representing a throttled reset attempt. * * @var string */ - const RESEND_TIMEOUT = 'passwords.timeout'; + const RESET_THROTTLED = 'passwords.throttled'; /** * Send a password reset link to a user. diff --git a/tests/Auth/AuthDatabaseTokenRepositoryTest.php b/tests/Auth/AuthDatabaseTokenRepositoryTest.php index 9c66908e9d63..446139c0c7da 100755 --- a/tests/Auth/AuthDatabaseTokenRepositoryTest.php +++ b/tests/Auth/AuthDatabaseTokenRepositoryTest.php @@ -107,7 +107,7 @@ public function testRecentlyCreatedReturnsFalseIfNoRowFoundForUser() $user = m::mock(CanResetPassword::class); $user->shouldReceive('getEmailForPasswordReset')->once()->andReturn('email'); - $this->assertFalse($repo->recentlyCreated($user)); + $this->assertFalse($repo->recentlyCreatedToken($user)); } public function testRecentlyCreatedReturnsTrueIfRecordIsRecentlyCreated() @@ -120,7 +120,7 @@ public function testRecentlyCreatedReturnsTrueIfRecordIsRecentlyCreated() $user = m::mock(CanResetPassword::class); $user->shouldReceive('getEmailForPasswordReset')->once()->andReturn('email'); - $this->assertTrue($repo->recentlyCreated($user)); + $this->assertTrue($repo->recentlyCreatedToken($user)); } public function testRecentlyCreatedReturnsFalseIfValidRecordExists() @@ -133,7 +133,7 @@ public function testRecentlyCreatedReturnsFalseIfValidRecordExists() $user = m::mock(CanResetPassword::class); $user->shouldReceive('getEmailForPasswordReset')->once()->andReturn('email'); - $this->assertFalse($repo->recentlyCreated($user)); + $this->assertFalse($repo->recentlyCreatedToken($user)); } public function testDeleteMethodDeletesByToken() diff --git a/tests/Auth/AuthPasswordBrokerTest.php b/tests/Auth/AuthPasswordBrokerTest.php index c495702fb94d..84e8523811fa 100755 --- a/tests/Auth/AuthPasswordBrokerTest.php +++ b/tests/Auth/AuthPasswordBrokerTest.php @@ -35,10 +35,10 @@ public function testIfTokenIsRecentlyCreated() $mocks['tokens'] = m::mock(TestTokenRepositoryInterface::class); $broker = $this->getMockBuilder(PasswordBroker::class)->setMethods(['emailResetLink', 'getUri'])->setConstructorArgs(array_values($mocks))->getMock(); $mocks['users']->shouldReceive('retrieveByCredentials')->once()->with(['foo'])->andReturn($user = m::mock(CanResetPassword::class)); - $mocks['tokens']->shouldReceive('recentlyCreated')->once()->with($user)->andReturn(true); + $mocks['tokens']->shouldReceive('recentlyCreatedToken')->once()->with($user)->andReturn(true); $user->shouldReceive('sendPasswordResetNotification')->with('token'); - $this->assertEquals(PasswordBrokerContract::RESEND_TIMEOUT, $broker->sendResetLink(['foo'])); + $this->assertEquals(PasswordBrokerContract::RESET_THROTTLED, $broker->sendResetLink(['foo'])); } public function testGetUserThrowsExceptionIfUserDoesntImplementCanResetPassword() @@ -130,5 +130,5 @@ protected function getMocks() interface TestTokenRepositoryInterface extends TokenRepositoryInterface { - public function recentlyCreated(CanResetPassword $user); + public function recentlyCreatedToken(CanResetPassword $user); }