Поле token в контексте otp токенов переименовано в totp

This commit is contained in:
ErickSkrauch 2017-09-06 20:17:52 +03:00
parent 2bdb79b43d
commit 2c08130f4e
16 changed files with 65 additions and 63 deletions

View File

@ -20,17 +20,17 @@ class ForgotPasswordForm extends ApiForm {
public $login; public $login;
public $token; public $totp;
public function rules() { public function rules() {
return [ return [
['captcha', ReCaptchaValidator::class], ['captcha', ReCaptchaValidator::class],
['login', 'required', 'message' => E::LOGIN_REQUIRED], ['login', 'required', 'message' => E::LOGIN_REQUIRED],
['login', 'validateLogin'], ['login', 'validateLogin'],
['token', 'required', 'when' => function(self $model) { ['totp', 'required', 'when' => function(self $model) {
return !$this->hasErrors() && $model->getAccount()->is_otp_enabled; return !$this->hasErrors() && $model->getAccount()->is_otp_enabled;
}, 'message' => E::OTP_TOKEN_REQUIRED], }, 'message' => E::TOTP_REQUIRED],
['token', 'validateTotpToken'], ['totp', 'validateTotp'],
['login', 'validateActivity'], ['login', 'validateActivity'],
['login', 'validateFrequency'], ['login', 'validateFrequency'],
]; ];
@ -44,7 +44,7 @@ class ForgotPasswordForm extends ApiForm {
} }
} }
public function validateTotpToken($attribute) { public function validateTotp($attribute) {
if ($this->hasErrors()) { if ($this->hasErrors()) {
return; return;
} }

View File

@ -17,10 +17,10 @@ class LoginForm extends ApiForm {
public $login; public $login;
public $password; public $password;
public $token; public $totp;
public $rememberMe = false; public $rememberMe = false;
public function rules() { public function rules(): array {
return [ return [
['login', 'required', 'message' => E::LOGIN_REQUIRED], ['login', 'required', 'message' => E::LOGIN_REQUIRED],
['login', 'validateLogin'], ['login', 'validateLogin'],
@ -30,10 +30,10 @@ class LoginForm extends ApiForm {
}, 'message' => E::PASSWORD_REQUIRED], }, 'message' => E::PASSWORD_REQUIRED],
['password', 'validatePassword'], ['password', 'validatePassword'],
['token', 'required', 'when' => function(self $model) { ['totp', 'required', 'when' => function(self $model) {
return !$model->hasErrors() && $model->getAccount()->is_otp_enabled; return !$model->hasErrors() && $model->getAccount()->is_otp_enabled;
}, 'message' => E::OTP_TOKEN_REQUIRED], }, 'message' => E::TOTP_REQUIRED],
['token', 'validateTotpToken'], ['totp', 'validateTotp'],
['login', 'validateActivity'], ['login', 'validateActivity'],
@ -58,7 +58,7 @@ class LoginForm extends ApiForm {
} }
} }
public function validateTotpToken($attribute) { public function validateTotp($attribute) {
if ($this->hasErrors()) { if ($this->hasErrors()) {
return; return;
} }

View File

@ -22,7 +22,7 @@ class TwoFactorAuthForm extends ApiForm {
const SCENARIO_ACTIVATE = 'enable'; const SCENARIO_ACTIVATE = 'enable';
const SCENARIO_DISABLE = 'disable'; const SCENARIO_DISABLE = 'disable';
public $token; public $totp;
public $timestamp; public $timestamp;
@ -44,8 +44,8 @@ class TwoFactorAuthForm extends ApiForm {
['timestamp', 'integer', 'on' => [self::SCENARIO_ACTIVATE]], ['timestamp', 'integer', 'on' => [self::SCENARIO_ACTIVATE]],
['account', 'validateOtpDisabled', 'on' => self::SCENARIO_ACTIVATE], ['account', 'validateOtpDisabled', 'on' => self::SCENARIO_ACTIVATE],
['account', 'validateOtpEnabled', 'on' => self::SCENARIO_DISABLE], ['account', 'validateOtpEnabled', 'on' => self::SCENARIO_DISABLE],
['token', 'required', 'message' => E::OTP_TOKEN_REQUIRED, 'on' => $bothScenarios], ['totp', 'required', 'message' => E::TOTP_REQUIRED, 'on' => $bothScenarios],
['token', TotpValidator::class, 'on' => $bothScenarios, ['totp', TotpValidator::class, 'on' => $bothScenarios,
'account' => $this->account, 'account' => $this->account,
'timestamp' => function() { 'timestamp' => function() {
return $this->timestamp; return $this->timestamp;

View File

@ -36,7 +36,7 @@ class AuthenticationForm extends ApiForm {
$loginForm->password = $this->password; $loginForm->password = $this->password;
if (!$loginForm->validate()) { if (!$loginForm->validate()) {
$errors = $loginForm->getFirstErrors(); $errors = $loginForm->getFirstErrors();
if (isset($errors['token'])) { if (isset($errors['totp'])) {
Authserver::error("User with login = '{$this->username}' protected by two factor auth."); Authserver::error("User with login = '{$this->username}' protected by two factor auth.");
throw new ForbiddenOperationException('Account protected with two factor auth.'); throw new ForbiddenOperationException('Account protected with two factor auth.');
} elseif (isset($errors['login'])) { } elseif (isset($errors['login'])) {

View File

@ -52,10 +52,10 @@ class TotpValidator extends Validator {
try { try {
$totp = TOTP::create($this->account->otp_secret); $totp = TOTP::create($this->account->otp_secret);
if (!$totp->verify((string)$value, $this->getTimestamp(), $this->window)) { if (!$totp->verify((string)$value, $this->getTimestamp(), $this->window)) {
return [E::OTP_TOKEN_INCORRECT, []]; return [E::TOTP_INCORRECT, []];
} }
} catch (RangeException $e) { } catch (RangeException $e) {
return [E::OTP_TOKEN_INCORRECT, []]; return [E::TOTP_INCORRECT, []];
} }
return null; return null;

View File

@ -54,8 +54,9 @@ final class Error {
const SUBJECT_REQUIRED = 'error.subject_required'; const SUBJECT_REQUIRED = 'error.subject_required';
const MESSAGE_REQUIRED = 'error.message_required'; const MESSAGE_REQUIRED = 'error.message_required';
const OTP_TOKEN_REQUIRED = 'error.token_required'; const TOTP_REQUIRED = 'error.totp_required';
const OTP_TOKEN_INCORRECT = 'error.token_incorrect'; const TOTP_INCORRECT = 'error.totp_incorrect';
const OTP_ALREADY_ENABLED = 'error.otp_already_enabled'; const OTP_ALREADY_ENABLED = 'error.otp_already_enabled';
const OTP_NOT_ENABLED = 'error.otp_not_enabled'; const OTP_NOT_ENABLED = 'error.otp_not_enabled';

View File

@ -24,7 +24,7 @@ class AuthenticationRoute extends BasePage {
if ((is_bool($rememberMeOrToken) && $rememberMeOrToken) || $rememberMe) { if ((is_bool($rememberMeOrToken) && $rememberMeOrToken) || $rememberMe) {
$params['rememberMe'] = 1; $params['rememberMe'] = 1;
} elseif ($rememberMeOrToken !== null) { } elseif ($rememberMeOrToken !== null) {
$params['token'] = $rememberMeOrToken; $params['totp'] = $rememberMeOrToken;
} }
$this->actor->sendPOST($this->getUrl(), $params); $this->actor->sendPOST($this->getUrl(), $params);
@ -39,7 +39,7 @@ class AuthenticationRoute extends BasePage {
$this->route = ['authentication/forgot-password']; $this->route = ['authentication/forgot-password'];
$this->actor->sendPOST($this->getUrl(), [ $this->actor->sendPOST($this->getUrl(), [
'login' => $login, 'login' => $login,
'token' => $token, 'totp' => $token,
]); ]);
} }

View File

@ -14,16 +14,16 @@ class TwoFactorAuthRoute extends BasePage {
$this->actor->sendGET($this->getUrl()); $this->actor->sendGET($this->getUrl());
} }
public function enable($token = null, $password = null) { public function enable($totp = null, $password = null) {
$this->actor->sendPOST($this->getUrl(), [ $this->actor->sendPOST($this->getUrl(), [
'token' => $token, 'totp' => $totp,
'password' => $password, 'password' => $password,
]); ]);
} }
public function disable($token = null, $password = null) { public function disable($totp = null, $password = null) {
$this->actor->sendDELETE($this->getUrl(), [ $this->actor->sendDELETE($this->getUrl(), [
'token' => $token, 'totp' => $totp,
'password' => $password, 'password' => $password,
]); ]);
} }

View File

@ -39,7 +39,7 @@ class ForgotPasswordCest {
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ 'errors' => [
'token' => 'error.token_required', 'totp' => 'error.totp_required',
], ],
]); ]);
@ -47,7 +47,7 @@ class ForgotPasswordCest {
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ 'errors' => [
'token' => 'error.token_required', 'totp' => 'error.totp_required',
], ],
]); ]);
@ -55,7 +55,7 @@ class ForgotPasswordCest {
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ 'errors' => [
'token' => 'error.token_incorrect', 'totp' => 'error.totp_incorrect',
], ],
]); ]);
} }
@ -73,7 +73,7 @@ class ForgotPasswordCest {
} }
public function testForgotPasswordByAccountWithOtp(FunctionalTester $I) { 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'); $totp = TOTP::create('BBBB');
$this->route->forgotPassword('AccountWithEnabledOtp', $totp->now()); $this->route->forgotPassword('AccountWithEnabledOtp', $totp->now());
$this->assertSuccessResponse($I, true); $this->assertSuccessResponse($I, true);

View File

@ -107,49 +107,49 @@ class LoginCest {
public function testLoginToken(FunctionalTester $I) { public function testLoginToken(FunctionalTester $I) {
$route = new AuthenticationRoute($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(); $route->login();
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, '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'); $route->login('non-exist-username', 'random-password');
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, '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'); $route->login('not-exist@user.com', 'random-password');
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, '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'); $route->login('not-exist@user.com', 'random-password');
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, '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'); $route->login('AccountWithEnabledOtp', 'password_0');
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ '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'); $route->login('AccountWithEnabledOtp', 'password_0', '123456');
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ 'errors' => [
'token' => 'error.token_incorrect', 'totp' => 'error.totp_incorrect',
], ],
]); ]);
} }

