Вы находитесь на странице: 1из 28

To Have and Have Not

Chamilo 2.0 Rights Management

Chamilo 2.0 Rights Management | 1


2 | To Have and Have Not
To Have and Have Not
Chamilo 2.0 Rights Management
By Hans De Bisschop

Chamilo 2.0 Rights Management | 3


Credits

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.

4 | To Have and Have Not


All our dreams can come
true, if we have the
courage to pursue them.

- Walt Disney (1901-1966)

Chamilo 2.0 Rights Management | 5


Contents

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

6 | To Have and Have Not


get_..._right_location ........................................................................................................................ 18
get_rights_icon .................................................................................................................................. 18
get_availabble_rights ........................................................................................................................ 18
get_allowed_users ............................................................................................................................ 18
Still standing: The Rights Editor Manager ............................................................................................. 18
Excluding users and groups ............................................................................................................... 19
Filtering users and groups ................................................................................................................. 19
RightsEditorManager modus ............................................................................................................. 19
The Iron Lion: A Nested Tree ................................................................................................................. 19
Why nested trees / nested set models?............................................................................................ 20
How does it work? ............................................................................................................................. 20
Sultan of Swing: The Rights Algorithm .................................................................................................. 21
The Invisible Touch: Where are rights visible? ...................................................................................... 23
End of the Line: Using Rights ................................................................................................................. 24
Defining a rights interface ................................................................................................................. 24
Installing static locations ................................................................................................................... 25
Adding dynamic locations ................................................................................................................. 25
Performing checks ............................................................................................................................. 26
Get involved .......................................................................................................................................... 26
Index ...................................................................................................................................................... 27

Chamilo 2.0 Rights Management | 7


Introduction

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?

 Who are you?


 Where are you?
 What do you want to do?

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”.

Sounds easy enough, right? Wrong. The theory of it all


might be deceivingly easy, but in reality rights
management is a combination of complex (hierarchical)
structures, inheritance, locks, algorithms and last but not
least: frustrated users. Because of the sheer complexity
and scale of such systems it’s often a nightmare to
maintain and an even bigger nightmare to set up or
implement.

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.

8 | To Have and Have Not


The Concepts: A Bridge over Troubled Water

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.

Chamilo 2.0 Rights Management | 9


Consider the following: I strongly believe in the concept of creative commons and want all my objects
to be shared with every other user. To make this happen I would have to define several rights for
every single object in my repository. That’s all nice and dandy if you have 10 objects, but if you have
a 1000 objects it would be frustrating to say the least. Wouldn’t it be easier to just define that right
for my user repository (the top location in that locations tree)? It obviously would be ... and that’s
what inheritance does. Unless explicitly disabled, the system will automatically check for permissions
with the location’s direct parents.

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.

Locking is a way to prevent this. As soon as someone flags a location as locked, no


one will be able to define rights for any of the children on that specific location.
They will automatically inherit the set of rights as defined by the locked parent.

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.

10 | To Have and Have Not


Summary
You can define:
 Rights
 Locations
 Templates

You can apply templates to:


 Users
 Groups

You can apply rights to:


 Users
 Groups
 Templates

Persistent storage: A Thriller

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.

Field Type Extra


id int(10) The numeric identifier of the template
name char(250) The name
description text The description
Table 1 – Storage unit structure for a (rights) template

Field Type Extra


id int(10) The numeric identifier of the user right location
user_id int(10) The numeric identifier of the user
right_id int(10) The numeric identifier of the right
location_id int(10) The numeric identifier of the location
value tinyint(3) The actual value, true (1) or false (0)
Table 2 – Storage unit structure for the relation between a user, a right and a location

Field Type Extra


id int(10) The numeric identifier of the group right location
group_id int(10) The numeric identifier of the group
right_id int(10) The numeric identifier of the right
location_id int(10) The numeric identifier of the location
value tinyint(3) The actual value, true (1) or false (0)
Table 3 – Storage unit structure for the relation between a group, a right and a location

