Поле 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 $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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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'])) {

View File

@ -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;

View File

@ -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';

View File

@ -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,
]);
}

View File

@ -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,
]);
}

View File

@ -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);

View File

@ -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',
],
]);
}

View File

@ -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',
],
]);

View File

@ -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',
],
]);

View File

@ -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() {

View File

@ -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'));
});
}

View File

@ -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;

View File

@ -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);