View File

@ -23,7 +23,7 @@ class TwoFactorAuthDisableCest {
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ 'errors' => [
'token' => 'error.token_required', 'totp' => 'error.totp_required',
'password' => 'error.password_required', 'password' => 'error.password_required',
], ],
]); ]);
@ -32,7 +32,7 @@ class TwoFactorAuthDisableCest {
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ 'errors' => [
'token' => 'error.token_incorrect', 'totp' => 'error.totp_incorrect',
'password' => 'error.password_incorrect', 'password' => 'error.password_incorrect',
], ],
]); ]);

View File

@ -23,7 +23,7 @@ class TwoFactorAuthEnableCest {
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ 'errors' => [
'token' => 'error.token_required', 'totp' => 'error.totp_required',
'password' => 'error.password_required', 'password' => 'error.password_required',
], ],
]); ]);
@ -32,7 +32,7 @@ class TwoFactorAuthEnableCest {
$I->canSeeResponseContainsJson([ $I->canSeeResponseContainsJson([
'success' => false, 'success' => false,
'errors' => [ 'errors' => [
'token' => 'error.token_incorrect', 'totp' => 'error.totp_incorrect',
'password' => 'error.password_incorrect', 'password' => 'error.password_incorrect',
], ],
]); ]);

View File

@ -41,19 +41,19 @@ class ForgotPasswordFormTest extends TestCase {
$this->assertEmpty($model->getErrors('login'), 'empty errors if login is exists'); $this->assertEmpty($model->getErrors('login'), 'empty errors if login is exists');
} }
public function testValidateTotpToken() { public function testValidateTotp() {
$model = new ForgotPasswordForm(); $model = new ForgotPasswordForm();
$model->login = 'AccountWithEnabledOtp'; $model->login = 'AccountWithEnabledOtp';
$model->token = '123456'; $model->totp = '123456';
$model->validateTotpToken('token'); $model->validateTotp('totp');
$this->assertEquals(['error.token_incorrect'], $model->getErrors('token')); $this->assertEquals(['error.totp_incorrect'], $model->getErrors('totp'));
$totp = TOTP::create('BBBB'); $totp = TOTP::create('BBBB');
$model = new ForgotPasswordForm(); $model = new ForgotPasswordForm();
$model->login = 'AccountWithEnabledOtp'; $model->login = 'AccountWithEnabledOtp';
$model->token = $totp->now(); $model->totp = $totp->now();
$model->validateTotpToken('token'); $model->validateTotp('totp');
$this->assertEmpty($model->getErrors('token')); $this->assertEmpty($model->getErrors('totp'));
} }
public function testValidateActivity() { public function testValidateActivity() {

View File

@ -72,31 +72,31 @@ class LoginFormTest extends TestCase {
}); });
} }
public function testValidateTotpToken() { public function testValidateTotp() {
$account = new AccountIdentity(['password' => '12345678']); $account = new AccountIdentity(['password' => '12345678']);
$account->password = '12345678'; $account->password = '12345678';
$account->is_otp_enabled = true; $account->is_otp_enabled = true;
$account->otp_secret = 'AAAA'; $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([ $model = $this->createModel([
'password' => '12345678', 'password' => '12345678',
'token' => '321123', 'totp' => '321123',
'account' => $account, 'account' => $account,
]); ]);
$model->validateTotpToken('token'); $model->validateTotp('totp');
$this->assertEquals(['error.token_incorrect'], $model->getErrors('token')); $this->assertEquals(['error.totp_incorrect'], $model->getErrors('totp'));
}); });
$totp = TOTP::create($account->otp_secret); $totp = TOTP::create($account->otp_secret);
$this->specify('no errors if password valid', function() use ($account, $totp) { $this->specify('no errors if password valid', function() use ($account, $totp) {
$model = $this->createModel([ $model = $this->createModel([
'password' => '12345678', 'password' => '12345678',
'token' => $totp->now(), 'totp' => $totp->now(),
'account' => $account, 'account' => $account,
]); ]);
$model->validateTotpToken('token'); $model->validateTotp('totp');
$this->assertEmpty($model->getErrors('token')); $this->assertEmpty($model->getErrors('totp'));
}); });
} }

