diff --git a/src/Assertion/OwnershipAssertion.php b/src/Assertion/OwnershipAssertion.php new file mode 100644 index 0000000..53a6a7e --- /dev/null +++ b/src/Assertion/OwnershipAssertion.php @@ -0,0 +1,38 @@ + + */ +class OwnershipAssertion implements AssertionInterface +{ + public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) + { + //Assert passes if role or resource is not proprietary + if (!$role instanceof ProprietaryInterface || !$resource instanceof ProprietaryInterface) { + return true; + } + + //Assert passes if resources does not have an owner + if ($resource->getOwnerId() === null) { + return true; + } + + return ($resource->getOwnerId() === $role->getOwnerId()); + } +} diff --git a/src/ProprietaryInterface.php b/src/ProprietaryInterface.php new file mode 100644 index 0000000..8da8f3b --- /dev/null +++ b/src/ProprietaryInterface.php @@ -0,0 +1,24 @@ + + */ +interface ProprietaryInterface +{ + /** + * @return mixed + */ + public function getOwnerId(); +} diff --git a/test/Assertion/OwnershipAssertionTest.php b/test/Assertion/OwnershipAssertionTest.php new file mode 100644 index 0000000..e523cec --- /dev/null +++ b/test/Assertion/OwnershipAssertionTest.php @@ -0,0 +1,64 @@ +assertTrue($acl->isAllowed('guest', 'blogPost', 'view')); + $this->assertFalse($acl->isAllowed('guest', 'blogPost', 'delete')); + } + + public function testAssertPassesIfResourceIsNotProprietary() + { + $acl = new UseCase2\Acl(); + + $author = new UseCase2\Author1(); + + $this->assertTrue($acl->isAllowed($author, 'comment', 'view')); + $this->assertFalse($acl->isAllowed($author, 'comment', 'delete')); + } + + public function testAssertPassesIfResourceDoesNotHaveOwner() + { + $acl = new UseCase2\Acl(); + + $author = new UseCase2\Author1(); + + $blogPost = new UseCase2\BlogPost(); + $blogPost->author = null; + + $this->assertTrue($acl->isAllowed($author, 'blogPost', 'write')); + $this->assertTrue($acl->isAllowed($author, $blogPost, 'edit')); + } + + public function testAssertFailsIfResourceHasOwnerOtherThanRoleOwner() + { + $acl = new UseCase2\Acl(); + + $author1 = new UseCase2\Author1(); + $author2 = new UseCase2\Author2(); + + $blogPost = new UseCase2\BlogPost(); + $blogPost->author = $author1; + + $this->assertTrue($acl->isAllowed($author2, 'blogPost', 'write')); + $this->assertFalse($acl->isAllowed($author2, $blogPost, 'edit')); + } +} diff --git a/test/TestAsset/UseCase2/Acl.php b/test/TestAsset/UseCase2/Acl.php new file mode 100644 index 0000000..66139fb --- /dev/null +++ b/test/TestAsset/UseCase2/Acl.php @@ -0,0 +1,32 @@ +addRole('guest'); + $this->addRole('member', 'guest'); + $this->addRole('author', 'member'); + $this->addRole('admin'); + + $this->addResource(new BlogPost()); + $this->addResource(new Comment()); + + $this->allow('guest', 'blogPost', 'view'); + $this->allow('guest', 'comment', array('view', 'submit')); + $this->allow('author', 'blogPost', 'write'); + $this->allow('author', 'blogPost', 'edit', new OwnershipAssertion()); + $this->allow('admin'); + } +} diff --git a/test/TestAsset/UseCase2/Author1.php b/test/TestAsset/UseCase2/Author1.php new file mode 100644 index 0000000..fe9e7f9 --- /dev/null +++ b/test/TestAsset/UseCase2/Author1.php @@ -0,0 +1,17 @@ +author === null) { + return null; + } + + return $this->author->getOwnerId(); + } +} diff --git a/test/TestAsset/UseCase2/Comment.php b/test/TestAsset/UseCase2/Comment.php new file mode 100644 index 0000000..7efeda6 --- /dev/null +++ b/test/TestAsset/UseCase2/Comment.php @@ -0,0 +1,23 @@ + + */ +class Comment implements Resource\ResourceInterface +{ + public function getResourceId() + { + return 'comment'; + } +} diff --git a/test/TestAsset/UseCase2/User.php b/test/TestAsset/UseCase2/User.php new file mode 100644 index 0000000..b0311e6 --- /dev/null +++ b/test/TestAsset/UseCase2/User.php @@ -0,0 +1,30 @@ +role; + } + + public function getOwnerId() + { + return $this->id; + } +}