Just how strong exactly is CakePHP's ACL Component? - cakephp

I have read numerous times here and on various websites that the ACL Component is a very powerful tool. Yet the next sentence usually begins with a version of "However, the cake manual poorly documents this and so this is the system that I am using instead." I am a programming novice and have definitely struggled with learning Cake because of the docs as well; I am at a loss as to what exactly makes the AclComponent so strong, since everyone recommends it but many are still using something else. Can anyone suggest specific examples or tutorials that will help me understand this component better? For example, what kinds of code it helps me avoid having to write, how to limit access by each user instead of the entire group, etc?

Cake's ACL system is powerful because it's extremely flexible and allows very fine grained control over the access to your application. Most people use it in a basic sort of way, i.e., this user/group has access to this controller method. While this is the common use case, you can also use it to say, limit a model's access to another model's create action.
For example, to limit an Admin group to only creating records for a PrivateModel model:
// add aco
$this->Acl->Aco->add(array(
'alias' => 'PrivateModel'
'model' => 'PrivateModel',
'foreign_key' => null
));
// add aro
$this->Acl->Aro->array(array(
'alias' => 'Admin'
'model' => 'Group',
'foreign_key' => 1
));
// allow Admin to read PrivateModel #1
$this->Acl->allow('Admin', 'PrivateModel1', 'create');
// check permission
$allowed = $this->Acl->check('Admin', 'PrivateModel', 'read'); // true
$allowed = $this->Acl->check('Admin', 'PrivateModel', 'create'); // false
The default answer to "should I use ACL" is often "no" simply because it can take some time to set up and understand. Cake's ACL examples were enough to get me going on it when I originally had no understanding of even the ACL concepts or MPTT trees.
Currently I have an app that has groups as AROs, and the ACLComponent uses the controller to check an isAuthorized() method which pulls the group from the logged in user and uses it to check see if the action is authorized. Again, this is probably the most basic approach to ACL.

As you have already pointed out in your question "However ... cake documentation", I couldn't agree more.
I have seen many of my team members in past struggle to grab the concept of whole Acl, Aro and Aco, and cake's documentation may be adds additional complexity to it than solving it.
The real magic of ACL begins with Aro and Aco.
ARO (Access Request Object) - They are the entities who request for services.
for eg. Users, Roles (Admin, Manager, Moderator)
ACO (Access Control Object) - They are the entities which are requested.
for eg. Posts, Posts->add, Posts->view, Posts->edit
We can configure our models with requester behavior such that everytime new record is created, it gets automatically synced with corresponding Aro.
As already demonstrated by jeremyharris, you can use Aro and Aco mapping with corresponding privilages to limit/control the access control of your application.
Also, you can make your life pretty easy with ACL by using a plugin Alaxos ACL This plugin really simplifies the whole ACL setup, especially from UI perspective
The cake console also provides a shell to initialize your db for ACL setup
cake acl initdb // cakephp 1.1, from shell with create db setup for acl
$ cake schema create DbAcl // cakephp 1.2 and above
There is an additional plugin from Markstory called AclExtras, it provides you a shell to make acl management more easy like creating aco nodes, testing and recovering aco, aro trees. It's an amazing plugin.

Related

Restrict access to resources by resource ID in CakePHP using ACL

Let's say I have a Project model and a User model in a CakePHP application. Using ACL I can control if users can access to projects and/or to particular actions in a ProjectsController.
But I would like to go further and control whether a user is allowed to view a specific project, e.g. accessing a project with id = 3 using a URL like http://example.com/projects/3.
Is this possible with ACL as well or I have to develop additional checks on top of it?
Thanks!
To restrict access to specific values of a model, you'll need to use something other than ACL.
It'll be best to define a relationship between the users and projects, whether that's inclusive or exclusive.
You're probably needing a ProjectUser model (HABTM in Project and User) and a simple function in that model, maybe userAllowed($projectId, $userId), that checks that the user has been given access to that project.

CakePHP ACL: Is a base group/ARO required

