implemented checking of source source attributs

This commit is contained in:
Kevin Frantz 2019-01-01 20:38:13 +01:00
parent 878eae62db
commit 1ee710e608
4 changed files with 179 additions and 3 deletions

View File

@ -4,9 +4,9 @@ namespace App\Domain\SecureLoadManagement;
use App\Entity\Source\SourceInterface; use App\Entity\Source\SourceInterface;
use App\Entity\Meta\RightInterface; use App\Entity\Meta\RightInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Doctrine\Common\Persistence\ObjectRepository; use Doctrine\Common\Persistence\ObjectRepository;
use App\Domain\SecureManagement\SecureSourceChecker; use App\Domain\SecureManagement\SecureSourceChecker;
use App\Exception\SourceAccessDenied;
/** /**
* @author kevinfrantz * @author kevinfrantz
@ -71,6 +71,6 @@ final class SecureSourceLoader implements SecureSourceLoaderInterface
if ($secureSourceChecker->hasPermission($requestedRight)) { if ($secureSourceChecker->hasPermission($requestedRight)) {
return $source; return $source;
} }
throw new AccessDeniedHttpException(); throw new SourceAccessDenied();
} }
} }

View File

@ -5,6 +5,7 @@ namespace App\Domain\SecureManagement;
use App\Entity\Meta\RightInterface; use App\Entity\Meta\RightInterface;
use App\Entity\Source\SourceInterface; use App\Entity\Source\SourceInterface;
use App\Domain\LawManagement\LawPermissionCheckerService; use App\Domain\LawManagement\LawPermissionCheckerService;
use App\Exception\SourceAccessDenied;
/** /**
* @author kevinfrantz * @author kevinfrantz
@ -24,10 +25,77 @@ final class SecureSourceChecker implements SecureSourceCheckerInterface
$this->source = $source; $this->source = $source;
} }
/**
* @param string $methodName
*
* @return bool
*/
private function isGetter(string $methodName): bool
{
return 'get' === substr($methodName, 0, 3);
}
/**
* @param mixed $value
*
* @return bool
*/
private function isSource($value): bool
{
return $value instanceof SourceInterface;
}
/**
* @param string $methodName
*
* @return SourceInterface|null
*/
private function getExpectedSource(string $methodName): ?SourceInterface
{
try {
return $this->source->{$methodName}();
} catch (\TypeError $typeError) {
return null;
}
}
/**
* @param RightInterface $requestedRight
*
* @throws SourceAccessDenied It's important to fire this exception to reduce complexity in debuging
*
* @return bool
*/
private function itterateOverSourceAttributs(RightInterface $requestedRight): bool
{
foreach (get_class_methods($this->source) as $methodName) {
if ($this->isGetter($methodName)) {
$attributExpectedSource = $this->getExpectedSource($methodName);
if ($attributExpectedSource) {
$requestedSubSourceRight = clone $requestedRight;
$requestedSubSourceRight->setSource($attributExpectedSource);
if ($this->isSource($attributExpectedSource)) {
$methodSecureSourceChecker = new self($attributExpectedSource);
if (!$methodSecureSourceChecker->hasPermission($requestedSubSourceRight)) {
throw new SourceAccessDenied('Access denied for subsource!');
}
}
}
}
}
return true;
}
/**
* {@inheritdoc}
*
* @see \App\Domain\SecureManagement\SecureSourceCheckerInterface::hasPermission()
*/
public function hasPermission(RightInterface $requestedRight): bool public function hasPermission(RightInterface $requestedRight): bool
{ {
$law = new LawPermissionCheckerService($this->source->getLaw()); $law = new LawPermissionCheckerService($this->source->getLaw());
return $law->hasPermission($requestedRight); return $law->hasPermission($requestedRight) && $this->itterateOverSourceAttributs($requestedRight);
} }
} }

View File

