Описана базовая миграция, добавлена модель аккаунта, добавлена модель авторизации, написаны первичные тесты для этой модели, добавлен модуль авторизации, настроен базовый контроллер. Короче много чего сделано

This commit is contained in:
ErickSkrauch 2016-01-03 03:18:37 +03:00
parent 841303b8ab
commit 7b650e2654
40 changed files with 694 additions and 292 deletions

View File

@ -13,7 +13,7 @@ return [
'controllerNamespace' => 'api\controllers',
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'identityClass' => 'common\models\Account',
'enableAutoLogin' => true,
],
'log' => [
@ -28,6 +28,17 @@ return [
'errorHandler' => [
'errorAction' => 'site/error',
],
'request' => [
'baseUrl' => '/api',
],
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [],
],
],
'modules' => [
'login' => 'api\modules\login\Module',
],
'params' => $params,
];

View File

@ -0,0 +1,17 @@
<?php
namespace api\controllers;
class Controller extends \yii\rest\Controller {
public $enableCsrfValidation = true;
public function behaviors() {
$parentBehaviors = parent::behaviors();
// xml нам не понадобится
unset($parentBehaviors['contentNegotiator']['formats']['application/xml']);
return $parentBehaviors;
}
}

View File

@ -1,17 +1,17 @@
<?php
namespace api\controllers;
use Yii;
use common\models\LoginForm;
use api\models\ContactForm;
use api\models\LoginForm;
use api\models\PasswordResetRequestForm;
use api\models\ResetPasswordForm;
use api\models\SignupForm;
use api\models\ContactForm;
use Yii;
use yii\base\InvalidParamException;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\web\BadRequestHttpException;
use yii\web\Controller;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
/**
* Site controller

View File

@ -1,6 +1,7 @@
<?php
namespace common\models;
namespace api\models;
use common\models\Account;
use Yii;
use yii\base\Model;
@ -65,12 +66,12 @@ class LoginForm extends Model
/**
* Finds user by [[username]]
*
* @return User|null
* @return Account|null
*/
protected function getUser()
{
if ($this->_user === null) {
$this->_user = User::findByUsername($this->username);
$this->_user = Account::findByEmail($this->username);
}
return $this->_user;

View File

@ -1,7 +1,7 @@
<?php
namespace api\models;
use common\models\User;
use common\models\Account;
use yii\base\Model;
/**
@ -22,7 +22,7 @@ class PasswordResetRequestForm extends Model
['email', 'email'],
['email', 'exist',
'targetClass' => '\common\models\User',
'filter' => ['status' => User::STATUS_ACTIVE],
'filter' => ['status' => Account::STATUS_ACTIVE],
'message' => 'There is no user with such email.'
],
];
@ -35,14 +35,14 @@ class PasswordResetRequestForm extends Model
*/
public function sendEmail()
{
/* @var $user User */
$user = User::findOne([
'status' => User::STATUS_ACTIVE,
/* @var $user Account */
$user = Account::findOne([
'status' => Account::STATUS_ACTIVE,
'email' => $this->email,
]);
if ($user) {
if (!User::isPasswordResetTokenValid($user->password_reset_token)) {
if (!Account::isPasswordResetTokenValid($user->password_reset_token)) {
$user->generatePasswordResetToken();
}

View File

@ -1,7 +1,7 @@
<?php
namespace api\models;
use common\models\User;
use common\models\Account;
use yii\base\InvalidParamException;
use yii\base\Model;
use Yii;
@ -14,7 +14,7 @@ class ResetPasswordForm extends Model
public $password;
/**
* @var \common\models\User
* @var \common\models\Account
*/
private $_user;
@ -31,7 +31,7 @@ class ResetPasswordForm extends Model
if (empty($token) || !is_string($token)) {
throw new InvalidParamException('Password reset token cannot be blank.');
}
$this->_user = User::findByPasswordResetToken($token);
$this->_user = Account::findByPasswordResetToken($token);
if (!$this->_user) {
throw new InvalidParamException('Wrong password reset token.');
}

View File

@ -1,7 +1,7 @@
<?php
namespace api\models;
use common\models\User;
use common\models\Account;
use yii\base\Model;
use Yii;
@ -39,13 +39,12 @@ class SignupForm extends Model
/**
* Signs user up.
*
* @return User|null the saved model or null if saving fails
* @return Account|null the saved model or null if saving fails
*/
public function signup()
{
if ($this->validate()) {
$user = new User();
$user->username = $this->username;
$user = new Account();
$user->email = $this->email;
$user->setPassword($this->password);
$user->generateAuthKey();

View File

@ -0,0 +1,9 @@
<?php
namespace api\modules\login;
class Module extends \yii\base\Module {
public $id = 'login';
}

View File

@ -0,0 +1,48 @@
<?php
namespace api\modules\login\controllers;
use api\controllers\Controller;
use api\modules\login\models\AuthenticationForm;
use Yii;
use yii\filters\AccessControl;
class AuthenticationController extends Controller {
public function behaviors() {
return array_merge(parent::behaviors(), [
'access' => [
'class' => AccessControl::className(),
'only' => ['login-info'],
'rules' => [
[
'actions' => ['login-info'],
'allow' => true,
'roles' => ['?'],
],
],
],
]);
}
public function verbs() {
return [
'loginInfo' => ['post'],
];
}
public function actionLoginInfo() {
$model = new AuthenticationForm();
$model->load(Yii::$app->request->post());
if (!$model->login()) {
return [
'success' => false,
'errors' => $model->getErrors(),
];
}
return [
'success' => true,
];
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace api\modules\login\controllers;
use api\controllers\Controller;
class DefaultController extends Controller {
public function actionIndex() {
return ['hello' => 'world'];
}
}

View File

View File

@ -0,0 +1,70 @@
<?php
namespace api\modules\login\models;
use common\models\Account;
use Yii;
use yii\base\Model;
class AuthenticationForm extends Model {
public $email;
public $password;
public $rememberMe = true;
private $_user;
public function rules() {
return [
['email', 'required', 'message' => 'error.email_required'],
['email', 'email', 'message' => 'error.email_invalid'],
['email', 'validateEmail'],
['password', 'required', 'message' => 'error.password_required'],
['password', 'validatePassword'],
['rememberMe', 'boolean'],
];
}
public function validateEmail($attribute) {
if (!$this->hasErrors()) {
if ($this->getAccount() === NULL) {
$this->addError($attribute, 'error.email_not_exist');
}
}
}
public function validatePassword($attribute) {
if (!$this->hasErrors()) {
$account = $this->getAccount();
if (!$account || !$account->validatePassword($this->password)) {
$this->addError($attribute, 'error.password_incorrect');
}
}
}
/**
* Logs in a user using the provided username and password.
*
* @return boolean whether the user is logged in successfully
*/
public function login() {
if (!$this->validate()) {
return false;
}
return Yii::$app->user->login($this->getAccount(), $this->rememberMe ? 3600 * 24 * 30 : 0);
}
/**
* @return Account|null
*/
protected function getAccount() {
if ($this->_user === NULL) {
$this->_user = Account::findByEmail($this->email);
}
return $this->_user;
}
}

View File

@ -34,17 +34,17 @@ AppAsset::register($this);
],
]);
$menuItems = [
['label' => 'Home', 'url' => ['/site/index']],
['label' => 'About', 'url' => ['/site/about']],
['label' => 'Contact', 'url' => ['/site/contact']],
['label' => 'Home', 'url' => ['site/index']],
['label' => 'About', 'url' => ['site/about']],
['label' => 'Contact', 'url' => ['site/contact']],
];
if (Yii::$app->user->isGuest) {
$menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']];
$menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];
$menuItems[] = ['label' => 'Signup', 'url' => ['site/signup']];
$menuItems[] = ['label' => 'Login', 'url' => ['site/login']];
} else {
$menuItems[] = [
'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
'url' => ['/site/logout'],
'url' => ['site/logout'],
'linkOptions' => ['data-method' => 'post']
];
}

View File

@ -2,10 +2,10 @@
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \common\models\LoginForm */
/* @var $model \api\models\LoginForm */
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
$this->title = 'Login';
$this->params['breadcrumbs'][] = $this->title;

View File

@ -0,0 +1,15 @@
<?php
namespace api\components;
/**
* Этот класс был использован для изначальной генерации паролей на Ely.by и сейчас должен быть планомерно выпилен
* с проекта с целью заменить этот алгоритм каким-нибудь посерьёзнее.
*/
class UserPass {
public static function make ($email, $pass) {
return md5($pass . md5(strtolower($email)));
}
}

View File

@ -13,5 +13,8 @@ return [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '@common/mail',
],
'security' => [
'passwordHashStrategy' => 'password_hash',
]
],
];

View File

@ -2,12 +2,12 @@
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $user common\models\User */
/* @var $user common\models\Account */
$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);
?>
<div class="password-reset">
<p>Hello <?= Html::encode($user->username) ?>,</p>
<p>Hello <?= Html::encode($user->email) ?>,</p>
<p>Follow the link below to reset your password:</p>

View File

@ -1,11 +1,11 @@
<?php
/* @var $this yii\web\View */
/* @var $user common\models\User */
/* @var $user common\models\Account */
$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);
?>
Hello <?= $user->username ?>,
Hello <?= $user->email ?>,
Follow the link below to reset your password:

208
common/models/Account.php Normal file
View File

@ -0,0 +1,208 @@
<?php
namespace common\models;
use api\components\UserPass;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
/**
* Поля модели:
* @property integer $id
* @property string $uuid
* @property string $password_hash
* @property integer $password_hash_strategy
* @property string $password_reset_token
* @property string $email
* @property string $auth_key
* @property integer $status
* @property integer $created_at
* @property integer $updated_at
*
* Геттеры-сеттеры:
* @property string $password пароль пользователя (только для записи)
*/
class Account extends ActiveRecord implements IdentityInterface {
const STATUS_DELETED = -10;
const STATUS_REGISTERED = 0;
const STATUS_ACTIVE = 10;
const PASS_HASH_STRATEGY_OLD_ELY = 0;
const PASS_HASH_STRATEGY_YII2 = 1;
public static function tableName() {
return '{{%accounts}}';
}
public function behaviors() {
return [
TimestampBehavior::className(),
];
}
public function rules() {
return [
];
}
/**
* @inheritdoc
*/
public static function findIdentity($id) {
return static::findOne(['id' => $id]);
}
/**
* @inheritdoc
*/
public static function findIdentityByAccessToken($token, $type = null) {
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}
/**
* @param string $email
* @return static|null
*/
public static function findByEmail($email) {
return static::findOne(['email' => $email]);
}
/**
* Finds user by password reset token
*
* @param string $token password reset token
*
* @return static|null
*
* TODO: этот метод нужно убрать из базовой модели
*/
public static function findByPasswordResetToken($token) {
if (!static::isPasswordResetTokenValid($token)) {
return null;
}
return static::findOne([
'password_reset_token' => $token,
'status' => self::STATUS_ACTIVE,
]);
}
/**
* Finds out if password reset token is valid
*
* @param string $token password reset token
*
* @return boolean
*
* TODO: этот метод нужно убрать из базовой модели
*/
public static function isPasswordResetTokenValid($token) {
if (empty($token)) {
return false;
}
$timestamp = (int) substr($token, strrpos($token, '_') + 1);
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
return $timestamp + $expire >= time();
}
/**
* @inheritdoc
*/
public function getId() {
return $this->getPrimaryKey();
}
/**
* @inheritdoc
*/
public function getAuthKey() {
return $this->auth_key;
}
/**
* @inheritdoc
*/
public function validateAuthKey($authKey) {
return $this->getAuthKey() === $authKey;
}
/**
* Validates password
*
* @param string $password password to validate
* @param integer $passwordHashStrategy
*
* @return bool if password provided is valid for current user
* @throws InvalidConfigException
*/
public function validatePassword($password, $passwordHashStrategy = NULL) {
if ($passwordHashStrategy === NULL) {
$passwordHashStrategy = $this->password_hash_strategy;
}
switch($passwordHashStrategy) {
case self::PASS_HASH_STRATEGY_OLD_ELY:
$hashedPass = UserPass::make($this->email, $password);
return $hashedPass === $this->password_hash;
case self::PASS_HASH_STRATEGY_YII2:
return Yii::$app->security->validatePassword($password, $this->password_hash);
default:
throw new InvalidConfigException('You must set valid password_hash_strategy before you can validate password');
}
}
/**
* @param string $password
* @throws InvalidConfigException
*/
public function setPassword($password) {
switch($this->password_hash_strategy) {
case self::PASS_HASH_STRATEGY_OLD_ELY:
$password = UserPass::make($this->email, $password);
break;
case self::PASS_HASH_STRATEGY_YII2:
$password = Yii::$app->security->generatePasswordHash($password);
break;
default:
throw new InvalidConfigException('You must specify password_hash_strategy before you can set password');
}
$this->password_hash = $password;
}
/**
* Generates "remember me" authentication key
*/
public function generateAuthKey() {
$this->auth_key = Yii::$app->security->generateRandomString();
}
/**
* Generates new password reset token
*
* TODO: этот метод нужно отсюда убрать
*/
public function generatePasswordResetToken() {
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
}
/**
* Removes password reset token
*
* TODO: этот метод нужно отсюда убрать
*/
public function removePasswordResetToken() {
$this->password_reset_token = null;
}
}

View File

@ -1,188 +0,0 @@
<?php
namespace common\models;
use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
/**
* User model
*
* @property integer $id
* @property string $username
* @property string $password_hash
* @property string $password_reset_token
* @property string $email
* @property string $auth_key
* @property integer $status
* @property integer $created_at
* @property integer $updated_at
* @property string $password write-only password
*/
class User extends ActiveRecord implements IdentityInterface
{
const STATUS_DELETED = 0;
const STATUS_ACTIVE = 10;
/**
* @inheritdoc
*/
public static function tableName()
{
return '{{%user}}';
}
/**
* @inheritdoc
*/
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
/**
* @inheritdoc
*/
public function rules()
{
return [
['status', 'default', 'value' => self::STATUS_ACTIVE],
['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
];
}
/**
* @inheritdoc
*/
public static function findIdentity($id)
{
return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
}
/**
* @inheritdoc
*/
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}
/**
* Finds user by username
*
* @param string $username
* @return static|null
*/
public static function findByUsername($username)
{
return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]);
}
/**
* Finds user by password reset token
*
* @param string $token password reset token
* @return static|null
*/
public static function findByPasswordResetToken($token)
{
if (!static::isPasswordResetTokenValid($token)) {
return null;
}
return static::findOne([
'password_reset_token' => $token,
'status' => self::STATUS_ACTIVE,
]);
}
/**
* Finds out if password reset token is valid
*
* @param string $token password reset token
* @return boolean
*/
public static function isPasswordResetTokenValid($token)
{
if (empty($token)) {
return false;
}
$timestamp = (int) substr($token, strrpos($token, '_') + 1);
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
return $timestamp + $expire >= time();
}
/**
* @inheritdoc
*/
public function getId()
{
return $this->getPrimaryKey();
}
/**
* @inheritdoc
*/
public function getAuthKey()
{
return $this->auth_key;
}
/**
* @inheritdoc
*/
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
/**
* Validates password
*
* @param string $password password to validate
* @return boolean if password provided is valid for current user
*/
public function validatePassword($password)
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
/**
* Generates password hash from password and sets it to the model
*
* @param string $password
*/
public function setPassword($password)
{
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
}
/**
* Generates "remember me" authentication key
*/
public function generateAuthKey()
{
$this->auth_key = Yii::$app->security->generateRandomString();
}
/**
* Generates new password reset token
*/
public function generatePasswordResetToken()
{
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
}
/**
* Removes password reset token
*/
public function removePasswordResetToken()
{
$this->password_reset_token = null;
}
}

