The Joomla! Community Magazine™

Adding Access Control through Overrides

Written by | Friday, 01 March 2013 00:00 | Published in 2013 March
What if the component you want to use does not implement the ACL features you need to have? One approach is to add access control through Joomla's output overrides.

I recently completed a project for a web firm whose client needed some access controls added to a component they were using. This component offered some hard-to-find features but lacked support for Joomla’s ACL. The client first approached the component's developer, offering to pay toward the work of adding ACL support into the component. The developer declined, so they turned to third-party development.

When I am asked to add access control to an extension, I walk through several options: My first choice is to find a different extension that offers the needed functionality as well as modern ACL. If we can’t find a suitable alternative, then I look to add the needed access control through overriding the views. Another option is to extend the component through object-oriented code and add the needed functionality to the new child classes. And sometimes a client can accept the limited access control for that one component. Only as a last resort would I resort to hacking the component or forking it.

The client was committed to a particular registration-based component, so we could not look for an alternative. The client could live without special permissions in the backend, and that made the project simpler. I would leverage Joomla’s ACL to limit who had access to this component, but whoever did have access could create, edit, or delete anything within this component.

What was needed was to limit what classes and categories of classes could be viewed by a user based upon that user’s registration type. So the real work required that I add access levels to each object and to its categories, and that the display of this content is to follow the ACL rules. And, of course, the solution must allow the client to upgrade the component.

The Ideal Place

Were I the extension’s developer, I would follow the examples of the Joomla core and add access control at the heart of the component. Examine a core component likecom_contactand you will see that access control is enforced primarily in the model classes. This is ideal, as it is the model that retrieves all data from the database, making this the most secure approach at protecting what data should be delivered to the user as well as allowing or denying certain actions upon the data.

access-control-on-modelWhen access control is enforced within the model, the view can freely display whatever content it receives.

The model is where access control should go. But it is not that simple for a third-party developer who needs to graft in functionality. Any third-party code change to the model is susceptible to being overwritten by a future upgrade. Only the developer can safely change code here. If we need to graft in functionality, either we fork the code and forego simple upgrades or we make changes in files that are not affected by upgrades. I prefer the latter.

Leveraging Overrides

Joomla allows us to safely change the display of content by overriding a “view” file. These overriding files cannot be overwritten by extension upgrades. Although the intent is to allow for varied and alternative layouts of the data, a view file can also include code that performs additional processing. Just as we can control how content is presented, arguably we can control what content is presented. I have found that one can inject an awful lot of functionality through an overriding view file.

It is beyond the scope here to explain output overrides in Joomla, but here is a starting point:http://docs.joomla.org/How_to_override_the_output_from_the_Joomla!_core

access-control-on-viewBy applying access control to the output overrides we can be assured that our changes will not be overwritten by upgrades. However, every view must diligently enforce access control.

There are limitations to this approach when handling access control. Because access control is not enforced within the model, it is possible that somehow a request for data bypasses the views that we override, and hence bypasses the access control we intended. As long as one properly applies access control on every view that displays this component’s content, security should be fine. But it is always possible that some new module might be introduced without us overriding its display. Or perhaps by human error access control is implemented inconsistently in one of the many views to be edited. If security is highly important, one might consider a more technical object-oriented approach for overriding the model.

My Experience

It is beyond my scope to explain exactly how I implemented access control for this client. Also, one must be mindful that each project is different and requires a careful inspection. Certainly, one can identiify some solution patterns within the implementation, and I will share some here.

In this project, the first step was to add access levels to each custom category and to each item. I added an “access” field in its database tables for the custom category and for the item. The field’s name, type, and attributes matched those of the “access” field in the core components (int, size of 10, unsigned). In the admin side I overrode the edit screen of each (category and item) so I could add the code for a dropdown list of access levels. That input field is linked to the item’s db field “access.” At this point, each category and each item now includes an access level that is set within their edit forms.

In the admin side I overrode the view that displays the categories and the view that displays the items. A new column was added to the HTML table so the access level would be displayed.

I coded the real work of access control into my own custom library class. As a need arose, I added to this class the needed public function. By the time I was done with the project, I had implemented an interface of the following functions:

filterToAccessibleCourses($courses=array() ) // reduces the array to just the accessible items
filterToAccessibleCategories($cats=array() )
canAccessCourse($item, $cat=null) // returns true if current user can access this item
canAccessCategory($cat)
getAccessLevelName($id) // id of the access level

Moving my focus to the site's front end I inspected over a dozen view files used by the component. I started with the view that generated a category list. This view file is handed an array of all categories. Were the component implementing access control through its model, the contents of this array would already be filtered to just those items that the user should be able to see. But since the developer did not provide access control, it is our overriding view that is responsible for filtering down the list. Each view that processes a list must be overridden so that we can filter the array by calling the respective filter function from our custom library:

$courses = $accessMgr->filterToAccessibleCourses($courses);
$categories = $accessMgr->filterToAccessibleCategories ($categories);

Some views display details about a particular category or item. Were the model handling access control, a view would never be sent content to display if the user was not authorised to view it. But that is not the case, and there could be many paths leading to a category or item page. So again, access control has become the responsibility of the view. The solution is to override these single-item views and add an authorization check at the beginning of the file: If the user is not authorised to access the item, output an appropriate message and invokereturn;Thereturn;statement stops this file from processing its content. Here is example code:

$accessMgr = new AccessManager(); // class from my custom library
if( ! $accessMgr->canAccessCourse($this->program,$program->cat) ){
   echo 'message that the user is not authorized to view this item';
   return;
}

Every view and alternative layout should be carefully examined for how access control ought to be implemented. Assume you will need to override every view and alternative layout – not just for the component but for any module that accesses this component’s data. If some installed plugins are associated with the component (e.g., a component-specific search plugin), you will need to review these as well.

When working with components that have yet to implement Joomla’s ACL, one should not be surprised to find unconventional approaches that disregard Joomla’s override feature. In more than one view I found the content being processed by a helper file and returned as completed HTML. The problem is that the helper file usurps the view’s ability to format the content as well as our opportunity to inject access control through an override. The solution was to copy the helper class into my custom library, rename the class to avoid naming conflicts, and code the needed access control there. Then the overriding output file references this new class instead of the original one. It's not a perfect solution, but it is a workable one –the original functionality is maintained while I can safely add access control.

Adding Custom Access Control

This exercise sketches an approach for adding access control to a component. The original component is effectively extends so that its core can be upgraded without losing the changes we implement.

This approach can be used also to inject additional or more specific rules that are not handled by Joomla’s standard ACL. For example, we can add the logic that an author can publish/unpublish only his own articles – a level of control that is more refined than what is provided out-of-the-box. but is attainable through overrides.

This extra work is does require a skilled developer and careful attention, but thanks to Joomla’s MVC architecture and the override feature, the problem is solvable. Better, of course, is if you can find an alternative component that does offer Joomla’s ACL.

Read 167603 times
Tagged under Sitebuilders, English
Randy Carey

Randy Carey

Randy is the web architect at Careytech Studios. Currently he is driving the iCue Project with the goal of developing best practices and Joomla extensions that will improve the client's user experience in website and content management