7.7. Integration With Page Controllers

At this point, we have a way to authenticate a user's identity, a way to find out his role assignments, and a way to discover what portions of the system he is allowed to access. Now we want to use that knowledge in a page controller.

[Note] Note

Because there are lots of different ways to deal with access control, and because most of the approaches end up being application-specific, we will only outline two alternatives herein.

7.7.1. Catch-All Access Control

One way to implement access control is to check the user's access automatically as part of the page-controller dispatch cycle, and pre-emptively change the requested action to something else. This approach makes access checks automatic before each action.

Here is what the code for the catch-all approach might look like:

<?php
abstract class Vendor_Controller_Page extends Solar_Controller_Page
{
    public $user;
    
    protected function _setup()
    {
        parent::_setup();
        $this->user = Solar_Registry::get('user');
    }
    
    protected function _preAction()
    {
        parent::_preAction();
        if (! $this->user->access->isAllowed($this, $this->_action)) {
            $this->_action = 'forbidden';
        }
    }
    
    public function actionForbidden()
    {
        $this->_view = null;
        $this->_response->setStatusCode(403);
        $this->_response->setContent('Access denied.');
    }
}

In the above code, we extend the Solar_Controller_Page class with a class specific to our particular vendor. The following bits of logic are implemented:

  • As part of construction, the _setup() method retrieves the registered Solar_User object so we have authentication, role, and access-control information for the current user. (Recall that instantiating the Solar_User object also handles authentication attempts for us.)

  • The _preAction() runs before each inidividual action. As part of that logic, it checks to see if the current user is allowed access to the current class and action. If not, it changes the value of $this->_action so that the user will be directed to the actionForbidden() method, instead of the one he's not allowed to access.

  • The actionForbidden() method turns off the view and layout, sets the response code to "403 Forbidden", and forcibly resets the resonse content. (Alternatively, you could create a forbidden.php view instead of setting the response content directly.)

Now when a user tries to access a controller and action that he doesn't have access to, he will get a "403 Forbidden" response.

7.7.2. Intra-Action Access Control

The above approach is straighforward, but doesn't allow for much nuance. Sometimes you need to check access permissions as part of an action that is already being executed. As such, you may wish to create a special method that checks access controls and call it from within your actions. For example:

<?php
abstract class Vendor_Controller_Page extends Solar_Controller_Page
{
    public $user;
    
    protected function _setup()
    {
        parent::_setup();
        $this->user = Solar_Registry::get('user');
    }
    
    protected function _isUserAllowed()
    {
        if ($this->user->access->isAllowed($this, $this->_action)) {
            return true;
        } else {
            $this->_error('Access denied.');
            $this->_response->setStatusCode(403);
            return false;
        }
    }
}

class Vendor_App_Foo extends Vendor_Controller_Page
{
    public function actionFoo()
    {
        // ... perform preliminary logic ...
        
        // check access
        if (! $this->_isUserAllowed()) {
            return;
        }
        
        // ... rest of the action here ...
    }
}

In this approach, we do two things. First, we extend the Solar_Controller_Page class with a class specific to our particular vendor, and implement the following bits of logic:

  • As part of construction, the _setup() method retrieves the registered Solar_User object so we have authentication, role, and access-control information for the current user. (Recall that instantiating the Solar_User object also handles authentication attempts for us.)

  • The _isUserAllowed() method checks the current class and action to see if the user is allowed access. If so, it returns true, but if not, it calls the Solar_Controller_Page::_error() method and sets the response status code to "403 Forbidden."

After the base page-controller class is in place, we modify our page-controller actionBar() to check user permissions in the middle of its logic. If the user is allowed, processing continues. If not, we exit the action and let the page controller render the error page automatically as a result of calling _error() in the _isUserAllowed() method.



Local