php CakePHP ACL 数据库设置:ARO/ACO 结构?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/54230/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
CakePHP ACL Database Setup: ARO / ACO structure?
提问by David Heggie
I'm struggling to implement ACL in CakePHP. After reading the documentation in the cake manualas well as several other tutorials, blog posts etc, I found Aran Johnson's excellent tutorial which has helped fill in many of the gaps. His examples seem to conflict with others I've seen though in a few places - specifically in the ARO tree structure he uses.
我正在努力在 CakePHP 中实现 ACL。在阅读了蛋糕手册中的文档以及其他几个教程、博客文章等之后,我发现 Aran Johnson 的优秀教程帮助填补了许多空白。他的例子似乎与我在一些地方看到的其他例子相冲突——特别是在他使用的 ARO 树结构中。
In his exampleshis user groups are set up as a cascading tree, with the most general user type being at the top of the tree, and its children branching off for each more restricted access type. Elsewhere I've usually seen each user type as a child of the same generic user type.
在他的示例中,他的用户组被设置为一个级联树,最通用的用户类型位于树的顶部,其子级针对每个更受限制的访问类型进行分支。在其他地方,我通常将每个用户类型视为相同通用用户类型的子级。
How do you set up your AROs and ACOs in CakePHP? Any and all tips appreciated!
你如何在 CakePHP 中设置你的 ARO 和 ACO?任何和所有提示表示赞赏!
回答by David Heggie
CakePHP's built-in ACL system is really powerful, but poorly documented in terms of actual implementation details. A system that we've used with some success in a number of CakePHP-based projects is as follows.
CakePHP 的内置 ACL 系统非常强大,但在实际实现细节方面的文档很少。我们在许多基于 CakePHP 的项目中成功使用的系统如下所示。
It's a modification of some group-level access systems that have been documented elsewhere. Our system's aims are to have a simple system where users are authorised on a group-level, but they can have specific additional rights on items that were created by them, or on a per-user basis. We wanted to avoid having to create a specific entry for each user (or, more specifically for each ARO) in the aros_acostable.
它是对其他地方记录的一些组级访问系统的修改。我们系统的目标是拥有一个简单的系统,其中用户在组级别获得授权,但他们可以对由他们创建的项目或基于每个用户的项目拥有特定的附加权限。我们希望避免必须为表中的每个用户(或更具体地说,为每个 ARO)创建特定条目aros_acos。
We have a Users table, and a Roles table.
我们有一个用户表和一个角色表。
Users
用户
user_id, user_name, role_id
user_id, user_name, role_id
Roles
角色
id, role_name
id, role_name
Create the ARO tree for each role (we usually have 4 roles - Unauthorised Guest (id 1), Authorised User (id 2), Site Moderator (id 3) and Administrator (id 4)) :
为每个角色创建 ARO 树(我们通常有 4 个角色 - Unauthorized Guest (id 1), Authorized User (id 2), Site Moderator (id 3) and Administrator (id 4)):
cake acl create aro / Role.1
cake acl create aro / Role.1
cake acl create aro 1 Role.2 ... etc ...
cake acl create aro 1 Role.2 ... etc ...
After this, you have to use SQL or phpMyAdmin or similar to add aliases for all of these, as the cake command line tool doesn't do it. We use 'Role-{id}' and 'User-{id}' for all of ours.
在此之后,您必须使用 SQL 或 phpMyAdmin 或类似工具为所有这些添加别名,因为 cake 命令行工具不会这样做。我们对我们所有人使用“Role-{id}”和“User-{id}”。
We then create a ROOT ACO -
然后我们创建一个 ROOT ACO -
cake acl create aco / 'ROOT'
cake acl create aco / 'ROOT'
and then create ACOs for all the controllers under this ROOT one:
然后为这个 ROOT 下的所有控制器创建 ACO:
cake acl create aco 'ROOT' 'MyController' ... etc ...
cake acl create aco 'ROOT' 'MyController' ... etc ...
So far so normal. We add an additional field in the aros_acos table called _editownwhich we can use as an additional action in the ACL component's actionMap.
到目前为止一切正常。我们在 aros_acos 表中添加了一个名为的附加字段_editown,我们可以将其用作 ACL 组件的 actionMap 中的附加操作。
CREATE TABLE IF NOT EXISTS `aros_acos` (
`id` int(11) NOT NULL auto_increment,
`aro_id` int(11) default NULL,
`aco_id` int(11) default NULL,
`_create` int(11) NOT NULL default '0',
`_read` int(11) NOT NULL default '0',
`_update` int(11) NOT NULL default '0',
`_delete` int(11) NOT NULL default '0',
`_editown` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `acl` (`aro_id`,`aco_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
We can then setup the Auth component to use the 'crud' method, which validates the requested controller/action against an AclComponent::check(). In the app_controller we have something along the lines of:
然后我们可以设置 Auth 组件以使用 'crud' 方法,该方法根据 AclComponent::check() 验证请求的控制器/操作。在 app_controller 中,我们有一些类似的东西:
private function setupAuth() {
if(isset($this->Auth)) {
....
$this->Auth->authorize = 'crud';
$this->Auth->actionMap = array( 'index' => 'read',
'add' => 'create',
'edit' => 'update'
'editMine' => 'editown',
'view' => 'read'
... etc ...
);
... etc ...
}
}
Again, this is fairly standard CakePHP stuff. We then have a checkAccess method in the AppController that adds in the group-level stuff to check whether to check a group ARO or a user ARO for access:
同样,这是相当标准的 CakePHP 东西。然后我们在 AppController 中有一个 checkAccess 方法,它添加组级别的东西来检查是否检查组 ARO 或用户 ARO 的访问权限:
private function checkAccess() {
if(!$user = $this->Auth->user()) {
$role_alias = 'Role-1';
$user_alias = null;
} else {
$role_alias = 'Role-' . $user['User']['role_id'];
$user_alias = 'User-' . $user['User']['id'];
}
// do we have an aro for this user?
if($user_alias && ($user_aro = $this->User->Aro->findByAlias($user_alias))) {
$aro_alias = $user_alias;
} else {
$aro_alias = $role_alias;
}
if ('editown' == $this->Auth->actionMap[$this->action]) {
if($this->Acl->check($aro_alias, $this->name, 'editown') and $this->isMine()) {
$this->Auth->allow();
} else {
$this->Auth->authorize = 'controller';
$this->Auth->deny('*');
}
} else {
// check this user-level aro for access
if($this->Acl->check($aro_alias, $this->name, $this->Auth->actionMap[$this->action])) {
$this->Auth->allow();
} else {
$this->Auth->authorize = 'controller';
$this->Auth->deny('*');
}
}
}
The setupAuth()and checkAccess()methods are called in the AppController's beforeFilter() callback. There's an isMinemethod in the AppControler too (see below) that just checks that the user_id of the requested item is the same as the currently authenticated user. I've left this out for clarity.
将setupAuth()和checkAccess()方法调用中AppController的beforeFilter()回调。isMineAppControler 中也有一个方法(见下文),它只检查所请求项目的 user_id 是否与当前经过身份验证的用户相同。为清楚起见,我已经省略了这一点。
That's really all there is to it. You can then allow / deny particular groups access to specific acos -
这就是它的全部内容。然后您可以允许/拒绝特定组访问特定 acos -
cake acl grant 'Role-2' 'MyController' 'read'
cake acl grant 'Role-2' 'MyController' 'read'
cake acl grant 'Role-2' 'MyController' 'editown'
cake acl grant 'Role-2' 'MyController' 'editown'
cake acl deny 'Role-2' 'MyController' 'update'
cake acl deny 'Role-2' 'MyController' 'update'
cake acl deny 'Role-2' 'MyController' 'delete'
cake acl deny 'Role-2' 'MyController' 'delete'
I'm sure you get the picture.
我相信你看懂了。
Anyway, this answer's way longer than I intended it to be, and it probably makes next to no sense, but I hope it's some help to you ...
无论如何,这个答案比我预期的要长,而且可能几乎没有任何意义,但我希望它对您有所帮助......
-- edit --
- 编辑 -
As requested, here's an edited (purely for clarity - there's a lot of stuff in our boilerplate code that's meaningless here) isMine()method that we have in our AppController. I've removed a lot of error checking stuff too, but this is the essence of it:
根据要求,这里有一个经过编辑的(纯粹是为了清楚起见 - 我们的样板代码中有很多东西在这里毫无意义)isMine()我们在 AppController 中的方法。我也删除了很多错误检查的东西,但这是它的本质:
function isMine($model=null, $id=null, $usermodel='User', $foreignkey='user_id') {
if(empty($model)) {
// default model is first item in $this->uses array
$model = $this->uses[0];
}
if(empty($id)) {
if(!empty($this->passedArgs['id'])) {
$id = $this->passedArgs['id'];
} elseif(!empty($this->passedArgs[0])) {
$id = $this->passedArgs[0];
}
}
if(is_array($id)) {
foreach($id as $i) {
if(!$this->_isMine($model, $i, $usermodel, $foreignkey)) {
return false;
}
}
return true;
}
return $this->_isMine($model, $id, $usermodel, $foreignkey);
}
function _isMine($model, $id, $usermodel='User', $foreignkey='user_id') {
$user = Configure::read('curr.loggedinuser'); // this is set in the UsersController on successful login
if(isset($this->$model)) {
$model = $this->$model;
} else {
$model = ClassRegistry::init($model);
}
//read model
if(!($record = $model->read(null, $id))) {
return false;
}
//get foreign key
if($usermodel == $model->alias) {
if($record[$model->alias][$model->primaryKey] == $user['User']['id']) {
return true;
}
} elseif($record[$model->alias][$foreignkey] == $user['User']['id']) {
return true;
}
return false;
}

