【问题标题】:CakePHP: authorizing actions based on belongsTo relationshipsCakePHP:基于 belongsTo 关系授权动作
【发布时间】:2013-08-13 17:34:47
【问题描述】:

让我们保持简单。

一个项目只有两个模型:

  1. 用户(hasMany 项目)
  2. 项目(属于用户)

用户只能对他们拥有的项目执行操作。没有其他人的。

我知道如何手动检查登录用户是谁以及他/她是否拥有特定项目,但有没有更好、更全球化的方法来做到这一点?我正在寻找更多的 D.R.Y.不需要在多个操作中重复相同验证的方式。例如,是否有类似可能的配置设置...

Configure::write('Enforce_belongs_to', true);

...或者可能是 Auth 组件上的设置/选项。

也许这很疯狂,但我想我会问。

【问题讨论】:

  • 您现在如何执行该验证?
  • 你考虑过使用行为吗?
  • @Nunser。我将项目 ID 传递给 $this->Project->find('first'...) 并添加 Project.User.id = ### 其中 ### 是 Auth 组件返回的当前用户。因此,如果是其他人的项目,则不会返回任何项目。
  • @AyoAkinyemi 不确定你在提议什么。有兴趣了解更多。

标签: php cakephp authorization cakephp-2.0


【解决方案1】:

添加到Nunser 的答案,这里将是行为方式的一般概念。然后,您可以将其附加到适用的模型。

    class StrongBelongBehavior extends ModelBehavior
    {
       public function beforeFind(  Model $Model, $query = array() ) { 
         $query['conditions'] = array_merge( (array)$query['conditions'], array( $Model->alias.'.user_id' => CakeSession::read("Auth.User.id" ) );
         return $query;
       }

       public function beforeSave( Model $Model ) {
        $projectId = Hash::get( $Model->data, 'Poject.id' );
        if( $projectId ) {
           $Model->loadModel('UserProject'); // UserProject is a custom model
           $canEdit =  $Model->UserProject->projectIDExists( $projectId ); // returns true if projectId belongs to the current user
           if ( ! $canEdit  ) {
             return false;
           }
        }
       return true;
       }
     }

【讨论】:

    【解决方案2】:

    我不确定我要回答的是否是最好的 - 最干燥 - 几乎是脱水的方法,但这是我能想到的最简单的方法。

    在项目模型中,创建一个函数,该函数返回与用户关联的项目 ID 数组。

    class Project extends AppModel {
        public function getByUserId($userId) {
            $projectsArray = array();
    
            if ($userId != "valid")
               //do all the checks, if it's not null, numeric, if the id exists, etc
    
            $projects = $this->Project->find('all', array('conditions'=>
                            array('user_id'=>$userId)));
            if (!empty($projects)) {
                foreach($projects as $i => $project)
                    $projectsArray[] = $project['Project']['id'];
            }
            return $projectsArray;
        }
    }
    

    您在评论中提到了find('first'),但我假设您想要与用户相关的所有项目,而不仅仅是第一个。如果不是,那只是对该函数的简单修改。另外,我只是获取 ID,但您可能需要一个 $id=>$name_project 数组,由您决定。

    现在,我不知道您所说的“仅允许执行操作”是什么意思,是否只是限制编辑?或者如果项目不是他/她的,列表或视图应该被限制甚至不向用户显示?

    对于第一种情况,限制编辑,修改beforeSave

    public function beforeSave($options = array()) {
        if(!$this->id && !isset($this->data[$this->alias][$this->primaryKey])) {
            //INSERT
            //not doing anything
        } else {
            //UPDATE
            //check if project inside allowed projects array
            $allowed = $this->getByUserId(CakeSession::read("Auth.User.id"));
            if (!in_array($this->id, $allowed))
               return false; //or throw error and catch it in the controller
        }
         return true;
    }
    

    代码未经测试,但一般而言,您可以防止在更新记录之前编辑不属于“用户”的项目。我假设每个人都可以免费插入新项目。根据this post,除saveAll 之外的所有保存函数都首先通过此过滤器,因此您需要覆盖saveAll 函数并添加类似于beforeSave 中的验证(如答案中所述) .

    对于第二部分,过滤结果以便用户甚至不知道有其他项目而不是他/她的,更改beforeFindThe docs 谈论根据用户角色限制结果,所以我想我们走在正确的轨道上。

    public function beforeFind($queryData) {
        //force the condition
        $allowed = $this->getByUserId(CakeSession::read("Auth.User.id"));
        $queryData['conditions'][$this->alias.'.user_id'] = $allowed;
    
        return $queryData;
    }
    

    由于$allowed 数组只有 id 值,它的工作方式类似于IN 子句,但如果您更改该数组结构,请务必相应地修改这些函数。

    就是这样。我在这里考虑更基本的情况,编辑,查看,删除...... Ups,删除......也改变beforeDelete功能,以避免任何想要删除其他财产的邪恶用户。逻辑保持不变(检查项目id是否在允许的数组中,如果不是,则返回false或抛出错误),因此我不会在此处添加该函数的示例。但这是基本的东西。如果出于某种原因您希望在控制器中拥有允许的项目,请调用 beforeFilter 中的 getByUserId 函数并在那里处理该 ids 数组。您甚至可以将其存储在会话中,但您必须记住在添加或删除项目时维护该会话。

    如果您想要一个可以查看和编辑所有内容的超级管理员,只需在getByUserId 中添加一个条件来检查用户的角色,如果是管理员,则返回所有项目。

    另外,请记住:可能Project有很多......子项目(没有太多想象力),因此,与项目相关的用户可以添加子项目,但与之前相同的邪恶用户修改了子项目隐藏的project_id并对其进行编辑。在这种情况下,我建议您还在子项目中添加一个验证,以避免对与项目相关的模型进行非他的操作。如果你有安全组件并且编辑和删除操作只能通过表单来实现,这是一件小事,因为安全组件使用得当可以避免表单篡改。但请考虑一下您是否还需要验证“子项目”实例。

    正如Ayo Akinyemi 提到的,您可以将所有这些用作一种行为。我个人没有这样做,但它符合the requirements,这里修改的所有回调都是你在行为中修改的。您必须封装逻辑、列名(需要是变量且未设置硬编码,如user_id)等,但它可以在您拥有的任何其他蛋糕项目中重复使用。类似于StrongBelongBehaviorMoreDRYBehavior。如果你这样做了,请分享:)

    我不确定 Auth 组件是否有某种方式可以做你想做的事,但我猜那将是最好的选择。直到有人启发我(我没有对这个问题进行太多调查),这是我会使用的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-15
      相关资源
      最近更新 更多