View File

@ -17,7 +17,8 @@
"php": ">=5.4.0",
"yiisoft/yii2": ">=2.0.6",
"yiisoft/yii2-bootstrap": "*",
"yiisoft/yii2-swiftmailer": "*"
"yiisoft/yii2-swiftmailer": "*",
"ramsey/uuid": "^3.1"
},
"require-dev": {
"yiisoft/yii2-codeception": "*",

View File

@ -21,5 +21,11 @@ return [
],
],
],
'controllerMap' => [
'migrate' => [
'class' => 'yii\console\controllers\MigrateController',
'templateFile' => '@console/views/migration.php',
],
],
'params' => $params,
];

21
console/db/Migration.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace console\db;
use yii\db\Migration as YiiMigration;
/**
* @property string $tableOptions
*/
class Migration extends YiiMigration {
public function getTableOptions($engine = 'InnoDB') {
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
// http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=' . $engine;
}
return $tableOptions;
}
}

View File

@ -1,34 +1,26 @@
<?php
use yii\db\Schema;
use yii\db\Migration;
use console\db\Migration;
class m130524_201442_init extends Migration
{
public function up()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
// http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
}
class m130524_201442_init extends Migration {
$this->createTable('{{%user}}', [
public function up() {
$this->createTable('{{%accounts}}', [
'id' => $this->primaryKey(),
'username' => $this->string()->notNull()->unique(),
'auth_key' => $this->string(32)->notNull(),
'password_hash' => $this->string()->notNull(),
'password_reset_token' => $this->string()->unique(),
'uuid' => $this->string(36)->notNull(),
'email' => $this->string()->notNull()->unique(),
'status' => $this->smallInteger()->notNull()->defaultValue(10),
'password_hash' => $this->string()->notNull(),
'password_hash_strategy' => $this->smallInteger()->notNull(),
'password_reset_token' => $this->string()->unique(),
'status' => $this->smallInteger()->notNull()->defaultValue(0),
'auth_key' => $this->string(32)->notNull(),
'created_at' => $this->integer()->notNull(),
'updated_at' => $this->integer()->notNull(),
], $tableOptions);
], $this->tableOptions);
}
public function down()
{
$this->dropTable('{{%user}}');
public function down() {
$this->dropTable('{{%accounts}}');
}
}

View File

@ -0,0 +1,19 @@
<?php
/* @var $className string the new migration class name */
echo "<?php\n";
?>
use console\db\Migration;
class <?= $className ?> extends Migration {
public function safeUp() {
}
public function safeDown() {
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace tests\codeception\api\_pages;
use yii\codeception\BasePage;
/**
* Represents loging page
* @property \tests\codeception\api\FunctionalTester $actor
*/
class LoginRoute extends BasePage {
public $route = 'login/authentication/login-info';
public function login($email, $password) {
$this->actor->sendPOST($this->getUrl(), [
'email' => $email,
'password' => $password,
]);
}
}

View File

@ -1,13 +1,13 @@
<?php
use tests\codeception\api\_pages\LoginRoute;
use tests\codeception\api\AcceptanceTester;
use tests\codeception\common\_pages\LoginPage;
/* @var $scenario Codeception\Scenario */
$I = new AcceptanceTester($scenario);
$I->wantTo('ensure login page works');
$loginPage = LoginPage::openBy($I);
$loginPage = LoginRoute::openBy($I);
$I->amGoingTo('submit login form with no data');
$loginPage->login('', '');

View File

@ -3,7 +3,7 @@
namespace tests\codeception\api\acceptance;
use tests\codeception\api\_pages\SignupPage;
use common\models\User;
use common\models\Account;
class SignupCest
{
@ -22,7 +22,7 @@ class SignupCest
*/
public function _after($event)
{
User::deleteAll([
Account::deleteAll([
'email' => 'tester.email@example.com',
'username' => 'tester',
]);

View File

@ -12,6 +12,7 @@ modules:
- Filesystem
- Yii2
- tests\codeception\common\_support\FixtureHelper
- REST
config:
Yii2:
configFile: '../config/api/functional.php'

View File

@ -1,23 +1,33 @@
<?php
use tests\codeception\api\_pages\LoginRoute;
use tests\codeception\api\FunctionalTester;
use tests\codeception\common\_pages\LoginPage;
/* @var $scenario Codeception\Scenario */
$I = new FunctionalTester($scenario);
$I->wantTo('ensure login page works');
$loginPage = LoginPage::openBy($I);
$loginPage = LoginRoute::openBy($I);
$I->amGoingTo('submit login form with no data');
$loginPage->login('', '');
$I->expectTo('see validations errors');
$I->see('Username cannot be blank.', '.help-block');
$I->see('Password cannot be blank.', '.help-block');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'email' => [
'error.email_required',
],
'password' => [
'error.password_required',
],
],
]);
/*
$I->amGoingTo('try to login with wrong credentials');
$I->expectTo('see validations errors');
$loginPage->login('admin', 'wrong');
$loginPage->login('', 'wrong');
$I->expectTo('see validations errors');
$I->see('Incorrect username or password.', '.help-block');
@ -27,3 +37,4 @@ $I->expectTo('see that user is logged');
$I->seeLink('Logout (erau)');
$I->dontSeeLink('Login');
$I->dontSeeLink('Signup');
*/

View File

@ -3,7 +3,7 @@
namespace tests\codeception\api\functional;
use tests\codeception\api\_pages\SignupPage;
use common\models\User;
use common\models\Account;
class SignupCest
{
@ -22,7 +22,7 @@ class SignupCest
*/
public function _after($event)
{
User::deleteAll([
Account::deleteAll([
'email' => 'tester.email@example.com',
'username' => 'tester',
]);

View File

@ -0,0 +1,15 @@
<?php
return [
[
'id' => 1,
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
'password_hash' => '$2y$13$CXT0Rkle1EMJ/c1l5bylL.EylfmQ39O5JlHJVFpNn618OUS1HwaIi', # password_0
'password_hash_strategy' => 1,
'password_reset_token' => NULL,
'email' => 'admin@ely.by',
'auth_key' => 'iwTNae9t34OmnK6l4vT4IeaTk-YWI2Rv',
'status' => 10,
'created_at' => 1451775316,
'updated_at' => 1451775316,
],
];

View File

@ -6,7 +6,7 @@ use Yii;
use tests\codeception\api\unit\DbTestCase;
use api\models\PasswordResetRequestForm;
use tests\codeception\common\fixtures\UserFixture;
use common\models\User;
use common\models\Account;
use Codeception\Specify;
class PasswordResetRequestFormTest extends DbTestCase
@ -54,7 +54,7 @@ class PasswordResetRequestFormTest extends DbTestCase
{
$model = new PasswordResetRequestForm();
$model->email = $this->user[0]['email'];
$user = User::findOne(['password_reset_token' => $this->user[0]['password_reset_token']]);
$user = Account::findOne(['password_reset_token' => $this->user[0]['password_reset_token']]);
expect('email sent', $model->sendEmail())->true();
expect('user has valid token', $user->password_reset_token)->notNull();

View File

@ -24,7 +24,6 @@ class SignupFormTest extends DbTestCase
$this->assertInstanceOf('common\models\User', $user, 'user should be valid');
expect('username should be correct', $user->username)->equals('some_username');
expect('email should be correct', $user->email)->equals('some_email@example.com');
expect('password should be correct', $user->validatePassword('some_password'))->true();
}

View File

@ -0,0 +1,124 @@
<?php
namespace tests\codeception\api\modules\login\models;
use api\modules\login\models\AuthenticationForm;
use Codeception\Specify;
use tests\codeception\api\unit\DbTestCase;
use tests\codeception\common\fixtures\AccountFixture;
use Yii;
class AuthenticationFormTest extends DbTestCase {
use Specify;
protected function tearDown() {
Yii::$app->user->logout();
parent::tearDown();
}
public function testValidateEmail() {
$model = new AuthenticationForm();
$this->specify('error.email_required expected if email is not set', function() use ($model) {
$model->validate(['email']);
expect($model->getErrors('email'))->equals(['error.email_required']);
});
$this->specify('error.email_invalid expected if email not correct', function() use ($model) {
$model->email = 'wrong-email-string';
$model->validate(['email']);
expect($model->getErrors('email'))->equals(['error.email_invalid']);
$model->email = 'wrong@email';
$model->validate(['email']);
expect($model->getErrors('email'))->equals(['error.email_invalid']);
});
$this->specify('error.email_not_exist expected if email not exists in database', function() use ($model) {
$model->email = 'not-exist@user.com';
$model->validate(['email']);
expect($model->getErrors('email'))->equals(['error.email_not_exist']);
});
$this->specify('no errors if email is correct and exists in database', function() use ($model) {
$model->email = 'admin@ely.by';
$model->validate(['email']);
expect($model->getErrors('email'))->isEmpty();
});
}
public function testValidatePassword() {
$model = new AuthenticationForm();
$this->specify('error.password_required expected if password is not set', function() use ($model) {
$model->validate(['password']);
expect($model->getErrors('password'))->equals(['error.password_required']);
});
$this->specify('error.password_incorrect expected if password not correct for passed email', function() use ($model) {
$model->email = 'non-exist@valid.mail';
$model->password = 'wrong-password';
$model->validate(['password']);
expect('if email incorrect, the error should be displayed in any case,', $model->getErrors('password'))
->equals(['error.password_incorrect']);
$model->email = 'admin@ely.by';
$model->password = 'wrong-password';
$model->validate(['password']);
expect($model->getErrors('password'))->equals(['error.password_incorrect']);
});
$this->specify('no errors if email and password is correct and exists in database', function() use ($model) {
$model->email = 'admin@ely.by';
$model->password = 'password_0';
$model->validate(['password']);
expect($model->getErrors('password'))->isEmpty();
});
}
public function testLoginNoUser() {
$model = new AuthenticationForm([
'email' => 'non-exist@valid.mail',
'password' => 'not_existing_password',
]);
$this->specify('user should not be able to login, when there is no identity', function () use ($model) {
expect('model should not login user', $model->login())->false();
expect('user should not be logged in', Yii::$app->user->isGuest)->true();
});
}
public function testLoginWrongPassword() {
$model = new AuthenticationForm([
'email' => 'admin@ely.by',
'password' => 'wrong_password',
]);
$this->specify('user should not be able to login with wrong password', function () use ($model) {
expect('model should not login user', $model->login())->false();
expect('error message should be set', $model->errors)->hasKey('password');
expect('user should not be logged in', Yii::$app->user->isGuest)->true();
});
}
public function testLoginCorrect() {
$model = new AuthenticationForm([
'email' => 'admin@ely.by',
'password' => 'password_0',
]);
$this->specify('user should be able to login with correct credentials', function () use ($model) {
expect('model should login user', $model->login())->true();
expect('error message should not be set', $model->errors)->hasntKey('password');
expect('user should be logged in', Yii::$app->user->isGuest)->false();
});
}
public function fixtures() {
return [
'user' => [
'class' => AccountFixture::className(),
'dataFile' => '@tests/codeception/api/unit/fixtures/data/models/accounts.php'
],
];
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace tests\codeception\common\_pages;
use yii\codeception\BasePage;
/**
* Represents loging page
* @property \codeception_api\AcceptanceTester|\codeception_api\FunctionalTester $actor
*/
class LoginPage extends BasePage
{
public $route = 'site/login';
/**
* @param string $username
* @param string $password
*/
public function login($username, $password)
{
$this->actor->fillField('input[name="LoginForm[username]"]', $username);
$this->actor->fillField('input[name="LoginForm[password]"]', $password);
$this->actor->click('login-button');
}
}

View File

@ -63,10 +63,10 @@ class FixtureHelper extends Module
public function fixtures()
{
return [
'user' => [
'class' => UserFixture::className(),
'dataFile' => '@tests/codeception/common/fixtures/data/init_login.php',
],
//'user' => [
// 'class' => UserFixture::className(),
// 'dataFile' => '@tests/codeception/common/fixtures/data/init_login.php',
//],
];
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace tests\codeception\common\fixtures;
use common\models\Account;
use yii\test\ActiveFixture;
class AccountFixture extends ActiveFixture {
public $modelClass = Account::class;
}

View File

@ -2,11 +2,11 @@
namespace tests\codeception\common\unit\models;
use Yii;
use tests\codeception\common\unit\DbTestCase;
use api\models\LoginForm;
use Codeception\Specify;
use common\models\LoginForm;
use tests\codeception\common\fixtures\UserFixture;
use tests\codeception\common\unit\DbTestCase;
use Yii;
/**
* Login form test

View File

@ -14,7 +14,7 @@ return [
],
'components' => [
'db' => [
'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_tests',
'dsn' => 'mysql:host=localhost;dbname=ely_accounts_test',
],
'mailer' => [
'useFileTransport' => true,