passwordResetter = new PasswordResetter(); $this->userModel = new Model(); $this->capturedToken = null; Manager::getInstance()->loadPluginTranslations(); } public function test_passwordReset_processWorksAsExpected() { $user = $this->userModel->getUser('superUserLogin'); $password = $user['password']; $passwordModified = $user['ts_password_modified']; $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEmpty($this->capturedToken); $user = $this->userModel->getUser('superUserLogin'); $this->assertEquals($password, $user['password']); $this->assertEquals($passwordModified, $user['ts_password_modified']); $this->passwordResetter->confirmNewPassword('superUserLogin', $this->capturedToken); $this->checkPasswordIs(self::NEWPASSWORD); } public function tests_passwordReset_worksUpToThreeTimesInAnHour() { $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEmpty($this->capturedToken); $token = $this->capturedToken; $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEquals($token, $this->capturedToken); $token = $this->capturedToken; $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEquals($token, $this->capturedToken); } public function test_passwordReset_notAllowedMoreThanThreeTimesInAnHour() { $this->expectException(\Exception::class); $this->expectExceptionMessage('You have requested too many password resets recently. A new request can be made in one hour. If you have problems resetting your password, please contact your administrator for help.'); $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEmpty($this->capturedToken); $token = $this->capturedToken; $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEquals($token, $this->capturedToken); $token = $this->capturedToken; $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEquals($token, $this->capturedToken); $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); } public function test_passwordReset_newRequestAllowedAfterAnHour() { $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $optionName = $this->passwordResetter->getPasswordResetInfoOptionName('superUserLogin'); $data = json_decode(Option::get($optionName), true); $data['timestamp'] = time()-3601; $data['requests'] = 3; Option::set($optionName, json_encode($data)); $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $optionName = $this->passwordResetter->getPasswordResetInfoOptionName('superUserLogin'); $data = json_decode(Option::get($optionName), true); $this->assertEquals(1, $data['requests']); } public function test_passwordReset_shouldNotAllowTokenToBeUsedMoreThanOnce() { $this->expectException(\Exception::class); $this->expectExceptionMessage('Token is invalid or has expired'); $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEmpty($this->capturedToken); $this->passwordResetter->confirmNewPassword('superUserLogin', $this->capturedToken); $this->checkPasswordIs(self::NEWPASSWORD); sleep(1); $oldCapturedToken = $this->capturedToken; $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', 'anotherpassword'); $this->assertNotEquals($oldCapturedToken, $this->capturedToken); $this->passwordResetter->confirmNewPassword('superUserLogin', $oldCapturedToken); } public function test_passwordReset_shouldNeverGenerateTheSameToken() { $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEmpty($this->capturedToken); sleep(1); $oldCapturedToken = $this->capturedToken; $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEquals($oldCapturedToken, $this->capturedToken); } public function test_passwordReset_shouldNotAllowOldTokenToBeUsedAfterAnotherResetRequest() { $this->expectException(\Exception::class); $this->expectExceptionMessage('Token is invalid or has expired'); $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEmpty($this->capturedToken); sleep(1); $oldCapturedToken = $this->capturedToken; $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD); $this->assertNotEquals($oldCapturedToken, $this->capturedToken); $this->passwordResetter->confirmNewPassword('superUserLogin', $oldCapturedToken); } /** * @param Fixture $fixture */ protected static function configureFixture($fixture) { parent::configureFixture($fixture); $fixture->createSuperUser = true; $fixture->extraTestEnvVars['loadRealTranslations'] = true; } private function checkPasswordIs($pwd) { $auth = StaticContainer::get(Auth::class); $auth->setLogin('superUserLogin'); $auth->setPassword($pwd); $auth->setTokenAuth(null); $auth->setPasswordHash(null); $result = Access::getInstance()->reloadAccess($auth); $this->assertTrue($result); } public function provideContainerConfig() { return [ 'observers.global' => \DI\add([ ['Test.Mail.send', function (PHPMailer $mail) { $body = $mail->createBody(); $body = preg_replace("/=[\r\n]+/", '', $body); preg_match('/resetToken=[\s]*3D([a-zA-Z0-9=\s]+)<\/p>/', $body, $matches); if (!empty($matches[1])) { $capturedToken = $matches[1]; $capturedToken = preg_replace('/=\s*/', '', $capturedToken); $this->capturedToken = $capturedToken; } }], ]), ]; } }