PHP Access Control - PHP5 CMS Framework Development
Asking RBAC Questions
Perhaps the most signifi cant class is the one that actually answers questions about permitted access. The aliroAuthoriser class is once again a singleton with the usual mechanisms. For convenience, it has getAllRoles and getTranslatedRole methods, but these are really implemented in the cache class described above.
The constructor does some relatively simple setting, including looking for cached data in the PHP super-global $_SESSION:
private function __construct()
{
// Make sure session started
aliroSessionFactory::getSession();
// Use session data as the source for cached user related data
foreach ($this->auth_vars as $one_var)
{
if (!isset($_SESSION['aliro_auth'][$one_var]))
$_SESSION['aliro_auth'][$one_var] = array();
$this->$one_var =& $_SESSION['aliro_auth'][$one_var];
}
$this->handler = aliroAuthoriserCache::getInstance();
$this->linked_roles = $this->handler->getLinkedRoles();
$this->database = aliroCoreDatabase::getInstance();
}
Getting the current session, even though it is not used directly for anything, ensures that a session has been started so that $_SESSION will contain data, if there is any. Since Aliro always activates a session, and much RBAC data is specifi c to the current user, it makes good sense to cache as session data. The handler and database objects are found using the usual singleton access method, getInstance, and linked roles are obtained from the authorizer cache.
Many RBAC questions involve roles, and the option of a hierarchy means that one role can imply another. This relationship is stored in the linked_roles property. Having roles implied means that a set of roles may include entries that are not really needed. The minimizeRoleSet method eliminates them:
public function minimizeRoleSet ($roleset)
{
if (0 == count($roleset)) return $roleset;
$first = array_shift($roleset);
foreach ($roleset as $key=>$role)
{
if (isset($this->linked_roles[$first][$role])) unset
($roleset[$key]);
if (isset($this->linked_roles[$role][$first])) return
$this->minimizeRoleSet ($roleset);
}
array_unshift($roleset, $first);
return $roleset;
}
There are about a score of other methods, some public, and some private. In detail, the key ones become quite complex. This is partly because of the nature of RBAC, and partly to do with attempts at effi ciency. Others are very simple, but this is because they are interfaces to the more substantial methods, but with simplifi ed parameters, so as to provide a more usable interface. Because of the complexity, a selection of the remaining classes is discussed in outline rather than being reviewed in detail. The full code is downloadable from the Aliro website.
Permissions refer to actions on subjects, and it is very likely that multiple queries will arise around similar subjects. The private method getSubjectData is used to load permissions, based on a subject and an action, that is, a specifi c permission. This method always ensures that all relevant rows from the permission table will be loaded. The number of directly relevant rows will be the number of roles that have the given permission. But the method also tries to get more data than is strictly necessary. Depending on the number of records involved, the method may load all permission data relating to the type of subject specifi ed, not merely to the specifi c subject. The precise number chosen is subject to optimization work. That is to say, all records where the subject type matches, not just those that match both subject type, and subject identifi er. This is done because it is common for a question about rights to a particular subject is often followed by a question about another subject of the same kind. The permission data that is loaded is organized into array structures to maximize the effi ciency of lookups, and it is also cached as session data.
The method getAccessorRoles is used both internally and externally. Its prototype is:
public function getAccessorRoles ($type, $id)
It also returns an array of roles. The processing is complicated by the storage of data in cache, something that is especially important for accessors since it is very likely that a number of questions will be asked about the current user. The parameters are the type of accessor (such as 'a User'), and the identifi er (such as a user ID number).
A private method, accessorPermissionOrControl, does the basic work of fi nding out whether a particular accessor has rights to a given subject for a stated action. The type of access is passed as a bit mask. This method is then used to create a series of very simple public methods. The most frequently used has a prototype:
public function checkPermission ($a_type, $a_id, $action, $s_type='*', $s_id='*')
The result is zero or one to indicate false or true respectively. The accessor type and ID together defi ne the accessor. Action is self explanatory. Subject type and ID together defi ne the subject. There are situations where wild cards are used. For example, when the action is to manage and the subjects are all users, then the subject ID will be the asterisk wild card. Other actions may have no subject at all, in which case both subject type and ID will be asterisks.
For ease of development, an alternative to checkPermission is the method with prototype:
public function checkUserPermission ($action, $s_type='*', $s_id='*')
It assumes that the accessor is the current user, whose details can be obtained from a standard class in the CMS, so only the action and the subject need be specifi ed. Similar methods to the last two also exist to handle the granting of rights.
While the link between accessors, and subjects via roles can often be kept under the covers and handled within the authorizer class, in some cases it is needed explicitly. It is therefore possible to ask whether a particular role can access a subject for a particular action:
public function checkRolePermission ($role, $action, $s_type, $s_id)
When it comes to deciding questions of access to objects that are generally managed by another piece of software, the most effective query is to fi nd out which items are not available. Let's return to our example of a fi le repository, where roles are given access to download from specifi c folders. A folder is identifi ed by its subject type, say remosFolder and an identifi er, which in this case, is an ID number. Because we have a rule saying that anything that does not have any specifi c permissions set is available to all, it is possible to identify a list of all the folders where there are permissions of some kind. For some of those, the user for whom we are asking may have been granted access, via their roles. So those folders are removed from the list. If any folders are left, they are the ones where access is not allowed. The method used to support these queries is:
public function getRefusedList ($a_type, $a_id, $s_type, $actionlist)
It returns an array of ID numbers, given an accessor defi ned by type, and ID along with a subject type, and an action list. The action list may be a single action, but for convenience, it is allowed to be a comma separated list of actions. The result is the ID numbers for all folders where the accessor is denied permission to carry out any of the actions.
Again to provide a more useful interface, an extended version of the method is available:
public function getRefusedListSQL ($a_type, $a_id, $s_type, $actionlist, $keyname)
It returns a fragment of SQL. Taking an example, if we call getRefusedListSQL( 'aUser', 47, 'remosFolder', 'download', 'id') we might get back a string containing CAST(id AS CHAR) NOT IN ('5', '14', '27'). This can be used as part of a SQL statement to select folders where the user with ID 47 is allowed to download. So, supposing we want to get a list of the repository container names that are available to our sample user, the full SQL will be constructed using SELECT name FROM #__downloads_containers WHERE followed by the partial SQL provided by getRefusedListSQL. The fi nal sample SQL is then SELECT name FROM #__ downloads_containers WHERE CAST(id AS CHAR) NOT IN ('5', '14', '27').
Summary
We've now got at least the outline of a highly fl exible role-based access control system. The principles are established, using standard notions of RBAC. Specifi c details, such as the way accessors and subjects are identifi ed are adapted to the particular situation of a CMS framework.
The implementation in the database has been established in detail. We've studied the code for administering RBAC, and considered in outline how questions about access can be answered. Further details are available by downloading the Aliro implementation.
This article is excerpted from the book PHP5 CMS Framework Development, by Martin Brampton, and published on June 6, 2008 by Packt Publications.
Where to buy this book
You can buy PHP5 CMS Framework Development from Amazon
- « first
- ‹ previous
- 1
- 2
- 3
- 4
- Login or register to post comments
- 12724 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)









