-
Get a monthly update on best practices for delivering successful software.

acl9 is a an authorization library for rails applications. It is one of the widely used library if not the most widely used now. Our experience with acl9 shows that it might be heavy weight if your authorization needs are simpler (which most projects are) but could be useful for other projects.
If you've used acegi/spring-security for authorization in your java apps, you know that acl9 is very similar in principle and hence very powerful. In addition to primary roles, it provides object level permissions which are stored in a generic way separately from the objects being controlled, all without the need for handcoding/distributing your authorization columns in each authorization-object tables.
One place where acl9 differs from acegi is how it doesn't differentiate between a role and a permission. Acegi signifies roles as global permission level which allows you to do certain things (some action on any object of a given class). Where as, a "permission" controls whether your can take that action on a certain object of a class or not. Acl9 calls them all "roles" (primary-roles and object-roles). As you can imagine, a given user may have a few roles in system but end up with lot and lots of permissions in system depending on how many objects user owns etc. This may seem like good idea at first but it presents a unique problem which is not apparent at first. Since roles and permissions are not conceptually separate in acl9 - and that a user can have lots of them (few roles and lots of permissions) - prevents us from loading and caching them in memory. Why do we need to keep them in memory? Because you are querying user's primary roles most often in your rendering of pages.
For example, consider navigation-bar which is common in most applications. Different users are presented with different tabs in navigation-bar and this bar gets rendered on each request/response cycle. Whether to render a particular tab is conditional to whether a user has certain role (primary role in particular) or not. Since acl9 cannot keep all roles (and permissions) in memory, it has to perform database query every time it has to find whether a user has_role?(admin) or not. Given that there can be only a few primary-roles that the user will have in any system, it seems in-efficient to not cache them and go to database each time.
The solution would be to separate these primary-roles from permission-roles and cache them for each request. In acl9 this means overriding User.has_role? and user.has_role!.
class User
def has_role?(role, object = nil)
if object || !Role.primary?(role)
super
else
primary_roles.collect(&:name).include?(role.to_s)
end
end
def has_role!(role, object = nil)
super
@primary_roles = extract_primary_roles if(Role.primary?(role))
end
def primary_roles
@primary_roles ||= extract_primary_roles
end
def extract_primary_roles
self.role_objects.select { |r| r.primary? }
end
private :extract_primary_roles
end
That does it. You cache the primary-roles and leverage those for has_role? queries.
Related posts: