让我们保持简单。
一个项目只有两个模型:
只允许用户对他们拥有的项目执行操作。别人的。
我知道如何手动检查登录用户是谁以及他/她是否拥有特定项目,但有更好,更全球化的方法吗?我正在寻找更多D.R.Y.不需要在多个操作中重复相同验证的方式。例如,是否有配置设置,如...
Configure::write('Enforce_belongs_to', true);
...或者可能是Auth组件上的设置/选项。
也许这很疯狂,但我想我会问。
答案 0 :(得分:1)
我不确定我所回答的是最好的 - 最好 - 干 - 它几乎脱水的方法,但这是我能想到的最简单的事情。
在Project模型中,创建一个函数,该函数返回与用户关联的项目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;
}
代码未经测试,但一般而言,您可以阻止在更新记录之前编辑不是“用户”的项目。我假设每个人都可以免费插入新项目。根据{{3}},除saveAll
之外的所有保存功能都首先通过此过滤器,因此您需要覆盖saveAll
函数并添加类似于beforeSave
中的验证的验证(正如答案中所解释的那样)。
对于第二部分,过滤结果使用户甚至不知道有其他项目而不是他/她,更改beforeFind
。 this post谈论根据用户的角色限制结果,所以我猜我们正走在正确的轨道上。
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
功能,以避免任何想要删除其他属性的恶意用户。逻辑保持不变(检查项目标识是否在允许的数组中,如果没有,返回false或抛出错误),所以我不会在这里添加该函数的示例。但那是基本的东西。如果由于某种原因你想在控制器中拥有允许的项目,请调用getByUserId
中的beforeFilter
函数并在那里处理那个id数组。您甚至可以将其存储在会话中,但在添加或删除项目时,您必须记住维护该会话。
如果你想要一个可以查看和编辑所有内容的超级运行,只需在getByUserId
中添加一个检查用户角色的条件,如果是管理员,则返回所有项目。
另外,请记住:也许项目有很多......子项目(想象力不大),因此,与项目相关的用户可以添加子项目,但与之前相同的恶意用户修改子项目具有的隐藏项目_id并编辑它。在这种情况下,我建议您在子项目中添加验证,以避免对与Project不相关的模型执行操作。如果您具有安全组件并且表单可以访问编辑和删除操作,这是次要的,因为使用的安全组件可以避免形式篡改。但请考虑一下,看看是否还需要验证“子项目”实例。
如上所述The docs,您可以将所有这些用作行为。我没有亲自这样做过,但它符合Ayo Akinyemi,这里修改的所有回调都是你在行为中修改的。你必须封装逻辑,列名(需要变量而不是硬编码,如user_id
)等,但它可以在你将拥有的任何其他蛋糕项目中重复使用。类似于StrongBelongBehavior
或MoreDRYBehavior
的内容。如果你这样做,请分享:)
我不确定Auth组件是否有某种方法可以做你想要的,但这是我猜的最佳选择。直到有人启发我(我没有对此问题进行过多调查),这就是我使用的解决方案。
答案 1 :(得分: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;
}
}