Try Tuts+ Premium, Get Cash Back!
How to Use CakePHP’s Access Control Lists

How to Use CakePHP’s Access Control Lists

Tutorial Details
  • Topic: CakePHP (PHP)
  • Difficulty: Intermediate
  • Estimated Completion Time: 1 hour

If you’re building a CMS, you’ll probably need different user roles—superusers, admins, users—with different permission levels. Too complicated to code? Enter CakePHP’s ACL (Access Control Lists). With the right setup, you’ll be checking user permissions with just one line.

Introduction: What Are Access Control Lists?

The ACL lets you create a hierarchy of users with their respective roles. Here’s a quick example.

  • Super Users
    • User #1
  • Admins
    • User #2
    • User #3
  • Users
    • User #4
    • User #5
    • User #6
    • ….

In this tutorial, we will be setting up an ACL for a simple blog. If you haven’t yet checked out Getting Started With CakePHP (and Part 2) here on Nettuts+, please do so and then return, as we will be taking for granted the framework basics.

With this hierarchy, we can assign several permissions for each role:

  • The Super Users can create, read, update and delete Posts and Users.
  • The Admins can create, read, update and delete Posts.
  • The Users can create and read Posts.
  • Everyone else can just read Posts.

Each permission will be given to the group, not to the user; so if user #6 is promoted to Admin, he will be checked against the group permission –not his. These roles and child nodes (users) are called Access Requests Objects, or AROs.

Now, on the other side, we have the Access Control Objects, or ACOs. These are the objects to be controlled. Above I mentioned Posts and Users. Normally, these objects are directly linked with the models, so if we have a Post model, we will need an ACO for this model.

Each ACO has four basic permissions: create, read, update and delete. You can remember them with the keyword CRUD. There’s a fifth permission, the asterisk, that is a shortcut for full access.

We will be using just two ACOs for this tutorial: Post and User, but you can create as many as you need.

The ACL Tables

Let’s proceed to create the database tables. You can find this code in db_acl.sql inside your app’s config/sql directory.

CREATE TABLE acos ( 
  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
  parent_id INTEGER(10) DEFAULT NULL, 
  model VARCHAR(255) DEFAULT '', 
  foreign_key INTEGER(10) UNSIGNED DEFAULT NULL, 
  alias VARCHAR(255) DEFAULT '', 
  lft INTEGER(10) DEFAULT NULL, 
  rght INTEGER(10) DEFAULT NULL, 
  PRIMARY KEY  (id) 
); 
 
CREATE TABLE aros_acos ( 
  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
  aro_id INTEGER(10) UNSIGNED NOT NULL, 
  aco_id INTEGER(10) UNSIGNED NOT NULL, 
  _create CHAR(2) NOT NULL DEFAULT 0, 
  _read CHAR(2) NOT NULL DEFAULT 0, 
  _update CHAR(2) NOT NULL DEFAULT 0, 
  _delete CHAR(2) NOT NULL DEFAULT 0, 
  PRIMARY KEY(id) 
); 
 
CREATE TABLE aros ( 
  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
  parent_id INTEGER(10) DEFAULT NULL, 
  model VARCHAR(255) DEFAULT '', 
  foreign_key INTEGER(10) UNSIGNED DEFAULT NULL, 
  alias VARCHAR(255) DEFAULT '', 
  lft INTEGER(10) DEFAULT NULL, 
  rght INTEGER(10) DEFAULT NULL, 
  PRIMARY KEY  (id) 
); 

We can begin now creating ARO and ACO nodes, but hey, we don’t have users! We’ll have to create a basic authentication system.


Step 1: A Basic Authentication System

As this tutorial is intended for CakePHP developers with a basic to moderate knowledge of the framework, I will be supplying the code and a brief explanation. However, the authentication system is not the goal of this tutorial.

The MySQL table:

CREATE TABLE users ( 
  id INTEGER(10) UNSIGNED AUTO_INCREMENT KEY, 
  username TEXT, 
  password TEXT 
); 

The User model (models/user.php)

<?php 
class User extends AppModel { 
    var $name = 'User'; 
} 
?> 

The Users controller (controllers/users_controller.php)

<?php 
class UsersController extends AppController { 
    var $name = 'Users'; 
    var $components = array('Auth'); 
 
    function beforeFilter(){ 
        $this->Auth->userModel = 'User'; 
        $this->Auth->allow('*'); 
    } 
 
