CoreCommunity ExtensionsIncubatorDistributionsTYPO3 4.5 ProjectsTYPO3 4.6 ProjectsTYPO3 4.7 ProjectsTYPO3 6.0 ProjectsTYPO3 6.1 ProjectsTYPO3 6.2 Projects (+)

Role-Based Access Controll (RBAC)

What does it do?

This extension provides simple Role-Based Access Controll functionality for TYPO3 and ExtBase. For a short summary on how this works and how it is implemented, take a look at http://www.sqlrecipes.com/database_design/fine_grained_role_based_access_control_rbac_system-3/

You can use this extension for checking access rights in ExtBase Controllers and to show / hide certain parts of templates in Fluid. See examples and code below.

Setting up the extension

In order to user RBAC on your system, you have to install the Extension via Extension-Manager. There is nothing more to do. During the installation process, RBAC will import some access rights from a Static TypoScript template into your database.

After installation, put a RBAC plugin page content element on a hidden page and use User-Manager for setting up a first RBAC user.

Using RBAC in third-party Extensions

The concept of rbac is to configure your basic setup in a TypoScript file which is later imported into database, when your extension is installed. To enable this behaviour, you first of all have to setup a TypoScript with your RBAC settings. Those settings contain actions, objects, domains, privileges and roles.

Sample TypoScript configuration for RBAC setup

Here is a sample TS configuration for a gallery RBAC setup.

plugin.tx_yag.settings.rbacSettings {

    ####################################################
    # Set up roles that can be assigned to fe_users
    ####################################################
    roles {

        administrator {
            description = Role for all administrators having full access to all functions on all objects
            importance = 100
            privileges {
                10 {
                    privilege = tx_yag_all_actions
                    domain = tx_yag_all_objects 
                    isAllowed = 1
                }
            }
        }   

        loggedInUser {
            description = Role for all logged in users
            importance = 10
            privileges {
                10 {
                    privilege = tx_yag_create
                    domain = tx_yag_comment
                    isAllowed = 1
                }
            }
        }

        guest {
            description = Role for all guests visiting gallery
            importance = 0
            privileges {
                view_action {
                    privilege = tx_yag_view
                    domain = tx_yag_all_objects 
                    isAllowed = 1
                }
            }
        }

    }

    ####################################################
    # Set up privileges that can be assigned to roles
    # for a certain domain
    ####################################################
    privileges {

        tx_yag_all_actions {
            actions = view, create, update, delete, sort, edit
            isSingular = 0
        }

        tx_yag_create {
            actions = create    
            isSingular = 1
        }

        tx_yag_view {
            actions = view
            isSingular = 1
        }

    }

    ####################################################
    # Set up actions that can be combined to privileges
    ####################################################
    actions {

        view {
            description = View action    
        }

        create {
            description = Create action    
        }

        update {
            description = Update action    
        }

        delete {
            description = Delete action    
        }

        sort {
            description = Sort action
        }

        edit {
            description = Edit action
        }

    }

    ####################################################
    # Set up domains that roles can be defined upon
    # Make sure you give your domain an extension-
    # specific name
    ####################################################    
    domains {

        tx_yag_all_objects {
            isSingular = false
            objects = Album, Gallery, Item, ItemMeta, Development, Setup
        }

        tx_yag_album {
            isSingular = true
            objects = Album
        }

        tx_yag_gallery {
            isSingular = true
            objects = Gallery 
        }

        tx_yag_item {
            isSingular = true
            objects = Item
        }

        tx_yag_itemMeta {
            isSingular = true
            objects = ItemMeta
        }

        tx_yag_comment {
            isSingular = true
            objects = Comment    
        }

        tx_yag_development {
            isSingular = true
            objects = Development
        }

        tx_yag_setup {
            isSingular = true
            objects = Setup
        }

    }

    ####################################################
    # Set up objects that can be combined to domains
    ####################################################    
    objects {

        Album {
            description = Album class in yag    
        }

        Gallery {
            description = Gallery class in yag
        }

        Item {
            description = Item class in yag
        }

        ItemMeta {
            description = ItemMeta class in yag
        }

        Comment {
            description = Comment class in yag
        }

        Development {
            description = Development controller has its own object
        }

        Setup {
            description = Setup controller has its own object
        }

    }    

}

Importing RBAC settings for your extension

The above TS configuration only acts as a template and won't be used, once your extension is installed. During the installation process of your extension, you can easily import the TS settings into your database using the folling pieces of code. First of all, you have to set up a ext_conf_template.txt file in the root folder of your extension containing the following piece of code:

  # cat=basic; type=user[EXT:yag/Classes/Install/PostInstallHook.php:Tx_Yag_Install_PostInstallHook->setupRbac]; label=Importing TypoScript RBAC settings into database
updateMessage=0

Then you have to create the actual hook that is processed during installation. You can use an Utility class implemented in RBAC that does the job for you:

class Tx_Yag_Install_PostInstallHook {

    /**
     * Imports RBAC settings of yag extension into database
     *
     * @return string Success message or Error message
     */
    public static function setupRbac() {
        // We get TS array of rbac settings
        $tsSetupFile = t3lib_extMgm::extPath('yag') . '/Configuration/TypoScript/Rbac/setup.ts';
        return Tx_Rbac_Install_Utility::doImportByExtensionNameTsFilePathAndTsKey('tx_yag', $tsSetupFile, 'plugin.tx_yag.settings.rbacSettings');
    }

}

As you can see, this hook assumes that we have our TS settings for rbac in a file 'setup.ts' inside the 'Configuration/TypoScript/Rbac' folder of our extension. All the rest follows ExtBase naming-conventions.

