Skip to content
This repository has been archived by the owner on Jul 3, 2020. It is now read-only.

Commit

Permalink
Merge pull request #238 from jmleroux/route-permissions-guard
Browse files Browse the repository at this point in the history
Add route permissions guard and controller permissions guard
  • Loading branch information
bakura10 committed Jun 19, 2014
2 parents fb6c30e + f26b666 commit ad6f08a
Show file tree
Hide file tree
Showing 13 changed files with 1,597 additions and 9 deletions.
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
"name": "Michaël Gallego",
"email": "[email protected]",
"homepage": "http://www.michaelgallego.fr"
},
{
"name": "Jean-Marie Leroux",
"email": "[email protected]"
}
],
"require": {
Expand Down
118 changes: 114 additions & 4 deletions docs/04. Guards.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ And here is a simple workflow with a route guard:

![Zend Framework workflow with guards](/docs/images/workflow-with-guards.png?raw=true)

Guards are not really aware of permissions (it does not make any sense) but rather only think about "roles". For
RouteGuard and ControllerGuard are not aware of permissions but rather only think about "roles". For
instance, you may want to refuse access to each routes that begin by "admin/*" to all users that do not have the
"admin" role.

If you want to protect a route for a set of permissions, you must use RoutePermissionsGuard. For instance,
you may want to grant access to a route "post/delete" only to roles having the "delete" permission.
Note that in a RBAC system, a permission is linked to a role, not to a user.

Albeit simple to use, guards should not be the only protection in your application, and you should always also
protect your service. The reason is that your business logic should be handled by your service. Protecting a given
route or controller does not mean that the service cannot be access from elsewhere (another action for instance).
Expand Down Expand Up @@ -57,7 +61,14 @@ deny policy is much more secure, but it needs much more configuration to work wi

## Built-in guards

ZfcRbac comes with two guards: RouteGuard and ControllerGuard. All guards must be added in the `guards` subkey:
ZfcRbac comes with four guards, in order of priority :

* RouteGuard : protect a set of routes based on the identity roles
* RoutePermissionsGuard : protect a set of routes based on roles permissions
* ControllerGuard : protect a controllers and/or actions based on the identity roles
* ControllerPermissionsGuard : protect a controllers and/or actions based on roles permissions

All guards must be added in the `guards` subkey:

```php
return [
Expand Down Expand Up @@ -99,12 +110,14 @@ return [
];
```

> Only one role in a rule need to be matched (it is an OR condition).
Those rules grant access to all admin routes to users that have the "admin" role, and grant access to the "login"
route to users that have the "guest" role (eg.: most likely unauthenticated users).

> The route pattern is not a regex. It only supports the wildcard (*) character, that replaces any segment.
You can also use the wildcard character for roles:
You can also use the wildcard character * for roles:

```php
return [
Expand Down Expand Up @@ -151,8 +164,75 @@ return [
```


### RoutePermissionsGuard

> The RoutePermissionsGuard listens to the `MvcEvent::EVENT_ROUTE` event with a priority of -8.
The RoutePermissionsGuard allows to protect a route or a hierarchy of route. You must provide an array of "key" => "value",
where the key is a route pattern, and value an array of permissions names:

```php
return [
'zfc_rbac' => [
'guards' => [
'ZfcRbac\Guard\RoutePermissionsGuard' => [
'admin*' => ['admin'],
'post/manage' => ['post.update', 'post.delete']
]
]
]
];
```

> All permissions in a rule must be matched (it is an AND condition).
In the previous example, one must have ```post.update``` **AND** ```post.delete``` permissions
to access the ```post/manage``` route.

> Permissions are linked to roles, not to users
Those rules grant access to all admin routes to roles that have the "admin" permission, and grant access to the
"post/delete" route to roles that have the "post.delete" or "admin" permissions.

> The route pattern is not a regex. It only supports the wildcard (*) character, that replaces any segment.
You can also use the wildcard character * for permissions:

```php
return [
'zfc_rbac' => [
'guards' => [
'ZfcRbac\Guard\RoutePermissionsGuard' => [
'home' => ['*']
]
]
]
];
```

This rule grants access to the "home" route to anyone.

Finally, you can also use an empty array to completly block a route, for maintenance purpose for example :

```php
return [
'zfc_rbac' => [
'guards' => [
'ZfcRbac\Guard\RoutePermissionsGuard' => [
'route_under_construction' => []
]
]
]
];
```

This rule will be inaccessible.


### ControllerGuard

> The RoutePermissionsGuard listens to the `MvcEvent::EVENT_ROUTE` event with a priority of -10.
The ControllerGuard allows to protect a controller. You must provide an array of array:

```php
Expand All @@ -170,6 +250,8 @@ return [
];
```

> Only one role in a rule need to be matched (it is an OR condition).
Those rules grant access to each actions of the MyController controller to users that have either the "guest" or
"member" roles.

Expand Down Expand Up @@ -218,7 +300,35 @@ return [
Those rules grant access to each actions of the controller to users that have the "member" role, but restrict the
"delete" action to "admin" only.

> The ControllerGuard listens to the `MvcEvent::EVENT_ROUTE` event with a priority of -10.
### ControllerPermissionsGuard

> The RoutePermissionsGuard listens to the `MvcEvent::EVENT_ROUTE` event with a priority of -13.
The ControllerPermissionsGuard allows to protect a controller using permissions. You must provide an array of array:

```php
return [
'zfc_rbac' => [
'guards' => [
'ZfcRbac\Guard\ControllerPermissionsGuard' => [
[
'controller' => 'MyController',
'permissions' => ['post.update', 'post.delete']
]
]
]
]
];
```

> All permissions in a rule must be matched (it is an AND condition).
In the previous example, the user must have ```post.update``` **AND** ```post.delete``` permissions
to access each actions of the MyController controller.

As for all other guards, you can use a wildcard (*) character for permissions.

The configuration rules are the same as for ControllerGuard.

### Security notice

Expand Down
67 changes: 67 additions & 0 deletions src/ZfcRbac/Factory/ControllerPermissionsGuardFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ZfcRbac\Factory;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\MutableCreationOptionsInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use ZfcRbac\Guard\ControllerPermissionsGuard;
use ZfcRbac\Guard\RouteGuard;

/**
* Create a controller guard for checking permissions
*
* @author JM Lerouxw <[email protected]>
* @licence MIT
*/
class ControllerPermissionsGuardFactory implements FactoryInterface, MutableCreationOptionsInterface
{
/**
* @var array
*/
protected $options = [];

/**
* {@inheritDoc}
*/
public function setCreationOptions(array $options)
{
$this->options = $options;
}

/**
* @param \Zend\ServiceManager\AbstractPluginManager|ServiceLocatorInterface $serviceLocator
* @return RouteGuard
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $serviceLocator->getServiceLocator();

/* @var \ZfcRbac\Options\ModuleOptions $moduleOptions */
$moduleOptions = $parentLocator->get('ZfcRbac\Options\ModuleOptions');

/* @var \ZfcRbac\Service\AuthorizationService $authorizationService */
$authorizationService = $parentLocator->get('ZfcRbac\Service\AuthorizationService');

$guard = new ControllerPermissionsGuard($authorizationService, $this->options);
$guard->setProtectionPolicy($moduleOptions->getProtectionPolicy());

return $guard;
}
}
68 changes: 68 additions & 0 deletions src/ZfcRbac/Factory/RoutePermissionsGuardFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ZfcRbac\Factory;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\MutableCreationOptionsInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use ZfcRbac\Guard\RouteGuard;
use ZfcRbac\Guard\RoutePermissionsGuard;

/**
* Create a route guard for checking permissions
*
* @author Michaël Gallego <[email protected]>
* @author JM Lerouxw <[email protected]>
* @licence MIT
*/
class RoutePermissionsGuardFactory implements FactoryInterface, MutableCreationOptionsInterface
{
/**
* @var array
*/
protected $options = [];

/**
* {@inheritDoc}
*/
public function setCreationOptions(array $options)
{
$this->options = $options;
}

/**
* @param \Zend\ServiceManager\AbstractPluginManager|ServiceLocatorInterface $serviceLocator
* @return RouteGuard
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $serviceLocator->getServiceLocator();

/* @var \ZfcRbac\Options\ModuleOptions $moduleOptions */
$moduleOptions = $parentLocator->get('ZfcRbac\Options\ModuleOptions');

/* @var \ZfcRbac\Service\AuthorizationService $authorizationService */
$authorizationService = $parentLocator->get('ZfcRbac\Service\AuthorizationService');

$routeGuard = new RoutePermissionsGuard($authorizationService, $this->options);
$routeGuard->setProtectionPolicy($moduleOptions->getProtectionPolicy());

return $routeGuard;
}
}
Loading

0 comments on commit ad6f08a

Please sign in to comment.