JosephSilber / bouncer

Laravel Eloquent roles and abilities.
MIT License
3.45k stars 332 forks source link

Check role's abilitiy #571

Closed hasak closed 3 years ago

hasak commented 3 years ago

Is it possible to have something like: $role->can($ability,$model); instead of assigning role to an user and then checking $user->can(...) ?

JosephSilber commented 3 years ago

Can you share your use-case for why you would want this?

hasak commented 3 years ago

For example: To change role's abilities in frontend, I would need something like: <input type="checkbox" {{$role->can('add',$model)?"checked":""}}> where $role and $model are going through foreach.

Another example: I want to check for all roles that have specific ability (in controller or model)

The way I was doing it until now is:

  1. Creating dummy user with id 0
  2. Removing all roles if it has any
  3. Assigning the role that I want to check to Dummy
  4. Check with $dummy->can(...)

Which I don't find elegant at all, and it is for some reason buggy when used in a loop (somehow it is lagging behind the rest of the code, but I will inspect this more in detail and create issue with more details)

JosephSilber commented 3 years ago

You don't want to check if $role->can('add', $model), since that doesn't check for a specific ability. It would return true if you have previously done any of the following:

  1. Bouncer::allow($role)->everything()
  2. Bouncer::allow($role)->toManage(TheModelType::class)
  3. Bouncer::allow($role)->to('add', TheModelType::class)
  4. Bouncer::allow($role)->to('add', $model)

Your code should only care about item 4 in that list.

So in essence, you're not trying to go through the gate, because you don't want to simply check if the user can perform the given action (as you would check before the action is actually performed). Rather, you want to check whether the role actually has this specific ability in the list you get from $role->getAbilities().

spelcaster commented 3 years ago
>>> $user->role->can('do-something')
^ "select * from `abilities` where (exists (select * from `roles` inner join `permissions` on `roles`.`id` = `permissions`.`entity_id` where `permissions`.`ability_id` = `abilities`.`id` and `permissions`.`forbidden` = ? and `permissions`.`entity_type` = ? and (exists (select * from `roles` inner join `assigned_roles` on `roles`.`id` = `assigned_roles`.`entity_id` where `assigned_roles`.`role_id` = `roles`.`id` and `assigned_roles`.`entity_type` = ? and `roles`.`id` = ?) or `level` < (select max(level) from `roles` where exists (select * from `roles` inner join `assigned_roles` on `roles`.`id` = `assigned_roles`.`entity_id` where `assigned_roles`.`role_id` = `roles`.`id` and `assigned_roles`.`entity_type` = ? and `roles`.`id` = ?)))) or exists (select * from `roles` inner join `permissions` on `roles`.`id` = `permissions`.`entity_id` where `permissions`.`ability_id` = `abilities`.`id` and `permissions`.`forbidden` = ? and `permissions`.`entity_type` = ? and `roles`.`id` = ?) or exists (select * from `permissions` where `permissions`.`ability_id` = `abilities`.`id` and `permissions`.`forbidden` = ? and `entity_id` is null)) and `only_owned` = ? and (`name` = ? and `entity_type` is null or (`name` = ? and (`entity_type` is null or `entity_type` = ?)))"
^ array:14 [
  0 => false
  1 => "roles"
  2 => "Silber\Bouncer\Database\Role"
  3 => 1
  4 => "Silber\Bouncer\Database\Role"
  5 => 1
  6 => false
  7 => "Silber\Bouncer\Database\Role"
  8 => 1
  9 => false
  10 => false
  11 => "do-something"
  12 => "*"
  13 => "*"

I've changed this line to dump the query, since I'm using custom models with morph map Silber\Bouncer\Database\Role should be renamed to roles

spelcaster commented 3 years ago

I was reading the code again and just found that someone added use Silber\Bouncer\Database\Role; on the User model so it was overriding my custom Role... smh