Using RBAC access controll inside your controller

At the moment, there is no 'ready-to-use' implementation of a controller that uses RBAC. You can still enable this functionality quite easily creating an abstract controller extending ExtBase's standard action controller and adding the following method:

abstract class Tx_Yag_Controller_AbstractController extends Tx_Extbase_MVC_Controller_ActionController {

    /**
     * Holds an instance of fe_user object
     *
     * @var Tx_Extbase_Domain_Model_FrontendUser
     */
    protected $feUser;

    /**
     * Holds an instance of rbac user object
     *
     * @var Tx_Rbac_Domain_Model_User
     */
    protected $rbacUser;

    /**
     * Constructor for all plugin controllers
     */
    public function __construct() {
        parent::__construct();
        $this->initAccessControllService();     
    }

    /**
     * Initializes Access Controll Service 
     *
     */
    protected function initAccessControllService() {
        $this->rbacAccessControllService = Tx_Rbac_Domain_AccessControllServiceFactory::getInstance();
    }

    /**
     * This action is final, as it should not be overwritten by any extended controllers
     */
    final protected function initializeAction() {
        $this->preInitializeAction();
        $this->initializeFeUser();
        $this->doRbacCheck();
        $this->yagContext->injectRequest($this->request);
        $this->postInitializeAction();
    }

    /**
     * Template methods to be implemented in extending controllers
     * (this is required since initializeAction() is final due to
     * access controll checks.
     */
    protected function postInitializeAction() {}
    protected function preInitializeAction() {}

    /**
     * Runs rbac check
     * 
     * Access restrictions to controller actions can be created by
     * using @rbacNeedsAccess, @rbacObject <rbacObjectName> and @rbacAction <rbacActionName> annotations in your
     * action comments.
     */
    protected function doRbacCheck() {
        $this->initializeRbacUser();
        $controller = $this->request->getControllerObjectName();
        $action = $this->actionMethodName;
        $methodTags = $this->reflectionService->getMethodTagsValues($controller, $action);

        if (array_key_exists('rbacNeedsAccess', $methodTags)) {
            if ($this->rbacUser) {
                $rbacObject = $methodTags['rbacObject'][0];
                $rbacAction = $methodTags['rbacAction'][0];
                if (!($this->rbacAccessControllService->hasAccess($this->rbacUser->getUid(), $rbacObject, $rbacAction))) {
                    $this->flashMessages->add('Access denied! You do not have the privileges for this function.');
                    $this->accessDeniedAction();
                }
            } else {
                if ($this->feUser) {
                    $this->flashMessages->add('Access denied - No RBAC user has been set up for your fe_user!');
                } else {
                    $this->flashMessages->add('Access denied - You are not logged in!');
                }
                $this->accessDeniedAction();
            }
        }
    }

    /**
     * Initializes fe user for current session
     * 
     */
    protected function initializeFeUser() {
        $feUserUid = $GLOBALS['TSFE']->fe_user->user['uid'];
        if ($feUserUid > 0) {
            $feUserRepository = t3lib_div::makeInstance('Tx_Extbase_Domain_Repository_FrontendUserRepository'); /* @var $feUserRepository Tx_Extbase_Domain_Repository_FrontendUserRepository */
            $feUser = $feUserRepository->findByUid($feUserUid);
            $this->feUser = $feUser;
        } else {
            $this->feUser = null;
        }
    }

    /**
     * Initializes rbac user object
     */
    protected function initializeRbacUser() {
        if ($this->feUser) {
            $query = t3lib_div::makeInstance(Tx_Rbac_Domain_Repository_UserRepository)->createQuery();
            $query->getQuerySettings()->setRespectStoragePage(FALSE);
            $query->matching($query->equals('feUser', $this->feUser->getUid()));
            $rbacUserArray = $query->execute();
            if (count($rbacUserArray) > 0) {
                // TODO refactor me!
                $this->rbacUser = $rbacUserArray[0];
                $this->yagContext->setRbacUser($this->rbacUser);
            }
            else $this->rbacUser = null;  // no rbac user found
        } else {
            $this->rbacUser = null; // no fe user is logged in
        }
    }

    /**
     * Redirects to gallery start page after access for another action has been denied
     *
     * Feel free to override this method in your respective controller
     * 
     * @param Tx_Yag_Domain_Model_Album $album      
     * @param Tx_Yag_Domain_Model_Gallery $gallery
     */
    protected function accessDeniedAction() {
        $this->redirect('list', 'Gallery');
    }

}

Please note that this is kind of experimental code which should show you how everything works together. The main functionality - once everything is set up - comes from the following line of code:

$rbacAccessControllService->hasAccess($this->rbacUser->getUid(), $rbacObject, $rbacAction);

which will return true, if a user has access or false if not.

Using RBAC as a ViewHelper

RBAC also comes with a Fluid-ViewHelper that lets you check whether a user is allowed to see a part of a template/partial or not. Here is an example of how to use this ViewHelper. Make sure you have

{namespace rbac=Tx_Rbac_ViewHelpers}
at the beginning of your template / partial.

<rbac:hasAccess user="{rbacUser}" object="object" action="action">
    <f:then>
        <--! Show whatever has to be shown if user is allowed to -->
    </f:then>
    <f:else>
        <--! Show whatever should be shown if user is not allowed to see the above stuff -->
    </f:else>
</rbac:hasAccess>

This should be quite self-explanatory.

Further Development

It is planned to implement a widget that lets you set all RBAC stuff for your Extension in a widget. All you have to do is something like

<rbac:widget.admin extension="ext_name" /> and RBAC will render a widget for configuration of your extension. But this is still to be implemented. Feel free to join the team, if you like!