    function register(){ 
        if(!empty($this->data)){ 
            // Here you should validate the username (min length, max length, to not include special chars, not existing already, etc) 
            // As well as the password 
            if($this->User->validates()){ 
                $this->User->save($this->data); 
                // Let's read the data we just inserted 
                $data = $this->User->read(); 
                // Use it to authenticate the user 
                $this->Auth->login($data); 
                // Then redirect 
                $this->redirect('/'); 
            } 
        } 
    } 
 
    function login(){ 
        if(!empty($this->data)){ 
            // If the username/password match 
            if($this->Auth->login($this->data)){ 
                $this->redirect('/'); 
            } else { 
                $this->User->invalidate('username', 'Username and password combination is incorrect!'); 
            } 
        } 
    } 
 
    function logout(){ 
        $this->Auth->logout(); 
        $this->redirect('/'); 
    } 
} 
?> 
 

Since we have new elements, let’s review them. First, we are setting a $components variable. This variable includes all the components in the array. We will be needing the Auth component, which is a core component, as are HTML and Form helpers, but since it isn’t included by default by Cake, we will have to include it manually.

The Auth component handles some basic authentication mechanics: it helps us to login a user and it handles an authenticated user’s session for us, as well as handling the log out and basic authorization for guests. Also, it hashes the password automatically. I’ll be explaining how to call each function in the following paragraphs.

Next, we are creating a function called beforeFilter. This is a callback function and it lets us set some actions before all the controller logic is processed. The Auth component requires us to specify a model to be used, in this case the User. Then, by default, it will deny all the access to users not logged in. We’ll have to overwrite this behaviour with allow() which requires one parameter. That parameter can be an asterisk, specifying that all methods inside said controller can be accessed by unauthenticated users. Or, it can be passed an array with the functions that can be accessed by unauthenticated users. In this case, since we have just three functions, the following lines are the same thing.

$this->Auth->allow('*'); 
$this->Auth->allow(array('register', 'login', 'logout')); 

For the login() function, the Auth component will handle all the login mechanics for us. We will just have to supply the function with an array with two keys: the username and the password. These keys can be changed, but by default, both username and password fields will be matched against the database and will return true if the user has been authenticated.

Finally, the controller’s login function will try to match a username/password combination against the database.

Please note that this code is very basic. You’ll need to validate the username characters, if the username exists, the minimum length for the password, and so on.

The Register view (views/users/register.ctp)

<h2>Register your account</h2> 
 
<form method="POST" action="<?=$this->here; ?>"> 
 
<p> 
    Username 
    <?=$form->text('User.username'); ?> 
</p> 
<p> 
    Password 
    <?=$form->password('User.password'); ?> 
</p> 
 
<?=$form->submit('Register'); ?> 
</form> 

The Login view (views/users/login.ctp)

<h2>Log in to your account</h2> 
 
<form method="POST" action="<?=$this->here; ?>"> 
 
<?=$form->error('User.username'); ?> 
<p> 
    Username 
    <?=$form->text('User.username'); ?> 
</p> 
<p> 
    Password 
    <?=$form->password('User.password'); ?> 
 
</p> 
<?=$form->submit('Log in'); ?> 
</form> 

Open /users/register in your web browser and register a new account. I suggest admin as username and 123 as password and if your session expires just go to /users/login and enter the correct username/password combination you just created.


Step 2: Denying Access To Unauthenticated Users

We’re not even working with ACL, but we already can deny posting, editing and deleting posts. Open your Posts controller and add the Auth component.

var $components = array('Auth'); 

Now go to /posts in your web browser. If you’re logged in, you should see the posts, but if you’re not, you will be redirected to /users/login. By simply including the Auth component, all the actions are by default, denied for guests. We need to deny three actions for unauthorized users: create, edit and delete. In other terms, we’ll have to allow index and view.

function beforeFilter(){ 
    $this->Auth->userModel = 'User'; 
    $this->Auth->allow(array('index', 'view')); 
} 

Go to edit or create a post; if you’re not logged in, you should be redirected to /users/login. Everything seems to be working quite good, but what about the views? The edit and delete links are being shown to everybody. We should make a conditional.

But before going into that, let’s see how Auth’s user() function works. Copy and paste these lines into the index function.

$user = $this->Auth->user(); 
pr($user); 
 

Open your /posts in your browser and, if logged in, the pr() will throw something like this.

Array 
( 
    [User] => Array 
        ( 
            [id] => 1 
            [username] => admin 
        ) 
) 
 

