Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

redirect Access rules with Yii

Tags:

php

yii

I am doing an app that requires authentication. In the index page of the app, I specified access rules like this

public function accessRules() {
    return array(
        array('deny',
            'actions'=>array('index','register','login','password'),
            'users'=>array('@'),
            ),
        array('allow',
                'users'=>array('*')
            ), 
        );
    }

At the first rule, the actions 'index','register','login' and 'password' are made unaccessible to authenticated users. However, I do not want to show this message

Unauthorized
You are not authorized to perform this action.

You do not have the proper credential to access this page.

If you think this is a server error, please contact the webmaster. 

...to the authenticated users when they try to access those actions. Instead, I want to redirect them to another page. It would be useful if i could do something like this at the first rule

array('redirect',
    'actions'=>array('index','register','login','password'),
    'users'=>array('@'),
    'url'=>array('home/index'),
    ),
like image 597
Hilmi Avatar asked Mar 21 '12 12:03

Hilmi


4 Answers

Since Yii v1.1.11 you can do the same thing with a callback and a closure, and just the default classes:

array('deny',
'actions'=>array('index','register','login','password'),
'users'=>array('@'),
'deniedCallback' => function() { Yii::app()->controller->redirect(array ('/home/index')); }
),
like image 65
Iain Avatar answered Oct 21 '22 04:10

Iain


They 'll make you an offer you can't refuse

Starting with Yii v1.1.11 CAccessRule defines the deniedCallback property that easily allows you to define a redirect when access is denied. I don't want to steal Iain Gray's thunder, so go upvote his answer (thanks to the commenter who alerted me to this as well).

The original answer follows.

Option 1: extend Yii to enable this functionality (correct)

To do this we will need to write our own classes to be used instead of CAccessRule and CAccessControlFilter. For CAccessRule we just need to add one additional property:

class MyAccessRule extends CAccessRule {
    public $redirect; // just add this property
}

For CAccessControlFilter we want to make it recognize the value of this property and act upon it. To do this, we need to override the preFilter method. Starting from the stock implementation, make a few changes:

class MyAccessControlFilter extends CAccessControlFilter {
    protected function preFilter($filterChain)
    {
        $app=Yii::app();
        $request=$app->getRequest();
        $user=$app->getUser();
        $verb=$request->getRequestType();
        $ip=$request->getUserHostAddress();

        foreach($this->getRules() as $rule)
        {
             if(($allow=$rule->isUserAllowed($user,
                                             $filterChain->controller,
                                             $filterChain->action,
                                             $ip,
                                             $verb))>0) // allowed
                 break;
             else if($allow<0) // denied
             {
                 // CODE CHANGED HERE
                 $request->redirect($app->createUrl($rule->redirect));
                 return false;
             }
        }

        return true;
    }
}

Then we also need to override the setRules method, to instruct the filter to use the MyAccessRule class instead of the standard CAccessRule. Again, we modify the stock implementation by changing the line

$r=new CAccessRule;

to read

$r=new MyAccessRule;

After creating these classes, we have to also inject them into Yii's pipeline. To do this, override filterAccessControl on the base controller class; again, taking the stock implementation as reference and making a small change:

public function filterAccessControl($filterChain)
{
        $filter=new MyAccessControlFilter;  // CHANGED THIS
        $filter->setRules($this->accessRules());
        $filter->filter($filterChain);
}

That's it! You can now take advantage of the extra functionality in any controller by supplying the new redirect parameter to the access control filters like this:

public function accessRules() {
    return array(
        array('deny',
              'actions'=>array('index','register','login','password'),
              'users'=>array('@'),
              'redirect'=>array('home/index'),
        ),
    );
}

Option 2: implement access control inside each action (to be avoided)

If you are not comfortable with subclassing Yii's core components, another option that I do not recommend is to embed both access control and redirection logic inside each controller action that you want to protect, or overriding the beforeAction method on your controllers to cover multiple actions from one location.

like image 38
Jon Avatar answered Oct 21 '22 06:10

Jon


this one worked for me since yii 1.1.11:

    array('deny',  // deny all users
            'users'=>array('*'),
            'deniedCallback' => $this->redirect('/')
        ),

or use a static method in a Class:

'deniedCallback' => array('ClassName', 'staticMethodName'),
like image 35
Sebastian Viereck Avatar answered Oct 21 '22 05:10

Sebastian Viereck


$request->redirect($app->createUrl($rule->redirect));

Should be:

if(is_array($rule->redirect) && isset ($rule->redirect[0])){
    $request->redirect($app->createUrl($rule->redirect[0]));
}
like image 23
Shlomi Avatar answered Oct 21 '22 05:10

Shlomi