From 1ee710e608c0320f8448866c4bbb7855e907ea37 Mon Sep 17 00:00:00 2001 From: Kevin Frantz Date: Tue, 1 Jan 2019 20:38:13 +0100 Subject: [PATCH] implemented checking of source source attributs --- .../SecureSourceLoader.php | 4 +- .../SecureManagement/SecureSourceChecker.php | 70 +++++++++++- .../SecureSourceCheckerInterface.php | 3 + .../Unit/Domain/SecureSourceCheckerTest.php | 105 ++++++++++++++++++ 4 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 application/tests/Unit/Domain/SecureSourceCheckerTest.php diff --git a/application/src/Domain/SecureLoadManagement/SecureSourceLoader.php b/application/src/Domain/SecureLoadManagement/SecureSourceLoader.php index fef0caf..89aa6c2 100644 --- a/application/src/Domain/SecureLoadManagement/SecureSourceLoader.php +++ b/application/src/Domain/SecureLoadManagement/SecureSourceLoader.php @@ -4,9 +4,9 @@ namespace App\Domain\SecureLoadManagement; use App\Entity\Source\SourceInterface; use App\Entity\Meta\RightInterface; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Doctrine\Common\Persistence\ObjectRepository; use App\Domain\SecureManagement\SecureSourceChecker; +use App\Exception\SourceAccessDenied; /** * @author kevinfrantz @@ -71,6 +71,6 @@ final class SecureSourceLoader implements SecureSourceLoaderInterface if ($secureSourceChecker->hasPermission($requestedRight)) { return $source; } - throw new AccessDeniedHttpException(); + throw new SourceAccessDenied(); } } diff --git a/application/src/Domain/SecureManagement/SecureSourceChecker.php b/application/src/Domain/SecureManagement/SecureSourceChecker.php index 90ebd9f..fcf8fae 100644 --- a/application/src/Domain/SecureManagement/SecureSourceChecker.php +++ b/application/src/Domain/SecureManagement/SecureSourceChecker.php @@ -5,6 +5,7 @@ namespace App\Domain\SecureManagement; use App\Entity\Meta\RightInterface; use App\Entity\Source\SourceInterface; use App\Domain\LawManagement\LawPermissionCheckerService; +use App\Exception\SourceAccessDenied; /** * @author kevinfrantz @@ -24,10 +25,77 @@ final class SecureSourceChecker implements SecureSourceCheckerInterface $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 { $law = new LawPermissionCheckerService($this->source->getLaw()); - return $law->hasPermission($requestedRight); + return $law->hasPermission($requestedRight) && $this->itterateOverSourceAttributs($requestedRight); } } diff --git a/application/src/Domain/SecureManagement/SecureSourceCheckerInterface.php b/application/src/Domain/SecureManagement/SecureSourceCheckerInterface.php index 4738901..a61e952 100644 --- a/application/src/Domain/SecureManagement/SecureSourceCheckerInterface.php +++ b/application/src/Domain/SecureManagement/SecureSourceCheckerInterface.php @@ -3,6 +3,7 @@ namespace App\Domain\SecureManagement; use App\Entity\Meta\RightInterface; +use App\Exception\SourceAccessDenied; /** * @author kevinfrantz @@ -10,6 +11,8 @@ use App\Entity\Meta\RightInterface; interface SecureSourceCheckerInterface { /** + * @throws SourceAccessDenied + * * @param RightInterface $right * * @return bool diff --git a/application/tests/Unit/Domain/SecureSourceCheckerTest.php b/application/tests/Unit/Domain/SecureSourceCheckerTest.php new file mode 100644 index 0000000..464cfda --- /dev/null +++ b/application/tests/Unit/Domain/SecureSourceCheckerTest.php @@ -0,0 +1,105 @@ +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); + } +}