| Maypole-Plugin-Authorization documentation | view source | Contained in the Maypole-Plugin-Authorization distribution. |
Maypole::Plugin::Authorization - Provide role-based authorization for Maypole applications
# In your main application driver class ...
package BeerDB;
use Maypole::Application qw(
Authentication::UserSessionCookie
Authorization);
use Maypole::Constants;
# Configuration will depend on the database design, which loader is
# used etc, so this is just one possibility ...
BeerDB->config->auth({
user_class => 'BeerDB::Users',
# other keys may be needed as well for the authentication module
});
sub authenticate {
my ($self, $r) = @_;
...
if ($self->authorize($r)) {
return OK;
} else {
# take application-specific authorization failure action
...
}
...
}
# make web page show just tables for this user
sub additional_data {
my $r = shift;
$r->config->display_tables(
[ map { $_->table } $r->get_authorized_classes ]
);
}
# meanwhile in a template somewhere ...
[% ok_methods = request.get_authorized_methods %]
Can be used to decide whether to display an edit button, for example
This module provides simple role-based authorization for Maypole. It uses the database to store permissions, which fits well with Maypole.
It determines whether users are authorized to invoke specific methods in classes. Normally these will be actions in model classes. Permission to invoke methods is not granted directly; it is assigned to roles, and each user may be assigned one or more roles.
The methods made available in your request object are described next, followed by an example database schema. Then we explain how you can customize the schema using configuration. Finally there are some hints on how to administer the database tables and a list of the various use cases associated with authorization.
As well as this description there are a few other files shipped in the distribution that you may want to look at:
A sqlite database containing tables and data for the example beer database, along with authorization tables and data.
A file containing SQL to create and load the sqlite database
A file containing SQL to create and load a MySQL InnoDB version of the database.
An example of a Maypole driver class that uses authorization. It may get you started towards your own application.
Note that there is a different BeerDB.pm in the t directory that is just designed to make the tests run, not to help you!
The module depends on four database tables to store the necessary data.
The users table records details of each individual who has an account
on the system.
It is not used by this module; only the id values are used as foreign
keys in the role_assignments table.
This table is used by Maypole::Plugin:Authentication::UserSessionCookie
to do user authentication and session management.
Refer to that module to understand the columns in this table.
Additional columns can be added to suit whatever other needs you have.
Users are not given permissions directly because that causes an
explosion in the table size and an administrative headache.
Instead roles are given permissions and users acquire those permissions
by being assigned to roles. The auth_roles table just records the
name of the role. You could add things like a description if you wish.
The table is not called roles in case your application wants to use
that name for its own purposes.
role_assignments is a classic many-many link table. Records contain
the id of a user and of a role which the user has been assigned.
The permissions table authorizes a specific role to execute a
particular method in a particular class. The classes are expected to be
the model subclasses and the methods will be the actions, but the scheme
will also work in other situations. To reduce administrative burden and
table size, it is allowed to use a '*' wildcard instead of a method name;
this grants permission to all methods in the class. It would be possible
to add a similar wildcard for classes but there's probably no action
that you want to allow on all classes!
One possible set of table definitions (DDL) to implement this scheme are shown below. The DDL uses various MySQL features and you may need to adapt it for other databases. The DDL also uses the InnoDB table type, because this supports foreign key checks within the database and it allows us to show how these constraints should be set up. You can use other table types and rely on Class::DBI to maintain integrity. If you do this, remove 'TYPE=InnoDB' from the end of each table definition.
Note that in some Linux distributions InnoDB support is in a different package to the base MySQL release. So if you have trouble, use your distribution's package manager to check that InnoDB support is installed.
CREATE TABLE users ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, UID VARCHAR(20) NOT NULL, password VARCHAR(20) NOT NULL, PRIMARY KEY (id), UNIQUE (UID) ) TYPE=InnoDB; CREATE TABLE auth_roles ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(40) NOT NULL, PRIMARY KEY (id) ) TYPE=InnoDB; CREATE TABLE role_assignments ( id INT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, auth_role_id INT NOT NULL, PRIMARY KEY (id), UNIQUE (user_id, auth_role_id), INDEX (auth_role_id), FOREIGN KEY (user_id) REFERENCES users (id), FOREIGN KEY (auth_role_id) REFERENCES auth_roles (id) ) TYPE=InnoDB; CREATE TABLE permissions ( id INT NOT NULL AUTO_INCREMENT, auth_role_id INT NOT NULL, model_class VARCHAR(100) NOT NULL, method VARCHAR(100) NOT NULL, PRIMARY KEY (id), UNIQUE (auth_role_id, model_class, method), INDEX (model_class(20)), INDEX (method(20)), FOREIGN KEY (auth_role_id) REFERENCES auth_roles (id) ) TYPE=InnoDB;
Maypole::Plugin::Authorization runs without any configuration,
sharing the auth component of your Maypole configuration with
whichever authentication plugin you are using.
You can also customize some aspects of it with explicit configuration:
The name of the model subclass that represents the users table.
It defaults to BeerDB::User, where BeerDB is the name of your
application driver class.
This subclass is used to execute the authorization SQL queries.
The name of the permissions table. It defaults to permissions.
The name of the table that assigns users to roles. It defaults to role_assignments.
The name of the foreign key column in the role assignment table that identifies the user. It defaults to user_id.
The permissions database can be maintained by any person who is assigned to the admin role. Most administration is performed using normal Maypole actions and templates such as list, search, addnew, view, edit and delete.
User administration is separated out to a user-admin role. I don't yet know whether this will prove beneficial but these people are the only ones who can access passwords and personal details.
There needs to be special code to allow users to edit their own passwords, since that is a data-dependent permission as opposed to the metadata-dependent nature of the authorizations scheme. Such code is part of the application's authentication scheme.
There is a default role that should be assigned to every user. Perhaps it should be hardwired in the SQL so that users don't have to be actually added to the role?
User administration mechanisms belong in the domain of the authentication system, though this authorization module imposes a few additional requirements. This action should be permitted to the user-admin role. Newly created users should automatically be assigned to the 'default' role.
Should be permitted to the individual user only and perhaps to the user-admin role.
People assigned to the admin role can edit the role_assignments, permissions and auth_roles tables in the normal Maypole way.
Presently, administrators need to type in the names of the model
subclasses and the actions. The methods get_authorized_classes and
get_authorized_methods could be used to build a specialized template
to populate the relevant form elements.
This is the get_authorized_classes method.
Given a user ID, find the list of classes for which s/he has some
permissions. This can be used to build the list of tabs in the navbar.
This is the get_authorized_methods method.
Given a user ID and class name, find the list of methods that the user
is entitled to invoke. This can be used to build a menu of permitted
actions.
There are several alternative possibilities for authorizable entities and permission checking in addition to the example implementation provided. You can consider them if you have special requirements:
1/ Authorize all actions (i.e. methods with the Exported attribute). Permission could be enforced in the model's process method just before calling the action. PRO: simple to implement, uniform and easy-to-understand CON: not as flexible as alternatives
2/ Explicit call to authorize() at the beginning of every method that needs to be authorized. PRO: Flexible. Very simple to implement initially. Obvious in code where auth occurs. Auth can be done at points other than method entry if needed. CON: Error-prone and awkward to maintain. Increases code complexity.
3/ Provide some other attribute that can be attached to methods to require them to be authorized, or perhaps in combination with Exported. For example, the Exported attribute could automatically invoke authorization as would a new 'Auth' attribute, while a new 'NoAuth' attribute would declare that the action could proceed without authorization.
Dave Howorth, djh#cpan.org
Please ask any questions on the Maypole mailing list and monitor that list for any announcements.
Everybody on the Maypole list, for support, help and code.
Copyright (c) 2004-2005 Dave Howorth. You may distribute this code under the same terms as Perl itself.
| Maypole-Plugin-Authorization documentation | view source | Contained in the Maypole-Plugin-Authorization distribution. |