Chamilo 2.0 Rights Management | 11


Field Type Extra
id int(10) The numeric identifier of the template right location
rights_template_id int(10) The numeric identifier of the template
right_id int(10) The numeric identifier of the right
location_id int(10) The numeric identifier of the location
value tinyint(3) The actual value, true (1) or false (0)
Table 4 – Storage unit structure for the relation between a (rights) template, a right and a location

Field Type Extra


user_id int(10) The numeric identifier of the user
rights_template_id int(10) The numeric identifier of the template
Table 5 – Storage unit structure for the relation between a user and a (rights) template

Field Type Extra


group_id int(10) The numeric identifier of the group
rights_template_id int(10) The numeric identifier of the template
Table 6 – Storage unit structure for the relation between a group and a (rights) template

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.

Field Type Extra


id int(10) The numeric identifier of the location
location_id char(50) A textual descriptor of the location
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?
Table 7 – A possible storage unit structure for locations?

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.

How do we map these locations to actual components and object instances?


You can’t, which is why a few extra properties are necessary. First and foremost there’s the
identifier, which in most cases is nothing more than the id of the object instance or a unique id which
was given to a specific component.

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

12 | To Have and Have Not


still get extremely big and the best example of that is the
repository. Imagine a repository location tree for a
platform with 10.000 users whom each have about a 100
content objects. That’s 1 million locations and more
importantly that’s quite disturbing. Recursion through
that isn’t hell, that’s the end of days, plain and simple.

But wait a minute. There’s little or no interaction


between location trees of different applications, couldn’t
we extend this principle a bit further? It turns out we

can, which is once more nicely illustrated by the


Fig. 2 – Simple trees like this one can quickly
become very big and very complicated… repository. Repositories are the personal property of
individual users. Rights management-wise there is
nothing that really spans multiple repositories. So once more we decided it would be acceptable to
split things up a bit more to allow sub trees per application. To make this possible two additional
properties were introduced: tree_identifier and tree_type. The same principle applies to them
as does to the regular identifier and type.

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.

We only store parent id’s, that can’t be ideal, can it?


Unless you have a mainframe at your disposal, there is no way that just using parent id’s is ever going
to be the best and fastest solution. We’re obviously not the first ones to come to this conclusion. To
solve these kinds of problems several alternative storage structures were conceived, one of which
are nested trees. For more information on nested trees, check the corresponding chapter. For now,
suffice it to say we’ll need 2 more properties: a left_value and a right_value.

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

Chamilo 2.0 Rights Management | 13


London is Calling for a Rights Interface

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.

Fig. 3 – An example of a typical course group in Chamilo 2.0’s courses application

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

14 | To Have and Have Not


by extending the default is_allowed helper method to not only check the default algorithm, but to
also take this additional relation into account.

... 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 Rights Utilities will rock you

The advantage of a central rights management system is that all default


functionality can be centralized as well, thus not bothering application developers
with the several complex layers of functionality and the boring / mind numbing
algorithms involved in making rights management work.

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.

Chamilo 2.0 Rights Management | 15


create_application_root_location | create_subtree_root_location
Helper methods used to create the root nodes of application (sub) trees during the installation of the
application or the initialization of certain components.

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

16 | To Have and Have Not


want to know what other users see, it could be a good idea to use the functionality that allows an
administrator to hijack another user’s account temporarily.

The actual algorithm has its own chapter.

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.

The methods use their respective get_..._rights_location method to verify


the actual permission and take inheritance into account when determining the
resulting permission.

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

Chamilo 2.0 Rights Management | 17


get_..._right_location
Gets the value of a specific rights relation based on the right_id, location_id and e.g. user_id. If
the requested relation doesn’t exist, false will returned.

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.

Still standing: The Rights Editor Manager

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.

$manager = new RightsEditorManager($this, $locations);


$manager->run();

