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

Commit

Permalink
[#36] Fix how boolean flags are honored when deny_by_default is enabled
Browse files Browse the repository at this point in the history
- Essentially, if deny_by_default is enabled, we need to build the list
  of flags differently, as the AclFactory creates allow rules instead of
  deny rules in that situation.
  • Loading branch information
weierophinney committed Jul 16, 2014
1 parent 6df8340 commit b103188
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 12 deletions.
4 changes: 2 additions & 2 deletions src/Authorization/AclAuthorizationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static function factory(array $config)
{
// Determine whether we are whitelisting or blacklisting
$denyByDefault = false;
if (isset($config['deny_by_default'])) {
if (array_key_exists('deny_by_default', $config)) {
$denyByDefault = (bool) $config['deny_by_default'];
unset($config['deny_by_default']);
}
Expand All @@ -29,7 +29,7 @@ public static function factory(array $config)
}

foreach ($config as $set) {
if (!is_array($set) || !isset($set['resource'])) {
if (! is_array($set) || ! isset($set['resource'])) {
continue;
}

Expand Down
28 changes: 19 additions & 9 deletions src/Factory/AclAuthorizationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,21 @@ public function createService(ServiceLocatorInterface $services)
*/
protected function createAclFromConfig(array $config)
{
$aclConfig = array();
$aclConfig = array();
$denyByDefault = false;

if (isset($config['zf-mvc-auth'])
&& isset($config['zf-mvc-auth']['authorization'])
) {
$config = $config['zf-mvc-auth']['authorization'];

if (array_key_exists('deny_by_default', $config)) {
$aclConfig['deny_by_default'] = (bool) $config['deny_by_default'];
$denyByDefault = $aclConfig['deny_by_default'] = (bool) $config['deny_by_default'];
unset($config['deny_by_default']);
}

foreach ($config as $controllerService => $privileges) {
$this->createAclConfigFromPrivileges($controllerService, $privileges, $aclConfig);
$this->createAclConfigFromPrivileges($controllerService, $privileges, $aclConfig, $denyByDefault);
}
}

Expand All @@ -82,29 +83,30 @@ protected function createAclFromConfig(array $config)
* @param string $controllerService
* @param array $privileges
* @param array $aclConfig
* @param bool $denyByDefault
*/
protected function createAclConfigFromPrivileges($controllerService, array $privileges, &$aclConfig)
protected function createAclConfigFromPrivileges($controllerService, array $privileges, &$aclConfig, $denyByDefault)
{
if (isset($privileges['actions'])) {
foreach ($privileges['actions'] as $action => $methods) {
$aclConfig[] = array(
'resource' => sprintf('%s::%s', $controllerService, $action),
'privileges' => $this->createPrivilegesFromMethods($methods),
'privileges' => $this->createPrivilegesFromMethods($methods, $denyByDefault),
);
}
}

if (isset($privileges['collection'])) {
$aclConfig[] = array(
'resource' => sprintf('%s::collection', $controllerService),
'privileges' => $this->createPrivilegesFromMethods($privileges['collection']),
'privileges' => $this->createPrivilegesFromMethods($privileges['collection'], $denyByDefault),
);
}

if (isset($privileges['entity'])) {
$aclConfig[] = array(
'resource' => sprintf('%s::entity', $controllerService),
'privileges' => $this->createPrivilegesFromMethods($privileges['entity']),
'privileges' => $this->createPrivilegesFromMethods($privileges['entity'], $denyByDefault),
);
}
}
Expand All @@ -113,9 +115,10 @@ protected function createAclConfigFromPrivileges($controllerService, array $priv
* Create the list of HTTP methods defining privileges
*
* @param array $methods
* @param bool $denyByDefault
* @return array|null
*/
protected function createPrivilegesFromMethods(array $methods)
protected function createPrivilegesFromMethods(array $methods, $denyByDefault)
{
$privileges = array();

Expand All @@ -125,12 +128,19 @@ protected function createPrivilegesFromMethods(array $methods)
}

foreach ($methods as $method => $flag) {
if (!$flag) {
// If the flag evaluates true and we're denying by default, OR
// if the flag evaluates false and we're allowing by default,
// THEN no rule needs to be added
if (( $denyByDefault && $flag)
|| (! $denyByDefault && ! $flag)
) {
if (isset($privileges[$method])) {
unset($privileges[$method]);
}
continue;
}

// Otherwise, we need to add a rule
$privileges[$method] = true;
}

Expand Down
123 changes: 122 additions & 1 deletion test/Factory/AclAuthorizationFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public function setUp()
$this->factory = new AclAuthorizationFactory();
}

public function testCreatingOAuth2ServerFromStorageService()
public function testCanCreateWhitelistAcl()
{
$config = array('zf-mvc-auth' => array('authorization' => array(
'Foo\Bar\RestController' => array(
Expand Down Expand Up @@ -81,4 +81,125 @@ public function testCreatingOAuth2ServerFromStorageService()
}
}
}

public function testBlacklistAclSpecificationHonorsBooleansSetForMethods()
{
$config = array('zf-mvc-auth' => array('authorization' => array(
'deny_by_default' => true,
'Foo\Bar\RestController' => array(
'entity' => array(
'GET' => false,
'POST' => false,
'PUT' => true,
'PATCH' => true,
'DELETE' => true,
),
'collection' => array(
'GET' => false,
'POST' => true,
'PUT' => false,
'PATCH' => false,
'DELETE' => false,
),
),
'Foo\Bar\RpcController' => array(
'actions' => array(
'do' => array(
'GET' => false,
'POST' => true,
'PUT' => false,
'PATCH' => false,
'DELETE' => false,
),
),
),
)));
$this->services->setService('config', $config);

$acl = $this->factory->createService($this->services);

$this->assertInstanceOf('ZF\MvcAuth\Authorization\AclAuthorization', $acl);

$authorizations = $config['zf-mvc-auth']['authorization'];
unset($authorizations['deny_by_default']);

foreach ($authorizations as $resource => $rules) {
switch (true) {
case (array_key_exists('entity', $rules)):
foreach ($rules['entity'] as $method => $expected) {
$assertion = 'assert' . ($expected ? 'False' : 'True');
$this->$assertion($acl->isAllowed('guest', $resource . '::entity', $method));
}
case (array_key_exists('collection', $rules)):
foreach ($rules['collection'] as $method => $expected) {
$assertion = 'assert' . ($expected ? 'False' : 'True');
$this->$assertion($acl->isAllowed('guest', $resource . '::collection', $method));
}
break;
case (array_key_exists('actions', $rules)):
foreach ($rules['actions'] as $action => $actionRules) {
foreach ($actionRules as $method => $expected) {
$assertion = 'assert' . ($expected ? 'False' : 'True');
$this->$assertion($acl->isAllowed('guest', $resource . '::' . $action, $method));
}
}
break;
}
}
}

public function testBlacklistAclsDenyByDefaultForUnspecifiedHttpMethods()
{
$config = array('zf-mvc-auth' => array('authorization' => array(
'deny_by_default' => true,
'Foo\Bar\RestController' => array(
'entity' => array(
'GET' => false,
'POST' => false,
),
'collection' => array(
'GET' => false,
'PUT' => false,
'PATCH' => false,
'DELETE' => false,
),
),
'Foo\Bar\RpcController' => array(
'actions' => array(
'do' => array(
'GET' => false,
'PUT' => false,
'PATCH' => false,
'DELETE' => false,
),
),
),
)));
$this->services->setService('config', $config);

$acl = $this->factory->createService($this->services);

$this->assertInstanceOf('ZF\MvcAuth\Authorization\AclAuthorization', $acl);

$authorizations = $config['zf-mvc-auth']['authorization'];
unset($authorizations['deny_by_default']);

$this->assertFalse($acl->isAllowed('guest', 'Foo\Bar\RestController::entity', 'PATCH'));
$this->assertFalse($acl->isAllowed('guest', 'Foo\Bar\RestController::entity', 'PUT'));
$this->assertFalse($acl->isAllowed('guest', 'Foo\Bar\RestController::entity', 'DELETE'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RestController::entity', 'GET'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RestController::entity', 'POST'));

$this->assertFalse($acl->isAllowed('guest', 'Foo\Bar\RestController::collection', 'POST'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RestController::collection', 'GET'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RestController::collection', 'PATCH'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RestController::collection', 'PUT'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RestController::collection', 'DELETE'));

$this->assertFalse($acl->isAllowed('guest', 'Foo\Bar\RpcController::do', 'POST'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RpcController::do', 'GET'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RpcController::do', 'PUT'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RpcController::do', 'PATCH'));
$this->assertTrue($acl->isAllowed('guest', 'Foo\Bar\RpcController::do', 'DELETE'));
}
}

0 comments on commit b103188

Please sign in to comment.