View File

@ -8,6 +8,7 @@ use common\models\Account;
use common\models\EmailActivation; use common\models\EmailActivation;
use common\models\UsernameHistory; use common\models\UsernameHistory;
use GuzzleHttp\ClientInterface; use GuzzleHttp\ClientInterface;
use ReflectionClass;
use tests\codeception\api\unit\TestCase; use tests\codeception\api\unit\TestCase;
use tests\codeception\common\fixtures\AccountFixture; use tests\codeception\common\fixtures\AccountFixture;
use tests\codeception\common\fixtures\EmailActivationFixture; use tests\codeception\common\fixtures\EmailActivationFixture;

View File

@ -19,13 +19,13 @@ class TotpValidatorTest extends TestCase {
$validator = new TotpValidator(['account' => $account]); $validator = new TotpValidator(['account' => $account]);
$result = $this->callProtected($validator, 'validateValue', 123456); $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()); $result = $this->callProtected($validator, 'validateValue', $controlTotp->now());
$this->assertNull($result); $this->assertNull($result);
$result = $this->callProtected($validator, 'validateValue', $controlTotp->at(time() - 31)); $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; $validator->window = 2;
$result = $this->callProtected($validator, 'validateValue', $controlTotp->at(time() - 31)); $result = $this->callProtected($validator, 'validateValue', $controlTotp->at(time() - 31));
@ -34,7 +34,7 @@ class TotpValidatorTest extends TestCase {
$at = time() - 400; $at = time() - 400;
$validator->timestamp = $at; $validator->timestamp = $at;
$result = $this->callProtected($validator, 'validateValue', $controlTotp->now()); $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)); $result = $this->callProtected($validator, 'validateValue', $controlTotp->at($at));
$this->assertNull($result); $this->assertNull($result);