Simple Authorization
Simple Authorization is just what the name implies — for Active4D.
It is patterned after a ruby plug-in “Declarative-Authorization”. It is not a full implementation, but close. I also stole a few things from another ruby plug-in CanCan, that stole from Declarative-Authorization! Simple Authorization is implemented as an Active4D library auth.
From the declarative authorization plugin:
The declarative authorization plugin offers an authorization mechanism inspired by RBAC. The most notable distinction to other authorization plugins is the declarative approach. That is, authorization rules are not defined programmatically in between business logic but in an authorization configuration.
Authorization Data Model
----- App domain ----|-------- Authorization conf ---------|------- App domain ------
includes includes
.--. .---.
| v | v
.------. can_play .------. has_permission .------------. requires .----------.
| User |----------->| Role |----------------->| Permission |<-----------| Activity |
'------' * * '------' * * '------------' 1 * '----------'
|
.-------+------.
1 / | 1 \ *
.-----------. .---------. .-----------.
| Privilege | | Context | | Attribute |
'-----------' '---------' '-----------'
Explaination of the model
In the application domain, each User may be assigned to Roles that should define the users’ job in the application, such as Administrator. On the right-hand side of this diagram, application developers specify which Permissions are necessary for users to perform activities, such as calling a controller action, viewing parts of a View or acting on records in the database. Note that Permissions consist of an Privilege that is to be performed, such as read, and a Context in that the Operation takes place, such as companies.
In the authorization configuration, Permissions are assigned to Roles and Role and Permission hierarchies are defined. Attributes may be employed to allow authorization according to dynamic information about the context and the current user, e.g. “only allow access on employees that belong to the current user’s branch.”
Simple Authorization implementation
- Only Three public methods
- hasRole($role) => True/False
- simple test if session{“user.roles”} contains the role
- hasPrivOn($priv;$controller {;$attributes}) => True/False
- test if the user has a role that authorizes the privilege on a controller {Really any entity}
- main use is turning on/off buttons links, blocks of code, etc.
- isAuthorized ($action;$controller;$attributes {; $exclude}) => nothing or redirects to unauth
- really just a call to hasPrivOn with the redirect
- main use to control access to a controller
- Uses simple tab indented text to describe roles and privileges
An example configuration
:privileges
read
includes
show
view
index
list
create
includes
new
update
includes
edit
delete
includes
destroy
manage
includes
create
read
update
delete
me
includes
read
update
register
manage_project
includes
create
read
update
:roles
guest
permissions_on
users
login
logout
citizens
register
employees
register
citizen
permissions_on
citizens
update:(session{"user.id"} = $attributes{"id"})
employee
permissions_on
all
read:($controller !~ kExcludeEmployee )
employees
read
me:(session{"user.id"} = $attributes{"id"})
projects
read
manage_project:(auth.hasRole("project_manager"))
project_manager
permissions_on
projects
manage_project
jobs
manage_project
dbadmin
permissions_on
all
manage
Privileges, in the simplest form are CRUD actions, but that does not give you much so you add user defined names such as “manage”. This is somewhat hierarchical in that a privilege can include another privilege.
Roles are defines in the users table as a list and put into a session array session{“user.roles”}. Roles then define what privileges that role has on a controller/model/whatever.
• Privileges in the roles area have a few features
◦ the keyword all is a wildcard that applies to all controllers
◦ the privilege can contain an optional boolean condition
▪ Citizen can only update their record
▪ Employee can only update their record
▪ Employee can read everything except what is defined in an excluded list
The above configuration is parsed and placed into the library “self” collection as a global self{“_auth”}
A painful recreation of an a4d.debug.dump.collection($auth)!:→
Roles and Privileges
privileges
Key Value
create 0 {"", "create", "new"}
delete 0 {"", "delete", "destroy"}
manage 0 {"", "manage", "create", "new", "read", "show", "view", "index",
"list", "update", "edit", "delete", "destroy"}
manage_project 0 {"", "manage_project", "create", "new", "read", "show", "view",
"index", "list", "update", "edit"}
me 0 {"", "me", "read", "show", "view", "index", "list", "update",
"edit", "register"}
read 0 {"", "read", "show", "view", "index", "list"}
update 0 {"", "update", "edit"}
roles
Key Value
citizen
Key Value
citizens 0 {"", "update"}
citizens:update '(session{"user.id"} = $attributes{"id"})'
dbadmin
Key Value
all 0 {"", "manage"}
employee
Key Value
all 0 {"", "read"}
all:read '($controller !~ kExcludeEmployee )'
employees 0 {"", "read", "me"}
employees:me '(session{"user.id"} = $attributes{"id"})'
projects 0 {"", "read", "manage_project"}
projects:manage_project '(auth.hasRole("project_manager"))'
guest
Key Value
citizens 0 {"", "register"}
employees 0 {"", "register"}
users 0 {"", "create", "update"}
project_manager
Key Value
jobs 0 {"", "manage_project"}
projects 0 {"", "manage_project"}
That collection is the basis on the three library calls – hasRole, hasPrivOn and isAuthorized.
It is just that simple.
Constraints – there are a few
• Variables used in the conditional booleans must be available, that is why $attributes is passed
◦ Don’t like that, but the only way I can think of at this point.
• This is not easy stuff, you can easily mess up. Notice that there are two ways to manage projects manage_project allows all crud except delete. I then had the project_manager role inherit manage which allowed delete.