The user() function returns an array just as a model would do. If we had more than three fields (password is not included), they will be shown in the array. If you’re not logged in, the array will be empty, so you can know a user is logged in if Auth’s user() array is not empty.

Now, Auth is a component, meant to be used in a controller. We need to know from a view if a user is logged in, preferably via a helper. How can we use a component inside a helper? CakePHP is so awesome and flexible that it is possible.

<? 
class AccessHelper extends Helper{ 
    var $helpers = array("Session"); 
 
    function isLoggedin(){ 
        App::import('Component', 'Auth'); 
        $auth = new AuthComponent(); 
        $auth->Session = $this->Session; 
        $user = $auth->user(); 
        return !empty($user); 
    } 
?> 
 

Save this snippet in views/helpers as access.php. Now let’s see the code, line by line. First, we are setting up a $helpers var. Helpers can include other helpers, just like $components can. The Session component is required for the Auth component, but we have no access to this component inside a helper. Fortunately we have a Session helper, that will help us.

Next, we create a function and use App::import which will let us import an element that normally we wouldn’t have access to. The next line creates the Auth component in a $auth variable, and now a little dirty hack; since the Auth component reads the session to know if we are logged or not, it requires the Session component, but as we are importing it from a place it shouldn’t belong to, we’ll have to give it a new Session object. Finally, we are using user() and setting it to $user and returning true if the variable is not empty, otherwise false.

Let’s get back to the posts controller and proceed to add the helper.

var $helpers = array('Access'); 

The access helper is now accessible from the view. Open index.ctp in views/posts and replace this line.

<small><a href="/posts/edit/<? echo $post['Post']['id'] ?>">edit</a> | <? echo $html->link('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Are you sure?'); ?></small> 
 

With this one.

<? if($access->isLoggedin()): ?><small><a href="/posts/edit/<? echo $post['Post']['id'] ?>">edit</a> | <? echo $html->link('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Are you sure?'); ?></small><? endif; ?> 
 

Go back to your web browser, reload the index page and if you’re logged in, you’ll see the edit and delete links for each post. Otherwise, you’ll see nothing.

While this would be enough if you have an app with one or two users, it is not enough if you have registrations open.


Step 3: Installing ACL

Open the Users controller and add the ACL component.

var $components = array('Auth', 'Acl'); 
 

Next, let’s create a function to install the ACOs and AROs nodes.

function install(){     
    if($this->Acl->Aro->findByAlias("Admin")){ 
        $this->redirect('/'); 
    } 
    $aro = new aro(); 
 
    $aro->create(); 
    $aro->save(array( 
        'model' => 'User', 
        'foreign_key' => null, 
        'parent_id' => null, 
        'alias' => 'Super' 
    )); 
 
    $aro->create(); 
    $aro->save(array( 
        'model' => 'User', 
        'foreign_key' => null, 
        'parent_id' => null, 
        'alias' => 'Admin' 
    )); 
 
    $aro->create(); 
    $aro->save(array( 
        'model' => 'User', 
        'foreign_key' => null, 
        'parent_id' => null, 
        'alias' => 'User' 
    )); 
 
    $aro->create(); 
    $aro->save(array( 
        'model' => 'User', 
        'foreign_key' => null, 
        'parent_id' => null, 
        'alias' => 'Suspended' 
    )); 
 
    $aco = new Aco(); 
    $aco->create(); 
    $aco->save(array( 
        'model' => 'User', 
        'foreign_key' => null, 
        'parent_id' => null, 
        'alias' => 'User' 
    )); 
 
    $aco->create(); 
    $aco->save(array( 
       'model' => 'Post', 
       'foreign_key' => null, 
       'parent_id' => null, 
       'alias' => 'Post' 
    )); 
 
    $this->Acl->allow('Super', 'Post', '*'); 
    $this->Acl->allow('Super', 'User', '*'); 
    $this->Acl->allow('Admin', 'Post', '*'); 
    $this->Acl->allow('User', 'Post', array('create')); 
} 
 

By importing the ACL component, we can access the ACOs and AROs model. We begin by creating an ARO, which is the requester for the objects; in other words, who will be accessing the objects. That would be the user (hence the User string in model). We are creating different roles; Super, Admin, User and Suspended.

Next, we do the same with ACOs, since we just have two objects to manage (Posts and Users), we’ll create two, one for each.

Now, a quick note. The array that we are saving for both ACOs and AROs have four fields: the model name, foreign key (which is useful if you want to give access to one ARO, like a post he created), parent id (which will be used later and is the basic parent-child nodes relationship; we will be creating users below these roles), and the alias (which is a quick form to find the objects).

Finally, we are using ACL’s allow() function. The first parameter is the ACO alias; second, the ARO alias, and third, the permissions given to said ARO. A Superuser has full access to the Post and User models, an Admin has full access to the Post model, and a User can just create posts.

At the function’s beginning, I declared a conditional to check if the Admin role exists in ACOs. You don’t want to install the same thing more than once, do you? It would mess up the database quite badly.

Open /users/install in your web browser, and, since we don’t have a view, CakePHP will throw an error, but just check the MySQL dump. All the relationships have been successfully created, it’s time to work with the child nodes.

Setting users to roles

Let’s clean the users table. Open phpMyAdmin, select your database, the users table and click empty. We’ll get back to the register() function on the Users Controller. Just below this line:

