1
votes

In my design, an action has a rank, 0-10 required to access it (0 being a guest [not logged in], 10 being an admin). Each user has a rank, 0-10. If you have the rank or higher, you can access the action, otherwise you can't. Nice and simple, it's all I need.

The problem is that CakePHP wants me to treat actions with two separate concepts. I have to mark them Auth->allow/deny to determine if the auth system even bothers with them, and then I control access to them with isAuthorized().

isAuthorized works great for my needs... except that any action I want to have access of rank 0 to has to be Auth->allow()... which then ignores my isAuthorized method completely. If I deny all the pages, the login triggers on the pages that should be rank 0, before it checks isAuthorized, so even if I grant authorization through it, the person has to log in first.

Is there any way to merge the two systems together, or a simple way to replace it? Most of the auth system is great, and takes care of business for me without me having to mess with it... but this is just awkward and is going to cause problems when I don't notice a mixed up allow/deny or something.

Thanks!

2
This is just a thought, but you could have a guest account with rank 0 and make it the current user when no user is logged in.dr Hannibal Lecter
How do you map the level to an action?floriank
dr Lecter - that sounds like a really promising idea. So you're suggesting just forcing login to a guest account, rank 0, when they first arrive, and then leave the option available for login if they're at rank 0? Any problems you can foresee off the top of your head I'll need to address?xtraorange
Burzum - I've just been doing if ($this->action === 'xyz') or switches within the isAuthorized method. Then I just call a method I wrote to check the ranks which returns true/false.xtraorange

2 Answers

4
votes

As far as I see it, the only way to do this is to create a guest user. This is because the Auth component checks the existence of a user before ever getting to isAuthorized() like you explained.

You can do this by writing directly to the session. This will tell the Auth component that someone is logged in, so your isAuthorized() method will be called.

AppController

public function beforeFilter() {
  // if no one is logged in, log in a guest
  if (!$this->Auth->user()) {
    $this->Session->write(AuthComponent::$sessionKey, array(
      'User' => array(
        'id' => 0
       )
    ));
  }
}

public function isAuthorized($user) {
  $authorized = false;
  if ($this->Auth->user('id') == 0) {
    // public guest user access
  }
  // other logic
  return $authorized;
}

A possibly better way to do this is to use a custom authentication object, which basically tells Cake to use that class to help authenticate. This splits the logic into a separate class, making it easier to test and even disable.

app/Controller/Component/Auth/GuestAuthenticate.php

App::uses('BaseAuthenticate', 'Controller/Component/Auth');

class GuestAuthenticate extends BaseAuthenticate {
    public function authenticate(CakeRequest $request, CakeResponse $response) {
        // no real authentication logic, just return a guest user
        return array('User' => array('id' => 0));
    }
}

AppController

public $components = array(
  'Auth' => array(
    'authenticate' => array(
      'Form',
      'Guest' // tell Cake to try Form authentication then Guest authentication
    )
  )
);

public function beforeFilter() {
  if (!$this->Auth->user()) {
    // no user? log in a guest (this will fail form authentication
    // then try guest authentication)
    $this->Auth->login();
  }
}

public function isAuthorized($user) {
  $authorized = false;
  if ($this->Auth->user('id') == 0) {
    // public guest user access
  }
  // other logic
  return $authorized;
}

You can find more information on custom authentication objects here: http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html

1
votes

Maybe this has already been dealt with satisfactorily but I have a solution.

In my AppController I have the following:

    public function isAuthorized($user = null) {
        if (in_array($this->action, $this->Auth->allowedActions)) {
            return true;
        }
        return false;
    }

As you've found, if you don't explicitly authorize actions, they are denied to authenticated users, even if they are allowed to the public. This snippet of code just makes the isAuthorized() method honour the settings in the Auth->allow() list.

It seems to work for me, so I hope it helps.