class for manipulating (edit+save) access control lists
Overview
--------
Every user account is associated with an access control list. This access control list boils down to a total of six tables in the database:
acls: acl_id serial* permissions_jobs int permissions_intranet int permissions_modules int permissions_nodes int acls_areas: acl_id int* (link to acls) area_id int* (link to areas) permissions_intranet int permissions_modules int permissions_nodes int acls_nodes: acl_id int* (link to acls) node_id int* (link to nodes) permissions_modules int permissions_nodes int acls_modules: acl_id int* (link to acls) module_id int* (link to modules) permissions_modules int acls_modules_areas: acl_id int* (link to acls) module_id int* (link to modules) area_id int* (link to areas) permissions_modules int acls_modules_nodes: acl_id int* (link to acls) module_id int* (link to modules) node_id int* (link to nodes) permissions_modules int *marked fields are (part of) the primary key
The six tables mentioned above deal with the following permission bitmasks.
3. Sometimes it is more convenient to specify the permissions on a higher level because otherwise the size of the database may get out of hand. Example: if every user has a permission bitmask for every node on the site, the corresponding acl would have number_of_users x number_of_nodes entries. That is completely unmanageable, even for small to medium size sites.
Users and group/capacities
--------------------------
A user can also participate in a group in a particular capacity, e.g. member of group 'grade8' in the 'pupil'- or the 'teacher'-capacity. Every combination of group and capacity (eg 'grade8/pupil') is also associated with an access control list.
The full access control list for a user is the combination of the ACL directly associated with the user account and the ACLs associated with the group/capacities that apply to the user account. The effective permissions for a user are the result of OR'ing the permissions of all ACLs.
A specific permission is always indicated by a bit set to '1'. If a particular bit is set to '0', the user does not have the corresponding permission. This implies that the (special) bitmask 0 (zero, 32 bits are all not set) corresponds to 'no permissions at all'. It also implies that the (special) bitmask -1 (minus one, 32 bits are all set) equates to 'all permissions'.
Therefore, the *easy* way to grant access is to set the permissions bitmask to -1. This is the so-called Guru-option or -role or the Guru-permissions. However, note that granting a user or a group/capacity Guru-permissions, means that that user (these users) can do serious harm to the system because she (they) are allowed to do anything. The *safe* way is to grant as few permissions as possible.
Roles
-----
In order to make it easier to setup the access controls and stay away from directly manipulating individual bits in a bitmask the various permission bits are combined into roles.
Two roles are always available for selection:
Example: suppose that there is a module called 'Forum' which works with authenticated users. Depending on this module's permission bits the users are allowed to perform certain actions, e.g.
Obviously these roles (defined via the module in this example) will end up in a dropdown list where the appropriate role can be assigned.
Note that it is not necessary to have hierarchical roles as demontrated in this example. It is very wel possible to define two roles that must work together: ROLE_EDITOR could be a bitmask that allows for adding (1), editing (2), deleting (4), previewing (8) news articles whereas ROLE_PUBLISHER could be limited to previewing (8) and publishing (16) news articles, but not editing them. That would make sure that at least two different people are required to create and publish an article. (However, any 'Guru', with all permissions granted due to the -1 bitmask, could create + publish articles by herself.)
Module-permissions in acls, acls_areas and acls_nodes
-----------------------------------------------------
The fields permissions_modules in the tables acls, acls_areas and acls_nodes should be considered as 'blanket permissions'. If a permission is set in either of these tables, the permissions apply to *all* modules at site level (acls), area_level (acls_areas) or node_level (acls_nodes).
Because these permissions apply to *all* modules, the only realistic roles in these cases can be either ROLE_NONE (permissions = 0) or ROLE_GURU (permissions = -1). Any other role could be meaningless for one or more modules.
Furthermore, it is a little over the top to specify permissions for *all* modules in a particular node. (It almost doesn't make sense). Therefore, the corresponding dialog only deals with these two roles ROLE_NONE and ROLE_GURU at the site level and the area level. The node level is not used for modules (but it is for pagemanager permissions - the field permissions_nodes - at the node level).
Typical usage
-------------
Example 1: displaying a dialog with intranet permissions for a group
$acl = new AclManager($output,$acl_id,ACL_TYPE_INTRANET); $acl->set_action(array('job'=>'accountmanager','task'=>'groupsave','group'=>'8'); $acl->set_dialog(GROUPMANAGER_DIALOG_INTRANET); $acl->show_dialog(); ...The result of this snippet is that a complete dialog is output to the content area of the $output object, including the current values from the database. The whole dialog is wrapped in a FORM-tag with action property based in the array set with the set_action() method. The dialog is POSTed with either a Save, a Done or a Cancel button.
Example 2: saving the data for the intranet permissions for a group
$acl = new AclManager($output,$acl_id,ACL_TYPE_INTRANET); $acl->set_action(array('job'=>'accountmanager','task'=>'groupsave','group'=>'8'); $acl->set_dialog($dialog); if (!$acl->save_data()) { $acl->show_dialog(); // redo dialog, but without a distracting menu this time return; } ...
The effect of this snippet is that an attempt is done to validate and save the data as it was POSTed (ie: the new values area available in $_POST[]). If, however, saving the data did not work, the dialog is displayed again, this time using the data from $_POST[] rather than from the database.
Example 3: displaying a dialog with admin permissions for a user
$related_acls = array($acl_id1 => "group1/capacity1",$acl_id2 => "group2/capacity2", ...); $acl = new AclManager($this->output,$acl_id,ACL_TYPE_ADMIN); $acl->set_related_acls($related_acls); $acl->set_action(array('job'=>'accountmanager','task'=>'usersave','user'=>'23'); $acl->set_dialog(USERMANAGER_DIALOG_ADMIN); $acl->show_dialog();
This comparable to example 1. The difference is that in a User-ACL there is an option to display existing permissions from the user's group/capacities. This information is displayed in the third column in the dialog. This provides a clue for the user that certain permissions might already be granted to the user via a group membership. The related permissions are communicated via an array with (integer) acl_id's as key and a string value identifying the group/capacity.
Located in /program/lib/aclmanager.class.php (line 322)
constructor for the AclManager
this constructs a new AclManager object. Essential inforation such as the acl_id and the acl_type are stored, for future reference.
calculate the total number of items (site, areas, nodes) to show in dialog
the 'open' or 'closed' status of an area is dictated by $open_areas:
The parameter $areas is used as a return value. It is keyed with $area_id and filled with pertinent information about the areas:
construct a form with a dialog in a table with 2 or 3 columns
this constructs a 2- or 3-column table and fills it with data from the dialogdef.
The first column holds the labels for the widgets. The second column holds the corresponding widget, e.g. a list box with roles. The optional third column (depends on the flag $show_related) shows related information. This is used to list group/capacities and roles from related groups (ie. groups of which the user is a member).
The table has headers for the columns: 'Realm','Role' and optional 'Related'. Rows in the table can have alternating colours via the odd/even class. This is done via the stylesheet.
further initialise the AclManager and enable the area expand/collapse feature
this stores the necessary information about 'open' and 'closed' areas. The parameter $areas_open indicates the current state of affairs: ($areas_open === FALSE) means all areas are closed ($areas_open === TRUE) means all areas are opened If $areas_open is an array, it contains area_id's as key and TRUE or FALSE as value. A value of TRUE indicates that an area is currently 'open', FALS or no value set means 'closed'.
The parameters in $a_params combined with $WAS_SCRIPT_NAME yield an URL where the changes are processed.
further initialise the AclManager and enable the dialog pagination feature
this stores the information that is necessary when a dialog has to be broken up into two or more screens (via the pagination facility in $output). This routine stores the essential information such as the parameters that lead to the correct page (in $a_params) and the current offset. The other necessary parameters are calculated dynamically before add_pagination() is called.
Note that pagination is only enabled after this routine is called at least once; by default we do NOT do pagination. (Actually: pagination is only used in the acl_types ACL_TYPE_PAGEMANAGER and ACL_TYPE_MODULE).
construct an array with the admin dialog information
this creates an array with widgets for all possible admin jobs for $acl_id.
This dialog is supposed to be rendered as a 2-column (group acl) or 3 column (user acl) table. The contents of the 3rd column is a list (an array) of related permissions, ie. the permissions a user has been granted via a group membership. The related information is stored in an extra array element 'related'.
The related information is constructed only in the case where $related_acls is not NULL.
The dialog is filled with the current values via $item['value'] but as a side effect the current value is also recorded in $item['old_value']). This makes it easier to determine whether any values have changed (see save_data_admin()).
construct an array with the intranet dialog information
this creates an array with 1 or more list boxes with the current roles for $acl_id for intranet access at the site level and for individual private areas. This dialog is supposed to be rendered as a 2-column (group acl) or 3 column (user acl) table. The contents of the 3rd column is a list (an array) of related permissions, ie. the permissions a user has been granted via a group membership. The related information is stored in an extra array element 'related'.
The related information is constructed only in the case where $related_acls is not NULL.
The dialog is filled with the current values via $item['value'] but as a side effect the current value is also recorded in $item['old_value']). This makes it easier to determine whether any values have changed (see save_data_internet()).
construct a dialog definition for pagemanager permissions
construct a clickable icon to open/close this area
This is a toggle: if the area is closed the closed icon is shown, but the action in the A-tag is to open the icon (and vice versa).
retrieve an array with 0, 1 or more records with permissions from table 'acls'
this constructs an array with all (or selected) permissions from the 'acls' table for the specified acl $acl_id and optionally for all related acl_id's in $related_acls. The resulting array is keyed by acl_id.
retrieve an array with 0, 1 or more records with permissions from table 'acls_areas'
this constructs an array with all permissions from the 'acls_areas' table for the specified acl $acl_id and optionally for all related acl_id's in $related_acls and optional areas. The resulting array is keyed by area_id and acl_id.
Note that by making the result keyed by area_id first (and then acl_id) it becomes possible to step throug a list of areas and have 0,1 or more acls for that area in a single array, e.g. $acls = $permissions[16] yields the selected acls that apply to area 16. That is handy when constructing dialogs iterating through areas such as intranet permissions.
retrieve an array with 0, 1 or more records with permissions from table 'acls_nodes'
this constructs an array with all permissions from the 'acls_nodes' table for the specified acl $acl_id and optionally for all related acl_id's in $related_acls and optional nodes. The resulting array is keyed by node_id and acl_id.
Note that by making the result keyed by node_id first (and then acl_id) it becomes possible to step throug a list of nodes and have 0,1 or more acls for that node in a single array, e.g. $acls = $permissions[16] yields the selected acls that apply to node 16. That is handy when constructing dialogs iterating through nodes such as pagemanager permissions.
contstruct an option list with roles for intranet access
construct an option list with roles for pagemanager access
save the changed data for the selected acl_type
this interprets the data from the selected dialog and saves the (changed) permission data accordingly. This, too, is merely a dispatcher to the subroutines that do the actual work.
save changed job permissions to the database
this saves the changed job permissions to the acls table.
If the user selected the guru option, we simply set the permissions to JOB_PERMISSION_GURU (i.e. all permissions set). If not, we iterate through all existing permissions and set the corresponding bits. Then the data is saved to the correct acls-record. After that we re-read the dialogdef again because the user may want to continue editing rather than be [Done] with job permissions.
save the changed roles for intranet access to the tables 'acls' and 'acls_areas'
this interprets the data from the intranet dialog and saves the changed roles accordingly
save the changed roles for pagemanager to the tables 'acls' and 'acls_areas' and 'acls_nodes'
this interprets the data from the pagemanager dialog and saves the changed roles accordingly
save the changed roles in the dialog to the corresponding tables 'acls'
this interprets the data from the current dialog and saves the changed roles accordingly. Note that the information about tables and fields etc. is all contained in the dialogdef so we can use this generic save_data() routine.
further initialise the AclManager with the dialog action property
this stores an array with parameters that must be added to the action property of the HTML form that will be POSTed, i.e. the URL to which the dialog will be posted. Example of such an array is: array('job' => 'accountmanager', 'task' => 'user_save', 'user' => 123); The information in this array is later combined with WAS_SCRIPT_NAME.
further initialise the AclManager with the dialog identification
this stores an integer number that is used to identify the dialog. This number is subsequently added to the dialog as a hidden field, which makes it possible to identify the dialog once it is POSTed
further initialise the AclManager with the dialog header
this stores a string that is used as a title for the dialog Note that this header may be extended with a (translated) string like '[{FIRST}-{LAST} of {TOTAL}]' in case of a paginated display.
further initialise the AclManager with the dialog introductory text
this stores a string that is displayed after the dialog header. This text supposedly contains some more information about the dialog.
further initialise the AclManager with related Acl's
this stores the array with 0, 1 or more key-value-pairs of the form $acl_id => $group_capacity_name, e.g. 3 => 'staff/member', 4 => 'grade7/teacher'
show the dialog where the selected Acl can be modified
this shows the dialog corresponding to the acl_type that was previously selected, including existing data from the previously selected acl_id. Note that this routine is only a simple dispatcher; actual work is done in subroutines.
display a tabular form for manipulating admin permissions
This dialog is a table consisting of 2 (group acl) or 3 (user acl) columns. The first column holds the various job names/descriptions. The second column holds a checkbox for the job. The optional third column holds corresponding (existing) permissions based on a group/capacity membership of the user. This 3rd column is displayed only when there are related acls (indicated via related_acls not empty)
display a tabular form for manipulating intranet permissions
This dialog is a table consisting of 2 (group acl) or 3 (user acl) columns. The first column holds the text 'All areas' or the name of a private area (if any) The second column holds a listbox where the user can select 1 out of 3 roles: 0 = "--", 1 = "Access", -1 = "Guru". The optional third column holds corresponding (existing) roles based on a group/capacity membership of the user. This 3rd column is displayed only when there are related acls (indicated via related_acls not empty)
display a tabular form for manipulating pagemanager permissions
This dialog is a table consisting of 2 (group acl) or 3 (user acl) columns. The first column identifies the site, areas or nodes within areas. The second column holds a listbox where the user can select a role for that particular item. The roles 0 = "--" and -1 = "Guru" are always available. The optional third column holds corresponding (existing) roles based on a group/capacity membership of the user. This 3rd column is displayed only when there are related acls (indicated by $related_acls not being empty).
The main purpose of this routine is to show some $this->pagination_limit table rows (starting at $this->pagination_offset) and corresponding [Save], [Done] and [Cancel] buttons that eventually lead to the save routine. (If pagination is not enabled, the full overview is displayed).
Note that the actual pagination is performed in get_dialogdef_pagemanager(). The additional feature of expanding/collapsing areas in the display is also done in get_dialogdef_pagemanager().
add the specified node to dialogdef, optionally all subtrees, and subsequently all siblings
this routine adds a widget to the dialogdef for the specified node After that, any subtrees of this node are added too, using recursion This continues for all siblings of the specified node until there are no more (indicated by a sibling_id equal to zero).
build a tree of all nodes in an area
this routine constructs a tree-structure of all nodes in area $area_id in much the same way as tree_build() does. However, in this routine we keep the cargo limited to a minimum: the fields we retrieve from the nodes table and store in the tree are:
Documentation generated on Tue, 28 Jun 2016 19:07:44 +0200 by phpDocumentor 1.4.0