Fig. 4 - The default layout of the RightsEditorManager in the repository

18 | To Have and Have Not


Some additional functionality was added to make the RightsEditorManager useable in as many
situations as possible.

Excluding users and groups


In some cases we may want to exclude a certain set of users or groups from the rights editor
manager. E.g. the currently logged in user when you’re configuring permissions for your own objects.
To exclude users or groups, simply call one of the following methods and pass on an array of
user_ids or group_ids.

//Exclude the currently logged in user


$manager->exclude_users(array($this->get_user_id()));
//Exclude the currently logged in user’s groups
$manager->exclude_groups($this->get_user()->get_groups(true));

Filtering users and groups


To allow flexible usage of the RightsEditorManager interface to the rights application, it’s necessary
to be able to filter the list of users and/or groups for whom it is possible to manage rights by means
of said instance.

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.

//Limit users to the currently logged in user


$manager->limit_users(array($this->get_user_id()));
//Limit groups to the currently logged in user’s groups
$manager->limit_groups($this->get_user()->get_groups(true));

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.

//Allow configuration of groups and users, the default


$manager->set_modus(RightsEditorManager :: MODUS_BOTH);
//Only allow configuration of groups
$manager->set_modus(RightsEditorManager :: MODUS_GROUPS);
//Only allow configuration of users
$manager->set_modus(RightsEditorManager :: MODUS_USERS);

The Iron Lion: A Nested Tree

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.

Chamilo 2.0 Rights Management | 19


The simple usage of the parent-child relation to manipulate the data is what he describes as the
adjacency list model and is far from quick or easy to use if you need to retrieve entire sets of
hierarchical data frequently.

Why nested trees / nested set models?

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.

How does it work?

Fig. 5 – A typical hierarchical tree structure with the assigned left and right values.

20 | To Have and Have Not


From Wikipedia:
The solution of the nested set model is to number the nodes according to a tree traversal, which
visits each node twice, assigning numbers in the order of visiting, and at both visits. This leaves
two numbers for each node, which are stored as two attributes. Querying becomes inexpensive:
hierarchy membership can be tested by comparing these numbers. Updating requires
renumbering and is therefore expensive.

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:

Finding all leaf nodes


SELECT * FROM table WHERE right = left + 1;

Finding all parents of a node


SELECT * FROM table WHERE left < node.left AND right > node.right

Finding all children of a node


SELECT * FROM table WHERE left > node.left AND right < node.right

Number of children
(node.right – node.left – 1) / 2

Is the node a child of ... ?


node.left > parent.left && parent.right > node.right

Is the node a parent of ... ?


node.left < child.left && child.right < node.right

Sultan of Swing: The Rights Algorithm

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

Chamilo 2.0 Rights Management | 21


This would result in the following logic flow:

1. Is the user_id set?


1.1. Yes, retrieve the user object for the given id
1.2. No, retrieve the current user_id from the session and retrieve the user object
2. Generate a unique cache_id from all parameters and verify whether the check was performed
before.
2.1. Yes, return the result
2.2. No, continue
3. Is the user a platform administrator?
3.1. Yes, immediately return true
3.2. No, continue
4. Retrieve the location based on the parameters (identifier, type, tree_identifier,
tree_type, application)

4.1. Return false if the location does not exist


4.2. If it does, continue
5. Does the location have a locked parent?
5.1. Yes, the locked parent becomes the new base location
5.2. No, continue
6. Retrieve the hierarchical sequence containing the location and it’s parents
7. Get the groups the user is a member of and for each group
7.1. Retrieve rights templates applied to the group and for each template and each location the
sequence
7.1.1.Check whether a template relation is defined and if it the right is granted, return true
7.1.2.If the location does not inherit from its parent, break the loop
7.2. For each location in the sequence
7.2.1.Check whether a group relation is defined and if it the right is granted, return true
7.2.2.If the location does not inherit from its parent, break the loop
8. Get the rights templates applied to the individual user and for each template and each location in
the sequence
8.1. Check whether a template relation is defined and if it the right is granted, return true
8.2. If the location does not inherit from its parent, break the loop
9. For each location in the sequence
9.1. Check whether a user relation is defined and if it the right is granted, return true
9.2. If the location does not inherit from its parent, break the loop
10. If we encountered no granted permissions by now, return false.
11. Cache the result for later reference

