Permalink
Browse files

On CSRF error display a flash message on the originating page

...instead of taking the user to a confusing error page
- Also changes getSession() calls to just 'session'
- Adds several tests to bump up coverage
  • Loading branch information...
CorWatts committed Dec 5, 2018
1 parent 462ccf9 commit 425267a460f9d6e96d7ae51dd8fcbc3f50af54f8
@@ -0,0 +1,39 @@
<?php
namespace common\components;
use Yii;
class Controller extends \yii\web\Controller {
/**
* {@inheritdoc}
*/
public function beforeAction($action)
{
if ($this->enableCsrfValidation
&& Yii::$app->getErrorHandler()->exception === null
&& !Yii::$app->getRequest()->validateCsrfToken()) {
Yii::$app->session->setFlash('error', 'Your security token has expired. Please retry your submission.');
$this->redirect(Yii::$app->request->referrer ?: Yii::$app->homeUrl);
return false;
}
return parent::beforeAction($action);
}
/**
* {@inheritdoc}
*/
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
],
];
}
}
@@ -1,6 +1,6 @@
<?php
namespace site\classes;
namespace common\components;
use yii\helpers\Html;
@@ -12,7 +12,8 @@ class View extends \yii\web\View {
*/
public $json;
public function registerJson($array, $key = null, $options = 0, $depth = 512)
// this is the key new function here
public function registerJson(array $array, $key = null, $options = 0, $depth = 512)
{
$json = json_encode($array, $options, $depth);
$key = $key ?: md5($json);
@@ -14,5 +14,8 @@
'viewPath' => '@common/mail',
'useFileTransport' => true,
],
'request' => [
'class' => \common\tests\_support\MockRequest::class
]
]
];
@@ -65,7 +65,7 @@ public function login()
if($user->isVerified()) {
return Yii::$app->user->login($user, $this->rememberMe ? 3600 * 24 * 30 : 0);
} else {
Yii::$app->getSession()->setFlash('warning', 'You must verify your account before you can proceed. Please check your email inbox for a verification email and follow the instructions.');
Yii::$app->session->setFlash('warning', 'You must verify your account before you can proceed. Please check your email inbox for a verification email and follow the instructions.');
}
}
return false;
@@ -0,0 +1,16 @@
<?php
namespace common\tests\_support;
class MockRequest extends \yii\web\Request {
public $csrfValidationReturn = true;
public function setCsrfToken($token) {
$this->_csrfToken = $token;
}
public function validateCsrfToken($clientSuppliedToken = null) {
return $this->csrfValidationReturn;
}
}
@@ -0,0 +1,40 @@
<?php
namespace common\tests\unit\components;
use Yii;
use common\components\Controller;
/**
* Controller test
*/
class ControllerTest extends \Codeception\Test\Unit {
use \Codeception\Specify;
public function testActions() {
$controller = new Controller('test', 'common');
expect('actions should return an array of action settings', $this->assertEquals([
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
],
], $controller->actions()));
}
public function testBeforeAction() {
$controller = new Controller('test', 'common');
$controller->enableCsrfValidation = true;
expect("If CSRF token is valid and parent's beforeAction() returns true, return true", $this->assertTrue($controller->beforeAction('test')));
// this is a terrible way to test this
// look in common/config/test.php, we are setting the request class to be
// \common\tests\_support\MockRequest
Yii::$app->getRequest()->csrfValidationReturn = false;
expect("If CSRF token is NOT valid and parent's beforeAction() returns true, return false", $this->assertFalse($controller->beforeAction('test')));
}
}
@@ -0,0 +1,37 @@
<?php
namespace common\tests\unit\components;
use Yii;
use common\components\View;
/**
* View test
*/
class ViewTest extends \Codeception\Test\Unit {
use \Codeception\Specify;
public function testRegisterJson() {
$view = new View();
$key = 'test';
$data = [
'name' => 'user one',
'email' => 'userone@example.com'
];
$view->registerJson($data, $key);
expect("The supplied array should be encoded as JSON and set on the View obj", $this->assertEquals($data, json_decode($view->json[$view::POS_READY][$key], true)));
$view = new View();
$data = [
'name' => 'user one',
'email' => 'userone@example.com'
];
$view->registerJson($data);
$expected_key = md5(json_encode($data, true));
expect("If no key is supplied it should default to the md5 of the json_encoded data", $this->assertArrayHasKey($expected_key, $view->json[$view::POS_READY]));
expect("If no key is supplied it should default to the md5 of the json_encoded data", $this->assertEquals(json_encode($data, true), $view->json[$view::POS_READY][$expected_key]));
}
}
@@ -114,7 +114,7 @@ public function testLogin() {
$this->form->password = 'hunter2';
expect('login should return false if account is not verified', $this->assertFalse($this->form->login()));
expect('login should set a flash message if account is not verified', $this->assertNotEmpty(Yii::$app->getSession()->getFlash('warning', null, true)));
expect('login should set a flash message if account is not verified', $this->assertNotEmpty(Yii::$app->session->getFlash('warning', null, true)));
});
$this->specify('login should fail if credentials are not valid', function() {
@@ -123,7 +123,7 @@ public function testLogin() {
$this->form->email = 'hunter2@hunter2.com';
$this->form->password = 'hunter2';
expect('login should return false credentials are bad', $this->assertFalse($this->form->login()));
expect('login should NOT set a flash message if credentials are bad', $this->assertEmpty(Yii::$app->getSession()->getFlash('warning', null, true)));
expect('login should NOT set a flash message if credentials are bad', $this->assertEmpty(Yii::$app->session->getFlash('warning', null, true)));
});
}
}
@@ -63,7 +63,7 @@
],
],
'view' => [
'class' => site\classes\View::class
'class' => common\components\View::class
]
],
'params' => $params,
@@ -11,12 +11,12 @@
use yii\di\Container;
use yii\base\InvalidParamException;
use yii\web\BadRequestHttpException;
use yii\web\Controller;
use common\components\Controller;
use yii\filters\VerbFilter;
use common\components\AccessControl;
use yii\helpers\ArrayHelper as AH;
class CheckinController extends \yii\web\Controller
class CheckinController extends Controller
{
public function behaviors()
{
@@ -4,7 +4,7 @@
use Yii;
use common\models\Question;
use yii\web\Controller;
use common\components\Controller;
use yii\filters\VerbFilter;
use common\components\AccessControl;
use League\Csv\Writer;
@@ -42,21 +42,6 @@ public function behaviors() {
];
}
/**
* @inheritdoc
*/
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
],
];
}
public function actionIndex() {
$editProfileForm = Yii::$container->get(\site\models\EditProfileForm::class, [Yii::$app->user->identity]);
$changePasswordForm = Yii::$container->get(\site\models\ChangePasswordForm::class, [Yii::$app->user->identity]);
@@ -73,7 +58,7 @@ public function actionIndex() {
if ($editProfileForm->load(Yii::$app->request->post())) {
$saved_user = $editProfileForm->saveProfile();
if($saved_user) {
Yii::$app->getSession()->setFlash('success', 'New profile data saved!');
Yii::$app->session->setFlash('success', 'New profile data saved!');
}
}
@@ -93,7 +78,7 @@ public function actionDeleteAccount() {
if($model->deleteAccount()) {
$this->redirect(['site/index']);
} else {
Yii::$app->getSession()->setFlash('error', 'Wrong password!');
Yii::$app->session->setFlash('error', 'Wrong password!');
}
}
@@ -105,9 +90,9 @@ public function actionChangePassword() {
if ($model->load(Yii::$app->request->post())) {
if($model->validate() && $model->changePassword()) {
Yii::$app->getSession()->setFlash('success', 'Password successfully changed');
Yii::$app->session->setFlash('success', 'Password successfully changed');
} else {
Yii::$app->getSession()->setFlash('error', 'Wrong password!');
Yii::$app->session->setFlash('error', 'Wrong password!');
}
}
@@ -119,7 +104,7 @@ public function actionRequestChangeEmail() {
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$model->changeEmail();
Yii::$app->getSession()->setFlash('success', "We've sent an email to your requested email address to confirm. Please click on the verification link to continue.");
Yii::$app->session->setFlash('success', "We've sent an email to your requested email address to confirm. Please click on the verification link to continue.");
}
$this->redirect(['profile/index']);
@@ -4,7 +4,7 @@
use Yii;
use yii\web\BadRequestHttpException;
use yii\web\Controller;
use common\components\Controller;
use yii\filters\VerbFilter;
use common\components\AccessControl;
@@ -47,21 +47,6 @@ public function behaviors()
];
}
/**
* @inheritdoc
*/
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
],
];
}
public function actionIndex()
{
return $this->render('index');
@@ -139,7 +124,7 @@ public function actionSignup()
$model = Yii::$container->get(\site\models\SignupForm::class);
if($model->load(Yii::$app->request->post()) && $model->validate()) {
$model->signup();
Yii::$app->getSession()->setFlash('success', 'We have sent a verification email to the email address you provided. Please check your inbox and follow the instructions to verify your account.');
Yii::$app->session->setFlash('success', 'We have sent a verification email to the email address you provided. Please check your inbox and follow the instructions to verify your account.');
return $this->redirect('/',302);
}
@@ -157,7 +142,7 @@ public function actionRequestPasswordReset()
Yii::warning("$ip has tried to reset the password for ".$model->email);
}
Yii::$app->getSession()->setFlash('success', 'If there is an account with the submitted email address you will receive further instructions in your email inbox.');
Yii::$app->session->setFlash('success', 'If there is an account with the submitted email address you will receive further instructions in your email inbox.');
return $this->goHome();
}
@@ -177,7 +162,7 @@ public function actionResetPassword($token)
if ($model->load(Yii::$app->request->post())
&& $model->validate()
&& $model->resetPassword()) {
Yii::$app->getSession()->setFlash('success', 'New password was saved.');
Yii::$app->session->setFlash('success', 'New password was saved.');
return $this->goHome();
}
@@ -199,12 +184,12 @@ public function actionVerifyEmail($token)
}
if($user->isTokenConfirmed($user->verify_email_token)) {
Yii::$app->getSession()->setFlash('success', 'Your account has already been verified. Please log in.');
Yii::$app->session->setFlash('success', 'Your account has already been verified. Please log in.');
return $this->redirect('/login',302);
} else if (Yii::$app->getUser()->login($user)) {
$user->confirmVerifyEmailToken();
$user->save();
Yii::$app->getSession()->setFlash('success', 'Your account has been verified. Please continue with your check-in.');
Yii::$app->session->setFlash('success', 'Your account has been verified. Please continue with your check-in.');
return $this->redirect('/welcome',302);
}
}
Oops, something went wrong.

0 comments on commit 425267a

Please sign in to comment.