RSS
 

Posts Tagged ‘Auth’

Auth – inline authorization the easy way

07 Apr

I wrote a wrapper class to make inline authorization easier.
Often times you want to check on certain roles inside an action or view and depending on the result display specific content or execute specific code.
As an example we only want to display the "admin infos" box on the home screen for an admin. All other users should not see this box.

Status quo

We would need to check the session manually against the roles we want to grant access to. This can get pretty hairy with more than one role allowed (if admins and moderators are allowed to see this box for example).

Preparations

We first need to make the class usable by putting this in our /Config/bootstrap.php:

// these IDs match the role_ids in the DB
define('ROLE_SUPERADMIN', '1');
define('ROLE_ADMIN', '2');
define('ROLE_MOD', '3');
define('ROLE_USER', '4');
// enable the Auth class
App::uses('Auth', 'Tools.Lib');

I like to use constants as they are shorter than Configure::read('admin') etc. But Configure would work just as fine.

Be aware: Since we already try to access the Tools plugin you need to assert first, that we enabled the Tools Plugin (using CakePlugin::loadAll() or a specific load call).

Then we need to decide whether we use single role (cake default) or multi role Authorization. I usually always use multi-roles. Therefore the default case for the Auth class is exactly this.
The session then contains:

Auth.User.Role (with Role being an array of role ids)

If you use single roles, you’re Session array should look like this:

Auth.User.role_id (with role_id being the single role we want to check against)

In this case you should set the following constant manually in your bootstrap:

define('USER_ROLE_KEY', 'role_id');

"Former" usage

For comparison I will outline the manual and "outdated" way of authorization first:

# we want to make sure that piece is only visible to admins and moderators
if ($this->Session->read('Auth.User.role_id') == ROLE_ADMIN || $this->Session->read('Auth.User.role_id') == ROLE_MOD) {}
# or with multi-role
if (in_array(ROLE_ADMIN, (array)$this->Session->read('Auth.User.Role')) || in_array(ROLE_MOD, (array)$this->Session->read('Auth.User.Role'))) {}

Quite a lot to write…

Note: This also only works in controller/component and view/helper scope. You would have to use the static CakeSession::read() in order to make this work in the model/behavior one etc.

Usage

Now the fun part. The wrapper class can be found in the Tools Plugin.

# Same thing as above
if (Auth::hasRoles(array(ROLE_ADMIN, ROLE_MOD)) {}

Now isn’t that nicer to write and read?

The default case is that if one of the roles is matched it will return true right away. If you want to connect them with AND instead of OR, you need to make the second param false:

# This only passed if the user has both roles!
if (Auth::hasRoles(array(ROLE_ADMIN, ROLE_MOD), false) {}

If we only want to check against a single role we could also use the shorthand:

if (Auth::hasRole(ROLE_MOD) {}

Advanced usage

You can also pass in the roles you want to check against. This can be useful if you want to check somebody else’s roles (and not your session roles). This can come in handy in CLI (command line / shell) environment and also in the admin backend.

if (Auth::hasRole(ROLE_MOD, $rolesOfThisUser) {}

And

if (Auth::hasRoles(array(ROLE_MOD, ROLE_USER), true, $rolesOfThisUser) {}

And there is more

There are also some convenience methods available.

Instead of $uid = $this->Session->read('Auth.User.id') you can just write

$uid = Auth::id(); // anywhere in your application

The roles can be fetched like this:

$myRoles = Auth::roles(); // string in single-role and array in multi-role context

Last but not least the user data:

$user = Auth::user(); // complete user array
$username = Auth::user('username'); // string: current username
...

Final notes

Although this wrapper can be used about anywhere in your application with ease does not mean one should do that.
Try to avoid using the auth (and therefore session) data in the model layer, for instance. Those should be kept state-less.
But I also know that there are cases where it is pretty convenient to ignore this warning 🙂

Disclaimer

Just for clarification: This class does not provide you with the Authentication or Authorization. This has to be up and running already. It is only a wrapper to check user roles a more efficient and cleaner way.

So if you need to setup a fresh authentication, you can just use the cake Form authenticate for example. If you want multi-role authorization you would want to throw in some additional spices:
You would need to write the Role array to your session upon successful login for the authorization module to work. See the above notes on how the session data array should look like.

For a new authorization take a look at my TinyAuth. It can handle single and multi role auth and those classes work well together (yeah – they have been designed to do that, of course^^).

 
No Comments

Posted in CakePHP

 

Qlogin – Quicklogins für CakePHP

08 Feb

Have you ever thought how nice it would be to send emails with an url that automatically logs you in?
Especially for a messaging system this can be quite handy: One click in the notification email and you can answer right away.

How does it work?

Here an example with a notification email:

$text = 'You got mail!'.PHP_EOL;
if (!isset($this->Qlogin)) {
	$this->Qlogin = ClassRegistry::init('Tools.Qlogin');
}
$text .= $this->Qlogin->url(array('admin'=>false, 'plugin'=>false, 'controller'=>'conversations', 'action'=>'view', $conversation['Conversation']['id'])), $user['User']['id']).PHP_EOL;
//send email

We create a quicklink using the url where the user should be redirect to after the login as first param and the actual user_id to authenticate with as second param.
Pretty straight forward.
Let’s say, the generated url is http://domain/qlogin/1234567890 .

Once the user clicks on the link, the "go" action of the QloginController gets triggered and tries to find the corresponding user as well as target url. If found, the user is logged in and directly passed on to the desired page.
Note: If the user is already logged in, he will be redirected immediately (skipping the login).

I added this to my routes.php

Router::connect('/qlogin/*', array('plugin'=>'tools', 'controller' => 'qlogin', 'action'=>'go'));

in order to result in the above url which is pretty short and convenient.

The code

Its available at github (currently only for 2.x): Tools plugin.
You need the model as well as the controller.

Some notes

The Module is still pretty basic. But it works flawlessly with my setup. I would like your feedback on it, though.
AuthComponent::login() should respect the scope you defined as well as the default redirect urls.
Right now I am trying out the Qlogins in combination with AutoLogin – if those cookies don’t intefere. But it seems to work fine.

Dependencies

Model Token (@see article) (to store the tokens) as well as the url validation method and get() of my MyModel (can also be put into your AppModel).
And since it is deeply integrated in my usual everyday apps it also requires at least the CommonComponent of the Tools plugin. For the optional admin backend there are more dependencies.
So basically you need:

//AppController
public $components = array('Tools.Common');
//AppModel
App::uses('MyModel', 'Tools.Lib');
class AppModel extends MyModel {}

Of course you may through out any methods you don’t need.

And you can always cherry-pick the stuff you specifically want to use.

 
No Comments

Posted in CakePHP