I'm implementing the ACL component for my CakePHP app (1.3.14). I have everything setup correctly, but there are a few areas where I'm still fuzzy.
Mainly, do I need to explicitly set rights (ACOs) for a special base user group (AROs)?
For simplicity, let's say I have Administrators and then everyone else (general users). So do I need to create a group for these general users and map all their allow rights? Seems like management of these rights would be never ending as the app grew.
Also, what about assigning users to multiple groups?
Ideally if a person had a user account the Auth component would grant access to the system as a whole. Then ACL would simply deny them from the sections that were protected by an existing group.
It seems like the coupling of ACL and Auth is too high. But it may be my new (limited) understanding. Any clarification would be greatly appreciated.
UPDATE
I've started a bounty. In summary, I want to implement CakePHP ACL (preferably, but a matching third-party component is acceptable) that meets/addresses the following:
Assign users to multiple groups
Easily maintain a "public" user group - don't have to constantly add the controllers/actions a general user can access
Code example of managing access to a controller/access
Code example of properly testing a user belongs to a group.
I think the best you can hope for using Cake's native ACL implementation is something like the following:
cake acl create aro root public
cake acl create aro root registered
cake acl create aro registered administrators
(create acos using AclExtras)
cake acl grant registered controllers
cake acl grant public controllers
cake acl deny public controllers/MySecureController
cake acl deny public controllers/Widgets update
cake acl deny public controllers/Widgets delete
(the above is all done through the cake shell, but is easily translated to the PHP variant)
Basically, you can either use a "default-deny" paradigm (as demonstrated in Cake's own ACL tutorial), or a "default-allow" paradigm as above. Whichever method you choose will depend on how you expect the application to grow: will most of your controllers be public with only a select few restricted to Administrators, or will most of your application be restricted with Public given specific access where needed? Either way, you still need to either grant or deny access.
Notice the two AROs created under root: public and registered. With this model, treat registered as if it were root when creating your ARO tree -- put all of your "real user" groups underneath it. That way, you can use ACL as normal on the objects under registered, and the public users will exist outside of that.
All that said, there's nothing stopping you from using Cake's authentication mechanism and rolling your own access control method. Here's an example: Simple Authentication and Authorization Application. (NOTE: This is written for CakePHP 2.0, but the concepts apply to 1.3 as well).
EDIT -
After re-reading the question and the other responses, I realized you're aiming more for a role-based access control model rather than the traditional one-aro-per-user model of the builtin ACL component. Here are a couple of examples of extending the built-in auth component for RBAC:
Role-based ACL in CakePHP
CakePHP Auth component: Users, Groups, Permissions
Also, this two-part article describes an approach to database-backed role-based authorization that can be applied to your Cake application.
You can have both ACOs tree and AROs tree. In the AROs tree you will have adminsGroup<-usersGroup. You will need to setup rights for these groups. In the ACOs tree you will have baseACO<-subACO<-treeOfACOsForUsers. You will not need to maintain any new ACOs if: 1) userGroups are allowed to use subACO, 2) any new ACO is a child of subACO. The idea is to organize a tree of ACOs, so that if you allow access to a parent all children are accessable automatically. You can have a branch with denied access also. So you will need to maintain (assigning permissions) only several branches close to the root.
You may be interested to look at my PoundCake Control Panel - a plugin implementing ACL with user friendly web interface (CakePHP v1.3 is supported).
UPDATE:
Here is what you need.

Generally splitting admin and web app into two entities

