JosephSilber/bouncer

Permission on belongToMany relation of entity

Closed this issue · 4 comments

Hi to all,

I'm trying to figure out how to realize a special type of permission I need:
I have an entity called Issue that have a belongToMany relation with the User entity, that represent who are involved in the issue resolution (the recipients of the issue).
I would like to give the ability to see or edit the issue entity to anyone who is targeted by that relation.
Usually in other case I would have solved giving the ownership of the entity to the user and checking the ability using the owning mechanism, but in this case multiple users should have access to this entity, and they're not the owner of the entity.
The User entity that is part of that relation is also the one that have the abilities and roles relations and is Authenticatable.
There's any way to check an ability on that type of relation? Any suggestion on how to manage this situation?

Many thanks
Daniele

That's what the ownedVia callback is for:

Bouncer::ownedVia(Issue::class, function ($issue, $user) {
    return $issue->users()->where('id', $user->id)->exists();
});

Once that's set up, you can add a regular ownership ability:

Bouncer::allow('developer')->toOwn(Issue::class);

Many thanks @JosephSilber :D

That's really close to what I was looking for. There's any way to do something like that but without owning the entity?
In my case I want to give read and write permission to the user that own the issue entity, and give read permission to some other "participants" to the issue.
So I will need to check a manyToMany relation in one case and another relation in the second case.
There's a way to have the ability name in the ownedVia callback, to make the check conditional?

Thanks again
Daniele

There's a way to have the ability name in the ownedVia callback, to make the check conditional?

Bouncer does not currently support this. I'll mark this issue as a feature proposal.


In the meantime, I would use a regular policy for this:

class IssuePolicy
{
    public function view(User $user, Issue $issue)
    {
        return $issue->created_by == $user->id
            || $issue->participants()->where('id', $user->id)->exists();
    }
}

If you want to be able to toggle it at runtime for specific users or based on roles, you can have a placeholder own ability in Bouncer for it...

Bouncer::allow('developer')->to('own', Issue::class);

...and then check that first in your policy:

class IssuePolicy
{
    public function view(User $user, Issue $issue)
    {
        if (! $user->can('own', $issue)) {
            return false;
        }

        return $issue->created_by == $user->id
            || $issue->participants()->where('id', $user->id)->exists();
    }
}

Sorry,

I haven't seen the answer. I've implemented a workaround similar to what you suggested but using a custom gate.
I leave the code here if someone need to do something similar:

Gate::define('gate-show.issue.asRecipients', function (Account $user, Issue $issue) {
	if($user->abilities->where('name', 'show.issue.asRecipients')->isEmpty()) {
		return false;
	}
	
	return $issue->recipients()->where('id', $user->id)->exists();
});

With that gate I can check if a user as the ability of show.issue.asRecipients and is in the recipients relation.

Thanks again
Daniele