#Possible to scope roles/permissions with spatie/laravel-permission?

16 messages · Page 1 of 1 (latest)

grand topaz
#

I'm hitting a roadblock using spatie's permissions package and wanted to reach out and see if anyone has successfully extended the package to handle the following situation:

Let's say I have 3 models, User Company, CompanyUser. A User belongsToMany Companies via CompanyUser.

I'd like a User to have global permissions afforded via the roles directly attached to the User model, but they should also inherit permissions for a specific Company given the role(s) on their CompanyUser record.

Issues:

  • $user->can('do something') will not take into account CompanyUser permissions
  • I don't think Gate::allows() calls in controllers will play nicely either?

I don't know if I'm trying to do something stupid, but basically my thought is:

  • When viewing a company page, i.e. /companies/xyz $user->can('do something') or Gate::allows() should take into account the company and also consider any CompanyUser permissions as part of the User. Can probably pass in $company->id to these methods.
  • Otherwise, just look at the User's permissions

In order to achieve this, I think I have to override both the ->can() method, how spatie creates the Gates, etc. and it's looking less and less like a neat solution.

TLDR;
How do you maintain global permissions + scoped permissons against another model for a User using spatie's permissions package? Is there another package that is more suited to this?

jagged silo
#

they should also inherit permissions for a specific Company given the role(s) on their CompanyUser record.
Could you expand on this?

#

Since I assume CompanyUser is a pivot/junction table, I'm not quite sure why that's connected to a role?

grand topaz
#

Sure, the CompanyUser would represent the user's role(s) for the Company in particular.

So maybe my user record is the Admin for one Company in particular, but not another.

Each user may or may not have roles for a given company. So if they are an admin for the company, they can see a certain section.

That makes it different than their global User role(s). Does that make sense?

#

Another example, maybe if my User record is a Super Admin globally, I can delete all the companies in the system.

If my user is just an admin for one company, I can delete users from that company, but not a different company.

jagged silo
#

Once a User is linked to a Company can it do anything regarding that company?

grand topaz
#

I think I may have come up with a solution, using their Gate::before as a reference and writing my own:

Gate::before(function ($user, $ability, array &$args = []) {
            if ($user instanceof User && isset($args[0]) && $args[0] instanceof Company) {
                $company = array_shift($args);

                return ($company->companyUserForCurrentUser?->checkPermissionTo($ability)
                    || $user->checkPermissionTo($ability)) ?: null;
            }

            return $user->checkPermissionTo($ability) ?: null;
        });
#

So basically... if I pass
Gate::allows('do something', $company)
or $user->can('do something', $company)
It should check if either the company user record or user record have permission. Otherwise if no company passed, just check user record.

Im writing some tests now to see if this actually works at all lol

jagged silo
#

We have a similar use-case and we use laravel policies in combination with the spatie/laravel-permission's package

#
public function update(Company $company, User $user)
{
  // Early return if User does not have the right permission.
  if ($user->cant('company-update')) {
    return $this->deny();
  }

  // Early return if User isn't linked to the Company to be updated.
  if (! canUserUpdateCompany($company, $user)) {
    return $this->deny()
  }

  return $this->allow()
}
#

pseudo-code for a potential CompanyPolicy class method

#

With this approach you have three layers of authorization

  1. Specify role-based permissions. (The 'administration' role can update all companies but can't delete them)
  2. Specify relationship-based permissions. (The 'administration' role can only update companies it has a relationship to)
  3. Specify model-based permissions. (A specific User can do x-y-z with a specific Company)
grand topaz
#

Thank you for that info @jagged silo appreciate you talking through this with me

#

I'm going to mark this solved for now I think my tests proved that the Gate::before achieved what I need with the CompanyUser relation