I am using cakephp ACL component to make site secure but the problem is when i am trying to deny a particular action for eg.cake acl deny Group.3 Notes 'delete' , it denies all the action of the controller for that group.The aros_acos table is as follows----
id aro_id aco_id _create _read _update _delete
1 1 1 1 1 1 1
2 2 10 1 1 1 1
3 3 10 1 1 1 -1
In the above table, the third row aro_id points to Group 3 and aco_id points to Notes controller.
What might be the problem.
I don't have access to my implementations at the moment, so this is from memory:
The crud settings in the acos_aros table don't map onto or control access to methods/functions/actions as such. It is the actual row in the table that does that. There will be a row for every permutation of Aro -> Aco that you have defined - they do not necessarily exist by default.
Hence the entry (row) for Administrators:AdminUser_1 => Posts::delete will be a bunch of 1s, 0s or -1s. Set all four numbers to 1 for access or -1 for deny.
I made this easier by building a (huge) matrix of checkboxes for each group, controller & action.
To summarise this, to turn on delete for a user:
find the corresponding row in the acos_aros table
Set all four _create, _read, _update, _delete to 1
e.g.
(3087, 1, 1314, '1', '1', '1', '1'), // allow
(3086, 1, 1313, '-1', '-1', '-1', '-1') // deny
Perhaps your database is corrupted since last changes.
I would recommend you to fix the tables, remember, this are hasMany relations plus TreeBehaviour, if the action kept out from the controller node for any reason would explain that behaviour.
Luckily, there is someone that thought about this and developed the ACL Manager plugin that allows you to fix this using the console.
https://github.com/FMCorz/AclManager
Download the plugin to your plugins folder.
Load the plugin in your bootstrap if you are not loading all already.
Login to you server and use the console to execute any of the following command:
./Console/cake AclExtras.AclExtras aco_sync
You can get a complete guide for all available commands like this:
./Console/cake AclExtras.AclExtras -h
./Console/cake AclExtras.AclExtras aco_sync -h
Any time, if you can not access to the APP, add Controller to the authorize method in your AppController and then: $this->Auth->allow() so any one with valid auth is valid.
Example, just acl check:
$this->Auth->authorize = array(
'Actions' => array('actionPath' => 'controllers')
);
Example Controller and ACL check:
$this->Auth->authorize = array(
'Controller',
'Actions' => array('actionPath' => 'controllers')
);
Using the second option, you can anytime include $this->Auth->allow() in your controller's beforeFilter to allow access who you want.
Related
I went through complete lesson on cakephp's ACL component, but gigantic ACL component do not seem to meet my very simple requirements.
I have only group based access control, three groups are users, managers and administrators the fourth is a anonymous users without logins for which I am not creating any group.
from acl concept it creates three table
aros -> this looks somewhat redundant data copied from groups table, I dont even need to have a group table but just field group_id in users table.
acos -> this is a list of public methods in controllers, I had to use AclExtra plugin to populate over 250+ actions in table, now this is the part which I think un-manageable, I noticed that tool used to populate acos table cannot reliably sync everytime when I do changes in controllers, the same work must be done at remote site for each changes that means terrible thing! this also mean i have to have a database backup during updates and migration.
Other side if I use php file based acos that is again un-manageable because we have to make sure syncing between controller and acl file.
aros_acos -> obviously
can we have a simpler mechanism something like i deny all actions using Auth component and then inside each action or maybe in beforeRender method i can specify what methods are open to what group ?
Thanks
There is an undocumented acl class PhpAcl it is much simpler to use than then the database driven ACL and more dynamic than the ini bassed ACL.
In Config/core.php
/**
* The class name and database used in CakePHP's
* access control lists.
*/
Configure::write('Acl.classname', 'PhpAcl');
// Configure::write('Acl.database', 'default');
This tells your ACL to use the PhpAcl
Then open up Config/acl.php
There are some good instructions there
Assumptions:
In your application you created a User model with the following properties: username, group_id, password, email, firstname,
lastname and so on.
You configured AuthComponent to authorize actions via $this->Auth->authorize = array('Actions' => array('actionPath' =>
'controllers/'),...)
Now, when a user (i.e. jeff) authenticates successfully and requests a
controller action (i.e. /invoices/delete) that is not allowed by
default (e.g. via $this->Auth->allow('edit') in the Invoices
controller) then AuthComponent will ask the configured ACL interface
if access is granted. Under the assumptions 1. and 2. this will be
done via a call to Acl->check() with
array('User' => array('username' => 'jeff', 'group_id' => 4, ...))
as ARO and
'/controllers/invoices/delete'
as ACO.
I wanted to use static names for Groups or Roles so you can add a role field to your user table, and then set up the $map like this:
**
* The role map defines how to resolve the user record from your application
* to the roles you defined in the roles configuration.
*/
$config['map'] = array(
'User' => 'User/username',
'Role' => 'User/role',
);
For my app we aren't using user based permissions only role, so we could remove the User from the $map.
Then you need to set up some roles:
/**
* role configuration
*/
$config['roles'] = array(
'Role/admin' => null,
);
Any role not in this array will get 'Role/default'
Now just set up your permissions, they are pretty self explanatory.
/**
* rule configuration
*/
$config['rules'] = array(
'allow' => array(
'*' => 'Role/admin',
'controllers/Reports/*' => 'Role/default',
'controllers/EurRates/*' => 'Role/default',
'controllers/Posts/index' => 'Role/default',
'controllers/Users/(edit|index)' => 'Role/default',
),
'deny' => array(
'controllers/ProtectedController/*' => 'Role/default',
'controllers/EurRates/(edit|add|delete)' => 'Role/default',
'controllers/Reports/(edit|add|delete)' => 'Role/default',
),
);
That's it, now you can allow or deny permission to actions based on role.
I am using CakePHP in my project and I am looking for a proper way to check advanced user rights in my views.
I have several pages in which the contents depend of your rights (you can view some blocks or not, edit some infos or not, etc...)
I searched and the only way I found is to implement an Auth Helper, but I thought the best way to to that is to implement methods in my "UserController" (such as canPerformAction($action, $controller = 'default_controller')), am I wrong ? And if I'm right, how to call that methods properly ?
Thanks.
EDIT : More precisions
For example I have an action "editEventProducts" that a user can perform only if he's the event owner and if the event status is <= 2.
I check that in my controller "isAuthorized" function, works like a charm.
But I have a page called "eventDetails", form which you can perfom several actions such as this one, and I want to show the edit button, only if you can do it.
If fact what I need is the output of the "isAuthorized" function for each action that you can call, but can I properly get it from a view ?
Solution
I implemented a Auth helper who does several check such as this one, which is finally a whitelist check, depending of the status of my event, hope it will help, the code :
App::uses('AppHelper', 'View/Helper');
class AuthHelper extends AppHelper {
var $helpers = array('Session');
private $_whitelist = array(
'controller1' => array(
'events' => array(
'action1' => array(1 => true, 2 => true),
'action2' => array(1 => true, 2 => true),
'action3' => array(3 => true),
'action4' => array(6 => true)
)
),
'user' => array(
'controller1' => array(
'action1' => array(1 => true, 2 => true),
'action2' => array(1 => true, 2 => true)
)
)
);
public function canPerformAction ($action, $event_infos, $controller = 'events') {
return isset($this->_whitelist[$this->Session->read('Auth.User.role')][$controller][$action][$event_infos['Event']['state_id']]);
}
}
It sounds to me like you just want to render some parts of a view based on the permissions of the user. Well, in this case I think a helper is the right choice. The user should already have all the permissions he has loaded - except they're very fine grained and you got thousands of permissions.
Check this AuthHelper, it allows you to check if the user is logged in, for a role or a set of roles in a field. Alternatively implement your own solution to match whatever your permission system is.
Note that the helper relies on passing the user data to the view in a view variable. It can be also configured to read the data from the auth part of the session directly.
Here is the example taken from it's documentation:
if ($this->Auth->isLoggedIn()) {
echo __('Hello %s!', $this->Auth->user('username'));
}
if ($this->Auth->isMe($record['Record']['user_id']) {
// or your edit button here
echo '<h2>' . __('Your records') . '</h2>';
}
if ($this->Auth->hasRole('admin') {
echo $this->Html->link(__('delete'), array('action' => 'delete'));
}
What you need is called authorization, and is the process of granting/denying actions usually built on top of an authentication step, which maps HTTP requests to logical users.
The authorization scheme can be implemented in a number of ways, for example with simple role-based rules, where users are grouped exactly for the purpose of assigning rights, or with more complex ACL (access control lists). Both can be adopted at the same time for different parts of the system, depending on your needs.
Whatever scheme you pick, you absolutely need to query it at the beginning of your controllers actions (if applicable, you may and up with a standardized authorization filter in your AppController), because the HTTP request doesn't need to come from a previously sent HTTP page, but could be a (possibly) malicious, hand-craften one. Also, you'll likely need to adjust the UI after the user rights. Maybe you'll better start with a bunch of if statements, and then after some days of work you'll be able to identify your needs and build your libraries/helpers/blocks/whatever to avoid code duplication and easing reading the templates.
If you have predefined user permissions (like 'admin', 'moderator', 'editor', 'publisher'...) you can just read the user role and current action in the controller function isAuthorized and set it to true or false.
If you want custom permissions per user, you can store those values in the database, read them in the isAuthorized function and make your logic to determine if you should allow him or not.
My solution to this was a separate table user_permissions that was something like this:
user_id | action
where action would be `controller/action' or 'view/block' or whatever you want to save there.
I would read all values for current user in the controller and if the current controller/action was found in the array, i'd set isAuthorized to true. You can apply your logic to the blocks also.
You can call function of controller from view using
requestAction(string $url, array $options)
Or you can create your custom Helper which will do this for you!
eg my URLs and their resulting actions , will be like:
1) a: For a user whose company_id belongs to company = Pepsi
/pepsi/ - controller - users, action - dashboard (their Auth->user 'company_id' will be used and their user->id)
/pepsi/companies/view - controller - companies, action - view
/pepsi/users/ - controller - users, action - index (trailing slash preferably optional)
/pepsi/users/dashboard - controller - users, action - dashboard
1) b: For a user who company_id belongs to company = Coke
/coke/ - controller - users, action - dashboard
/coke/companies/view - controller - companies, action - view
/coke/users/ - controller - users, action - index (trailing slash preferably optional)
/coke/users/dashboard - controller - users, action - dashboard
2) For an admin, which uses routing prefix adminperson
/adminperson/users - controller - users, action - index
/adminperson/users/view/3 - controller - users, action - view , id = 3
/adminperson/companies/delete/6 - controller - companies, action - delete Id = 6
3) important Then also when a user isnt logged in , there are ALSO public pages.
/contents/view/3 - controller - contents, action - view AND id = 3
Below is the best I have got so far, but it always demands the action index to be explicitly written to the URL - but in fact I guess its not that bad a problem as the Html->link helper should produce it. I am happy to build all my links with the helper as I usually do that.
best try :
Router::connect('/:companyslug/:controller/:action', array('controller' => 'companies', 'action'=>'index', 'companyslug'=>'test'));
I have a similar setup, except I'm keeping track of the first part of the route to determine what records are displayed. Here's my setup, where ":app" is in place of what you call ":companyslug":
$app_names = "app1|app2|test_app"; // In real code these are pulled from a model
Router::connect(
'/:app/:controller/:action/*',
array( ),
array(
'persist'=>array('app'),
'app'=>$app_names
)
);
Router::connect(
'/:app/:controller',
array('action'=>'index'),
array(
'persist'=>array('app'),
'app'=>$app_names
)
);
Router::connect(
'/:app',
array('controller'=> 'pages', 'action'=>'display', 'home'),
array(
'persist'=>array('app'),
'app'=>$app_names
)
);
// Allow non-app specific access. (I have disabled the default CakePHP routes.)
Router::connect('/:controller', array('action' => 'index'));
Router::connect('/:controller/:action/*');
Note: this is from Cake 2.0RC3, but I believe it should work the same in 1.3.
This way you can retrieve the value of app in the controller (under request parameters) and make sure they are in the correct URL (make sure company_id = companyslug in your case).
One tricky bit is the 'persist' option, which means you always need to use the Html Helper's link function to maintain the correct prefix in the URL.
You can use Regex in route, like -
Router::connect('/:companyslug/:controller/:action', array('controller' => 'companies', 'action'=>'index'), array('companyslug'=>'[A-Za-z0-9_\-]*'));
I'm trying to implement PDF functionality to my application. So, I added some new actions in controllers (like 'viewpdf').
After this, I rebuild the ACL tree with the build_acl action (from Mark Story Tutorial Automated tool for creating ACOS ).
So, I can see with MySQL that a new node is created.
Until this, everything is fine. But now I try to test the viewpdf button, and I get a 'You are not authorized to access that location.' error (even being admin). I check the error.log file and I see a warning:
> Aco: controllers/Specializations/viewpdf in [/usr/share/php/cake/libs/controller/components/acl.php, line 273]
2011-02-24 11:40:34 Warning: Warning (512): DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:
Aro: Array
(
[User] => Array
(
[id] => 1
[email] => admin#gmail.com
[group_id] => 1
)
)
Aco: controllers/Specializations/viewpdf in [/usr/share/php/cake/libs/controller/components/acl.php, line 273]
Then I check the aros_acos table in the database and I see that there's no 'viewpdf' ACO related to any node, so there's an ARO, an ACO, but not an ARO_ACO, so I suppose that this is the reason why I'm getting this error.
¿Are my suppositions right? If they are, how could I create this aro_aco? I'm afraid that I could break anything if I do it manually...
Thanks in advance,
Alf.
alfizqu,
if you have an ARO and an ACO but no connection between these by means of entries in the ACO_ARO table, this means you have not set up the permissions your AROs have on the ACOs.
Proceed like this:
/*
* Copied from the tutorial, and modified, this function initializes the per-
* missions for accessing controller actions.
*/
function initDB() {
$group =& $this->User->Group;
// A D M I N S
$group->id = 3;
$this->Acl->allow($group, 'controllers');
// M A N A G E R S
$group->id = 2;
$this->Acl->deny($group, 'controllers');
$this->Acl->allow($group, 'controllers/Items','*'); ... ...
Once you have set up such an initDB function, you have to run it once by calling it from your browser.
If this does not suffice to help you back on the track, just go over the basic AUTH/ACL tutorial again.
Yours, Benjamin.
Edit 1:
One of the crucial points is to call the parent::beforeFilter() in the beforeFilter() methods of self-defined controllers and properly setting up the app_controller.
If these tips do not help, the most time-efficient way is to go over the ACL/AUTH tutorial very carefully, starting from a fresh cake environment. Once you can get it up and running there, you are confident to do it in your app.
Edit 2:
And don't be afraid to throw out everything ACL/AUTH related of your app. It just sounds daunting, but it can safe a lot of debugging headaches/time.
P.S.: Btw there should be some moderately usable ACL/AUTH plugins at the bakery and one at sourceforge.
Try to create an sample action in users controller like this
function install(){
$aco = new Aco();
$aco->create();
$aco->save(array(
'parent_id' => <Id of the Specializations in acos table>,
'alias' => 'viewpdf',
));
$this->Acl->allow('admin','controllers/Specializations/viewpdf','*');
}
If u run the action the a new Aco node will be created in acos table. and for the admin user u can give the whole permission.you can use any valid user (username should be in Aros table) instead admin.
hope it helps.
I have plugin
plugin: tree
-----controller: tree
-------------action : admin_index
DB Table aros_acos has updated _CRUD 1 1 1 1 for this aco.
I code $this->Acl->check(array('model'=>'User','foreign_key'=>2),'Tree/Tree/admin_index');
error:
DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:
Aro: Array
(
[model] => User
[foreign_key] => 2
)
Aco: Tree/Tree/admin_index [CORE\cake\libs\controller\components\acl.php, line 273]
All other action is permission valid but action of plugins, I cant find reason . Anyone help me.
I don't understand why you have Tree as a plugin - it's a core behaviour.
Please post the appropriate rows from the aros_acos table.
The array you pass into Acl->check doesn't look right to me. I pass in the output of $this->Auth->user() which looks like:
Array
(
[User] => Array
(
[id] => 12
[username] => somebody
[group_id] => 1
)
)
Have a look at Mark Story's tutorial - it really is excellent:
http://mark-story.com/posts/view/auth-and-acl-an-end-to-end-tutorial-pt-1
There was a change in Cakephp 1.3. Only the index route is supported for the controller that is named the same as plugin. So, you will need to handle that first if you use 1.3 version.
Check the ARO and ACO tree in the console: cake acl view aco, cake acl view aro. Try to check the permissions from console cake acl check YOURARO YOURACO all