CakePHP Auth Component - Users, Groups & Permissions Revisited
Published: on 20/6/08 | Comments (58)
In my earlier article CakePHP Auth Component - Users, Groups & Permissions I demonstrated step by step how to create a complete Authentication & Authorization system based around cakePHPs' Auth component.
Judging from the number of visits the article has had and the great comments I have received from the article, it prooved to be a bit of a winner, and as the last couple of weeks I haven't had a chance to post, due to sheer workload, I thought today, I would sit down and refactor the code to reflect some of the comments and requests I have received about it.
Caching Permissions to Session
One of the biggest changes I have made to the system is to move the permissions cache into SESSION, the advantage of this approach is that once the permissions have been cached to Session, the database doesn't have to be re-queried on each page request, thus saving a substantial amount of time and speeding up your application.
Wildcard permissions for controller wide access
Along with the previous permission format of controller:action for each permission, I have added the ability to set permissions in the form of controller:* so that you can set controller wide permissions for groups.
Making use of UUID's
With this version of the code I have also switched to UUIDs (Universally Unique Identifiers) for my database primary ids, this is a personal choice and the system will work just as well using an auto increment int(11) but for larger systems I find that UUIDs are little more versatile, especially when it comes to merging multiple databases.
CakePHP 1.2.0.7125RC1 support
Having begun playing with the new release, I have built this update using the latest release candidate and it's working fine without modification.
On with the code
So, preamble over, here is the code with comments about where I have changed or re-factored things a little.
Database Schema
While this is pretty much the same as my earlier version, I have as outlined above used UUIDs for my primary keys here, which in CakePHP can be implemented simply by making the id field a char(36).
CREATE TABLE 'groups' (
'id' char(36) NOT NULL,
'name' varchar(40) NOT NULL,
'created' datetime NOT NULL,
'modified' datetime NOT NULL,
PRIMARY KEY ('id')
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE 'groups_permissions' (
'group_id' char(36) NOT NULL,
'permission_id' char(36) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE 'groups_users' (
'group_id' char(36) NOT NULL,
'user_id' char(36) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE 'permissions' (
'id' char(36) NOT NULL,
'name' varchar(40) NOT NULL,
'created' datetime NOT NULL,
'modified' datetime NOT NULL,
PRIMARY KEY ('id')
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE 'users' (
'id' char(36) NOT NULL,
'email_address' varchar(127) NOT NULL,
'password' varchar(40) NOT NULL,
'active' tinyint(4) NOT NULL default '0',
'created' datetime NOT NULL,
'modified' datetime NOT NULL,
PRIMARY KEY ('id')
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
All pretty standard so far, note that I am using email_address instead of a user name as these should already be unique for every user (and your user is less likely to forget it), and I have an active flag set in the Users table which indicates whether the user can log into the system, you can use this as a way of implementing an email authentication system, where you send a user an email with a link to click, then once it's clicked set active to 1 and the user will then be able to log in.
Model Code
The models haven't been refactored as they are pretty much identicle to those in my earlier article, but I have put them here for the sake of completeness.
User Model
app/models/user.php
class User extends AppModel {
var $displayField = 'email_address';
var $name = 'User';
var $validate = array(
'email_address' => array('email'),
'password' => array('alphaNumeric'),
'active' => array('numeric')
);
var $hasAndBelongsToMany = array(
'Group' => array('className' => 'Group',
'joinTable' => 'groups_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'group_id',
'unique' => true
)
);
}
Group Model
app/models/group.php
class Group extends AppModel {
var $name = 'Group';
var $hasAndBelongsToMany = array(
'Permission' => array('className' => 'Permission',
'joinTable' => 'groups_permissions',
'foreignKey' => 'group_id',
'associationForeignKey' => 'permission_id',
'unique' => true
),
'User' => array('className' => 'User',
'joinTable' => 'groups_users',
'foreignKey' => 'group_id',
'associationForeignKey' => 'user_id',
'unique' => true
)
);
}
Permissions Model
app/models/permission.php
class Permission extends AppModel {
var $name = 'Permission';
var $hasAndBelongsToMany = array(
'Group' => array('className' => 'Group',
'joinTable' => 'groups_permissions',
'foreignKey' => 'permission_id',
'associationForeignKey' => 'group_id',
'unique' => true
)
);
}
The Controllers
Users Controller
Again, the controllers remain pretty much as per my last article except in the Users Controller, there is now an extra line to delete the permissions array from the session just prior to the user logging out.
app/controllers/users_controller.php
class UsersController extends AppController {
var $name = 'Users';
var $scaffold;
function login(){}
function logout(){
$this->Session->del('Permissions');
$this->redirect($this->Auth->logout());
}
}
Groups Controller
app/controllers/groups_controller.php
class GroupsController extends AppController {
var $name = 'Groups';
var $scaffold;
}
Permissions Controller
app/controllers/permissions_controller.php
class PermissionsController extends AppController {
var $name = 'Permissions';
var $scaffold;
}
App Controller
The App Controller is where the majority of the refactoring has taken place, so I have left in a full set of comments with the code below.
app/app_controller.php
class AppController extends Controller {
/**
* components
*
* Array of components to load for every controller in the application
*
* @var $components array
* @access public
*/
var $components = array('Auth');
/**
* beforeFilter
*
* Application hook which runs prior to each controller action
*
* @access public
*/
function beforeFilter(){
//Override default fields used by Auth component
$this->Auth->fields = array('username'=>'email_address','password'=>'password');
//Set application wide actions which do not require authentication
$this->Auth->allow('display');
//Set the default redirect for users who logout
$this->Auth->logoutRedirect = '/';
//Set the default redirect for users who login
$this->Auth->loginRedirect = '/';
//Extend auth component to include authorisation via isAuthorized action
$this->Auth->authorize = 'controller';
//Restrict access to only users with an active account
$this->Auth->userScope = array('User.active = 1');
//Pass auth component data over to view files
$this->set('Auth',$this->Auth->user());
}
/**
* beforeRender
*
* Application hook which runs after each action but, before the view file is
* rendered
*
* @access public
*/
function beforeRender(){
//If we have an authorised user logged then pass over an array of controllers
//to which they have index action permission
if($this->Auth->user()){
$controllerList = Configure::listObjects('controller');
$permittedControllers = array();
foreach($controllerList as $controllerItem){
if($controllerItem <> 'App'){
if($this->__permitted($controllerItem,'index')){
$permittedControllers[] = $controllerItem;
}
}
}
}
$this->set(compact('permittedControllers'));
}
/**
* isAuthorized
*
* Called by Auth component for establishing whether the current authenticated
* user has authorization to access the current controller:action
*
* @return true if authorised/false if not authorized
* @access public
*/
function isAuthorized(){
return $this->__permitted($this->name,$this->action);
}
/**
* __permitted
*
* Helper function returns true if the currently authenticated user has permission
* to access the controller:action specified by $controllerName:$actionName
* @return
* @param $controllerName Object
* @param $actionName Object
*/
function __permitted($controllerName,$actionName){
//Ensure checks are all made lower case
$controllerName = low($controllerName);
$actionName = low($actionName);
//If permissions have not been cached to session...
if(!$this->Session->check('Permissions')){
//...then build permissions array and cache it
$permissions = array();
//everyone gets permission to logout
$permissions[]='users:logout';
//Import the User Model so we can build up the permission cache
App::import('Model', 'User');
$thisUser = new User;
//Now bring in the current users full record along with groups
$thisGroups = $thisUser->find(array('User.id'=>$this->Auth->user('id')));
$thisGroups = $thisGroups['Group'];
foreach($thisGroups as $thisGroup){
$thisPermissions = $thisUser->Group->find(array('Group.id'=>$thisGroup['id']));
$thisPermissions = $thisPermissions['Permission'];
foreach($thisPermissions as $thisPermission){
$permissions[]=$thisPermission['name'];
}
}
//write the permissions array to session
$this->Session->write('Permissions',$permissions);
}else{
//...they have been cached already, so retrieve them
$permissions = $this->Session->read('Permissions');
}
//Now iterate through permissions for a positive match
foreach($permissions as $permission){
if($permission == '*'){
return true;//Super Admin Bypass Found
}
if($permission == $controllerName.':*'){
return true;//Controller Wide Bypass Found
}
if($permission == $controllerName.':'.$actionName){
return true;//Specific permission found
}
}
return false;
}
}
Once again, the major change here is that the permissions are cached to session and thereafter retreived from session to reduce the workload on your database and cut down page loading times, I have also refactored some code for passing over a list of controllers to the view for which the current user has authorization to access the index action, and I have implemented the ability for you to set a permission as for example users:*, which would give a user access to all actions for the given controller.
Within the session permissions are set as controller:action, i.e. users:index, users:view, and there is a wildcard permission of *, which allows unrestricted access to all controllers and actions for development purposes.
The login page
In order for the system to work, you'll need a login page as follows:
app/views/users/login.ctp
echo $form->create('User', array('action' => 'login'));
echo $form->input('email_address',array('between'=>'
','class'=>'text'));
echo $form->input('password',array('between'=>'
','class'=>'text'));
echo $form->end('Sign In');
And that's pretty much all the code you need to get up and running.
Setting the system up
Now to prime the system so that you can access it yourself, start by changing the line in app_controller that reads:
$this->Auth->allow('display');
to read:
$this->Auth->allow('*');
Assuming you have this set up on localhost:
- Go to http://localhost/permissions
- Click on New Permission and enter '*' for the permission name
- Click the submit button.
- Click on New Group and enter 'System Developers' for the group name
- Select the '*' permission.
- Click the submit button.
- Now click on New User
- Enter your email address and a password
- Ensure that Active is ticked
- Select System Developers group
- Click the Submit button
Now change the line of code in app_controller.php that reads:
$this->Auth->allow('*');
back to read:
$this->Auth->allow('display');
And you are all ready to go.
Using the system
By now you have an application where you can add as many users, groups and permissions as you need, each user can belong to multiple groups and each group can have multiple permissions, permissions are expressed as Controller:Action and there is a widecard Controller:* giving controller wide access along with the * wildcard for giving system wide access.
By default all actions except for any named display are protected, but you can override this for any individual controller by adding
function beforeFilter(){
parent::beforeFilter();
$this->Auth->allow('display','and','a','list','of','any','other','actions','here');
}
Every view is passed a variable called $Auth which you can test for to determine whether a user is currently logged in, which is an array of the current users record, along with an array called $permittedControllers which again you can test to see whether it exists and if it does, use it to create a rudimentary admin menu, within an element for example.
That's about it for this time, hope you find this update usefull, if you do, please leave a comment or hit the Share This link to help spread the word, and untill next time...
...happy baking!
Comments
-
1:
Wendy says
on 21/6/08
First off, thanks for your previous post on this. I was able to get your system up and running in minutes. Absolutely a godsend.
However, I just tried out your "new" version from this post and it didn't work the way I expected. In particular, I was using the same database as the "old" version since I am still using autoincremented numbering in most of my online sites. I expected the same permissions and lack thereof to be in play. But I did not get that.
I only have two groups:
Systems Developers group with '*' permissions Staff group with 'users:profile' permissions (this function allows them to edit their own profile as the logged in user).
However, now when a staff member logs in, they are able to get into the groups, permissions, etc.
Any suggestions?
-
2:
Wendy says
on 21/6/08
Ok. My bad. I somehow forgot to copy the line $this->Session->del('Permissions'); into the logout function of the users controller. Definitely explains why.
Keep up the good work and I look forward to seeing what else you come up with!
-
3:
McMapU says
on 23/6/08
Hey ! first thx for the hardwork :) I've been running through all this tutorial, then copy and paste all the files, set up the right folder, follow all the instructions.... Loged in and :
Warning (2): Cannot modify header information - headers already sent by (output started at /home/*****/www/testcake/app/models/user.php:24) [CORE/cake/libs/controller/controller.php, line 576]
or user.php:24 = blank line and end of script
when I click on the warning link this is what I get :
$status = "Location: http://localhost/views/pages/home"
header - [internal], line ?? Controller::header() - CORE/cake/libs/controller/controller.php, line 576 Controller::redirect() - CORE/cake/libs/controller/controller.php, line 557 AuthComponent::startup() - CORE/cake/libs/controller/components/auth.php, line 309 Component::startup() - CORE/cake/libs/controller/component.php, line 98 Dispatcher::start() - CORE/cake/dispatcher.php, line 307 Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 219 [main] - APP/webroot/index.php, line 84
I'm pretty knew to cakephp so forgive me if I ask for help for something maybe I could solved alone if I was more experienced.
is this a problem someone else encountered ? and how to solve it ? thx for your answer :)
-
4:
McMapU says
on 23/6/08
Ok just solved the problem :
http://www.justkez.com/cakephp-cannot-modify-header-information/
it is just about blanc space after the closing ?> php tag to remove
pretty wierd, but anyway now I can use your auth app.
Thx again for the good work :)
-
5:
Nicol says
on 24/6/08
great tutorial but im having a problem when i login it redict me to the cake php home page and i tried to change the login_redic but it doesnt work can u help me to redict the logged user to an index
-
6:
Peter Butler says
on 25/6/08
@Wendy & @McMapU, Glad you both found the system usefull and enjoyed the tutorial, both of you managed to answer your own questions before I could respond.
@Nicol: You set the redirection path with the line $this->Auth->redirect('/') in the beforeFilter of app_controller, not sure why your site is redirecting to the cakePHP home page, my first suggestion would be to check your code carefully for any typo's as this system is in use in quite a few production sites of mine without problem, plus quite a few of the responses I've had suggest that other people are not experiencing the same problem. If you still have problems, either comment again, or contact me by email and I will be glad to help however I can to get the system working for you. (I have sent you a private email, which you can respond to if I can be of any further help).
Many thanx to everyone for the feedback so far
Also if anyone has any requests or suggestions for other components or code snippets either drop me a line(see my contact page for email address) or leave a comment here.
Best Regards
Peter
-
7:
Stas says
on 25/6/08
Hi, thanks a lot for a great system. I have some troubles with it. The system not always logs in :-( I have an another model/controller - Posts, and when I list the posts all is OK, but when I'm going to add/edit/delete one of them, the system logs me off and I cannot log in again before visiting the host again (browser cache? I don't know...).
Can you help me with this please?
-
8:
John says
on 25/6/08
had a nice time goin thru the tutorial... i copy-paste the code as it it, finally when i hit http://localhost/permissions , i get a permissions page with no record , when i hit 'New permission' link , i get a input box for permission and a empty select box. As u said i enter '*' in permission name ,but the page cannot be submitted unless you mention the group and the group selectbox is empty..... plz help
-
9:
John says
on 25/6/08
I tried to manually enter the data in the database..but still when i add new user , i get tis message "Please correct errors below"... if anybody can help plz
-
10:
Peter Butler says
on 26/6/08
@John: Have you added some $validation rules to any of the models, or changed any of the field names, and have you checked your SQL code carefully. Also when you are trying to add a new user, are you getting any kind of error message?
-
11:
Peter Butler says
on 26/6/08
@John: One other thing that just occurred to me, the development environment you are using, have you already used it for developing working applications, the only reason I ask is that there is a mySQL gotcha that has caught me out a couple of times after setting up a new development machine where the line: sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
in the mySQL configuration file (my.ini) needs to be commented out and isn't after a fresh installation. May not be the case with you but its always worth checking through everything as the forms should not require a group to be set when creating your permissions and I know it is working on my system and various other visitors who have gone through the tutorial.
Finally, if you can't get it running, then please zip up your app (including a database dump) and send it over by email and I will gladly take a look at the code and test it on my system for you, you can find my email address on the contact page of this site, though do remember it might not be instant, as well as running this blog, and a local IT company here in Gran Canaria, I am also working on three large scale client projects at the moment and my working days are hitting the 12 hour a day mark at present. But if anyone does have problems, I am always willing to take a look and help anyway I can.
Peter
-
12:
McMapu says
on 27/6/08
Hey Peter, I 've been waiting before asking you to find solutions to my problems, but my knowledge isn't that accurate on cakephp (I'm a beginner) :
1- I successfully installed your users/groups/permissions management system, between I really appreciate the way you thought it, it might be a bit long to set up every permissions for groups and users while you have a lot of them, but when it is all set up it rocks !
2- I'm learning cakephp and testing it with the simple blog tutorial, so I've added your system to the simple blog app, I really apologize if I sound noobish ;)
3- The Permissions : a- for testing purpose I sat up 2 accounts admin and user, admin is part of the System Developers group and user part of the registered group
b- System developers have the '*' permission that gives full control over any actions (controller/functions ?) and the registered has no permissions set up at all. note : So if I followed well your tutorial registered shouldn't have access to any actions at the moment.
problem : registered can still access function like 'add post' or 'edit' or delete'. It looks like registered have the '*' permission to, but they haven't.
question : what did I miss ? I've copy and paste all your tutorial, followed it all step by steps. the login system works perfectly, I'm asked to login if I want to access to http://localhost/posts. I can create new user/group/permission and link them all through the process you sat up.
4- to set up a permission on 'add post' function from posts_controller.php, for example, in the new permission form what is the right syntax : a- posts:add b- add
5- how do I name an action 'display' so it is always available ?
6- as I know you have a lot of work maybe someone else would be kind enough to help me to set up a registration system based on Peter authcomponent. this is all I see missing in you componet.
Again I know I'm asking a lot but and hope you'll have the time to respond
keep up the good work
regards Mc Mapu
-
13:
Peter Butler says
on 27/6/08
@McMapu: Hi, glad you like the system, OK your item 3b, first thing to check is that you added the line:
$this->Session->del('Permissions');to users_controller.php function logout, this deletes the session, if you don't put that in then even if you log out and log back in as the same user, you'll keep the first set of permissions. (See Wendy's comment above)
Item 4. the correct permission would be 'posts:add' or 'posts:*' for access to all post actions.
Not quite sure what you mean with item 5 on your list, action names correspond to the functions you create so just name a function display and put what ever you want to appear in there, if you want other actions to be available to everyone, then in the controller add a function called beforeFunction() and inside that add the lines
parent::beforeFilter(); $this->Auth->allow('and','all','the','open','actions','here');this basically inherits and overrides the allow array of Auth component that is being set in the app_controller version of beforeFilter().
Hope that makes sense (Tis' Friday afternoon), so for example if you wanted 'index' to be available instead of 'display' use
$this->Auth->allow('index');For index and display use:
$this->Auth->allow('index','display');For Item 6 Just create an action in your users controller called 'register' and using the blog tutorial as a guide, create a form and form handler. e.g.
function register(){ if(!empty($this->data){ $this->data['Group']['id']=2;//This adds the user to group id 2 replace with your own group id for registered users. if($this->User->save($this->data)){ $this->redirect(array('controller'=>'pages','action'='registered'); } } }Then add register to your users controller allow array as shown above with
function beforeFilter(){ parent::beforeFilter(): $this->Auth->allow('display','register'); }add a file called register.ctp to app/views/pages with whatever HTML you want for a message to send users to after they have registered, and create a view called register.ctp in app/views/users with code such as:
echo $form->create('User',array('action'=>'register')); echo $form->input('email_address'); echo $form->input('password'); echo $form->end('Join Up');And that should give a basis to begin with. Depending wether you set the default value of User.active to 1 or 0, the user should now be able to log in, or if you want to authenticate or approve their membership you can write code to send email with an authentication code for them to click which will then set active to 1.
Hope that answers your immediate question and gives you a little to go on with the registration functionality you require, May post an article on extending the system with a registration form if anyone else is interested let me know through these comments and I will post something next week.
Peter
-
14:
McMapu says
on 28/6/08
Hey Peter,
this is what I call support :) ! I'm not home at the moment to test your solutions and tips, but thx I really appreciate it.
I'm a graphic designer if you're interested I could work on a template for your authcomponent, I think it would be cool for beginers like me, but also why not for pros, to have a all in one pack authcomponent + registration + template, this way you'll deliver a complete module for cakephp.
from all other authcomponent I've seen, yours is the one which is the most complete and offer unlimmited possibilities. This is really a good point because usually authsytems are too limited.
tell me if you're interested, I've worked a lot on icons and magazines layouts.
Regards
Manuel
-
15:
Paddy says
on 3/7/08
Hey Peter,
First of all thanks for your great tutorial. It was the first one so far that really worked for me. I think it's really good that you explained all steps newbie compatible.
However, I was wondering how your solution relates to the CakePHP's ACL. Does it replace ACL or can it be combined with it? I'm sorry if this question sounds too obvious to you. But I'm still a starter.
Cheers Paddy
-
16:
Peter Butler says
on 3/7/08
@Paddy: Hi, the system doesn't replace ACL, rather it is an alternative that works in a different way for systems that don't require ACL. The motivation for writing this was for a few sites I had that did not really fit the ACL model and in the past ACL has been quite buggy and difficult to work with, That said, ACL can be extremely powerfull and flexible, but this system is much easier to use for beginners and a hell of a lot easier to get to grips with than ACL.
-
17:
John says
on 10/7/08
Hi.. i successfully implemented the system mentioned above...but i have a requirement as in when an user fills the new user registration form and clicks the submit button,the user should be logged-in automatically without having him to go to login page. Is there any way by which we can authenticate the user without having to go to login page i.e pass username and password to Auth variables an redirecting the user..... plz help
-
18:
Peter Butler says
on 11/7/08
@John - Glad you enjoyed the tutorial and had no problems getting everthing up and running, as to your requirement for automatically logging a new user in on registration, first thing would be to change you sql so that User.active is set to true by default, then you can actually use the built in $Auth component method
$this->Auth->login($data);
To accomplish exactly what you are looking to do, check out cake/libs/controller/components/auth.php for full documentation on how to use the call.
Hope that helps
-
19:
John says
on 11/7/08
Peter, great work! Of course there's always a followup question...
I'm trying to implement my own "add_user" view, and action within the user_controller. (mainly because I'm adding a confirm password field) Now, in the view, how do I return the Group Choices that are shown in the scaffolding that's being pulled from the model? I'm studying the API, and helper model, but not quite getting it...
echo $form->create('User', array('action' => 'add_user')); echo $form->input('username'); echo $form->input('password'); echo $form->input('password_confirm', array('type' => 'password'));
echo "the group data that's present"
echo $form->submit(); echo $form->end();
Thanks!
-
20:
Baz L says
on 12/7/08
I have a dumb problem.
I set Auth to allow '*', but it doesn't want to go to anything in the User model, hence I can't create new users.
Any ideas?
-
21:
Baz L says
on 13/7/08
Ok, I solved my above problem (I had two $this->allow('*'), so Auth got confused I guess).
But here's a deeper issue. It's a bit confusing:
Here's the setup: user: admin has no permissions for "groups" controller. And he's not logged in yet. Um, that's about it :)
- try to access /groups/ and get redirected to login page as expected.
- login with "admin" and get infinite redirects.
I've had this problem in my own, cheap permission setup of Auth. It happens when you try to login with a user that doens't have permissions. I'm not sure what the deal is. Any help would be appreciated.
-
22:
John says
on 14/7/08
@Peter: I've got that auto login thing worked .Thanks a lot for ur help.
@All
I don't know where am going wrong, i have replicated the entire code mentioned above. Now when I try to add permissions/group , it gives me this error saying "Please correct errors below." From the posts it is evident that many of you have successfully implemented this system, has any one faced this problem . Any help would be highly appreciated.
-
23:
John says
on 15/7/08
I tried putting some extra code in dbo_source.php to get the exact error and i got this following error:
DATABASE ERROR: 1110: Column 'id' specified twice SQL: INSERT INTO
permissions(id,name,modified,created,id) VALUES ('','ty','2008-07-15 07:33:22','2008-07-15 07:33:22','487c52c2-fcac-453a-9e23-012c050e8a54')Why is it taking the column 'id' twice..... any help wld be appreciated.
-
24:
John says
on 15/7/08
Its due to uuids, refer the link below: https://trac.cakephp.org/ticket/4123
but how come some of you managed to overcome this ???
-
25:
John says
on 15/7/08
finally got it.... referred this to fix the above issue :https://trac.cakephp.org/ticket/4743
Thanks all.....
-
26:
frycake says
on 15/7/08
Hello Peter,
Thanks for your work.
I'm successfully use your code as you shown it above.
But I got a problem when I try to add my test controller which one doesn't use your system.
class TestsController extends AppController { var $name = 'Tests'; var $uses = ''; // No model for this controller
function test () { }}
I'm able to access this controller where as there is no one permission defined for it. I cannot access to other controller (users, permissions ...)
Could you help me ?
-
27:
frycake says
on 15/7/08
It seems that using the name Tests for a controller is not good... :-/ All right, thx for your work
-
28:
fito says
on 15/7/08
Hi Peter.
Thanks a lot for your work. It's a great tut. I was developing a similar system but yours safe me a lot of time.
I live in Gran Canaria too. See you.
-
29:
Baz L says
on 16/7/08
Hey,
ThanX again for this.
Using this I've developed an Authentication plugin. Hope everyone likes it:
http://code.google.com/p/cakeusermanagement/
-
30:
John says
on 21/7/08
Problem with session... Whenever an user logs in he is taken to the page visited by earlier user before logging out instead of taking him to Auth->redirect.
eg: say if user1 logs in and visits pageA before logging out, now when user2 logs in, he is taken to pageA instead of pageIndex . This is happening coz the last visited page is persisted in the session. when i do p_r($this->Session->read()), i get the following:
Array ( [Config] => Array ( [userAgent] => 41499b4277fcf56a9172c0f11b22c639 [time] => 1216636815 [rand] => 12182 [timeout] => 10 ) [Auth] => Array ( [redirect] => /players/add ) )
Now since 'players/add' is stored in the session, next user is take to the same page.
How to destroy that data from session.
But I am bugged with the fact that even after user1 logs out, the data in the session is not destroyed. I tried putting the following code in the logout action, but it was of no use: function logout(){ if ($this->Session->valid()){ $this->Session->destroy(); } }
Thanks in advance
-
31:
Jesse Hattabaugh says
on 22/7/08
Thanks for the tutorial! You say to use the form to create a user, but you don't include any code to encrypt the password before storing it in the DB. When I go to login, it can't find my user, and the SQL log seems to be looking for an MD5 string. How would you reccomend I have the User's password encrypted when it is saved? Model::beforeSave()?
-
32:
Peter says
on 26/7/08
@Jesse: Sorry for the delay in replying, moving home at the moment and had no internet all last week, CakePHP's built in AUTH component automatically hashes all passwords when they are created, in order to create your first user follow the instructions in the section 'Setting the system up'. Also make sure you have the auth component in your app_controller components array
-
33:
Chirayu says
on 29/7/08
I have tried this with plugin named admin. Here is the directory structure. "app/plugins/admin/admin_app_controller.php" and "app/plugins/admin/admin_app_model.php". i have written all the appController code into "admin_app_controller.php". rest is as it is. Now i am getting error :: Class 'User' not found in /var/www/html/cake/app/plugins/admin/admin_app_controller.php on line 97
and on line 97 it is App::import('Model','User');
pls help me out.
-
34:
AaronP says
on 30/7/08
I was thinking of adding "title" and a boolean "inmenu" column to my permissions table and using it to build an admin menu. It's very similar to what I have done in the past with my home grown framework. Is this a bad idea -- should I just use cakephp ACL at this point? Also I was going to throw in some containable behavior when you're caching permissions to the session in __permitted() and I'm not sure if there was a reason you didn't. My User model has many hasMany with nothing to do with authentication.
-
35:
Peter Childs says
on 31/7/08
A really good tutorial - especially for people (like me) learning both to code and cake at the same time.
The comments are really helpful and documenting the directory structure and use cases is fantastic.
You should consider submitting it to the bakery (if you haven't already) and possibly helping the documentation team develop standards for submitted code and the examples in the manual.
A question though - I don't think I saw anything on the license it's made available under.
Also is this the best approach if most of a site open - as each view is checked to see it the user has permission?
-
36:
TommyO says
on 31/7/08
Sorry, but I think this is riddled with holes, even after the refactoring, and would encourage all beginners to discuss this with seasoned Cake users before digging in.
The most glaring is this: $this->set('Auth',$this->Auth->user());
There is no need whatsoever(besides, it is a break of MVC), to access this component from the view. Everything the Auth component does that may be necessary in the view is available in the Session, and reachable using SessionHelper.
I also find the __permitted() to be a kludge. You have a login() method that has access to the User model because it is in the UsersController. Populate the session with your group data then, in 2 lines, and be done with it. Then all of your tests for group data will be possible using either Auth or Session component, without such heavy logic.
Peter, I'm sorry it took me so long to find your blog, and I look forward to more Cake articles.
-
37:
Steve says
on 4/8/08
Hey Peter, great article.
@TommyO:
Though I agree that "$this->set('Auth',$this->Auth->user());" is unnecessary, it does not break MVC. Setting the user array in the view does not equate to "accessing the component from the view"
This is like saying "$this->set('posts', $this->Post->find('all'));" is accessing the model from the view. That's incorrect. All that's being done in both those lines is setting a data array to the view, which is perfectly acceptable and expected.
-
38:
Rob says
on 6/8/08
Hi Peter, Thanks for the great article. I'm just getting started with Cake so maybe I'm missing something. I do have everything installed and operational, but I'm not seeing a way to associate a permission to a group or a user to a group. Did I miss something or am I not understanding a portion of the code? FYI, I can add new users, groups and permissions. Thanks again for taking the time to write this, Rob
-
39:
Sean says
on 7/8/08
Wonderful article, I have one reminder, I had a problem with camel cased controller functions. You simply need to remember that you set low($thisAction) in app controller so that you need add permissions in lower case or it wont work.
Thanks again for your efforts, highly useful!
Sean
-
40:
Chris Hartjes says
on 11/8/08
@Peter
I wrote some of the earliest documentation on using the Auth component, and this is probably the most comprehensive example I've seen.
Why don't you add this information to the online CakePHP manual over at http://book.cakephp.org and help out the entire Cake community.
-
41:
Ben says
on 11/8/08
Is there an easy way to determine from within the controller what the username of the current logged in user is? I'd like to include it in my audit trail. Thanks!
-
42:
Baz L says
on 13/8/08
@Tommy0 I think the "problem" (if you can call it that) is that Auth requires "isAuthorized" to do most of it's heavy lifting.
So trying to put that __permitted functionality in login() would be a weird task.
I think I like Chris' definition of this the best: "How I failed at ACL", lol. ACL is all well and good, when/if you can get it to work, which I haven't been able to.
I'm very grateful for something like this. Also, if you decide to stick this in a CMS or something, your users don't need specific knowledge of ACL to just use it. Things look like URLs, in there, so everyone's happy.
-
43:
chirayu says
on 18/8/08
Undefined index: Permission i get this warning while i login first time, and permissions stored in database is not writing in session array.
-
44:
AaronP says
on 18/8/08
I think the containable version of $thisGroups is like so:
$thisGroups = $thisUser->find( 'first', array( 'conditions' => array( 'User.id' => $this->Auth->user('id') ), 'contain' => array( 'Group' => array( 'Permission' ) ) ) );
-
45:
senseBOP says
on 20/8/08
Hey Peter,
Excellent tutorial you have here! I've been able to easily deploy it and use it with out much headaches, save for one, which took me a while to figure out...
In your app_controller, the following should be changed from this:
foreach($permissions as $permission) { if($permission == '*') {
to this:
// Iterate through permissions for a positive match. foreach($permissions as $permission) { $permission = low($permission); if($permission == '*') {
Without converting the $permission variable to lower-case, one will not be able to create permissions with upper-case letters, such as this: "Users:profile".
It was driving me crazy for a couple of hours, until I decided to go through the permissions code line by line and noticed you're not converting EVERYTHING to the same letter case. :)
Keep up the awesome work!
-
46:
Al says
on 23/8/08
Thanks for sharing. Hope to see more.
-
47:
jothirajan says
on 25/8/08
Hi all
I am new to CAKE PHP FRAMEWORK. Can you guys please give me some ideas on how to make use of this? I have integrated everything from cake_1.1.19.6305.zip but i dont know how to make the further movement....Please give me your review..... where to place the HTML files ,where i can get the result...Please if you....
-
48:
Luke says
on 10/9/08
Hi Peter, great sequence of articles.
I Was wondering if you had any luck with this and RC 2? Havent yet tried it yet, but am about to try tit with RC1 (which is stable anyway) but was just wondeirng on the overall upgradeability of the system of Permissions you outline here.
-
49:
Peter Butler says
on 10/9/08
@Luke - Been using it with RC2 since it was first released and had no problems
-
50:
Chirayu says
on 11/9/08
Hi Peter, Great article and tutorial as well. I am trying to put this system into plugin. everything works fine. But when i use /users/logout, and then if i try to login i get warning of "Undefined index: Permission [APP/plugins/admin/admin_app_controller.php, line 101]" , and interesting part is if i clear cookies of name "CAKEPHP", i can login and this warning doesnt appear, i am using version RC2 of cakephp.
-
51:
Henk says
on 17/9/08
Hi Peter,
Great article!
But in Firefox 3 I have to login 2 times before I am logged in. IE and Opera works great.
Gr Henk
-
52:
Henk says
on 17/9/08
Unbelievable, I found it! It was my favicon.ico which wasn't on the right spot.
-
53:
Sho says
on 22/9/08
Thx. this is really helpful as im new to cakephp.
-
54:
Ivan says
on 2/10/08
Hi peter
If i change the value of $this->Auth->loginRedirect to my preferred value it doesnt work . instead of redirecting to my preferred page it goes all the way to the default localhost/permission . i think the cache is not being cleared properly ?
thanks Ivan
-
55:
Pacfan says
2 weeks, 6 days ago
I suggest that you also convert $permission to lowercase in this part of the code:
if($permission == $controllerName.':*'){
return true;//Controller Wide Bypass Found
}
if($permission == $controllerName.':'.$actionName){
return true;//Specific permission found
}
...since $controllerName and $actionName are both in lower case. The values in the DB might contain uppercase characters.
-
56:
Nahedh says
1 week, 5 days ago
Thanks a lot for this great system.. it works fine .... Now I can use controller:* .. Are there any way to use the *:edit ?
Thanks you again and goodbye for ACL :)
-
57:
Chris W says
2 days, 9 hours ago
Huge thanks for the article, and hats off to you for responding to all the comments. This is exactly what the cake community needs.
I agree with Nahedh, a wildcard for controllers would also be good e.g. a valid permission value of *:delete
Great work and thanks again, Chris
-
58:
Chris W says
2 days, 6 hours ago
Hi again, I have managed to get this working now, it was really easy for me to plug this into a large project I'm working on. I then went on to bake the controllers and then the views for the new users, groups and permissions controllers. Now, when I add a user, the password is not hashed when the record is saved. I assume that the scaffolding does this automatically, but once baked that functionality is not persisted.
Do you know what kind of hashing the scaffolding uses so that I can implement it in my baked user controller? I suspect it is salted as well, but any combination of md5, sha1 and my salt I have tried have not produced the same output.
Thanks, Chris
