Skip to content

Latest commit

 

History

History
341 lines (278 loc) · 10.2 KB

File metadata and controls

341 lines (278 loc) · 10.2 KB

Policy


Quick start

\MVC\Policy::set(    
    '\Foo\Controller\Index',    // <= for this Controller ...    
    '*',                        // <= ... and any (*) of its method ...
    array(                      // apply these rules:            
        '\Foo\Policy\Index::requestMethodHasToMatchRouteMethod',
    )
);
  • write the commands in a policy file inside your module's policy folder, like modules/Foo/etc/policy/policy.php
  • create the Policy Class::method \Foo\Policy\Index::requestMethodHasToMatchRouteMethod and write your Policy Logic there

create Policy class

php emvicy policy:create Index Foo
  • creates a Policy class Index in the module Foo

list Policies

php emvicy policy:list
  • list available Policy configurations in a markdown table

Enabling Policy Rules

Policies are bonded to a any/certain Controller::method.
And they are executed after all Routes are initialized by Emvicy.

Path to module's policy configuration files

module/{module}/etc/config/policy/*.php
module/{module}/etc/config/policy/policy.php

Set a single Rule

(assuming module is "Foo")

Set a single Rule to any (*) method of a controller

\MVC\Policy::set(
    '\Foo\Controller\Index', 
    '*', 
    '\Foo\Policy\Index::requestMethodHasToMatchRouteMethod'    
);
  • when any (*) method of controller \Foo\Controller\Index is being called
  • execute \Foo\Policy\Index::requestMethodHasToMatchRouteMethod

Set a single Rule to a certain method of a controller

\MVC\Policy::set(
    '\Foo\Controller\Index', 
    'index', 
    '\Foo\Policy\Index::requestMethodHasToMatchRouteMethod'
);
  • when method index of controller \Foo\Controller\Index is being called
  • execute \Foo\Policy\Index::requestMethodHasToMatchRouteMethod

Setting multiple Rules

simply wrap the rules into an array.

\MVC\Policy::set(
    '\Foo\Controller\Index', 
    '*', [
        '\Foo\Policy\Index::foo',
        '\Foo\Policy\Index::bar',
        '\Foo\Policy\Index::baz',
    ]
);

Bind Policy Rules to a Route

You can also bind a policy to a certain Route.

Example: bind policies checkUserRights and isAdmin to Route /foo/bar/

\MVC\Policy::bindOnRoute(
    \MVC\Route::$aRoute['/foo/bar/'], [
        '\Foo\Policy\Index::checkUserRights',
        '\Foo\Policy\Index::isAdmin'
    ]
);

Bind Policy Rules to multiple Routes

consider you have these following routes and you want to apply Policy Rules to /edit-Routes only.

/client/:number/contract/new
/client/:number/address/new
/client/:number/person/new
/client/:number/contract/:id/edit
/client/:number/address/:id/edit
/client/:number/person/:id/edit

All Route Indices you can get by \MVC\Route::getIndices().

Now all you have to do is to grep in those Indices for any match and then to apply your Policy Rules to that match.

Examples

Apply a regex to all routes
// iterate all '/edit' Routes ...
foreach (preg_grep('/^([\\p{L}\\p{M}\\p{Z}\\p{S}\\p{N}\\p{P}]*)\/edit/i', \MVC\Route::getIndices()) as $sRoute)
{
    // ... and bind 'isAdmin' Policy to those Routes  
    \MVC\Policy::bindOnRoute(
        \MVC\Route::$aRoute[$sRoute], ['\Foo\Policy\Index::isAdmin']
    );
}

Build a separate policy Ruleset array, then proceed the array
/*
 * Ruleset
 */
$aPolicyOnRoute = array(
    '/@/productroute/new'                                       => ['\Foo\Policy\Index::isUser'],
    '/@/productroute/:id/edit'                                  => ['\Foo\Policy\Index::isUser'],
    '/@/client/:number/credentials/new'                         => ['\Foo\Policy\Index::isUser'],
    '/@/client/:number/credentials/:id/edit'                    => ['\Foo\Policy\Index::isUser'],
    '/@/client/:number/credentialsproduct/new'                  => ['\Foo\Policy\Index::isUser'],
    '/@/client/:number/credentialsproduct/:id/edit'             => ['\Foo\Policy\Index::isUser'],
    '/@/client/:number/edit'                                    => ['\Foo\Policy\Index::isUser'],
    '^([\\p{L}\\p{M}\\p{Z}\\p{S}\\p{N}\\p{P}]*)/contract/new'   => ['\Foo\Policy\Index::isAdmin'],
);

/*
 * Implementation
 */
