diff --git a/api/models/base/PasswordProtectedForm.php b/api/models/base/PasswordProtectedForm.php deleted file mode 100644 index da82ba0..0000000 --- a/api/models/base/PasswordProtectedForm.php +++ /dev/null @@ -1,31 +0,0 @@ - E::PASSWORD_REQUIRED], - [['password'], 'validatePassword'], - ]; - } - - public function validatePassword() { - if (!$this->getAccount()->validatePassword($this->password)) { - $this->addError('password', E::PASSWORD_INVALID); - } - } - - /** - * @return \common\models\Account - */ - protected function getAccount() { - return Yii::$app->user->identity; - } - -} diff --git a/api/models/profile/ChangeEmail/InitStateForm.php b/api/models/profile/ChangeEmail/InitStateForm.php index fbc5410..fd14bba 100644 --- a/api/models/profile/ChangeEmail/InitStateForm.php +++ b/api/models/profile/ChangeEmail/InitStateForm.php @@ -1,7 +1,8 @@ $this->account], + ]; } public function validateFrequency($attribute) { @@ -90,7 +94,6 @@ class InitStateForm extends PasswordProtectedForm { } public function sendCode(EmailActivation $code) { - /** @var \yii\swiftmailer\Mailer $mailer */ $mailer = Yii::$app->mailer; $fromEmail = Yii::$app->params['fromEmail']; if (!$fromEmail) { @@ -98,7 +101,6 @@ class InitStateForm extends PasswordProtectedForm { } $acceptor = $code->account; - /** @var \yii\swiftmailer\Message $message */ $message = $mailer->compose([ 'html' => '@app/mails/current-email-confirmation-html', 'text' => '@app/mails/current-email-confirmation-text', diff --git a/api/models/profile/ChangePasswordForm.php b/api/models/profile/ChangePasswordForm.php index d4d0cb6..a8edc50 100644 --- a/api/models/profile/ChangePasswordForm.php +++ b/api/models/profile/ChangePasswordForm.php @@ -1,7 +1,8 @@ $this->_account], ]); } @@ -49,7 +53,8 @@ class ChangePasswordForm extends PasswordProtectedForm { } /** - * @return boolean + * @return bool + * @throws ErrorException */ public function changePassword() : bool { if (!$this->validate()) { diff --git a/api/models/profile/ChangeUsernameForm.php b/api/models/profile/ChangeUsernameForm.php index ae42fe8..c72b59b 100644 --- a/api/models/profile/ChangeUsernameForm.php +++ b/api/models/profile/ChangeUsernameForm.php @@ -1,7 +1,9 @@ Error::USERNAME_REQUIRED], ['username', 'validateUsername'], - ]); + ['password', PasswordRequiredValidator::class], + ]; } public function validateUsername($attribute) { @@ -84,4 +88,8 @@ class ChangeUsernameForm extends PasswordProtectedForm { Amqp::sendToEventsExchange('accounts.username-changed', $message); } + protected function getAccount() : AccountIdentity { + return Yii::$app->user->identity; + } + } diff --git a/api/validators/PasswordRequiredValidator.php b/api/validators/PasswordRequiredValidator.php new file mode 100644 index 0000000..bfdf6e3 --- /dev/null +++ b/api/validators/PasswordRequiredValidator.php @@ -0,0 +1,45 @@ +account === null) { + $this->account = Yii::$app->user->identity; + } + + if (!$this->account instanceof Account) { + throw new InvalidConfigException('account should be instance of ' . Account::class); + } + } + + protected function validateValue($value) { + if (empty($value)) { + return [E::PASSWORD_REQUIRED, []]; + } + + if ($this->account->validatePassword($value) === false) { + return [E::PASSWORD_INVALID, []]; + } + + return null; + } + +} diff --git a/common/models/Account.php b/common/models/Account.php index 944430a..98b61c4 100644 --- a/common/models/Account.php +++ b/common/models/Account.php @@ -89,7 +89,7 @@ class Account extends ActiveRecord { * @return bool if password provided is valid for current user * @throws InvalidConfigException */ - public function validatePassword($password, $passwordHashStrategy = NULL) { + public function validatePassword($password, $passwordHashStrategy = NULL) : bool { if ($passwordHashStrategy === NULL) { $passwordHashStrategy = $this->password_hash_strategy; } diff --git a/tests/codeception/api/unit/models/base/BasePasswordProtectedFormTest.php b/tests/codeception/api/unit/models/base/BasePasswordProtectedFormTest.php deleted file mode 100644 index 2256163..0000000 --- a/tests/codeception/api/unit/models/base/BasePasswordProtectedFormTest.php +++ /dev/null @@ -1,38 +0,0 @@ -specify('error.password_invalid on passing invalid account password', function() { - $model = new DummyBasePasswordProtectedForm(); - $model->password = 'some-invalid-password'; - $model->validatePassword(); - expect($model->getErrors('password'))->equals(['error.password_invalid']); - }); - - $this->specify('no errors on passing valid account password', function() { - $model = new DummyBasePasswordProtectedForm(); - $model->password = 'password_0'; - $model->validatePassword(); - expect($model->getErrors('password'))->isEmpty(); - }); - } - -} - -class DummyBasePasswordProtectedForm extends PasswordProtectedForm { - - protected function getAccount() { - return new Account([ - 'password' => 'password_0', - ]); - } - -} diff --git a/tests/codeception/api/unit/models/profile/ChangeUsernameFormTest.php b/tests/codeception/api/unit/models/profile/ChangeUsernameFormTest.php index 72ab52e..71ad483 100644 --- a/tests/codeception/api/unit/models/profile/ChangeUsernameFormTest.php +++ b/tests/codeception/api/unit/models/profile/ChangeUsernameFormTest.php @@ -1,6 +1,7 @@ getAccountId()); + Yii::$app->user->setIdentity($account); + } + public function testChange() { $this->specify('successfully change username to new one', function() { - $model = $this->createModel([ + $model = new ChangeUsernameForm([ 'password' => 'password_0', 'username' => 'my_new_nickname', ]); @@ -36,7 +44,7 @@ class ChangeUsernameFormTest extends DbTestCase { public function testChangeWithoutChange() { $this->specify('no new UsernameHistory record, if we don\'t change nickname', function() { - $model = $this->createModel([ + $model = new ChangeUsernameForm([ 'password' => 'password_0', 'username' => $this->accounts['admin']['username'], ]); @@ -53,7 +61,7 @@ class ChangeUsernameFormTest extends DbTestCase { public function testChangeCase() { $this->specify('username should change, if we change case of some letters', function() { $newUsername = mb_strtoupper($this->accounts['admin']['username']); - $model = $this->createModel([ + $model = new ChangeUsernameForm([ 'password' => 'password_0', 'username' => $newUsername, ]); @@ -65,7 +73,7 @@ class ChangeUsernameFormTest extends DbTestCase { public function testValidateUsername() { $this->specify('error.username_not_available expected if username is already taken', function() { - $model = $this->createModel([ + $model = new ChangeUsernameForm([ 'password' => 'password_0', 'username' => 'Jon', ]); @@ -74,7 +82,7 @@ class ChangeUsernameFormTest extends DbTestCase { }); $this->specify('error.username_not_available is NOT expected if username is already taken by CURRENT user', function() { - $model = $this->createModel([ + $model = new ChangeUsernameForm([ 'password' => 'password_0', 'username' => $this->accounts['admin']['username'], ]); @@ -84,27 +92,12 @@ class ChangeUsernameFormTest extends DbTestCase { } public function testCreateTask() { - $model = $this->createModel(); + $model = new ChangeUsernameForm(); $model->createEventTask('1', 'test1', 'test'); // TODO: у меня пока нет идей о том, чтобы это как-то успешно протестировать, увы // но по крайней мере можно убедиться, что оно не падает где-то на этом шаге } - private function createModel(array $params = []) : ChangeUsernameForm { - /** @noinspection PhpUnusedLocalVariableInspection */ - $params = array_merge($params, [ - 'accountId' => $this->getAccountId(), - ]); - - return new class($params) extends ChangeUsernameForm { - public $accountId; - - protected function getAccount() { - return Account::findOne($this->accountId); - } - }; - } - private function getAccountId() { return $this->accounts['admin']['id']; } diff --git a/tests/codeception/api/unit/validators/PasswordRequiredValidatorTest.php b/tests/codeception/api/unit/validators/PasswordRequiredValidatorTest.php new file mode 100644 index 0000000..471c7ab --- /dev/null +++ b/tests/codeception/api/unit/validators/PasswordRequiredValidatorTest.php @@ -0,0 +1,32 @@ + '12345678']); + $this->specify('get error.password_required if password is empty', function () use ($account) { + $model = new PasswordRequiredValidator(['account' => $account]); + expect($this->callProtected($model, 'validateValue', ''))->equals(['error.password_required', []]); + }); + + $this->specify('get error.password_invalid if password is incorrect', function () use ($account) { + $model = new PasswordRequiredValidator(['account' => $account]); + expect($this->callProtected($model, 'validateValue', '87654321'))->equals(['error.password_invalid', []]); + }); + + $this->specify('no errors, if password is correct for provided account', function () use ($account) { + $model = new PasswordRequiredValidator(['account' => $account]); + expect($this->callProtected($model, 'validateValue', '12345678'))->null(); + }); + } + +}