 $this->Auth->login($data); 

Paste this code:

// Set the user roles 
$aro = new Aro(); 
$parent = $aro->findByAlias($this->User->find('count') > 1 ? 'User' : 'Super'); 
 
$aro->create(); 
$aro->save(array( 
     'model'        => 'User', 
     'foreign_key'    => $this->User->id, 
     'parent_id'    => $parent['Aro']['id'], 
     'alias'        => 'User::'.$this->User->id 
)); 
 

In the first line we create a new ARO object. Then we’ll get the parent node in which the user will be created. If there’s a record in the database, we’ll set it to the User ARO, else, the first user should be the Super.

Then we’ll save a new ARO; the model is the one we are working currently on, User, the foreign_key is the last record’s id we just created. The parent_id is the node we began with; we’ll just pass it the id and finally the alias. It is a good idea to call it Model_name, then a separator ::, and then the identifier. It will be much easier to find it.

Now we’re done. Create four users: superuser, adminuser, normaluser and suspendeduser. I suggest the same username and password for testing purposes. Don’t forget that, until this point, only the superuser has a role of Super; all the remaining will be Users!


Step 4: Reading Permissions

Because ACL is a component, it is accessible only within the controller. Later, it will be also in the view; but first things first. Include the ACL component in the Posts controller, as we did in the Users controller. Now you can begin checking permissions. Let’s go to the edit function and do a quick test. In the first line of said method, add this.

$user = $this->Auth->user(); 
if(!$this->Acl->check('User::'.$user['User']['id'], 'Post', 'update')) die('you are not authorized'); 
 

ACL’s check() function needs three parameters: the ARO, the ACO and the action. Go and edit a post, and, if you’re not logged in as the super user, the script will die. Go to /users/login and access as the Super user and go back to editing. You should be able to edit the post. Check below the MySQL dump to see the magic. Four database queries: that sure isn’t scalable.

We have two issues. First, two lines for the permissions check. Second, the ACLs aren’t being cached. And don’t forget all the logged users can see the edit link, even if just the Super user can use it.

Let’s create a new component. Let’s call it Access.

<?php 
class AccessComponent extends Object{ 
    var $components = array('Acl', 'Auth'); 
    var $user; 
 
    function startup(){ 
        $this->user = $this->Auth->user(); 
    } 
} 
?> 

Save it in controllers/components as access.php. The class’s $user var will be initiated as the component is loaded, and startup() is a callback function, so it’s now set in the class. Now let’s create our check() function.

function check($aco, $action='*'){ 
    if(!empty($this->user) && $this->Acl->check('User::'.$this->user['User']['id'], $aco, $action)){ 
        return true; 
    } else { 
        return false; 
    } 
} 

Our check() method will need just two parameters: the ACO and the action (which is optional). The ARO will be the current user for each session. The action parameter will default to *, which is full access for the ARO. The function begins by checking if the $this->user is not empty (which really tells us if a user is logged in) and then we go to ACL. We have already covered this.

We can now include the Access component in our Posts controller and check the permissions with just one line.

if(!$this->Access->check('Post', 'update')) die('you are not authorized'); 

The same result is achieved with less code, but the error message is ugly. You’d better replace the die() with the CakePHP error handler:

$this->cakeError('error404'); 

Permissions in views

This might be ugly, but it works. We’ll have to create a helper that loads a component with a custom method for use in the helper.

Add this function in the Access component (controllers/components/access.php).

function checkHelper($aro, $aco, $action = "*"){ 
    App::import('Component', 'Acl'); 
    $acl = new AclComponent(); 
    return $acl->check($aro, $aco, $action); 
} 
 

Now,let’s rewrite the access helper (views/helpers/access.php).

<?php 
class AccessHelper extends Helper{ 
    var $helpers = array("Session"); 
    var $Access; 
    var $Auth; 
    var $user; 
 
