obAuth Component Tutorial
Download code
<?php $this->obAuth->lock(array(1)); ?>
At the top of my action should mean only users belonging to usergroup 1 are allowed to use this action.
Before we begin this tutorial, it's important to note that this component is in it's alpha stages. I would love feedback from some of the more experienced bakers out there. Enjoy
1. Download the component
2. Recommended database tables
3. Include the component and create models
4. Required actions
5. Start securing our private areas!
6. Extra Tips & Tricks
1. Download the component
Get the component here: http://bakery.cakephp.org/articles/view/130
2. Recommended database tables
Essentially you'll want a users table and a groups table. The users table will hold the regular user data and a foreign key called "group_id". The groups table is where you store your groups. Your tables should look like the following:
Download code
-- --------------------------------------------------------
--
-- Table structure for table `groups`
--
CREATE TABLE `groups` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(50) NOT NULL default '',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT;
--
-- Dumping data for table `groups`
--
INSERT INTO `groups` (`id`, `name`, `created`, `modified`) VALUES (1, 'Member', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `groups` (`id`, `name`, `created`, `modified`) VALUES (2, 'Admin', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL auto_increment,
`username` varchar(50) NOT NULL default '',
`password` varchar(32) NOT NULL default '',
`fname` varchar(50) NOT NULL,
`lname` varchar(50) NOT NULL,
`email` varchar(100) NOT NULL default '',
`group_id` int(10) unsigned NOT NULL default '0',
`active` tinyint(1) unsigned NOT NULL default '0',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT;
You can do whatever you want with your users table as long as the fields id, group_id, username, password and active are there
3. Include the component and create models
Ok, this is relatively easy. Once you've downloaded the component you need to place it in your /app/controllers/components directory. Once you've placed it in the right directory you need to call it in your controller file. So let's use posts_controller.php as an example:
Model Class:
Download code
<?php
class PostsController extends AppController
{
var $name = 'Posts';
var $components = array("obAuth");
}
?>
Next you should have a User model and a Group model with the proper associations. Users belong to a group and groups has many users. Please refer to this tutorial if you don't understand how to setup associations go here: http://wiki.cakephp.org/docs:understanding_associations
That's it. You can now start using the obAuth component.
4. Required actions
To begin using the obAuth component we need to have to important actions. "Login" and "Logout". They are self-explanatory, so I don't need to tell you what they're for. Instead I'll show you how mine look:
In /app/controllers/users_controller.php
Download code
<?php
function login()
{
if(isset($this->data['User']))
{
if($this->obAuth->login($this->data['User']))
{
$this->redirect('/users');
}
$this->flash("Username/Password is incorrect");
}
}
function logout()
{
$this->obAuth->lock();
$this->obAuth->logout();
$this->flash('You are now logged out.');
$this->redirect('/');
}
?>
Notice I use a method obAuth::login($data). This method is used to check the data posted with a user in the database. If one exists then the user will be authenticated
Now for the view you really only need one for the login action because logout just redirects. So the view should look something like this
View Template:
Download code
<?php echo $html->formTag('/users/login')?>
<fieldset>
<legend>User Login</legend>
<label for="username">Username: </label>
<?php echo $html->input('User/username', array('style' => 'width: 150px'))?><br />
<label for="password">Password: </label>
<?php echo $html->password('User/password', array('style' => 'width: 150px'))?><br />
<label for="submit"> </label><br />
<?php echo $html->submit('Sign In')?>
</fieldset>
</form>
5. Start securing your private areas
Alright, now that we have our login and logout actions we can get users authenticated. So let's start securing our actions by setting the permissions. Let's say we have an action in our Posts controller called "add" and we only want users from the group id "3" allowed to use it. Well this is all we need to do to secure the action
Download code
<?php
function add()
{
$this->obAuth->lock(array(3)); // Only users with the group_id '3' are allowed here
if (!empty($this->data))
{
if ($this->Post->save($this->data))
{
$this->flash('Your post has been saved.','/posts');
}
}
}
?>
Now notice that 1 line of code used to secure this action. It uses obAuth::lock($groups=null, $redirect=null). The array passed is an array of group ids allowed to access this action. So if you had Super Administrators (3) and Administrators (2) and wanted them to have access to this action you would write:
Download code
$this->obAuth->lock(array(2,3));.
If you leave $groups parameter empty then it'll allow any AUTHENTICATED user to access the action. So you're still securing it from anonymous visitors. The $redirect parameter allows you set a url that will redirect a user who's not allowed to access the action. If you leave it empty it will redirect the user to the login page.
Comments
Question
1 associations
Comment
2 Associations
You need to create associations between user and group - or get Bake.php to do it for you.
Question
3 Lock() inside views
Comment
4 Lock() inside views
Lock is more for preventing actions from being run from unauthorized user/groups.
If a user has authorization to use an action but shouldn't see certain links, you'll have to put if statements in your view where user ID and group ID are the conditions in it to only show the link to authorized users.
Bug
5 user is seen as array not object
When I tried this tutorial out, it worked, but I got the Notice:
Notice: Trying to get property of non-object in C:webaclusc-legappcontrollerscomponentsob_auth.php on line 73
Line 73 was:
$usergroup = $this->user->usergroup;
Its seems as if the line in obAuth::startup()
$this->user = $this->Session->read($this->sesskey);
creates an array rather than an object. So in my test, $this->user->usergroup does not exist, but I could get a usergroup id by using:
$usergroup = $this->user['Group']['id'];
Is there something I'm missing? Should $this->user be an object with the property usergroup?
Bug
6 Bug
You can replace:
$usergroup = $this->user->usergroup;
With:
$usergroup = $this->user['Group']['id']; // fix
Or why not use the "function getGroupId()" in the code?
$usergroup = $this->getGroupId(); // fox
Comment
7 Bug fix
Nate:
Yes, that's a good way of doing it as well and I may change it to that way. As mentioned this is still in alpha stages, haven't had time recently to tweak it, but will be doing so soon.
Ultimately I want the option for it to be controlled manually (Put in the permissions right in the code) or automatically (handle permissions through admin interface and storing them in DB)
Question
8 where is obauth
i've been recommended to use obauth by a friend of mine.
the link to http://bakery.cakephp.org/articles/view/130 is inactive. has this auth method evolved to something better or is it gone for good?
Comment
9 obauth back
Comment
10 Tutorial should mention that the password stored in the Db should be hashed...
Question
11 using lock() in beforeFilter()
Comment
12 Helper
And suddenly Session->valid - doesn't work
But in view you can get TRUE, or FALSE and combine your view elements for admin/members/etc.
class obAuthHelper extends Helper {
var $helpers = array (
'Session'
);
var $sesskey = "mYpERsOnALhaSHkeY";
// Check is user is part of usergroup specified
function lock($groups=null)
{
$hasAccess = false;
if ($this->Session->check($this->sesskey))
{
$this->user = $this->Session->read($this->sesskey);
}
if (!empty($this->user))
{
if (!empty($groups))
{
foreach ($groups as $group)
{
if ($this->user["Group"]['id'] == $group || $this->user["Group"]['name'] == $group)
$hasAccess = true;
}
}
else
{
$hasAccess = true;
}
}
if ($hasAccess) return 1;
else return 0;
}
}
Comment
13 RE using lock() in beforeFilter()
I use something like this
function beforeFilter(){
$this->obAuth->startup($this);
if(isset($this->params['admin']))
{
$this->obAuth->lock(array(1,2));
}
Comment
14 Getting this error...
Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /home/6813/domains/cheesecake.convandesign.com/html/app/controllers/components/ob_auth.php:118) in /home/6813/domains/cheesecake.convandesign.com/html/cake/libs/session.php on line 146
Warning: Cannot modify header information - headers already sent by (output started at /home/6813/domains/cheesecake.convandesign.com/html/app/controllers/components/ob_auth.php:118) in /home/6813/domains/cheesecake.convandesign.com/html/cake/libs/session.php on line 147
Comment
15 Getting this error...
Question
16 Error msg.
I receive the following error, while literally following yout tutorial:
Notice: Trying to get property of non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 72
Notice: Trying to get property of non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 74
Fatal error: Call to a member function redirect() on a non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 96
Bug
17 Authentication gone wrong
When calling an action, it seems that although the lock method tries to redirect the browser (to the deny page) the controller does finish the remaning code before the actual redirection takes place!!!
This means that calling for instance http://domain.com/blog/delete/3
Does not require me to be logged in to delete the blog with id=3 although the first line of code in this action is:
$this->obAuth->lock(array(1));
.........
Ofcourse you are able to fix this problem by letting the lock method return true/false and using an if statement in your action to determine the access of the user
function action() {
if($this->obAuth->lock(array(1))) {
do something securely!
}
}
Also you have to add: "return $hasAccess;" just before the closing } of the lock method inside the obAuth component.
Comment
18 Update to bug report
Could you please fix this is the component on the linked page, because this can cause serious problems, since it is easily missed!
Comment
19 Get errors...
in the lines i have:
72: if ($this->controller->action != "login")
74: $this->last_page = $this->controller->here;
96: $this->controller->redirect($page);
Might Ob or someone else help me here??
Question
20 User in more than one group
Is it possible to use this auth component with Users -> HABTM -> Groups so that a User can belong to more than one group?
It seems like authentication would be more flexible this way.
Or is there an existing authentication component that does this?
Thanks. Later...
Comment
21 Major Security Issue
YES it sucked BIG time. However it was a very easy fix (thanks to Bo's advice) and simply putting an exit(); after the redirect in the lock method of ob_auth.php (this is after it does the whole redirect if you don't have access, before that if statement closes..so the if(!$hasAccess)...part) solves the issue quite nicely.
If someone directly puts in www.com/controller/delete/5 for example, it will direct them to log in and all will be fine.
This is a MAJOR security issue that I'm glad it was simple to fix.
Comment
22 Not working on Cake 1.1.18
Anybody know how to fix this?
Comment
23 Cake 1.18
Bug
24 flash vs. setFlash
Comment
25 Working like a charm
Comment
26 Add Ajax Request Support
var $components = array('Session','RequestHandler');
...
if(!$hasAccess)
{
if (!$this->RequestHandler->isAjax()){
$page = (!empty($redirect)) ? $redirect : $this->login_page;
$this->controller->redirect($page);
} else {
$this->controller->render('..'.$this->login_page,'ajax');
}
}
...
that`s all.
Comment
27 Does not stay logged in
Any ideas?