foreach ($aPolicyOnRoute as $sRoute => $aRule)
{
    $sRouteEscaped = str_replace('/', '\/', $sRoute);

    foreach (preg_grep('/^' . $sRouteEscaped . '/i', \MVC\Route::getIndices()) as $sRoute)
    {
        \MVC\Policy::bindOnRoute(
            \MVC\Route::$aRoute[$sRoute],
            $aRule
        );
    }
}

Disabling Policy Rules

Unset a single Rule

assuming module is "Foo"

Unset the single rule '\Foo\Policy\Index::requestMethodHasToMatchRouteMethod' set to controller \Foo\Controller\Index and method index

Policy::unset(
    '\Foo\Controller\Index', 
    'index', 
    '\Foo\Policy\Index::requestMethodHasToMatchRouteMethod'    
);

Unset multiple Rules

Unset certain, multiple rules set to controller \Foo\Controller\Index and method index

\MVC\Policy::unset(
    '\Foo\Controller\Index', 
    'index', [
        '\Foo\Policy\Index::foo',
        '\Foo\Policy\Index::bar',
        '\Foo\Policy\Index::baz',
    ]
);

Unset all rules set to controller \Foo\Controller\Index and method index

Policy::unset(
    '\Foo\Controller\Index', 
    'index'
);

Unset all rules set to controller '\Foo\Controller\Index'

Policy::unset(
    '\Foo\Controller\Index'
);

Unbind a Policy Rule from a Route

Unbind a certain policy rule

\MVC\Policy::unbindRoute(
    \MVC\Route::$aRoute['/foo/bar/'], [
        '\Blg\Policy\Index::checkUserRights'
    ]
);

Unbind all policy rules bonded to a route

\MVC\Policy::unbindRoute(
    \MVC\Route::$aRoute['/foo/bar/']
);

Write methods to be executed by policy

For a better overview, you note the required class::method not in "module/{module}/Model/" but in a separate folder.

Path to module's policy folder

module/{module}/Policy/

Example: file module/Foo/Policy/Index.php

<?php

/**
 * @name $FooPolicy
 */
namespace Foo\Policy;

use MVC\DataType\DTArrayObject;
use MVC\DataType\DTKeyValue;
use MVC\Event;

/**
 * Index
 */
class Index
{
    /**
     * @return void
     * @throws \ReflectionException
     */
    public static function requestMethodHasToMatchRouteMethod ()
    {
        
        $oDTArrayObject = DTArrayObject::create()
            ->add_aKeyValue(DTKeyValue::create()->set_sKey('sRequestmethod')->set_sValue(\MVC\Request::in()->get_requestMethod()))
            ->add_aKeyValue(DTKeyValue::create()->set_sKey('aMethodsAssigned')->set_sValue(\MVC\Route::getCurrent()->get_methodsAssigned()))
            ->add_aKeyValue(DTKeyValue::create()->set_sKey('bGrant')->set_sValue(false))
            ->add_aKeyValue(DTKeyValue::create()->set_sKey('sMessage')->set_sValue('access denied'))
            ->add_aKeyValue(DTKeyValue::create()->set_sKey('sMethod')->set_sValue(__METHOD__))
            ->add_aKeyValue(DTKeyValue::create()->set_sKey('sRedirect')->set_sValue('/404/'))
            ->add_aKeyValue(DTKeyValue::create()->set_sKey('sClosure')->set_sValue(
                function(\MVC\DataType\DTArrayObject $oDTArrayObject) {
                    \MVC\RequestHelper::redirect($oDTArrayObject->getDTKeyValueByKey('sRedirect')->get_sValue());
                }
            ))
        ;

        // grant access
        if (
            // Route is of type ANY
            '*' === \MVC\Route::getCurrent()->get_requestMethod()
            OR
            // Request and Route methods do match
            true === in_array(\MVC\Request::in()->get_requestMethod(), \MVC\Route::getCurrent()->get_methodsAssigned(), true)
        )
        {
            $oDTArrayObject->setDTKeyValueByKey(DTKeyValue::create()->set_sKey('bGrant')->set_sValue(true));
            $oDTArrayObject->setDTKeyValueByKey(DTKeyValue::create()->set_sKey('sMessage')->set_sValue('access granted'));
        }

        // give any middleware option to modify $oDTArrayObject
        Event::run('policy.index.requestMethodHasToMatchRouteMethod.after', $oDTArrayObject);

        // deny access; call closure
        if (false === $oDTArrayObject->getDTKeyValueByKey('bGrant')->get_sValue())
        {
            call_user_func($oDTArrayObject->getDTKeyValueByKey('sClosure')->get_sValue(), $oDTArrayObject);
        }
    }
}