mirror of
https://github.com/elyby/accounts.git
synced 2024-11-26 16:52:02 +05:30
Добавлены тесты для включения/отключения OTP
This commit is contained in:
parent
be4c7908b2
commit
6aab2592b4
@ -17,16 +17,22 @@ class TwoFactorAuthController extends Controller {
|
||||
'class' => AccessControl::class,
|
||||
'rules' => [
|
||||
[
|
||||
'allow' => true,
|
||||
'class' => ActiveUserRule::class,
|
||||
'actions' => [
|
||||
'credentials',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function verbs() {
|
||||
return [
|
||||
'credentials' => ['GET'],
|
||||
'activate' => ['POST'],
|
||||
'disable' => ['DELETE'],
|
||||
];
|
||||
}
|
||||
|
||||
public function actionCredentials() {
|
||||
$account = Yii::$app->user->identity;
|
||||
$model = new TwoFactorAuthForm($account);
|
||||
@ -37,6 +43,7 @@ class TwoFactorAuthController extends Controller {
|
||||
public function actionActivate() {
|
||||
$account = Yii::$app->user->identity;
|
||||
$model = new TwoFactorAuthForm($account, ['scenario' => TwoFactorAuthForm::SCENARIO_ACTIVATE]);
|
||||
$model->load(Yii::$app->request->post());
|
||||
if (!$model->activate()) {
|
||||
return [
|
||||
'success' => false,
|
||||
@ -52,6 +59,7 @@ class TwoFactorAuthController extends Controller {
|
||||
public function actionDisable() {
|
||||
$account = Yii::$app->user->identity;
|
||||
$model = new TwoFactorAuthForm($account, ['scenario' => TwoFactorAuthForm::SCENARIO_DISABLE]);
|
||||
$model->load(Yii::$app->request->getBodyParams());
|
||||
if (!$model->disable()) {
|
||||
return [
|
||||
'success' => false,
|
||||
|
@ -61,7 +61,7 @@ class TwoFactorAuthForm extends ApiForm {
|
||||
}
|
||||
|
||||
public function activate(): bool {
|
||||
if (!$this->validate()) {
|
||||
if ($this->scenario !== self::SCENARIO_ACTIVATE || !$this->validate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ class TwoFactorAuthForm extends ApiForm {
|
||||
}
|
||||
|
||||
public function disable(): bool {
|
||||
if (!$this->validate()) {
|
||||
if ($this->scenario !== self::SCENARIO_DISABLE || !$this->validate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,8 @@ final class Error {
|
||||
const SUBJECT_REQUIRED = 'error.subject_required';
|
||||
const MESSAGE_REQUIRED = 'error.message_required';
|
||||
|
||||
const OTP_TOKEN_REQUIRED = 'error.otp_token_required';
|
||||
const OTP_TOKEN_INCORRECT = 'error.otp_token_incorrect';
|
||||
const OTP_TOKEN_REQUIRED = 'error.token_required';
|
||||
const OTP_TOKEN_INCORRECT = 'error.token_incorrect';
|
||||
const OTP_ALREADY_ENABLED = 'error.otp_already_enabled';
|
||||
const OTP_NOT_ENABLED = 'error.otp_not_enabled';
|
||||
|
||||
|
@ -8,9 +8,24 @@ use yii\codeception\BasePage;
|
||||
*/
|
||||
class TwoFactorAuthRoute extends BasePage {
|
||||
|
||||
public $route = '/two-factor-auth';
|
||||
|
||||
public function credentials() {
|
||||
$this->route = '/two-factor-auth';
|
||||
$this->actor->sendGET($this->getUrl());
|
||||
}
|
||||
|
||||
public function enable($token = null, $password = null) {
|
||||
$this->actor->sendPOST($this->getUrl(), [
|
||||
'token' => $token,
|
||||
'password' => $password,
|
||||
]);
|
||||
}
|
||||
|
||||
public function disable($token = null, $password = null) {
|
||||
$this->actor->sendDELETE($this->getUrl(), [
|
||||
'token' => $token,
|
||||
'password' => $password,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace tests\codeception\api\functional;
|
||||
|
||||
use OTPHP\TOTP;
|
||||
use tests\codeception\api\_pages\TwoFactorAuthRoute;
|
||||
use tests\codeception\api\FunctionalTester;
|
||||
|
||||
class TwoFactorAuthDisableCest {
|
||||
|
||||
/**
|
||||
* @var TwoFactorAuthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new TwoFactorAuthRoute($I);
|
||||
}
|
||||
|
||||
public function testFails(FunctionalTester $I) {
|
||||
$I->loggedInAsActiveAccount('AccountWithEnabledOtp', 'password_0');
|
||||
|
||||
$this->route->disable();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'token' => 'error.token_required',
|
||||
'password' => 'error.password_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->route->disable('123456', 'invalid_password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'token' => 'error.token_incorrect',
|
||||
'password' => 'error.password_incorrect',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->loggedInAsActiveAccount('AccountWithOtpSecret', 'password_0');
|
||||
$this->route->disable('123456', 'invalid_password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'account' => 'error.otp_not_enabled',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccessEnable(FunctionalTester $I) {
|
||||
$I->loggedInAsActiveAccount('AccountWithEnabledOtp', 'password_0');
|
||||
$totp = new TOTP(null, 'secret-secret-secret');
|
||||
$this->route->disable($totp->now(), 'password_0');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
61
tests/codeception/api/functional/TwoFactorAuthEnableCest.php
Normal file
61
tests/codeception/api/functional/TwoFactorAuthEnableCest.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace tests\codeception\api\functional;
|
||||
|
||||
use OTPHP\TOTP;
|
||||
use tests\codeception\api\_pages\TwoFactorAuthRoute;
|
||||
use tests\codeception\api\FunctionalTester;
|
||||
|
||||
class TwoFactorAuthEnableCest {
|
||||
|
||||
/**
|
||||
* @var TwoFactorAuthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new TwoFactorAuthRoute($I);
|
||||
}
|
||||
|
||||
public function testFails(FunctionalTester $I) {
|
||||
$I->loggedInAsActiveAccount('AccountWithOtpSecret', 'password_0');
|
||||
|
||||
$this->route->enable();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'token' => 'error.token_required',
|
||||
'password' => 'error.password_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->route->enable('123456', 'invalid_password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'token' => 'error.token_incorrect',
|
||||
'password' => 'error.password_incorrect',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->loggedInAsActiveAccount('AccountWithEnabledOtp', 'password_0');
|
||||
$this->route->enable('123456', 'invalid_password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'account' => 'error.otp_already_enabled',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccessEnable(FunctionalTester $I) {
|
||||
$I->loggedInAsActiveAccount('AccountWithOtpSecret', 'password_0');
|
||||
$totp = new TOTP(null, 'some otp secret value');
|
||||
$this->route->enable($totp->now(), 'password_0');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
namespace tests\codeception\api\unit\models\profile;
|
||||
|
||||
use api\models\profile\TwoFactorAuthForm;
|
||||
use common\helpers\Error as E;
|
||||
use common\models\Account;
|
||||
use OTPHP\TOTP;
|
||||
use tests\codeception\api\unit\TestCase;
|
||||
|
||||
class TwoFactorAuthFormTest extends TestCase {
|
||||
@ -64,4 +66,100 @@ class TwoFactorAuthFormTest extends TestCase {
|
||||
$this->assertEquals('some valid totp secret value', $result['secret']);
|
||||
}
|
||||
|
||||
public function testActivate() {
|
||||
/** @var Account|\PHPUnit_Framework_MockObject_MockObject $account */
|
||||
$account = $this->getMockBuilder(Account::class)
|
||||
->setMethods(['save'])
|
||||
->getMock();
|
||||
|
||||
$account->expects($this->once())
|
||||
->method('save')
|
||||
->willReturn(true);
|
||||
|
||||
$account->is_otp_enabled = false;
|
||||
$account->otp_secret = 'mock secret';
|
||||
|
||||
/** @var TwoFactorAuthForm|\PHPUnit_Framework_MockObject_MockObject $model */
|
||||
$model = $this->getMockBuilder(TwoFactorAuthForm::class)
|
||||
->setMethods(['validate'])
|
||||
->setConstructorArgs([$account, ['scenario' => TwoFactorAuthForm::SCENARIO_ACTIVATE]])
|
||||
->getMock();
|
||||
|
||||
$model->expects($this->once())
|
||||
->method('validate')
|
||||
->willReturn(true);
|
||||
|
||||
$this->assertTrue($model->activate());
|
||||
$this->assertTrue($account->is_otp_enabled);
|
||||
}
|
||||
|
||||
public function testDisable() {
|
||||
/** @var Account|\PHPUnit_Framework_MockObject_MockObject $account */
|
||||
$account = $this->getMockBuilder(Account::class)
|
||||
->setMethods(['save'])
|
||||
->getMock();
|
||||
|
||||
$account->expects($this->once())
|
||||
->method('save')
|
||||
->willReturn(true);
|
||||
|
||||
$account->is_otp_enabled = true;
|
||||
$account->otp_secret = 'mock secret';
|
||||
|
||||
/** @var TwoFactorAuthForm|\PHPUnit_Framework_MockObject_MockObject $model */
|
||||
$model = $this->getMockBuilder(TwoFactorAuthForm::class)
|
||||
->setMethods(['validate'])
|
||||
->setConstructorArgs([$account, ['scenario' => TwoFactorAuthForm::SCENARIO_DISABLE]])
|
||||
->getMock();
|
||||
|
||||
$model->expects($this->once())
|
||||
->method('validate')
|
||||
->willReturn(true);
|
||||
|
||||
$this->assertTrue($model->disable());
|
||||
$this->assertNull($account->otp_secret);
|
||||
$this->assertFalse($account->is_otp_enabled);
|
||||
}
|
||||
|
||||
public function testValidateOtpDisabled() {
|
||||
$account = new Account();
|
||||
$account->is_otp_enabled = true;
|
||||
$model = new TwoFactorAuthForm($account);
|
||||
$model->validateOtpDisabled('account');
|
||||
$this->assertEquals([E::OTP_ALREADY_ENABLED], $model->getErrors('account'));
|
||||
|
||||
$account = new Account();
|
||||
$account->is_otp_enabled = false;
|
||||
$model = new TwoFactorAuthForm($account);
|
||||
$model->validateOtpDisabled('account');
|
||||
$this->assertEmpty($model->getErrors('account'));
|
||||
}
|
||||
|
||||
public function testValidateOtpEnabled() {
|
||||
$account = new Account();
|
||||
$account->is_otp_enabled = false;
|
||||
$model = new TwoFactorAuthForm($account);
|
||||
$model->validateOtpEnabled('account');
|
||||
$this->assertEquals([E::OTP_NOT_ENABLED], $model->getErrors('account'));
|
||||
|
||||
$account = new Account();
|
||||
$account->is_otp_enabled = true;
|
||||
$model = new TwoFactorAuthForm($account);
|
||||
$model->validateOtpEnabled('account');
|
||||
$this->assertEmpty($model->getErrors('account'));
|
||||
}
|
||||
|
||||
public function testGetTotp() {
|
||||
$account = new Account();
|
||||
$account->otp_secret = 'mock secret';
|
||||
$account->email = 'check@this.email';
|
||||
|
||||
$model = new TwoFactorAuthForm($account);
|
||||
$totp = $model->getTotp();
|
||||
$this->assertInstanceOf(TOTP::class, $totp);
|
||||
$this->assertEquals('check@this.email', $totp->getLabel());
|
||||
$this->assertEquals('mock secret', $totp->getSecret());
|
||||
$this->assertEquals('Ely.by', $totp->getIssuer());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -146,4 +146,34 @@ return [
|
||||
'created_at' => 1474404139,
|
||||
'updated_at' => 1474404149,
|
||||
],
|
||||
'account-with-otp-secret' => [
|
||||
'id' => 12,
|
||||
'uuid' => '9e9dcd11-2322-46dc-a992-e822a422726e',
|
||||
'username' => 'AccountWithOtpSecret',
|
||||
'email' => 'sava-galkin@mail.ru',
|
||||
'password_hash' => '$2y$13$2rYkap5T6jG8z/mMK8a3Ou6aZxJcmAaTha6FEuujvHEmybSHRzW5e', # password_0
|
||||
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
|
||||
'lang' => 'ru',
|
||||
'status' => \common\models\Account::STATUS_ACTIVE,
|
||||
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
|
||||
'otp_secret' => 'some otp secret value',
|
||||
'is_otp_enabled' => false,
|
||||
'created_at' => 1485124615,
|
||||
'updated_at' => 1485124615,
|
||||
],
|
||||
'account-with-enabled-otp' => [
|
||||
'id' => 13,
|
||||
'uuid' => '15d0afa7-a2bb-44d3-9f31-964cbccc6043',
|
||||
'username' => 'AccountWithEnabledOtp',
|
||||
'email' => 'otp@gmail.com',
|
||||
'password_hash' => '$2y$13$2rYkap5T6jG8z/mMK8a3Ou6aZxJcmAaTha6FEuujvHEmybSHRzW5e', # password_0
|
||||
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
|
||||
'lang' => 'ru',
|
||||
'status' => \common\models\Account::STATUS_ACTIVE,
|
||||
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
|
||||
'otp_secret' => 'secret-secret-secret',
|
||||
'is_otp_enabled' => true,
|
||||
'created_at' => 1485124685,
|
||||
'updated_at' => 1485124685,
|
||||
],
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user