I'm using Yii2
and utilising their behaviors within my controllers.
I am building my own permissions system and because the permissions are rather complex I need to make use of a matchCallback.
Here is an example:
public function behaviors() {
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['view'],
'rules' => [
[
'allow' => true,
'actions' => ['view'],
'matchCallback' => function ($rule, $action) {
return Yii::$app->authManager->can($rule, $action);
}
],
// everything else is denied
],
],
];
}
Now, unfortunately the way the matchCallback
works is by returning true
or false
on if it should continue to execute the rule, rather than being able to return true or false of they are allowed or not.
So if I return false
that it shouldn't continue (and hence disallow them) then I am unable to customise the denyCallback
as it quits executing the rule.
Is there anyway I can customise the denyCallback
even if I return false
from the matchCallback
- or should I be handling my situation in a different way?
You can define denyCallback
as property of AccessControl
instead of defining it in AccessRule
. It will get called if allow
after rule check returns null. It has the same signature as denyCallback
in AccessRule
:
public function behaviors() {
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['view'],
'rules' => [
[
'allow' => true,
'actions' => ['view'],
'matchCallback' => function ($rule, $action) {
return Yii::$app->authManager->can($rule, $action);
}
],
'denyCallback' => function ($rule, $action){...}
// everything else is denied
],
],
];
}
As another option you can extend AccessRule
class and override allows()
method to return false instead of null when match check fails, and your rule's denyCallback
will be called then:
class MyAccessRule extends AccessRule
{
public function allows($action, $user, $request)
{
$allows = parent::allows($action, $user, $request);
if ($allows === null) {
return false;
} else {
return $allows;
}
}
}
matchCallback
only determines should rule be applied or not, and if matchCallback
returns true and other parameters are match(e.g. roles, verbs etc.), call to allows()
will return rule's allow
parameter true or false as you set it in configuration. And if matchCallback
returns false - allow
will be null and rule's denyCallback will not be called, but AccessControl denyCallback
will be called instead if it is set in configuration.
As you mentioned in the comments third option is to make allows()
return result of callback.
class MyAccessRule extends AccessRule
{
public $allowCallback;
public function allows($action, $user, $request)
{
if(!empty($this->allowCallback) {
return call_user_func($this->allowCallback);
}
$allows = parent::allows($action, $user, $request);
if ($allows === null) {
return false;
} else {
return $allows;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With