22 | To Have and Have Not


The Invisible Touch: Where are rights visible?

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

Chamilo 2.0 Rights Management | 23


Basically a lot of things could be described as rights. The current system of subscribing users to a
course is also a way of defining certain globalised right and could just as easily be expressed as a set
of rights. At the same time we should carefully consider where and when to use complete rights
management and where to use other solutions.

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.

End of the Line: Using Rights

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?

Defining a rights interface


Before you can get started you’ll have to implement a rights interface for your application which
makes using the RightsUtilities class just a little easier. As discussed before this interface also
contains the definition of your elementary rights, so you may want to take some time to determine
what those rights should be.

The result could be as simple as this:

<?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();

foreach ($rights as $key => $right)


{
if (substr(strtolower($key), 0, 8) == 'location')
{
unset($rights[$key]);
}
}

return $rights;
}

function is_allowed($right, $location, $type)


{
return RightsUtilities :: is_allowed($right, $location, $type, LaikaManager
:: APPLICATION_NAME);
}
}
?>

24 | To Have and Have Not


Installing static locations
As you may have noticed, the above example defines a few locations within the application as
constants. Why define them that way? The answer is simple: performance. Storing and querying
relatively short integers is a lot faster than storing and especially querying long strings.

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:

<?xml version="1.0" encoding="UTF-8"?>


<location name="laika" type="root" identifier="0">
<children>
<location name="Analyzer" type="laika_component" identifier="1" />
<location name="Browser" type="laika_component" identifier="2" />
<location name="Grapher" type="laika_component" identifier="3" />
<location name="Home" type="laika_component" identifier="4" />
<location name="Mailer" type="laika_component" identifier="5" />
</children>
</location>

Adding dynamic locations


If your application will contain dynamic locations, defined by users, you’ll have to add some code to
the respective objects to make sure corresponding locations are created whenever a new instance of
such a user-defined object is created. An example of this is the creation of content objects:

function create()
{

$dm = RepositoryDataManager :: get_instance();


$this->set_object_number($dm->get_next_content_object_number());

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

Chamilo 2.0 Rights Management | 25


references to a general RightsUtilities method with a few more predefined parameters. Just have
a look at the helper methods from the code snippet for the creation of content objects.

<?php
class RepositoryRights
{

static function create_location_in_user_tree($name, $type, $identifier,


$parent, $user_id)
{
return RightsUtilities :: create_location($name, RepositoryManager ::
APPLICATION_NAME, $type, $identifier, 0, $parent, 0, $user_id,
'user_tree');
}

function get_user_root_id($user_id)
{
return RightsUtilities :: get_root_id(RepositoryManager ::
APPLICATION_NAME, 'user_tree', $user_id);
}

static function get_location_id_by_identifier_from_user_subtree($type,


$identifier, $user_id)
{
return RightsUtilities :: get_location_id_by_identifier(RepositoryManager
:: APPLICATION_NAME, $type, $identifier, $user_id, 'user_tree');
}
}
?>

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:

if (! LaikaRights :: is_allowed(LaikaRights :: VIEW_RIGHT, LaikaRights ::


LOCATION_GRAPHER, 'laika_component'))
{
$this->display_header($trail);
$this->display_error_message(Translation :: get('NotAllowed'));
$this->display_footer();
exit();
}

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.

26 | To Have and Have Not


Index

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

Chamilo 2.0 Rights Management | 27


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
28 | To Have
Creative and Have
Commons, 171Not
Second Street, Suite 300, San Francisco, California, 94105, USA.

Вам также может понравиться