I am building a public facing web/mobile application that will have a substantial admin controlled back end. Although this question is quite general, I am using CakePHP to build the application.
I am looking into whether it would be wise to split the admin and public applications into two applications. Both applications would use the same database. The main reason I am looking into this would be for improved security, but also portability of the front end.
I have also thought about developing a CakePHP based RESTful API that both the front and back end would share.
Would an API be the best way to go about this or should each application simply only share the database, or is splitting the applications just creating more work in the long run?
I think it's best to keep both the Admin functionality and REST API in your main CakePHP application. (You don't specify a version, but I'm assuming since you're making a new app you're using 2.0. It has some benefits below.)
As mark mentioned you can do something called prefix routing that allows you to create special actions that only admins can use in your existing controllers. There's a full explanation in the Prefix Routing docs.
The gist is that you specify the prefix you want in core.php:
Configure::write('Routing.prefixes', array('admin'));
So going to /admin/users/edit/5 would call the method admin_edit of our UsersController passing 5 as the first parameter. The view file used would be /views/users/admin_edit.ctp.
You can set a default "admin" homepage in routes.php:
Router::connect('/admin', array('controller' => 'pages', 'action' => 'index', 'admin' => true));
As for the REST API, if you're using 2.0 this is a built-in that is pretty easy to turn on. There's a good intro on the REST page.
Activating it just requires adding these lines to routes.php:
Router::mapResources('recipes');
Router::parseExtensions();
This sets up some default REST routes:
#HTTP format URL.format Controller action invoked
GET /recipes.format RecipesController::index()
GET /recipes/123.format RecipesController::view(123)
POST /recipes.format RecipesController::add()
PUT /recipes/123.format RecipesController::edit(123)
DELETE /recipes/123.format RecipesController::delete(123)
POST /recipes/123.format RecipesController::edit(123)
There's more info in the doc so please check it out.
I don't think it makes sense to split the application into two separate ones.
it can easily be accomplished that user frontend and admin backend are separated completly using an "admin" prefix.
So all user actions
/
/controller/
/controller/action
etc
all admin actions
/admin/
/admin/controller/
/admin/controller/action
and so on

CakePHP ACL disable automatic ARO creation

I (finally) got ACL to work properly, based on group permissions. However when I create a new user (Users/add) it automagically.. I mean.. autoinconveniently creates a User ARO..
While this is not really a big problem, I would like my ARO table to stay as clean as possible. Just my groups.
How do I disable the automatic creation of a User ARO object when creating a new user through CRUD?
i had the same problem and, like you, i said to myself "its not a big deal as long as it's working"... but when i started to have more and more users and when i added new groups, i found that ACL was not working correctly.. If you're using a group-based permissions, you MUST ONLY have groups in your AROS table.
Brief, the documentation says that you need to add the bindNode() in your Users model if you want a group-based ACL, but what they don't tell you is that for group-based permissions your User model doesn't have to implement the requester behavior and you don't need the parentNode() neither. Remove those two and it should be ok.
I added a note on the documentation, i hope it gets published :)
Good Luck

How do I build internal Access Control for site features in CakePHP

I went through the tutorials and examples on the CakePHP Acl and Auth components today in detail. I configured my Auth component to use $this->Auth->authorize = 'actions'. With this I was able to successfully restrict access to specific site actions without a problem.
However, my application needs to go a bit beyond this and I'm unsure of how best to accomplish my goals for this application.
Within the application that I am developing using CakePHP 1.3.8, there are specific "site features". For example, users of the application will have the ability to message one another.
I want to treat each message as an ACO so that I can control who can and cannot view or delete the message.
Another site feature is the earning of "badges" for achieving certain goals. For these badges I'd like to treat them as ACO's for the locking and unlocking of these badges.
I do not think that I can do this with the out-of-the-box ACL functionality of CakePHP as this goes beyond restricting access to actions. I'm looking for any ideas on how best to achieve this functionality.
With the standard ACL functionality in CakePHP it's only possible to create ACO entries for controller actions. This means that your setup (treating every single message as a seperate ACO) will not fly. Same as with badges.
For your messages I would go for a setup in which you would check in your view/edit/delete methods if a certain user is the sender or receiver of the message.
Something like
# in messages_controller
function view($id) {
if(!$isSender($loggedInUserId) || !$isReceiver($loggedInUserId)) {
$this->Session->setFlash("You're not allowed to view this message")
$this->redirect('index');
}
# do view stuff here
}
function edit($id) {
if(!$isSender($loggedInUserId)) {
$this->Session->setFlash("You're not allowed to edit this message")
$this->redirect('index');
}
# do edit stuff here
}
Regarding the badges, I would go for a regular HABTM (HasAndBelongsToMany) relation between a User and a Badge. When a User reaches a certain goal, you make a call like Badge::unlock($badge, $user) which saves the new badge for the user to the users_badges join table.
From there you can do basic stuff like listing all the badges for a certain user, etc.
I studied over Cake's Acl component much closer and figured that I could write something that sort of imitates this functionality for my internal "feature" access control. My thoughts are that I could have a Faro (Feature access request objects), Faco, and Facos_Faros table. Faco and Faro will have a HABTM relationship. I could then create my own component that manages it all.

Resources