    function beforeRender(){ 
        App::import('Component', 'Access'); 
        $this->Access = new AccessComponent(); 
 
        App::import('Component', 'Auth'); 
        $this->Auth = new AuthComponent(); 
        $this->Auth->Session = $this->Session; 
 
        $this->user = $this->Auth->user(); 
    } 
 
    function check($aco, $action='*'){ 
        if(empty($this->user)) return false; 
        return $this->Access->checkHelper('User::'.$this->user['User']['id'], $aco, $action); 
    } 
 
    function isLoggedin(){ 
        return !empty($this->user); 
    } 
} 
?> 
 

The beforeRender() method is a callback, similar to the component’s startup(). We are loading two components and since these will be used in most functions, it’s a good idea to start all at once, than to manually start them each time the method is called.

Now on your index.ctp view in views/posts you can replace this line.

<small><a href="/posts/edit/<? echo $post['Post']['id'] ?>">edit</a> | <? echo $html->link('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Are you sure?'); ?></small> 

With this one.

<? if($access->check('Post')): ?><small><a href="/posts/edit/<? echo $post['Post']['id'] ?>">edit</a> | <? echo $html->link('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Are you sure?'); ?></small><? endif; ?> 

Just don’t forget to check permissions, both in the views and the controllers!

User data

You can access the user data for a logged user with the user() method in the Auth component. Then you can access the array and get the info you want. But there must be a better way. Let’s add the following function in the Access component.

function getmy($what){ 
    return !empty($this->user) && isset($this->user['User'][$what]) ? $this->user['User'][$what] : false; 
} 
 

This is quite useful when you need to save a Post with a user_id relationship.

$this->data['Post']['user_id'] = $this->Access->getmy('id'); 

And in the view, we can do something similar with the helper.

function getmy($what){ 
    return !empty($this->user) && isset($this->user['User'][$what]) ? $this->user['User'][$what] : false; 
} 

In your template file you can do something like below to greet a user by his username.

Welcome <?=$access->isLoggedIn() ? $access->getmy('username') : 'Guest'; ?> 

Step 5: Modifying Permissions

Let’s say we need to switch roles for the user #4: he needs to be a Super User. So, User’s id is 4 and Aro’s id is 1.

$user_id = 4; 
$user_new_group = 1; 

Now we need to find the User’s Aro in order to modify its parent Aro.

$aro_user =  $this->Acl->Aro->find('first', 
    array( 
        'conditions' => array( 
            'Aro.parent_id !=' => NULL, 
            'Aro.model' => 'User', 
            'Aro.foreign_key' => $user_id 
        ) 
    ) 
); 
 

Finally, if the $aro_user variable is not empty, let’s update the Aro.parent_id field.

if(!empty($aro_user)){ 
    $data['id'] = $aro_user['Aro']['id']; 
    $data['parent_id'] = $user_new_group; 
    $this->Acl->Aro->save($data); 
} 

Please note that you have to validate both the user’s id and the new aro’s id. If one of these do not exist, it will create a mess in your ACL tables.


Step 6: Optimization

Although the component we just created are both simple and useful, the database is queried with each and every check. It is not ready for production yet. First, the database should be queried as little as possible. To optimize this, we should take advantage of CakePHP’s Cache.

Using Cache will reduce the load quite a bit, but if we have ten posts showing up in the index, and, for each one, we are checking if the user has update permissions for the Post Aco, the framework will be reading and parsing a file to return the same result…ten times for each page load.

That’s the second point: a variable inside the class and some conditionals will make the work lighter, so that a repeated request will be checking against Cache just once.

Both these changes are reflected in access_cache.php, inside the controllers/components directory. So make sure you download the source!


Conclusions

Access Control Lists are a basic features that most apps need. CakePHP has a nice implementation, but lacks in both good documentation and examples. I hope that, with this tutorial, these two issues will now be covered. Thanks for reading!

Rafael Soto is faelsoto on Codecanyon
Tags: cakePHP
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://tundeadeyemi.blogspot.com Babatunde Adeyemi

    Thanks for shedding more light on the most complicated part of CakePHP

  • Dan

    Nice, finally some CakePHP stuff on NetTuts.

    • http://www.therror.com Rafael Soto
      Author

      What feature would you like to be consider for another Cake tutorial?
      Thanks for your comments.

      • John

        how about a cakephp from scratch like the codeigniter from scratch?

