autoriser()
–
the function which determines whether a user should be permitted to perform a
given operation. As I couldn’t find any document on using this function, I’ll
provide a few notes below.
Checking that a user if authorised to perform an action is a little more
haphazard than I’d like in SPIP (“No authorisation check? No worries!” is not
a particularly comforting approach), but it seems to get the job done even if
it does depend on more developer discipline than seems warranted. Checking
that a user is “authorised” to perform an action is done by calling the
autoriser()
function with arguments to
describe the operation. If it returns true
then the operation is authorised,
it not, then it isn’t.
Like most of SPIP’s core functions, autoriser
is implemented in a way that
makes it easy to override and extend its functions: rather than make any
decision itself, it simply delegates the decision to the first function it
finds that can decide for that type of operation and object (or object, or
operation).
First, though, lets look at autoriser
’s arguments:
function autoriser_dist($faire, $type='', $id=0, $qui = NULL, $opt = NULL)
This first (and only required) argument is $faire
(French, I’m told, for “to
do”) which takes a string: the name of the operation. The second argument
$type
is another string: the type of object being operated on; and the third
is an integer: the ID of the particular object, if there is one. The fourth,
$qui
(“who”) is an array of details of the current user; and the fifth, I
assume, is an array of optional values if the previous four are not enough to
make some decisions).
Only the first of these – the operation being performed – is required and
only the first should need to be specified in the vast majority of situations
(it’ll work out the user by itself and there are many operations without a
$type
or an $id
). Once it’s been called, autoriser
uses these values to
look for a function that can make a decision for the given type and
operations.
You can see the code of /ecrire/inc/autoriser.php
(around line
87)
for the particular functions that it will call, but the full list of
alternatives that ‘’autoriser($faire, $type, $id, $qui, $opts)’’ is (in order
of preference):
autoriser_$type_$faire()
autoriser_$type()
autoriser_$faire()
autoriser_default()
autoriser_$type_$faire_dist()
autoriser_$type_dist()
autoriser_$faire_dist()
autoriser_default_dist()
Adding authorisation checks to your plug-in is easy: just implement one of
these checking functions (in a file that’ll be included by a <fonctons>
entry in your plugin.xml
file is probably best) and then get autoriser
to
call it when appropriate.
From aplugin_fonctions.php
or some other file:
/**
* Perform authorisation checks for "elephant" objects.
*/
function autoriser_elephant($faire, $type='elephant', $id=0, $qui=NULL, $opt=NULL) {
if ( '0minirezo' == $qui['statut'] ) {
return true;
}return false;
}
With this code in place, only administrators will be able to perform actions
(or, strictly speaking, perform actions checked with the autoriser
function)
on elephant objects. Any call specifying $type='elephant'
will use the
above function to determine if the operation should proceed.
In exec/anaction.php
or similar, we might use code like this:
if ( autoriser('kill', 'elephant', $id_elephant) ) {
'elephant', $id_elephant);
launch_missiles_at(else {
} echo _T('aplugin:cannot_shoot_elephant'), _T('aplugin:permission_denied');
}
Which will try the following functions, in order, to decide whether or not to
launch_missiles_at()
our poor elephant:
autoriser_elephant_kill()
autoriser_elephant()
autoriser_kill()
autoriser_default()
autoriser_elephant_kill_dist()
autoriser_elephant_dist()
autoriser_kill_dist()
autoriser_default_dist()
That’s about all there is to it. Of course, there’s a lot more you can do to make your authorisation decisions: per-user and per-object configurations you might like implement (similar to the way SPIP allows us to restrict administrators and editors “to a section”), time-based or geographic restrictions (editing during working hours only, or from an IP address in Africa), or restricting access to those within your organisation’s network.
The world of authorisation is your oyster!
]]>Rights are denoted by dot-separated, reverse hierarchical names (e.g: ‘’Application.Component.Subcomponent.Right’’).
Permissions can specify rights with wild-cards (e.g. ’’Application.*’’).
Rights can also be negated (e.g. ’‘-Application.Shutdown’’).
Thus a user who can do anything except shut the application down might have a permissions list like:
$user->perm = array("Application.*", "-Application.Shutdown");
As such schemes go this one is adequate, but as a replacement for Drupal’s current system it’s not that crash hot. In fact, all it brings to the table is a little more convenience: it is no more expressive than the current system. Now convenience is a pretty cool thing and the Drupal permissions system could really, really, really do with some more convenience, but if there’s to be a new system, I’d much rather see something genuinely more powerful.
My suggestion is this: rather than have a single hierarchy of rights which jumbles variously the rights, the objects, and the implementers of both into one great big messy tree, why not have rights permissions be a tuple of a right and an object specifier. This will result in two smaller, less complicated trees the paths of which can be clearly and easily interpreted. With this scheme, many of the permissions seen on most Drupal sites would be replaced with a much smaller collection of rights and a similarly small number of object specifiers.
The current system includes five permissions (create, delete own, delete any, edit own, and edit any) for each and every content type. In my suggested system, these would be replaces with just those five rights which would, when granted, be applied to particular object specifiers. The “edit any story content” permission might become “(Core.EditAny, Node.Story)”, for example.
One advantage such a scheme would have is in breaking down some of the super permissions like the various “administer …” permissions. Rather than creating large numbers of new permissions, it would be possible to grant “Core.Administer” right to a particular group of settings. Again, this will reduce the large number of permissions (approaching one per module) and replace them with a single reusable permission applied to a range of domains. The “administer menu” permission might be replaced with “(Core.Administer, Menu)” or, more excitingly, with “(Core.Administer, Menu.PrimaryLinks)”.
Having written a little about this, I’m not entirely sure that it’s a good idea or a good fit for Drupal. Such a flexible system would certainly have a smaller UI (in terms of screen real-estate) than the current approach, but it would be more complex. It would make core more powerful, but it wouldn’t obviate the need for third-party modules to augment or replace the system when it can’t implement a required policy. In fact it’s a stupid idea, so forget about it and lets all just pretend that I didn’t say anything.
]]>