I am using the default captcha implementation of the yii2 advanced framework. I have a problem: I want to change my captcha code every time I refresh a page but when I refresh the page my captcha code does not change.
The most correct solution will be to create your own CaptchaAction, that extends yii\captcha\CaptchaAction and override the run() method as follows:
namespace app\actions; // Change to your own
class CaptchaAction extends yii\captcha\CaptchaAction {
public $autoRegenerate = true;
public function run()
{
if ($this->autoRegenerate && Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) === null) {
$this->setHttpHeaders();
Yii::$app->response->format = Response::FORMAT_RAW;
return $this->renderImage($this->getVerifyCode(true));
}
return parent::run();
}
}
try this
<script>
window.onload = hello;
function hello()
{
document.getElementById('loginform-captcha-image').click();
}
</script>
because you have set YII_ENV to TEST like this defined('YII_ENV') or define('YII_ENV', 'test'); change it to defined('YII_ENV') or define('YII_ENV', 'prod');
I found a dirty way round this - simply trigger the click event when the page loads. Add this code at the very end of your view file, after the end of the form;
$js = <<<JS
$('#loginform-captcha-image').trigger('click');
JS;
$this->registerJs($js, $this::POS_READY);
It's not very pretty, but it works and it's the only way I've found to get aroun d this problem, which has also plagued my own sites.
In your controller, just unset the session of captcha:
session_start();
unset($_SESSION["__captcha/panel/panel-auth/captcha"]);
unset($_SESSION["__captcha/panel/panel-auth/captchacount"]);
This worked for me
$(document).ready(function(){
setTimeout(() => {
$("#form-captcha-image").click();
}, 100);
});
Update Your CaptchaAction as
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => null,
],
];
}
Read Fixed Verify Code
IF fixedVerifyCode if set then captcha is same as value setted in fixedVerifyCode
// code from yii\captcha\CaptchaAction in Yii2
public function getVerifyCode($regenerate = false)
{
if ($this->fixedVerifyCode !== null) {
return $this->fixedVerifyCode;
}
$session = Yii::$app->getSession();
$session->open();
$name = $this->getSessionKey();
if ($session[$name] === null || $regenerate) {
$session[$name] = $this->generateVerifyCode();
$session[$name . 'count'] = 1;
}
return $session[$name];
}
Related
I'm completely lost as to why this is happening, and it happens about 50% of the time.
I have a check to see if a user exists by email and last name, and if they do, run some code. If the user doesn't exist, then create the user, and then run some code.
I've done various testing with dummy data, and even if a user doesn't exist, it first creates them, but then runs the code in the "if" block.
Here's what I have.
if (User::existsByEmailAndLastName($params->email, $params->lastName)) {
var_dump('user already exists');
} else {
User::createNew($params);
var_dump("Creating a new user...");
}
And here are the respective methods:
public static function existsByEmailAndLastName($email, $lastName) {
return User::find()->where([
'email' => $email,
])->andWhere([
'last_name' => $lastName
])->one();
}
public static function createNew($params) {
$user = new User;
$user->first_name = $params->firstName;
$user->last_name = $params->lastName;
$user->email = $params->email;
$user->address = $params->address;
$user->address_2 = $params->address_2;
$user->city = $params->city;
$user->province = $params->province;
$user->country = $params->country;
$user->phone = $params->phone;
$user->postal_code = $params->postal_code;
return $user->insert();
}
I've tried flushing the cache. I've tried it with raw SQL queries using Yii::$app->db->createCommand(), but nothing seems to be working. I'm totally stumped.
Does anyone know why it would first create the user, and then do the check in the if statement?
Editing with controller code:
public function actionComplete()
{
if (Yii::$app->basket->isEmpty()) {
return $this->redirect('basket', 302);
}
$guest = Yii::$app->request->get('guest');
$params = new CompletePaymentForm;
$post = Yii::$app->request->post();
if ($this->userInfo || $guest) {
if ($params->load($post) && $params->validate()) {
if (!User::isEmailValid($params->email)) {
throw new UserException('Please provide a valid email.');
}
if (!User::existsByEmailAndLastName($params->email, $params->lastName)) {
User::createNew($params);
echo "creating new user";
} else {
echo "user already exists";
}
}
return $this->render('complete', [
'model' => $completeDonationForm
]);
}
return $this->render('complete-login-or-guest');
}
Here's the answer after multiple tries:
Passing an 'ajaxParam' parameters with the ActiveForm widget to define the name of the GET parameter that will be sent if the request is an ajax request. I named my parameter "ajax".
Here's what the beginning of the ActiveForm looks like:
$form = ActiveForm::begin([
'id' => 'complete-form',
'ajaxParam' => 'ajax'
])
And then I added this check in my controller:
if (Yii::$app->request->get('ajax') || Yii::$app->request->isAjax) {
return false;
}
It was an ajax issue, so thanks a bunch to Yupik for pointing me towards it (accepting his answer since it lead me here).
You can put validation like below in your model:
public function rules() { return [ [['email'], 'functionName'], [['lastname'], 'functionforlastName'], ];}
public function functionName($attribute, $params) {
$usercheck=User::find()->where(['email' => $email])->one();
if($usercheck)
{
$this->addError($attribute, 'Email already exists!');
}
}
and create/apply same function for lastname.
put in form fields email and lastname => ['enableAjaxValidation' => true]
In Create function in controller
use yii\web\Response;
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
else if ($model->load(Yii::$app->request->post()))
{
//place your code here
}
Add 'enableAjaxValidation' => false to your ActiveForm params in view. It happens because yii sends request to your action to validate this model, but it's not handled before your if statement.
To make login necessary for all controllers and actions I did as said in Yii2 require all Controller and Action to login and added the below code to web.php
'as beforeRequest' => [
'class' => 'yii\filters\AccessControl',
'rules' => [
[
'allow' => true,
'actions' => ['login', 'forgot'],
],
[
'allow' => true,
'roles' => ['#'],
],
],
'denyCallback' => function () {
return Yii::$app->response->redirect(['user/login']);
},
],
but the problem is that ALL other actions like Forgot password are redirected to login page, I want to exclude user/forgot route from the login required condition. please help!
Thanks
I know I'm 3-years late, but it could be useful for other people searching for this answer :)
In config/web.php
$config => [
/* ... */
'as AccessBehavior' => [
'class' => 'app\components\AccessBehavior',
'allowedRoutes' => [
'/auth/register',
'/auth/forgot',
'/auth/resend',
],
'redirectUri' => '/auth/login',
],
/* ... */
Then create a "components" folder in your root project and create a "components\AccessBehavior.php" file with the following code:
<?php
namespace app\components;
use Yii;
use yii\base\Behavior;
use yii\console\Controller;
use yii\helpers\Url;
class AccessBehavior extends Behavior
{
protected $redirectUri;
protected $allowedRoutes = [];
protected $allowedUrls = [];
public function setRedirectUri($uri)
{
$this->redirectUri = $uri;
}
public function setAllowedRoutes(array $routes)
{
if (count($routes)) {
foreach ($routes as $route) {
$this->allowedUrls[] = Url::to($route);
}
}
$this->allowedRoutes = $routes;
}
public function init()
{
if (empty($this->redirectUri)) {
$this->redirectUri = Yii::$app->getUser()->loginUrl;
}
}
private function removeParams()
{
//enabled pretty url
if (strpos(Yii::$app->getRequest()->url, "?") === false)
{
$requestUrl = explode('/', Yii::$app->getRequest()->url);
$params = array_values(Yii::$app->getRequest()->queryParams);
$result = implode('/', array_diff($requestUrl, $params));
}
else
{//not enabled pretty url
$result = explode("?", \Yii::$app->getRequest()->url);
}
return $result;
}
public function events()
{
return [Controller::EVENT_BEFORE_ACTION => 'beforeAction'];
}
public function beforeAction()
{
$requestUrl = $this->removeParams();
if (Yii::$app->user->isGuest)
{
if ($requestUrl !== Url::to($this->redirectUri) && !in_array($requestUrl, $this->allowedUrls))
{
Yii::$app->getResponse()->redirect($this->redirectUri)->send();
exit(0);
}
}
}
}
This code simply checks if user is logged and checks the route requested. If guest user is accessing to allowed routes (you can add allowed routes in the config) does nothing, else redirects user to the login page :)
In the code above, I set the dektrium prefix route as "auth". Of course in the allowed route, you have to set the route you actually use to make the user register, confirm, change password..
Haven't tested it but it should work.
'denyCallback'=>function() {
if($this->action->id == 'forgot')
return Yii::$app->response->redirect(['whatever/whatever']);
else
return Yii::$app->response->redirect(['user/login']);
},...
When i call this function first time then file downloaded but after refresh the page it show me some not understanding character on my browser screen
// Controller Code
public function actionDownload($id)
{
$model = $this->findModel($id);
$file ='../frontend/uploads/users/'.$model->image;
if(file_exists($file))
{
return Yii::$app->response->sendFile($file);
exit;
}
//Button Code
[
'attribute'=>'resume',
'label'=>'Resume',
'format'=>'raw',
'value'=>function($data)
{
if($data->resume != null)
{
// $url = Yii::$app->params['application_base'].'admin/user/download/'.$data->id;
return Html::a('Download', ['download','id'=> $data->id]);
}
else
{
return 'NA';
}
},
],
Try to omit a pjax amd use
return Html::a('Download', ['download','id'=> $data->id, 'data-pjax' => 0]);
instead of
return Html::a('Download', ['download','id'=> $data->id]);
I have disabled autoRedirect so I can do some extra jazz in the login method of my users controller and use a Session to send them back to where they came from.
class UsersController extends AppController
{
var $name = 'Users';
var $components = array('Session');
function beforeFilter()
{
parent::beforeFilter();
$this->Auth->allow(array('login','logout','admin_logout','admin_login'));
$this->Session->write('back_to', $this->referer());
}
/**
* Log in
*/
function admin_login ()
{
$this->set('title_for_layout', 'Log in – Admin —');
if(!(empty($this->data)) && $this->Auth->user())
{
$back_to = $this->Session->read('back_to');
if($back_to)
{
$this->redirect($back_to, null, true);
}
else
{
$this->redirect($this->Auth->redirect(), null, true);
}
}
if($this->Auth->user())
{
$this->redirect($this->Auth->redirect(), null, true);
}
}
So the idea is that if a user has the session (99.% of the time) then on submit of form it will send the user TO the previous page, if not then send to the default loginRedirect.
NOTE: by setting autoRedirect to false, CakePHP no longer uses the Auth.Redirect session! So the value stored there is not used by the app anymore and is intentional!
The problem I am having is that my app is ALWAYS sending the user to the dashboard because of the function below 'This one' comment in the code above. If I remove that function then the user is just sent BACK to the login form all the time BUT they will be logged in!
Can anyone help?
Thanks
UPDATE: here is my appcontroller code:
class AppController extends Controller
{
var $components = array('Auth','Session');
public function beforeFilter()
{
parent::beforeFilter();
$this->Auth->authorize = 'controller';
$this->Auth->autoRedirect = false;
$this->Auth->loginAction = array('controller'=>'users','action'=>'login','admin'=>true
);
$this->Auth->loginRedirect = array('admin'=>true,'controller' => 'dashboard', 'action' => 'index');
$this->Auth->logoutRedirect = array('admin'=>false,'controller' => 'pages', 'action' => 'display','home');
}
function isAuthorized()
{
return true;
}
}
You're not existing after redirection. Try changing your redirection signature to:
$this->redirect( $back_to, null, true );
The 2nd argument is a status code and the third is whether to stop processing the current action. This should prevent you from dropping down to the last redirection which I'm guessing is the one being executed.
Given our long comment "discussion" below, try tweaking your admin_login() method like this:
if(!(empty($this->data)) && $this->Auth->user()) {
$back_to = $this->Session->read('back_to');
if($back_to) {
$this->redirect($back_to, null, true);
}
else {
$this->redirect($this->Auth->redirect(), null, true);
}
}
else {
# Only write back_to when you arrive at the login page without data
$this->Session->write('back_to', $this->referer());
}
function login (){
if(!empty($this->data) && !$this->Auth->user()){
$this->Session->write('back_to', $this->referer());
}
remove that Session->write in your beforeFilter. And you don't need $this->Auth->allow(array('login','logout'));
if(!(empty($this->data)) && $this->Auth->user())
{
$back_to = $this->Session->read('back_to');
$auth_redirect = $this->Session->read('Auth.redirect');
if($auth_redirect)
{
$this->redirect($auth_redirect, null, true);
}
else if($back_to)
{
$this->redirect($back_to, null, true);
}
else
{
$this->redirect($this->Auth->redirect(), null, true);
}
}
else
{
$this->Session->write('back_to', $this->referer());
}
I am trying to use the isAuthorized() method to do a check for an admin flag, but the function never seems to be called. Even when I set the function to always return false, it allows any user. It just seems like it isn't being called.
Do I need to do something more than setting $this->Auth->authorize = 'controller' ?
from /app/app_controller.php
class AppController extends Controller
{
var $components = array('Auth');
function beforeFilter()
{
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'pages', 'display' => 'home');
$this->Auth->logoutRedirect = '/';
$this->Auth->authorize = 'controller';
$this->Auth->userScope = array('User.active' => 1);
}
function isAuthorized()
{
if (strpos($this->action, "admin_") != false)
{
if ($this->Auth->user('isAdmin') == '0')
{
return false;
}
}
return true;
}
}
You should check if you're overriding your Auth settings in your other controller.
First, to verify that isAuthorized() is being called, try putting a simple debug($this); die; in it.
If it is not dying, you're probably overriding it in some other controller (you're missing the parent::isAuthorized() call).
If it's not that, then you're probably doing that same thing with beforeFilter().
Additional to the Answer of dr Hannibal Lecter, there is another possible reason if you experience this problem (as i did ...):
If your controller is named tests_controller, the startUp method of the Auth-Component aborts without starting the authentication (at least in cakePHP 1.3.10 - haven't checked 2.x). So be sure that you never name a Controller tests_controller...
Excerpt from cake\libs\controller\components\auth.php
function startup(&$controller) {
$isErrorOrTests = (
strtolower($controller->name) == 'cakeerror' ||
(strtolower($controller->name) == 'tests' && Configure::read() > 0)
);
if ($isErrorOrTests) {
return true;
}
...
you need to make sure that 'Auth' is in the components array for that controller:
$this->components[] = 'Auth';
drop that in the constructor and it should work (unless, of course, it doesn't). Good luck!