On this page

Reference

Ownership Assertions

Available since version 2.7.0

When setting up permissions for an application, site owners common will want to allow roles to manipulate resources owned by the user with that role. For example, a blog author should have permission to write new posts, and also to modify his or her own posts, but not posts of other authors.

To accomodate this use case, we provide two interfaces:

  • Laminas\Acl\ProprietaryInterface is applicable to resources and roles. It provides information about the owner of an object. Objects implementing this interface are used in conjunction with the OwnershipAssertion.

  • Laminas\Acl\Assertion\OwnershipAssertion ensures that a resource is owned by a specific role by comparing it to owners provided by ProprietaryInterface implementations.

Example

Consider the following entities:

namespace MyApp\Entity;

use Laminas\Permissions\Acl\ProprietaryInterface;
use Laminas\Permissions\Acl\Resource\ResourceInterface;
use Laminas\Permissions\Acl\Role\RoleInterface;

class User implements RoleInterface, ProprietaryInterface
{
    protected $id;

    protected $role = 'guest';

    public function __construct($id, $role)
    {
        $this->id = $id;
        $this->role = $role;
    }

    public function getRoleId()
    {
        return $this->role;
    }

    public function getOwnerId()
    {
        return $this->id;
    }
}

class BlogPost implements ResourceInterface, ProprietaryInterface
{
    public $author = null;

    public function getResourceId()
    {
        return 'blogPost';
    }

    public function getOwnerId()
    {
        if ($this->author === null) {
            return null;
        }

        return $this->author->getOwnerId();
    }
}

The User marks itself as an owner by implementing ProprietaryInterface; its getOwnerId() method will return the user identifier provided during instantiation.

A BlogPost marks itself as a resource and an owner by also implementing ProprietaryInterface; in its case, it returns the author identifier, if present, but null otherwise.

Now let's wire these up into an ACL:

namespace MyApp;

use MyApp\Entity;
use Laminas\Permissions\Acl\Acl;
use Laminas\Permissions\Acl\Assertion\OwnershipAssertion;

$acl = new Acl();
$acl->addRole('guest');
$acl->addRole('member', 'guest');
$acl->addRole('author', 'member');
$acl->addRole('admin');

$acl->addResource('blogPost');
$acl->addResource('comment');

$acl->allow('guest', 'blogPost', 'view');
$acl->allow('guest', 'comment', array('view', 'submit'));
$acl->allow('author', 'blogPost', 'write');
$acl->allow('author', 'blogPost', 'edit', new OwnershipAssertion());
$acl->allow('admin');

$author1 = new User(1, 'author');
$author2 = new User(2, 'author');

$blogPost = new BlogPost();
$blogPost->author = $author1;

The takeaways from the above should be:

  • An author can write blog posts, and edit posts it owns.
  • $author1 and $author2 are both authors.
  • $author1 is the author of $blogPost.

Knowing these facts, we can expect the following assertion results:

$acl->isAllowed($author1, 'blogPost', 'write'); // true
$acl->isAllowed($author1, $blogPost, 'edit');   // true
$acl->isAllowed($author2, 'blogPost', 'write'); // true
$acl->isAllowed($author2, $blogPost, 'edit');   // false