将控制器操作限制为Yii2中的帖子创建者

时间:2016-08-30 03:24:06

标签: yii2

是否有一种简单的方法可以将控制器操作限制为帖子的所有者/创建者,而无需使用完整的RBAC?

现在我为每个控制器执行此操作:

public function actionUpdate( $id ) {
    $model = $this->findModel( $id );
    if ( $model->user_id != Yii::$app->user->identity->id ) {
        throw new NotFoundHttpException( 'The requested page does not exist.' );
    }
}

但我认为必须有更好的方法将某些控制器限制为创建正在编辑的$model的用户。

2 个答案:

答案 0 :(得分:3)

1)推荐的方法是使用RBAC和规则。在official docs中,根据专门章节对其进行了详细介绍。

检查作者ID是否与通过params传递的当前用户ID匹配的规则示例:

namespace app\rbac;

use yii\rbac\Rule;

/**
 * Checks if authorID matches user passed via params
 */
class AuthorRule extends Rule
{
    public $name = 'isAuthor';

    /**
     * @param string|integer $user the user ID.
     * @param Item $item the role or permission that this rule is associated with
     * @param array $params parameters passed to ManagerInterface::checkAccess().
     * @return boolean a value indicating whether the rule permits the role or permission it is associated with.
     */
    public function execute($user, $item, $params)
    {
        return isset($params['post']) ? $params['post']->createdBy == $user : false;
    }
}

然后你需要将它与现有权限联系起来(可以在迁移或扩展中完成):

$auth = Yii::$app->authManager;

// add the rule
$rule = new \app\rbac\AuthorRule;
$auth->add($rule);

// add the "updateOwnPost" permission and associate the rule with it.
$updateOwnPost = $auth->createPermission('updateOwnPost');
$updateOwnPost->description = 'Update own post';
$updateOwnPost->ruleName = $rule->name;
$auth->add($updateOwnPost);

// "updateOwnPost" will be used from "updatePost"
$auth->addChild($updateOwnPost, $updatePost);

// allow "author" to update their own posts
$auth->addChild($author, $updateOwnPost);

然后,您可以检查用户是否可以像这样更新帖子:

use yii\web\ForbiddenHttpException;
use Yii;

public function actionUpdate($id)
{
    $model = $this->findModel($id);
    if (!Yii::$app->user->can('updatePost', ['post' => $model])) {
        throw new ForbiddenHttpException('You are not allowed to edit this post');
    }

    ...
}

另请注意,如果您首先找到模型并且用户无权编辑它,那么逻辑上最好抛出403 Forbidden异常而不是404,因为它已找到,但不允许编辑

不要忘记在AccessControl行为中包含类似规则:

[
    'allow' => true,
    'actions' => ['update'],
    'roles' => ['@'],
],

这意味着此控制器的update操作只能由不包括访客的授权用户访问。

2)如果由于某种原因您不想使用RBAC,您可以使用您的方法:

use yii\web\ForbiddenHttpException;

public function actionUpdate($id)
{
    $model = $this->findModel($id);
    if ($model->user_id != Yii::$app->user->id ) {
        throw new ForbiddenHttpException('You are not allowed to edit this post.');
    }

    ...
}

要改进这一点,您可以通过将此逻辑移动到辅助方法来从此检查中抽象出来:

namespace app\posts\components;

use Yii;

class PostPermission
{
    /**
     * @param $model Post
     * @return boolean
     */
    public static function allowedToUpdate($model)
    {
        return $model->user_id = Yii:$app->user->id;
    }
}

然后这样称呼:

use app\posts\components\PostPermission;
use yii\web\ForbiddenHttpException;

if (!PostPermission::allowedToUpdate($model) {
    throw new ForbiddenHttpException('You are not allowed to edit this post.');
}

这只是一个示例,方法不必是静态的,您可以使用$model构建实例。

您可以直接在Post模型中创建方法,但最好不要使用此类逻辑污染模型。

3)我可以建议的另一个选择是在找到模型时将范围最初限制为当前用户:

use yii\web\NotFoundHttpException;

/**
 * @param integer $id
 * @return Post
 * @throws NotFoundHttpException
 */
protected function findModel($id)
{
    $model = Post::find(['id'=> $id, 'user_id' => Yii::$app->user->id])->one();
    if ($model) {
        return $model;
    } else {
        throw new NotFoundHttpException('This post does not exist.');
    }
}

这可以针对网站管理员进行改进:

use yii\web\NotFoundHttpException;

/**
 * @param integer $id
 * @return Post
 * @throws NotFoundHttpException
 */
protected function findModel($id)
{
    $query = Post::find()->where(['id' => $id]);
    if (!Yii::$app->user->is_admin) { // replace with your own check
        $query->andWhere(['user_id' => Yii::$app->user->id]);
    }
    $model = $query->one();
    if ($model) {
        return $model;
    } else {
        throw new NotFoundHttpException('This post does not exist.');
    }
}

然后你只写:

public function actionUpdate($id)
{
    $model = $this->findModel($id);
    ...
}

在这两种情况下(未找到模型且当前用户不允许编辑),将引发404 Not Found异常。从另一方面来说,没有任何问题,因为从技术上讲,这个模型不存在(因为他不是它的作者)。

答案 1 :(得分:0)

我们可以使用

  

AccessControlFilter

用于限制控制器操作而不是RBAC。如果只传递了denyCallback,则下面的代码将允许访问actionUpdate。

use yii\filters\AccessControl;

class SiteController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'only' => ['update','delete'],
                'rules' => [
                    [
                        'actions' => ['update'],
                        'allow' => false,
                        'denyCallback' => function ($rule, $action) { //PHP callable that should be called when this rule will deny the access.
                            //Write your logic here to deny the action
                            throw new \Exception('You are not allowed to access this page');
                        }
                    ],
                ],
            ],
        ];
    }

    public function actionUpdate()
    {
        return $this->render('update');
    }
}

供参考https://github.com/yiisoft/yii2/blob/master/docs/guide/security-authorization.md