diff --git a/api/models/authentication/ForgotPasswordForm.php b/api/models/authentication/ForgotPasswordForm.php index 9e4d355..b90ba64 100644 --- a/api/models/authentication/ForgotPasswordForm.php +++ b/api/models/authentication/ForgotPasswordForm.php @@ -20,17 +20,17 @@ class ForgotPasswordForm extends ApiForm { public $login; - public $token; + public $totp; public function rules() { return [ ['captcha', ReCaptchaValidator::class], ['login', 'required', 'message' => E::LOGIN_REQUIRED], ['login', 'validateLogin'], - ['token', 'required', 'when' => function(self $model) { + ['totp', 'required', 'when' => function(self $model) { return !$this->hasErrors() && $model->getAccount()->is_otp_enabled; - }, 'message' => E::OTP_TOKEN_REQUIRED], - ['token', 'validateTotpToken'], + }, 'message' => E::TOTP_REQUIRED], + ['totp', 'validateTotp'], ['login', 'validateActivity'], ['login', 'validateFrequency'], ]; @@ -44,7 +44,7 @@ class ForgotPasswordForm extends ApiForm { } } - public function validateTotpToken($attribute) { + public function validateTotp($attribute) { if ($this->hasErrors()) { return; } diff --git a/api/models/authentication/LoginForm.php b/api/models/authentication/LoginForm.php index 3b6e463..133ccf4 100644 --- a/api/models/authentication/LoginForm.php +++ b/api/models/authentication/LoginForm.php @@ -17,10 +17,10 @@ class LoginForm extends ApiForm { public $login; public $password; - public $token; + public $totp; public $rememberMe = false; - public function rules() { + public function rules(): array { return [ ['login', 'required', 'message' => E::LOGIN_REQUIRED], ['login', 'validateLogin'], @@ -30,10 +30,10 @@ class LoginForm extends ApiForm { }, 'message' => E::PASSWORD_REQUIRED], ['password', 'validatePassword'], - ['token', 'required', 'when' => function(self $model) { + ['totp', 'required', 'when' => function(self $model) { return !$model->hasErrors() && $model->getAccount()->is_otp_enabled; - }, 'message' => E::OTP_TOKEN_REQUIRED], - ['token', 'validateTotpToken'], + }, 'message' => E::TOTP_REQUIRED], + ['totp', 'validateTotp'], ['login', 'validateActivity'], @@ -58,7 +58,7 @@ class LoginForm extends ApiForm { } } - public function validateTotpToken($attribute) { + public function validateTotp($attribute) { if ($this->hasErrors()) { return; } diff --git a/api/models/profile/TwoFactorAuthForm.php b/api/models/profile/TwoFactorAuthForm.php index 8f6c31f..a388d8a 100644 --- a/api/models/profile/TwoFactorAuthForm.php +++ b/api/models/profile/TwoFactorAuthForm.php @@ -22,7 +22,7 @@ class TwoFactorAuthForm extends ApiForm { const SCENARIO_ACTIVATE = 'enable'; const SCENARIO_DISABLE = 'disable'; - public $token; + public $totp; public $timestamp; @@ -44,8 +44,8 @@ class TwoFactorAuthForm extends ApiForm { ['timestamp', 'integer', 'on' => [self::SCENARIO_ACTIVATE]], ['account', 'validateOtpDisabled', 'on' => self::SCENARIO_ACTIVATE], ['account', 'validateOtpEnabled', 'on' => self::SCENARIO_DISABLE], - ['token', 'required', 'message' => E::OTP_TOKEN_REQUIRED, 'on' => $bothScenarios], - ['token', TotpValidator::class, 'on' => $bothScenarios, + ['totp', 'required', 'message' => E::TOTP_REQUIRED, 'on' => $bothScenarios], + ['totp', TotpValidator::class, 'on' => $bothScenarios, 'account' => $this->account, 'timestamp' => function() { return $this->timestamp; diff --git a/api/modules/authserver/models/AuthenticationForm.php b/api/modules/authserver/models/AuthenticationForm.php index 2a8b5dc..aab29bd 100644 --- a/api/modules/authserver/models/AuthenticationForm.php +++ b/api/modules/authserver/models/AuthenticationForm.php @@ -36,7 +36,7 @@ class AuthenticationForm extends ApiForm { $loginForm->password = $this->password; if (!$loginForm->validate()) { $errors = $loginForm->getFirstErrors(); - if (isset($errors['token'])) { + if (isset($errors['totp'])) { Authserver::error("User with login = '{$this->username}' protected by two factor auth."); throw new ForbiddenOperationException('Account protected with two factor auth.'); } elseif (isset($errors['login'])) { diff --git a/api/validators/TotpValidator.php b/api/validators/TotpValidator.php index 68bbc5e..da84468 100644 --- a/api/validators/TotpValidator.php +++ b/api/validators/TotpValidator.php @@ -52,10 +52,10 @@ class TotpValidator extends Validator { try { $totp = TOTP::create($this->account->otp_secret); if (!$totp->verify((string)$value, $this->getTimestamp(), $this->window)) { - return [E::OTP_TOKEN_INCORRECT, []]; + return [E::TOTP_INCORRECT, []]; } } catch (RangeException $e) { - return [E::OTP_TOKEN_INCORRECT, []]; + return [E::TOTP_INCORRECT, []]; } return null; diff --git a/common/helpers/Error.php b/common/helpers/Error.php index 3baa1b8..d31a9f1 100644 --- a/common/helpers/Error.php +++ b/common/helpers/Error.php @@ -54,8 +54,9 @@ final class Error { const SUBJECT_REQUIRED = 'error.subject_required'; const MESSAGE_REQUIRED = 'error.message_required'; - const OTP_TOKEN_REQUIRED = 'error.token_required'; - const OTP_TOKEN_INCORRECT = 'error.token_incorrect'; + const TOTP_REQUIRED = 'error.totp_required'; + const TOTP_INCORRECT = 'error.totp_incorrect'; + const OTP_ALREADY_ENABLED = 'error.otp_already_enabled'; const OTP_NOT_ENABLED = 'error.otp_not_enabled'; diff --git a/tests/codeception/api/_pages/AuthenticationRoute.php b/tests/codeception/api/_pages/AuthenticationRoute.php index bfe5784..c0d178b 100644 --- a/tests/codeception/api/_pages/AuthenticationRoute.php +++ b/tests/codeception/api/_pages/AuthenticationRoute.php @@ -24,7 +24,7 @@ class AuthenticationRoute extends BasePage { if ((is_bool($rememberMeOrToken) && $rememberMeOrToken) || $rememberMe) { $params['rememberMe'] = 1; } elseif ($rememberMeOrToken !== null) { - $params['token'] = $rememberMeOrToken; + $params['totp'] = $rememberMeOrToken; } $this->actor->sendPOST($this->getUrl(), $params); @@ -39,7 +39,7 @@ class AuthenticationRoute extends BasePage { $this->route = ['authentication/forgot-password']; $this->actor->sendPOST($this->getUrl(), [ 'login' => $login, - 'token' => $token, + 'totp' => $token, ]); } diff --git a/tests/codeception/api/_pages/TwoFactorAuthRoute.php b/tests/codeception/api/_pages/TwoFactorAuthRoute.php index ed677bb..74978cd 100644 --- a/tests/codeception/api/_pages/TwoFactorAuthRoute.php +++ b/tests/codeception/api/_pages/TwoFactorAuthRoute.php @@ -14,16 +14,16 @@ class TwoFactorAuthRoute extends BasePage { $this->actor->sendGET($this->getUrl()); } - public function enable($token = null, $password = null) { + public function enable($totp = null, $password = null) { $this->actor->sendPOST($this->getUrl(), [ - 'token' => $token, + 'totp' => $totp, 'password' => $password, ]); } - public function disable($token = null, $password = null) { + public function disable($totp = null, $password = null) { $this->actor->sendDELETE($this->getUrl(), [ - 'token' => $token, + 'totp' => $totp, 'password' => $password, ]); } diff --git a/tests/codeception/api/functional/ForgotPasswordCest.php b/tests/codeception/api/functional/ForgotPasswordCest.php index 36023bd..823015d 100644 --- a/tests/codeception/api/functional/ForgotPasswordCest.php +++ b/tests/codeception/api/functional/ForgotPasswordCest.php @@ -39,7 +39,7 @@ class ForgotPasswordCest { $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_required', + 'totp' => 'error.totp_required', ], ]); @@ -47,7 +47,7 @@ class ForgotPasswordCest { $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_required', + 'totp' => 'error.totp_required', ], ]); @@ -55,7 +55,7 @@ class ForgotPasswordCest { $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_incorrect', + 'totp' => 'error.totp_incorrect', ], ]); } @@ -73,7 +73,7 @@ class ForgotPasswordCest { } public function testForgotPasswordByAccountWithOtp(FunctionalTester $I) { - $I->wantTo('create new password recover request by passing username and otp token'); + $I->wantTo('create new password recover request by passing username and otp totp'); $totp = TOTP::create('BBBB'); $this->route->forgotPassword('AccountWithEnabledOtp', $totp->now()); $this->assertSuccessResponse($I, true); diff --git a/tests/codeception/api/functional/LoginCest.php b/tests/codeception/api/functional/LoginCest.php index 6f2adaf..2b17313 100644 --- a/tests/codeception/api/functional/LoginCest.php +++ b/tests/codeception/api/functional/LoginCest.php @@ -107,49 +107,49 @@ class LoginCest { public function testLoginToken(FunctionalTester $I) { $route = new AuthenticationRoute($I); - $I->wantTo('see token don\'t have errors if email, username or token not set'); + $I->wantTo('see totp don\'t have errors if email, username or totp not set'); $route->login(); $I->canSeeResponseContainsJson([ 'success' => false, ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.token'); + $I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp'); - $I->wantTo('see token don\'t have errors if username not exists in database'); + $I->wantTo('see totp don\'t have errors if username not exists in database'); $route->login('non-exist-username', 'random-password'); $I->canSeeResponseContainsJson([ 'success' => false, ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.token'); + $I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp'); - $I->wantTo('see token don\'t has errors if email not exists in database'); + $I->wantTo('see totp don\'t has errors if email not exists in database'); $route->login('not-exist@user.com', 'random-password'); $I->canSeeResponseContainsJson([ 'success' => false, ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.token'); + $I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp'); - $I->wantTo('see token don\'t has errors if email correct, but password wrong'); + $I->wantTo('see totp don\'t has errors if email correct, but password wrong'); $route->login('not-exist@user.com', 'random-password'); $I->canSeeResponseContainsJson([ 'success' => false, ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.token'); + $I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp'); - $I->wantTo('see error.token_required if username and password correct, but account have enable otp'); + $I->wantTo('see error.totp_required if username and password correct, but account have enable otp'); $route->login('AccountWithEnabledOtp', 'password_0'); $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_required', + 'totp' => 'error.totp_required', ], ]); - $I->wantTo('see error.token_incorrect if username and password correct, but token wrong'); + $I->wantTo('see error.totp_incorrect if username and password correct, but totp wrong'); $route->login('AccountWithEnabledOtp', 'password_0', '123456'); $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_incorrect', + 'totp' => 'error.totp_incorrect', ], ]); } diff --git a/tests/codeception/api/functional/TwoFactorAuthDisableCest.php b/tests/codeception/api/functional/TwoFactorAuthDisableCest.php index 1e288d1..6ba350f 100644 --- a/tests/codeception/api/functional/TwoFactorAuthDisableCest.php +++ b/tests/codeception/api/functional/TwoFactorAuthDisableCest.php @@ -23,7 +23,7 @@ class TwoFactorAuthDisableCest { $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_required', + 'totp' => 'error.totp_required', 'password' => 'error.password_required', ], ]); @@ -32,7 +32,7 @@ class TwoFactorAuthDisableCest { $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_incorrect', + 'totp' => 'error.totp_incorrect', 'password' => 'error.password_incorrect', ], ]); diff --git a/tests/codeception/api/functional/TwoFactorAuthEnableCest.php b/tests/codeception/api/functional/TwoFactorAuthEnableCest.php index 5fea216..3e7bc0d 100644 --- a/tests/codeception/api/functional/TwoFactorAuthEnableCest.php +++ b/tests/codeception/api/functional/TwoFactorAuthEnableCest.php @@ -23,7 +23,7 @@ class TwoFactorAuthEnableCest { $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_required', + 'totp' => 'error.totp_required', 'password' => 'error.password_required', ], ]); @@ -32,7 +32,7 @@ class TwoFactorAuthEnableCest { $I->canSeeResponseContainsJson([ 'success' => false, 'errors' => [ - 'token' => 'error.token_incorrect', + 'totp' => 'error.totp_incorrect', 'password' => 'error.password_incorrect', ], ]); diff --git a/tests/codeception/api/unit/models/authentication/ForgotPasswordFormTest.php b/tests/codeception/api/unit/models/authentication/ForgotPasswordFormTest.php index e6d82c9..ad73d27 100644 --- a/tests/codeception/api/unit/models/authentication/ForgotPasswordFormTest.php +++ b/tests/codeception/api/unit/models/authentication/ForgotPasswordFormTest.php @@ -41,19 +41,19 @@ class ForgotPasswordFormTest extends TestCase { $this->assertEmpty($model->getErrors('login'), 'empty errors if login is exists'); } - public function testValidateTotpToken() { + public function testValidateTotp() { $model = new ForgotPasswordForm(); $model->login = 'AccountWithEnabledOtp'; - $model->token = '123456'; - $model->validateTotpToken('token'); - $this->assertEquals(['error.token_incorrect'], $model->getErrors('token')); + $model->totp = '123456'; + $model->validateTotp('totp'); + $this->assertEquals(['error.totp_incorrect'], $model->getErrors('totp')); $totp = TOTP::create('BBBB'); $model = new ForgotPasswordForm(); $model->login = 'AccountWithEnabledOtp'; - $model->token = $totp->now(); - $model->validateTotpToken('token'); - $this->assertEmpty($model->getErrors('token')); + $model->totp = $totp->now(); + $model->validateTotp('totp'); + $this->assertEmpty($model->getErrors('totp')); } public function testValidateActivity() { diff --git a/tests/codeception/api/unit/models/authentication/LoginFormTest.php b/tests/codeception/api/unit/models/authentication/LoginFormTest.php index 1307eca..7ee65fc 100644 --- a/tests/codeception/api/unit/models/authentication/LoginFormTest.php +++ b/tests/codeception/api/unit/models/authentication/LoginFormTest.php @@ -72,31 +72,31 @@ class LoginFormTest extends TestCase { }); } - public function testValidateTotpToken() { + public function testValidateTotp() { $account = new AccountIdentity(['password' => '12345678']); $account->password = '12345678'; $account->is_otp_enabled = true; $account->otp_secret = 'AAAA'; - $this->specify('error.token_incorrect if totp invalid', function() use ($account) { + $this->specify('error.totp_incorrect if totp invalid', function() use ($account) { $model = $this->createModel([ 'password' => '12345678', - 'token' => '321123', + 'totp' => '321123', 'account' => $account, ]); - $model->validateTotpToken('token'); - $this->assertEquals(['error.token_incorrect'], $model->getErrors('token')); + $model->validateTotp('totp'); + $this->assertEquals(['error.totp_incorrect'], $model->getErrors('totp')); }); $totp = TOTP::create($account->otp_secret); $this->specify('no errors if password valid', function() use ($account, $totp) { $model = $this->createModel([ 'password' => '12345678', - 'token' => $totp->now(), + 'totp' => $totp->now(), 'account' => $account, ]); - $model->validateTotpToken('token'); - $this->assertEmpty($model->getErrors('token')); + $model->validateTotp('totp'); + $this->assertEmpty($model->getErrors('totp')); }); } diff --git a/tests/codeception/api/unit/models/authentication/RegistrationFormTest.php b/tests/codeception/api/unit/models/authentication/RegistrationFormTest.php index 2db914f..2ece316 100644 --- a/tests/codeception/api/unit/models/authentication/RegistrationFormTest.php +++ b/tests/codeception/api/unit/models/authentication/RegistrationFormTest.php @@ -8,6 +8,7 @@ use common\models\Account; use common\models\EmailActivation; use common\models\UsernameHistory; use GuzzleHttp\ClientInterface; +use ReflectionClass; use tests\codeception\api\unit\TestCase; use tests\codeception\common\fixtures\AccountFixture; use tests\codeception\common\fixtures\EmailActivationFixture; diff --git a/tests/codeception/api/unit/validators/TotpValidatorTest.php b/tests/codeception/api/unit/validators/TotpValidatorTest.php index 1954009..118a7cb 100644 --- a/tests/codeception/api/unit/validators/TotpValidatorTest.php +++ b/tests/codeception/api/unit/validators/TotpValidatorTest.php @@ -19,13 +19,13 @@ class TotpValidatorTest extends TestCase { $validator = new TotpValidator(['account' => $account]); $result = $this->callProtected($validator, 'validateValue', 123456); - $this->assertEquals([E::OTP_TOKEN_INCORRECT, []], $result); + $this->assertEquals([E::TOTP_INCORRECT, []], $result); $result = $this->callProtected($validator, 'validateValue', $controlTotp->now()); $this->assertNull($result); $result = $this->callProtected($validator, 'validateValue', $controlTotp->at(time() - 31)); - $this->assertEquals([E::OTP_TOKEN_INCORRECT, []], $result); + $this->assertEquals([E::TOTP_INCORRECT, []], $result); $validator->window = 2; $result = $this->callProtected($validator, 'validateValue', $controlTotp->at(time() - 31)); @@ -34,7 +34,7 @@ class TotpValidatorTest extends TestCase { $at = time() - 400; $validator->timestamp = $at; $result = $this->callProtected($validator, 'validateValue', $controlTotp->now()); - $this->assertEquals([E::OTP_TOKEN_INCORRECT, []], $result); + $this->assertEquals([E::TOTP_INCORRECT, []], $result); $result = $this->callProtected($validator, 'validateValue', $controlTotp->at($at)); $this->assertNull($result);