Академический Документы
Профессиональный Документы
Культура Документы
Author
Hans De Bisschop (hans.de.bisschop@ehb.be)
Developers
Hans De Bisschop (hans.de.bisschop@ehb.be)
Sven Vanpoucke (sven.vanpoucke@hogent.be)
Special thanks
Eduard Vossen (eduard.vossen@ehb.be)
Jean-Marie Maes (jeanmarie.maes@hogent.be)
... and the entire Chamilo community
This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. To
view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to
Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
Credits ..................................................................................................................................................... 4
Introduction............................................................................................................................................. 8
The Concepts: A Bridge over Troubled Water......................................................................................... 9
Rights ................................................................................................................................................... 9
Locations ............................................................................................................................................. 9
Inheritance ...................................................................................................................................... 9
Locking ........................................................................................................................................... 10
Relations ............................................................................................................................................ 10
Templates .......................................................................................................................................... 10
Summary ........................................................................................................................................... 11
Persistent storage: A Thriller ................................................................................................................. 11
How do we map these locations to actual components and object instances? ............................... 12
Won’t having one single location tree result in gigantic tree structures? ........................................ 12
We only store parent id’s, that can’t be ideal, can it? ...................................................................... 13
Summary ........................................................................................................................................... 13
London is Calling for a Rights Interface ................................................................................................. 14
Helper methods ................................................................................................................................. 14
Example ......................................................................................................................................... 14
Available rights .................................................................................................................................. 15
The Rights Utilities will rock you ........................................................................................................... 15
create_location.................................................................................................................................. 15
create_application_root_location | create_subtree_root_location ................................................ 16
parse_locations_file | parse_tree ..................................................................................................... 16
is_allowed.......................................................................................................................................... 16
get_right ............................................................................................................................................ 16
is_allowed_for_... .............................................................................................................................. 17
move_multiple .................................................................................................................................. 17
get_root | get_root_id ...................................................................................................................... 17
get_location_by_identifier | get_location_id_by_identifier ............................................................ 17
get_rights_legend.............................................................................................................................. 17
invert_..._right_location .................................................................................................................... 17
set_..._right_location_value .............................................................................................................. 17
switch_location_inherit | switch_location_lock ............................................................................... 17
User account control, (digital) rights management, access control, credentials verification ... It hardly
matters what you call it, it’s one of those annoying realities of life. In the ideal world there would be
no need to check any kind of access rights, but since that utopia is still more or less a dream, we need
a system. What does “the” system have to know?
The reply is simple enough; it can be reduced to the most basic components a computer can
understand: a simple 0 or 1, true or false. For the humans among you that would be a “go ahead” or
“no can do, buster”.
The fact that you hardly ever see the system doesn’t make Fig. 1 - Dante shown holding a copy of the Divine
things any easier either. Apart from the odd error Comedy, next to the entrance to Hell, the seven
terraces of Mount Purgatory and the city of
message when you try to do something you’re not Florence, with the spheres of Heaven above.
allowed to, you’ll never really see the system in action.
That being said a lot of the options we have in e.g. Chamilo are there because something in the
background has determined that we can see those options.
Even though it’s not strictly related to the actual rights management application, one of the most
basic examples of such a check is the login procedure. Even before a user can see anything the
system will have verified who you are, where you are going and what you want to do. If it’s your first
login, you’ll obviously get to see the login screen. Access to all other components will automatically
be denied. But if it’s not your first visit you’ll get the go ahead and you’ll be presented with a
plethora of different features and options.
So sit back and enjoy the ride. Welcome to the first circle of hell.
Before we can delve into the actual management and the structures related to it, it’s essential that
we explain a few basic Chamilo rights management concepts. These concepts will be used
throughout this document, so be sure you understand the completely before reading on.
Rights
“Elementary, dear Watson” is how the great detective would describe rights.
Rights are the most basic of components in the entire rights framework, hence
it’s name. What are rights? To name a few simple examples: add, edit, delete,
view, share, use... They’re the basic actions someone may want to perform in
the context of a certain Chamilo component or application. Elementary rights
can be defined on a per-application basis.
Locations
So that was the easy part, now the going gets tough. We already have the what (right); now we need
a where, we need a location that allows you to perform the specified action. What is a location?
A course is a location
A content object is a location
The course browser is a location
The homepage is a location
A personal calendar publication is a location
An application configuration form is a location
It shouldn’t take you long to notice that there are 2 types of locations in that list. Some locations are
user-defined and there’s no way to know how many of them there will be (if any at all). The second
kind is somewhat less complex. They usually correspond to components available in Chamilo and its
applications. Things like the course browser, the homepage or a configuration form are set in stone.
There’s no way an end-user can add or delete instances of these components.
A very basic usage example of this second type of locations: Can Jane Doe publish
a calendar event in the personal calendar? In this case the “location” would be
the publisher component of the application. It is not an instance of a user-
definable object, but a component as defined by the developer.
Locations are almost obviously related to each other in some way. Practically
speaking locations will form a hierarchical tree with the actual platform-location
as the root node of the entire system. If you consider locations to be a tree in which separate
locations are linked, you can introduce some additional concepts to reduce the amount of overhead
and configuration required.
Inheritance
If a right is not specifically enabled for a specific location, the user will be denied access to that
location. That means defining rights for all locations throughout the entire system.
Locking
So inheritance goes upwards, what about downwards? Imagine you want all content objects to be
shareable with everyone and at the same time you want to prevent anyone from not sharing their
own objects. To override the general setting all they would have to do is disable
inheritance for their own repository and set their own rights.
Relations
After the what (right) and the where (location) comes the who. “Who are you?” is the basic question
the system will be concerned about and depending on the answer, the results will be very different
indeed. For every right on every location a relation can be defined with one or more users and/or
one or more user groups. It’s this combination of a right, a location and a user or group, which can be
set to true or false, thus defining the actual “right” as a whole.
Please keep in mind that while you can define rights for individual users, this is not considered a best
practice. In most cases it will be possible to group users which will require similar or identical access
rights, e.g. university faculties, organisation branches, interest groups. It is far more effective to
define general rights for entire groups of users and grant additional rights when and if necessary.
Templates
Defining groups can be practical and save you a lot of time, but even then there
are limitations. Groups are hierarchical by nature. E.g. a university contains many
faculties; each faculty contains teachers and students. So basically you’ll end up
having several teachers and students groups which, for the sake of this example,
share the exact same rights.
Again, if you have 10 groups this is manageable, but if you have e.g. over 450.000
students on 64 campuses like the State University of New York with who knows how many hundreds
or thousands of subgroups, this would be hell. Even in less extreme scenarios with about 50 groups
and a few thousand users it would be an administrative nightmare to constantly (and consistently!)
manage all those rights.
Wouldn’t it be easier to just define e.g. a student template and apply it / link it to all student groups?
That way you would only have change the template and it would automatically apply to all linked
users and groups. Of course it would be, so that’s exactly what we did in Chamilo 2.0.
Most of the concepts described before can be mapped to storage units relatively easy. Locations and
rights are the exception though, but before we get to that, let’s have a look at the more
straightforward storage units.
The tables that follow reflect typical implementations of these storage units in most of the commonly
used DBMS-systems.
So what about the rights? Where are they stored? Short answer: not in the persistent storage. Long
answer: check the next chapter. That brings us to the more complicated part: the locations. Taking
into account what we learned from the description of a location in the previous chapter, locations
would have the following properties.
There are more than a few problems with this kind of approach though, so let’s have a look at the
ones which make the aforementioned table of properties largely insufficient.
The rocket scientists among you may be realizing right now that there is no way that all object
instances throughout the entire system have unique id’s … and they would be quite right too. They
don’t and that’s why we store an additional property “type” which is more than likely the object
instance class. E.g. content_object, group.
Won’t having one single location tree result in gigantic tree structures?
It most definitely will and performance wise that is a very bad thing. On top of that there will hardly
ever be a reason to interlink location trees of different applications. So let’s store the application the
location is stored in, that should help a bit. It does, but it’s not enough. Some location trees would
The combination of these 4 properties allows us to isolate a very specific place in the entire system
without too much recursion and without having to retrieve lots of data which is irrelevant for what
we want to achieve.
Summary
Field Type Extra
id int(10) The numeric identifier of the location
location_id char(50) A textual descriptor of the location
left_value int(10) The left value of the location node
right_value int(10) The right value of the location node
parent_id int(10) The numeric identifier of the parent location
inherit tinyint(3) Does the location inherit rights?
locked tinyint(3) Is the location locked?
identifier int(10) The numerical identifier of the object instance or component the
location represents
type char(20) The type of the object instance or component the location
represents
tree_identifier int(10) The numerical identifier of this specific subtree of an application
tree_type char(20) The type of this specific subtree of an application
application char(30) The name of the application
Table 8 – Storage unit structure for a location
As explained in the previous chapter, locations are strictly related to applications. Within the context
of a specific application the application-property of a location will therefore mostly be identical all
the time. We thought it would be somewhat counterintuitive to force a developer to continuously
pass on the application name when performing a rights check.
Helper methods
To make the passing of the additional parameter superfluous the application rights classes were
implemented as an interface to the actual workhorse which is located in the rights folder. (The
RightsUtilities class, which is the subject of the next chapter) All methods in these classes are
implemented statically for easy access throughout the application and/or platform.
Apart from these straightforward helper methods, the application rights classes could also contain
functionality to extend the basic rights checking algorithm or any of the other default rights methods.
Example
Courses contain a structure called course groups. These course groups group any number of users
already subscribed to the course. Supposing we want to define rights for this specific course group
within the confines of the course, it becomes necessary to store an additional relation between the
course group, the location and the right.
Even more important is the fact that we would have to check this relation whenever we access a
specific tool inside a course for additional rights. Implementing such an extension could be achieved
... And of course there’s always the possibility to put functionality specific to the application in
question in its own rights interface.
Available rights
As mentioned in the chapter on storage units, there is no storage unit for rights. Available rights are a
static fact, within the confines of an application they are universal constants which the user cannot
manipulate in any way. He can only use them. That is why they are not stored in the database as you
may have expected but are defined in the application’s rights interface as constants.
<?php
class RepositoryRights
{
const ADD_RIGHT = 1;
const EDIT_RIGHT = 2;
const DELETE_RIGHT = 3;
const SEARCH_RIGHT = 4;
const VIEW_RIGHT = 5;
const USE_RIGHT = 6;
const REUSE_RIGHT = 7;
}
?>
Considering that they are defined locally, available rights can vary greatly from application to
application. Keep in mind though that every single additional right will complicate things
considerably for the end-user (and the developers!), so please consider the available rights and their
relations well. Simplicity really is essential for an already complex system.
The most visible part, which is pretty relative, of the system is the
RightsUtilities class which, just like the rights interfaces, contains a number of static methods to
make life easier for the developer and per extension the end-user. What follows is an overview of the
methods in this class, their function and what they require to work correctly.
create_location
A helper method which allows us to quickly create a new location object instance. As can be
expected it expects all essential properties as parameters: name, application, type, identifier,
inherit, parent_id, locked, tree_identifier and tree_type. Additionally you can request that
the location object be returned by setting the last parameter, return_location, to true.
parse_locations_file | parse_tree
Predefined rights locations are defined as XML-files and processed by these helper methods during
installation and the initialization of certain components.
is_allowed
The start of the big one. This is the method you’ll be using (indirectly) whenever you’re checking
permissions for the currently logged in user. It shouldn’t come as a surprise that it requires a range of
parameters to work correctly.
Parameter Description
right The right to be checked
location The numeric identifier of the location’s object instance id or
component identifier
type The type of the location
application The application name
user_id The numeric identifier of the user we’re checking rights for
tree_identifier The numeric identifier of the locations subtree
tree_type The type of the location subtree
Table 9 – Parameters of the is_allowed method in the RightsUtilities class
So why is this just the beginning? Don’t we have everything we need to perform
the actual permissions check? We do, but there is that nasty bugger called
performance. That’s why is_allowed caches all requests made during the course
of a single PHP request and quickly returns a cached value once it detects we’re
verifying the same permission once more. The performance gain of such a
relatively simple bit of functionality is not to be underestimated in a system that
will no doubt be processing an ever growing number of requests all the time. Rights are everywhere;
checks are everywhere, so speed is essential.
As far as the parameters go, most of them are in essence optional, although not passing them to the
method doesn’t make much sense. The only one which will be ignored in most regular usages of the
method is the user_id. If and when the user_id is not set the system will default to the currently
logged in user, which is what we’ll want it to do most of the time anyway.
get_right
is_allowed was the start of the big one, this is THE big one. get_right takes the same parameters
as is_allowed but will actually verify the permissions for the given set of parameters. The result
couldn’t be simpler: true or false. The consequences couldn’t be bigger as it determines what
someone can do in Chamilo.
Interesting to know is that users which have been flagged as platform administrator bypass all these
checks and immediately get true returned for every single rights request on the system. So if you
is_allowed_for_...
The two previous methods take every single relation, group membership and template into account
which might in some way be related to the user we’re checking permissions for. This is the desired
behaviour most of the times, but in some case we’ll want to be more specific. Methods exist to check
permissions for a specific rights template, user and group. They’ll prove particularly useful when
granting or denying rights to users, groups and rights templates.
move_multiple
Method which allows us to move multiple locations to a new parent at the same time.
get_root | get_root_id
Sometimes we will want to retrieve the root node of a location (sub) tree or its id. get_root_id is a
simple extension on get_root that simply returns the id of the location returned by the
aforementioned method.
get_location_by_identifier | get_location_id_by_identifier
Helper methods to retrieve a location object based on the application, identifier, type,
tree_identifier and tree_type. As was already the case with the get_root_id method,
get_location_id_by_identifier returns the numerical id of the location object returned by
get_location_by_identifier.
get_rights_legend
Returns some HTML that explains the meaning of the icons used in the rights configuration pages.
invert_..._right_location
Convert true to false and vice versa for a combination of a right, location and, depending on the
variant, a user /group / rights template. Considering that the rights management system returns false
unless told otherwise, an invert on a relation which has not yet been defined will result in the
creation of that relation and it’s being set to true.
set_..._right_location_value
Similar to the invert internally, but the method takes an additional parameter being the value to use
for the relation between the right, location and e.g. user. Like the invert-methods, this method will
create the relation if it doesn’t exist yet.
switch_location_inherit | switch_location_lock
Invert the inheritance and lock switches for a given location object
get_rights_icon
Helper method that returns a visual status of permission instead of a simple true or false. Particularly
useful to help the end-users / administrators to use rights.
get_availabble_rights
Returns the rights available in a specific application as defined in their respective rights interface.
get_allowed_users
Returns an array of user_ids containing all users which have permissions for a certain right on a
certain location. E.g. moderators for English in the Chamilo translation tool are determined this way.
Having a fantastic back-end is a start, but it’s nothing if not completely useless for end-users or
platform administrators. To provide developers with an easy and consistent way of offering rights
management to their intended audience a general rights editor manager was implemented. This
manager allows you to configure rights for one or more locations linked to any number of
(predefined) users and/or groups.
Instantiating a new RightsEditorManager is easy to say the least and once instantiated all you have
to do is run it. The manager takes two parameters, the first being the current object which offers the
manager a context to run in. The second parameter is an array of locations for which we’ll be
allowing the manager instance to manipulate rights for.
This functional requirement resulted in 2 new methods called limit_users and limit_groups,
both of which accept an array of respectively user ids and group ids.
This does not interfere with the exclude_users or exclude_groups functionality. Users and/or
groups excluded by means of those methods will still be excluded.
RightsEditorManager modus
Some applications use groups, some applications only take users into account, others use both. To
reflect this difference in context, a special modus property was added to the RightsEditorManager.
Per default the modus will be set to both, enabling the configuration of both users and groups.
Locations in Chamilo 2.0 are visually represented as a hierarchical tree. This implicates that each
node had one parent and can have one or more siblings and/or children. As explained in a previous
chapter, several problems arose when we only stored a parent for each location node in the tree.
The absolute reference concerning the storage of trees in SQL would have to be Joe Celko’s Trees and
hierarchies in SQL for smarties. It’s a must-read if you want to know more about this method of
storing and retrieving data, as well as the other alternatives or solutions that exist for this problem
depending on the context.
From Wikipedia:
The technique is an answer to the problem that the standard SQL operations are unable to
express all desirable operations on hierarchies directly. A hierarchy can be expressed in terms of a
parent-child relation but if it can have arbitrary depth, this does not allow the expression of
operations such as determining whether an element is somewhere in the subhierarchy of another
element. When the hierarchy is of fixed depth, the operations are possible, but expensive, due to
the necessity of performing one relational join per level.
Several resolutions exist and are available in some relational database management systems:
support for a dedicated hierarchy data type, such as in SQL's hierarchical query facility;
extending the relational language with hierarchy manipulations, such as in the nested
relational algebra.
extending the relational language with transitive closure; this allows a parent-child
relation to be used, but execution remains expensive;
the queries can be expressed in a language that supports iteration and is wrapped around
the relational operations, such as PL/SQL or T-SQL
When these solutions are not available or not feasible, another approach must be taken.
Fig. 5 – A typical hierarchical tree structure with the assigned left and right values.
The expensive nature of the updates is part of the reason we introduced trees per application at first
and also sub trees later on. Limiting the number of related nodes directly limits the number of left
and right values that have to be recalculated and per extension the expense of updates.
There are plenty of examples on the internet of practical ways to use this structure, what follows are
just a few basic examples which proved essential for rights management:
Number of children
(node.right – node.left – 1) / 2
Now that you fully understand the way the rights management system works it’s time to go over the
entire permissions checking algorithm as used in Chamilo 2.0. Imagine a developer has called the
is_allowed method and passed on all commonly used parameters.
Parameter Description
right The right to be checked
location The numeric identifier of the location’s object instance id or
component identifier
type The type of the location
application The application name
tree_identifier The numeric identifier of the locations subtree
tree_type The type of the location subtree
Table 10 – The parameters being passed to the is_allowed method of RightsUtilities
If implemented well throughout the platform then the rights management should be fairly invisible.
With the exception of things like configuring sharing-rights in the repository, the system almost
always operates in the background. Additional options will be made visible or will be hidden
according to the rights which a user is granted, either directly or by means of a rights template or
group membership.
Should you happen to be trying to access a page which you have no permissions for, you’re likely to
encounter error messages like the one below. They are, quite obviously, a direct result of a
permissions check via the rights management application.
Fig. 6 – A possible error message indicating a user does not have the necessary permissions
A somewhat less obvious example is the selection of moderators in the Chamilo translation
application. Visually it looks like just another element finder, but in the background the selection is
converted to basic rights. The get_allowed_users method is used to retrieve the moderators for a
specific language and as you may have guessed individual languages are defined as locations.
Fig. 7 – An example of rights management operating in the background of the translations tool
It’s by no means the solution to everything and forcing it to be something it’s not would only lead to
serious performance issues and unnecessary complicating of the existing structures.
Up until now you haven’t actually had to do anything, have you? The entire framework is there, but
how do you go about performing actual permissions checks in your applications?
<?php
class LaikaRights
{
const VIEW_RIGHT = 1;
const ADD_RIGHT = 2;
const EDIT_RIGHT = 3;
const DELETE_RIGHT = 4;
const LOCATION_ANALYZER = 1;
const LOCATION_BROWSER = 2;
const LOCATION_GRAPHER = 3;
const LOCATION_HOME = 4;
const LOCATION_MAILER = 5;
function get_available_rights()
{
$reflect = new ReflectionClass('LaikaRights');
$rights = $reflect->getConstants();
return $rights;
}
These components are not defined by the user when he or she runs the application, they are added
whenever the application is installed and never change. Such static locations are defined in an xml
file located in the applications rights folder and could look like this:
function create()
{
if (! $dm->create_content_object($this, 'new'))
{
return false;
}
$parent = $this->get_parent_id();
if (! $parent)
{
$parent_id = RepositoryRights :: get_user_root_id($this->get_owner_id());
}
else
{
$parent_id = RepositoryRights ::
get_location_id_by_identifier_from_user_subtree('repository_ca
tegory', $this->get_parent_id(), $this->get_owner_id());
}
if (! RepositoryRights :: create_location_in_user_tree($this->get_title(),
'content_object', $this->get_id(), $parent_id, $this->get_owner_id()))
{
return false;
}
return true;
}
Please note that if your object is part of a hierarchic structure, you will have to determine the rights
locations of those parents. It might be interesting to create some helper methods in your rights
interface that can handle these things for you. In a lot of cases these methods will simply be
<?php
class RepositoryRights
{
function get_user_root_id($user_id)
{
return RightsUtilities :: get_root_id(RepositoryManager ::
APPLICATION_NAME, 'user_tree', $user_id);
}
Performing checks
The culmination of all these objects, interfaces and algorithms is without a doubt the actual check for
a certain right on a certain location, so here goes:
Talk about an anti-climax ... but let’s face it: would you be inclined to use a system that forces you to
perform six calls to different methods and who knows how many other operations to perform such a
basic check? We sure wouldn’t.
Get involved
Do you like the possibilities that rights management creates for Chamilo 2.0? Would you like to get
involved or are you interested in cooperating?
Don’t hesitate to contact us via e-mail or let us know about your project(s) via the community
website located at http://www.chamilo.org. We’re looking forward to hearing from you.
A
N
Adjacent List .................................................................. 20
Algorithm..................................................... 14, 15, 17, 21 Nested Set .................................................................... 20
Nested Tree ........................................................13, 19, 20
C
P
Child ........................................................................ 20, 21
Code snippet ................................................................. 26 Parent..................10, 12, 13, 15, 17, 19, 20, 21, 22, 25, 26
Component................................... 9, 12, 13, 16, 21, 25, 26
R
D
Relation ................................ 10, 11, 12, 14, 17, 18, 20, 22
DBMS ............................................................................. 11 Right .... 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22,
Dynamic........................................................................ 25 24, 26
RightsEditorManager .............................................. 18, 19
G RightsUtilities ................................... 14, 15, 16, 21, 24, 26
Root.......................................................... 9, 16, 17, 25, 26
Group ............................. 10, 11, 12, 14, 17, 18, 19, 22, 23
S
H
Static ..................................................................15, 25, 26
Helper ..............................................14, 15, 16, 17, 18, 25 Storage unit .................................................. 11, 12, 13, 15
I T
identifier ................... 11, 12, 13, 15, 16, 17, 21, 22, 25, 26 Template .......................................... 10, 11, 12, 17, 22, 23
Inheritance ............................... 8, 9, 10, 12, 13, 15, 17, 22 tree_identifier .................................. 13, 15, 16, 17, 21, 22
Interface ..........................................14, 15, 18, 19, 24, 25 tree_type ......................................... 13, 15, 16, 17, 21, 22
is_allowed ........................................15, 16, 17, 21, 24, 26 type .......................9, 12, 13, 15, 16, 17, 21, 22, 24, 25, 26
L U
Left .................................................................... 13, 20, 21 User .... 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23,
Locking .............................................10, 12, 13, 15, 17, 22 24, 25, 26