@ -3,6 +3,7 @@
namespace App\Domain\SecureManagement; namespace App\Domain\SecureManagement;
use App\Entity\Meta\RightInterface; use App\Entity\Meta\RightInterface;
use App\Exception\SourceAccessDenied;
/** /**
* @author kevinfrantz * @author kevinfrantz
@ -10,6 +11,8 @@ use App\Entity\Meta\RightInterface;
interface SecureSourceCheckerInterface interface SecureSourceCheckerInterface
{ {
/** /**
* @throws SourceAccessDenied
*
* @param RightInterface $right * @param RightInterface $right
* *
* @return bool * @return bool

View File

@ -0,0 +1,105 @@
<?php
namespace Tests\Unit\Domain;
use PHPUnit\Framework\TestCase;
use App\Entity\Source\SourceInterface;
use App\Domain\SecureManagement\SecureSourceCheckerInterface;
use App\Entity\Source\AbstractSource;
use App\Domain\SecureManagement\SecureSourceChecker;
use App\Entity\Meta\Right;
use App\DBAL\Types\LayerType;
use App\DBAL\Types\RightType;
use App\Entity\Attribut\SourceAttribut;
use App\Entity\Attribut\SourceAttributInterface;
use App\Exception\SourceAccessDenied;
/**
* @author kevinfrantz
*/
class SecureSourceCheckerTest extends TestCase
{
/**
* @var SourceInterface|SourceAttributInterface
*/
private $source;
/**
* @var SourceInterface
*/
private $recieverSource;
/**
* @var SecureSourceCheckerInterface
*/
private $securerSourceChecker;
private function createSourceMock(): SourceInterface
{
return new class() extends AbstractSource implements SourceAttributInterface {
use SourceAttribut;
};
}
public function setUp(): void
{
$this->source = $this->createSourceMock();
$this->recieverSource = $this->createSourceMock();
$this->securerSourceChecker = new SecureSourceChecker($this->source);
}
public function testFirstLevel(): void
{
$right = new Right();
$right->setLayer(LayerType::SOURCE);
$right->setType(RightType::WRITE);
$right->setReciever($this->recieverSource);
$right->setSource($this->source);
$this->source->getLaw()->getRights()->add($right);
$requestedRight = clone $right;
$this->assertTrue($this->securerSourceChecker->hasPermission($requestedRight));
$requestedRight->setType(RightType::READ);
$this->assertFalse($this->securerSourceChecker->hasPermission($requestedRight));
}
public function testSecondLevel(): void
{
$right = new Right();
$right->setLayer(LayerType::SOURCE);
$right->setType(RightType::WRITE);
$right->setReciever($this->recieverSource);
$right->setSource($this->source);
$this->source->getLaw()->getRights()->add($right);
$attributSource = $this->createSourceMock();
$childRight = clone $right;
$attributSource->getLaw()->getRights()->add($childRight);
$this->source->setSource($attributSource);
$requestedRight = clone $right;
$this->assertTrue($this->securerSourceChecker->hasPermission($requestedRight));
$childRight->setType(RightType::READ);
$this->expectException(SourceAccessDenied::class);
$this->securerSourceChecker->hasPermission($requestedRight);
}
public function testThirdLevel(): void
{
$right = new Right();
$right->setLayer(LayerType::SOURCE);
$right->setType(RightType::WRITE);
$right->setReciever($this->recieverSource);
$right->setSource($this->source);
$this->source->getLaw()->getRights()->add($right);
$attribut1Source = $this->createSourceMock();
$attribut1Source->getLaw()->getRights()->add($right);
$this->source->setSource($attribut1Source);
$childRight = clone $right;
$attribut2Source = $this->createSourceMock();
$attribut2Source->getLaw()->getRights()->add($childRight);
$attribut1Source->setSource($attribut2Source);
$requestedRight = clone $right;
$this->assertTrue($this->securerSourceChecker->hasPermission($requestedRight));
$childRight->setType(RightType::READ);
$this->expectException(SourceAccessDenied::class);
$this->securerSourceChecker->hasPermission($requestedRight);
}
}