      • Dan

        I agree, a CakePHP from scratch series would be great. Then I think people will see CakePHP’s strengths over Codeigniter : )

      • http://www.jacobwhenderson.com Jacob Henderson

        Cakephp from scratch series would be so rad!

      • Jimp

        CakePHP from scratch series would be GREAT! :D

      • http://alexwind.com Alex Wind

        Definitely CakePHP from Scratch, Cake is better than CodeIgniter :D

    • sophy

      I like cakephp from scratch series. It will good if have this series

  • http://apps.facebook.com Josh

    Thanks for posting this! I’ve created a Facebook app on CakePHP but I didn’t use the ACL because I was only learning the basics when I started. Now that I have a little more experience I’d like to use it in my next endeavor.

    However, there’s one thing that bugs me with it (from what I understand), and it’s that you have to create a row in the database for each method users might run? To me it’s an annoying extra step and it caused me a lot of confusion reading through their 1.3 Book on it.

    Maybe I’m just not familiar with it enough, though, which is definitely a possibility :D

    • http://www.therror.com Rafael Soto
      Author

      Uhm, not for each method, but for each resource. If you have an online auction site, you should create a method for users, listings, bids and comments. Then you can create roles: superadmin, admin, moderators, normal users and suspended users.

      But if you have a simple app, you can create a ‘Stuff’ resource and assign it to the different roles:

      $this->Acl->allow(‘Admin’, ‘Stuff’, ‘*’);
      $this->Acl->allow(‘User’, ‘Stuff’, ‘read’);

      Then you can $access->check(‘Stuff’, ‘*’); if it’s an admin or $access->check(‘Stuff’, ‘read’); if it is at least an user.

  • http://www.seo-geek.de Daniel

    Thank you very much for sharing knowledge about ACL.
    I think it’s the most complicated function in cakePHP. Now it’s a lot easier to understand :)

  • Steinkel

    Nice tut about one weird part of the great cakephp project.
    I like the built-in acl system because it’s so powerful, but I think it’s too complicated.
    If you need acl+auth and you like simple things, you can try one of many auth and acl plugins available for cakephp.

    Check this one:
    (SparkPlug) simple auth and acl for cakephp -> http://github.com/jedt/spark_plug

    Direct download link here -> http://github.com/jedt/spark_plug/archives/master

    Please tell me if you use it or need improvements !

  • Red

    Yeah finnaly some CakePHP. Nice article, many thanks!

  • http://www.crearedesign.co.uk Steve Maggs

    Like a lot of people I’ve been put off CakePHP because of the lack of documentation for this kind of thing so this is a really useful post. I have a site that needs a CMS to build soon so will definitely use this for reference when doing it.

  • http://www.jsxtech.com Jaspal Singh

    Thanks Rafael,
    I’m waiting for your next article on CakePhp.

    • http://www.therror.com Rafael Soto
      Author

      Thanks. What would you like that tutorial be about?

  • damith

    I also want more cakephp stuff!!!
    cakephp rocks

  • http://www.duncanbrown.me.uk/ Duncan Brown

    Nice in depth tutorial, but most of what you have shown breaks all MVC and cakePHP conventions. There are better ways of completing these things with what is already in the cakePHP core.

    Session data for the logged in user can be retrieved using the session helper, http://book.cakephp.org/view/1465/Session.

    There is no need to check if the current user has permission within the view, this can be done in the controller and then pass a variable to the view with “set”.

    When creating new users, the best option is to create the aro’s in the model using the Acl behavior, as show in the docs here http://book.cakephp.org/view/1547/Acts-As-a-Requester.

    • http://www.therror.com Rafael Soto
      Author

      What part is breaking MVC and CakePHP conventions? Yes, you can check whether or not a user is logged in with $session, but what about his permissions?

      if(!empty($session->read(‘User.id’)) && $access->check(‘Post’))

      Why do you have to make it so complicated if you already are creating a custom helper that can handle session data for you.

      if($access->check(‘Post’))

      Yes, you can set it in the controller, but it will create a mess in your controller:

      $this->set(‘user_can_post’, $this->Access(‘Post’, ‘write’));
      $this->set(‘user_can_read’, $this->Access(‘Post’, ‘read’));;
      $this->set(‘user_can_update, $this->Access(‘Post’, ‘update’));
      $this->set(‘user_can_everything, $this->Access(‘Post’, ‘*’));

      And about the last link you posted, I don’t quite get it. ACL by itself is hard to understand, do you think that would fit right in the tutorial?

      However, no harsh feelings here. One problem can be solved one way or another, just that I’ve coded a few apps and this is the way it makes the most sense to me. And hopefully to the readers, too.

      • Michael

        Thanks for a very lucid discussion about Views and ACL. This one area that has been very poorly covered in other CakePHP ACL discussions. And should be handled better with a dedicated ACL Helper. I find it inconceivable that CakePHP makes so much of it’s ACL support but leaves views out of the picture completely – as if users would accept hit “Unauthorized!” when they click on an action.

        Would it be possible for you to expand a bit on how your performance optimizations work? I sort of get the caching but how do you reduce the cache hits?

  • http://www.jacobwhenderson.com Jacob Henderson

    WOW this is great. Thanks! In my opinion, Cakephp is way better than code igniter.

    I would love to see more cake tutorials here. Even very simple stuff, Like how to make a blog, or how to deal with static pages.

    or something more advanced like an e-commerce app.

    anything would be much appreciated.

    Thanks again!

  • http://p-hp.blogspot.com vikas

    Relay good article, i will be trying ALC today. thanks a lot.

  • http://www.unifiedarts.de Flowreen

    Yes i agree… great tutorial for cakePHP users! I would like to see more of these at nettuts. I Would be nice to have something like this as a screencast, its more ease to become something explained where you can listen to. Maybe a whole screencast series where you complete a whole Project from scratch.

    Thanks again!

  • Sun

    Bravo! Finally a word about Cakephp.

  • Mzeid

    Great post! Hope we see more of cakephp stuff for beginners.

  • http://startvids.wordpress.com StartVids

    Awesome\! Thanks Man.

    ————————–

    startvids.wordpress.com

  • http://www.weebernet.com Penang Web Design

    Nice tutorial…i love cakePHP….

  • John

    ACL in CakePHP has always been a difficult thing for me. Thankfully I use Croogo (CMS based on CakePHP) for managing all my ACL stuff from a very usable admin panel. Anyone interested in CakePHP should check it out: http://croogo.org

  • aristo

    Why aren’t you using form helper in login.ctp and register.ctp?
    you shouldn’t write ‘by hand’ your forms and finally why are you still using short tags?

    Nettuts+ should check better this tutorial

  • Bart

    I thought you have to validate everything in the model, not as you said in the controller.

  • yasam phani

    Thank you so much for this really great plug in…!!!

  • http://www.coventrylabs.com John Morris

    How about moving straight into a Lithium from scratch series? I appears as though Cake may be dead.

  • http://itvillage.site11.com It Village

    As a cakephp developer I would like to say that this is good article.

  • http://www.alaxos.net nIcO

    For those who would like to start using ACL within your CakePHP application, you can try this ACL plugin I have developped for my own projects: http://www.alaxos.net/blaxos/pages/view/plugin_acl

    It can detect controllers and actions automatically and create ACOs nodes for them. The same applies for users and roles AROs.

    Then it is easy to grant/deny access to any action to any role and/or user.

  • http://www.obsidiann.com kakaroto developer

    very good work, I also found it weird its controller
    more has to give an overview of the ACL, I’m riding one here in Brazil an acl, but I’ll use an idea you have on worpdress User management through a web interface
    more I enjoyed your work:)

  • eko

    i’m using cake 1.3 and i have download that source code and implement to my app but i found this error i my browser:

    Warning: include(cake\bootstrap.php) [function.include]: failed to open stream: No such file or directory in C:\xampp\htdocs\cakephp\app\webroot\index.php on line 76

    Warning: include() [function.include]: Failed opening ‘cake\bootstrap.php’ for inclusion (include_path=’\PHP\cake_1.3.2;C:\xampp\htdocs\cakephp\app\;.;C:\xampp\php\PEAR’) in C:\xampp\htdocs\cakephp\app\webroot\index.php on line 76

    Fatal error: CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your \cake core directory and your \vendors root directory. in C:\xampp\htdocs\cakephp\app\webroot\index.php on line 77

    how can i solve it???
    sorry, i’m newbie

  • Jag Bhandal

    This article is AWESOMEE! Thank you so much for shedding some light into this topic.

  • Petr

    Can I use ACL for controll access to files or any entries in database?
    For example some posts are only for Superadmin role, editor only own posts.

    Can i controll this with ACL and how.
    THX

  • Alejandro

    Hey excellent but just one question!! HOW CAN I ADD MORE ADMINS?!?!? thanks :)

  • varun

    Now This is wat i call an original cakephp tutorial…..rest of them are outdated and useless stuff written by people who wants show their technical knowledge…thaks alot…this one rocks!!!…

  • Jerry Wham

    Thank you very much for this wonderful tutorial.

    Just a point I do not understand : how to use the cache (access_cache.php)? Should we rename the file access_cache.php as access.php (I tried but the only thing I get is displaying the code directly into the browser)?

    Could you tell us a bit more about it?

    PS: sorry for my English

  • geromey

    Thanks for this tutorial!

    I used it to understand about the Acl, then I made a more robust Helper:

    https://github.com/geromey/acl_utilities/blob/master/views/helpers/acl.php

    It does all the hard work and display only the links that the user can access:

    all is needed is to use $this->Acl->link() instead of $this->Html->link()

    Info on how to use it here: (it’s wrapped in a plugin…)

    https://github.com/geromey/acl_utilities

    It makes the views much neater since there is no more links that can’t be accessed :)

  • Jerry Wham

    Just a precision : in step 3, you said : “Finally, we are using ACL’s allow() function. The first parameter is the ACO alias; second, the ARO alias, and third, the permissions given to said ARO. “.

    The first parameter is not ACO but ARO alias and the second is not ARO but ACO alias.

  • http://sendage.com Jamie Chong

    Instead of doing dirty hacks in the AccessHelper, I pass in the Auth object as an option:

    access.php:

    class AccessHelper extends Helper
    {
    private $user = null;
    function __construct($options = null)
    {
    parent::__construct($options);
    if (array_key_exists(“AuthUser”, $options))
    $this->user = $options["AuthUser"]["User"];
    }

    public function isLoggedIn()
    {
    return !empty($this->user);
    }

    public function user($key=null)
    {
    return $key !== null ? $this->user[$key] : $this->user;
    }

    }

    app_controller.php:

    function beforeFilter()
    {
    $this->helpers["Access"] = array(“AuthUser” => $this->Auth->user());
    }

  • mike

    Good stuff and great explanations, but could you post the entire SQL database dump? When I downloaded the tutorial, it says that the posts table is missing. I attempted to create it, but I’m missing a few rows.

    thanks!

  • vanessa

    Does anyone know how to add the logout functionality?

    • vanessa

      nevermind

  • jafar

    I tried this but i am getting this errro

    Parse error: syntax error, unexpected ‘;’, expecting T_FUNCTION in /var/www/login/app/views/helpers/access.php on line 12

    only one thing i used differently from this tutorial is…i am using
    $this->Auth->fields = array(
    ‘username’ => ‘email’,
    ‘password’ => ‘password’ in my user_controller

  • jafar

    its fixed…now i want to know how to use different user as aro…as we empty users table………..somebody plz help me

  • Jean Luis

    Hey, first of all excellent tut……I was just wondering if you used multiple databases, one for users (usernames and passwords) and another for saving posts, if it not the case, what did you use.

    Thx in advance…..

  • Jean Luis

    More specifically how is the database “blog” structured???

  • Jean Luis

    OK, i’ve finally figured out what i wanted to ask, can you implement this just by adding another table to your DB or that should’ve been done from the first time you started constructing the app???……..sorry for not being so specific……

  • Bernabe

    Thanks !!!! really good document

    • kailas

      but my code not working..

  • flyleaf

    Nice tutorial. Helped me a lot. Hope to see some robust updates with cakephp 2.0. Thanks!

  • Manoj

    Very very helpful tutorial who is novice to cake framwork. Better explained of ACL.

    Thank you

  • http://www.designaeon.com/ raman

    tnxxx

  • Ezequiel

    Great tutorial. This area is really lacking on Cake. One thing I wonder is why is ACL handled from the Controller instead of using the models.

    As I see it, I want to be able to update AROs on the User’s afterSave method and in a Group’s model. And check permissions before reading/deleting/modifying ACOs that have a related model. Yet, I don’t seem to find how to do this.

    Thank you

  • http://www.propolismurah.com propolis

    hi, this is nice posts
    I have been surfing online more than three hours today, yet I never found
    any interesting article like yours. It’s pretty worth enough for me. In my opinionPersonally In my view, if all webmasters and bloggers made good content as you did, the web will be much more useful than ever before.

  • http://www.kachelofen-angebote.de Kachelkamin Kaufen

    Having read this I thought it was extremely enlightening.
    I appreciate you taking the time and energy
    to put this content together. I once again find myself personally spending a significant amount
    of time both reading and commenting. But so what,
    it was still worth it!