View Source

<h2>What is RBAC?</h2>

<p>From Wikipedia:</p>

<p>&quot;In computer systems security, role-based access control (RBAC) is an approach to restricting system access to authorized users. It is used by the majority of enterprises with more than 500 employees,<ac:link><ri:page ri:content-title="3" /></ac:link> and can implement mandatory access control (MAC) or discretionary access control (DAC). RBAC is sometimes referred to as role-based security.&quot;</p>

<h2>Proposal</h2>

<p>The primary goals for this RFC are:</p>

<ul>
<li>Implement role-based access control as an <strong>alternative</strong> to access control lists (Zend\Permission\Acl).</li>
<li>Utilize PHP 5.3 SPL datastructures (RecursiveIterator and RecursiveIteratorIterator).</li>
</ul>


<h3>Architecture</h3>

<p>The requirements are as follows:</p>

<ul>
<li>Many to many relationship between identities and roles.</li>
<li>Many to many relationship between roles and permissions.</li>
<li>A role can have a parent (inheritance must be supported).</li>
<li>Dynamic assertions <strong>must</strong> be supported.</li>
</ul>


<p>Given the requirements, RBAC is a perfect fit for a composite pattern (<a href="http://en.wikipedia.org/wiki/Composite_pattern">http://en.wikipedia.org/wiki/Composite_pattern</a>) combined with SPL RecursiveIterator.</p>

<h3>Class skeletons</h3>

<ac:macro ac:name="code"><ac:parameter ac:name="title">Zend\Permission\Rbac\AbstractIterator.php</ac:parameter><ac:parameter ac:name="borderStyle">solid</ac:parameter><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Zend\Permission\Rbac;

use RecursiveIterator;

abstract class AbstractIterator implements RecursiveIterator
{
public function current();

public function next();

public function key();

public function valid();

public function rewind();

public function hasChildren();

public function getChildren();
}
]]></ac:plain-text-body></ac:macro>

<ac:macro ac:name="code"><ac:parameter ac:name="title">Zend\Permission\Rbac\AbstractRole.php</ac:parameter><ac:parameter ac:name="borderStyle">solid</ac:parameter><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Zend\Permission\Rbac;

abstract class AbstractRole extends AbstractIterator
{
public function getName();

public function addPermission($permission);

public function hasPermission($permission);

public function addChild($child);
}
]]></ac:plain-text-body></ac:macro>

<ac:macro ac:name="code"><ac:parameter ac:name="title">Zend\Permission\Rbac\Rbac.php</ac:parameter><ac:parameter ac:name="borderStyle">solid</ac:parameter><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Zend\Permission\Rbac;

class Rbacextends AbstractIterator
{
public function addRole($role);

public function hasRole($role);

public function getRole($role);

public function isGranted($permission);
}
]]></ac:plain-text-body></ac:macro>

<ac:macro ac:name="code"><ac:parameter ac:name="title">Zend\Permission\Rbac\Role.php</ac:parameter><ac:parameter ac:name="borderStyle">solid</ac:parameter><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Zend\Permission\Rbac;

class Role extends AbstractRole
{
}
]]></ac:plain-text-body></ac:macro>

<h3>Setting up roles and permissions</h3>
<ac:macro ac:name="code"><ac:parameter ac:name="title">Roles</ac:parameter><ac:parameter ac:name="borderStyle">solid</ac:parameter><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[

// Creating roles manually
$foo = new \Zend\Permission\Rbac\Role('foo');
$bar = new \Zend\Permission\Rbac\Role('bar');

// Adding a child
$foo->addChild($bar);
$foo->addChild('baz'); // If a string is given creates default Role with name 'baz'

// Using Rbac container
$rbac = new \Zend\Permission\Rbac\Rbac;
$rbac->addRole($foo);
$rbac->addRole('baz');
$rbac->addRole($bar, array($foo)); // Add role bar with parents $foo, can also just give it a single object instead of array

// Adding permissions
$foo->addPermission('test');
var_dump($foo->hasPermission('test')); // true

// Using Rbac container
$rbac->getRole('foo')->addPermission('barperm');
]]></ac:plain-text-body></ac:macro>

<h3>Dynamic assertions</h3>

<p>Dynamic assertions can be provided via an AssertionInterface (Zend\Permission\Rbac\AssertionInterface) or by simply passing a closure. For example,</p>

<ac:macro ac:name="code"><ac:parameter ac:name="title">Zend\Permission\Rbac\AssertionInterface</ac:parameter><ac:parameter ac:name="borderStyle">solid</ac:parameter><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$event = new \My\Event;
$event->setUserId(1);

// Verify user has event.update permission and event user matches user.
$rbac->isGranted('event.update', function($rbac) use ($user, $event) {
return $user->getId() === $event->getUserId();
});
]]></ac:plain-text-body></ac:macro>

<h3>Working example</h3>

<p>The SpiffySecurity module currently implements the Rbac code and can be found at <a class="external-link" href="https://github.com/SpiffyJr/SpiffySecurity/tree/master/src/SpiffySecurity/Rbac">https://github.com/SpiffyJr/SpiffySecurity/tree/master/src/SpiffySecurity/Rbac</a>.</p>