Hey all,
thanks for this awesome package - i really like it as it is quite easy to use.
Recently, i stumbled upon a problem, which i thought i could share with you - this might be a contribution for your package.
In my application i have Teams (a User can be in multiple Teams) and Projects (a Project can have multiple Teams). Furthermore, a Team can have a specific Role within a Project. Therefore, one User can have multiple Roles within a Project.
For now, let us call these Roles Owner, Manager and Member (as they already imply some kind of Hierarchy: Owner > Manager > Member.
I would like to _retrieve the highest (i.e., the role with the most access rights) Role for one specific User within a Project_.
I thought it may be possible to enhance the Role table and add an additional level (int, default 0) field to the database. This field would act as some kind of hierarchy.
In the given example, one could just define Owner (10) > Manager (5) > Member (1). Note that the numbers indicate the level.
When retrieving all Roles for the User within a Project we would just need to sort by level and we're already set up!
Furthermore, this idea could be enhanced in order to provide some additional functions to restrict access not only by Role or Permission but also by Level. Think about a method like this hasAtLeastRoleLevel(int level) to check, if the level of the User is higher than - therefore he has access..
What do you think about this feature?
Best
Hi @johannesschobel
Thanks for the suggestions! However I feel like this is a pretty specific use case. You should be able to easily extend the package's Role model and overwrite some methods from in HasRoles to implement this logic in your own application.
I'll leave this issue open for now to get some feedback from other people.
Hey @AlexVanderbist
thank you very much for your response. Before your package, i used to rely on romanbican/roles (see this package), which supported this kind of level.
Wouldn't adding the level at first be a good idea? There is no need (at least for now) to add the hasAtLeastRoleLevel() method as well..
I would provide a PR if you would accept it..
Ya, the romanbican package has the extra numeric level tier which can be used as you describe.
That package isn't maintained anymore, but ultraware/roles is a newer and maintained fork of it.
I was toying with the idea of adding that sort of feature to this spatie package, but the project I was considering it for got canceled, so I don't need the feature now, and didn't write code for it.
Hey @drbyte , thanks for pointing this out. However, switching to another package might not be possible for me at the moment.
Therefore, I was suggesting to implement it in this package here, as - at least I think - this would be a valuable contribution..
hey @AlexVanderbist and @drbyte , just wanted to drop by and ask about any updates on this!? What do you think about the proposal?
Cheers from Germany
I like the idea of levels to have another "simple" way to allow access to certain functionality. However I'm not sure whether the majority of users need this functionality. In most use cases I think something like hasAtLeastRoleLevel(5) or isAtLeastRole('editor') can easily be refactored to check for the relevant permissions (hasPermissionTo('edit')).
I'd be happy to implement it if @drbyte and @freekmurze like it tho!
Woooooooooooooooha, that is awesome news @AlexVanderbist
Then let's hope they agree on implementing it!
I'm all for the idea of having a numbered heirarchy that's user-configurable (not auto-incremented), where the numbers are merely a secondary way to access the same thing, but with "greater than"/"less than" support, etc.
In practice, I'd be inclined to assign numeric values in jumps of 10, not 1 (ie: user:10, author:20, moderator:80, super-admin:90, etc) ... that way I can insert other things in between those numbers with relative ease and not have to edit previous records.
@AlexVanderbist do take a look at the ultraware package for a few ideas of what they've done with it, in case it helps round out the features you envision.
thanks for the positive feedback for my proposal! Glad you like the idea.. Looking forward for your implementation!
I'm not really a fan of this. Not a lot of users need this kind of functionality. To me this way of handling permissions feels kinda brittle.
I like to keep the package very light. If you need this you can use a custom model and add the necessary functions there.
so its no then, @freekmurze ? 馃槥
You don't know, if "a lot of users don't need this kind of functionality", because the users don't have it.. maybe they would use it , if they were able to use it "out of the box"...
Both, @AlexVanderbist and @drbyte like the idea as well and could see a need for it.. This would, in fact, allow to use the package in much more "lightweight" version, in fact, because for easy setups you could completely "remove" (i.e., not adding) Permissions. You may be able to do this entirely by role-level instead..
It's a no I'm afraid.
Level based permissions could just be it's own lightweight package. Stuffing the solution for that use case in laravel-permission doesn't feel right to me.
Thank you for your detailed proposal.
oh boys.. if i would have bet money on this..
While I appreciate the authors point on this one, I do think a role level is kinda essential if you are also building the UI to manage users.
If you have an application with roles like Super Admin, Admin and Editor, and you want to give admins the ability to edit other users, it would make sense to have a built in way to allow that without risking Admins creating or giving themselves Super Admin credentials.
lmartins is exactly the problem I'm running into. I have users that can have the roles owner, admin, manager, and user. Owners can create users with any role. Admins can create users with any role except owner. Managers can create managers and users. It would be nice to have a level column so that I can pull all Roles where (Role->level) <= (User->Role->level) for creating forms and validating that users aren't assigning roles they shouldn't. I'm going to implement this in my app, but just wanted to add my thoughts since I really love the way this package works.
Thanks for the input everyone. We're toying with a few ideas for V3. The feedback is appreciated.
@robinsonryan this is the exact issue I have as well. I need a hierarchy for users.
@johannesschobel @lmartins @robinsonryan @NickGiacopuzzi
It's been awhile since this discussion was started (and #685 also).
What approaches have you taken in the meantime?
I'm looking at next steps with this package and it would be helpful to have feedback about what direction you've taken in the absence of this feature so far.
I didn't do anything fancy. I just created a migration to add a "level" column to the 'roles' table. The, I extended you 'Role' model like so
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Models\Role as BaseRole;
use Illuminate\Support\Facades\DB;
class Role extends BaseRole
{
public static function createInsert($attributes) {
if(!$attributes['level']) $attributes['level'] = 0;
DB::table('Roles')->where('level', '>=', $attributes['level'])->increment('level');
return static::create($attributes);
}
public function scopeByLevel($query, $level) {
return $query->where('level', '<=', $level);
}
}
The createInsert method is used when creating a new Role. It will increase the level of all the other roles above it. Deleting a role will leave a gap in the sequence, but won't have negative effects on the hierarchy. I will probably eventually write a delete method that handles removing the gap, but it works as is.
Then, when I am creating a new user, I populate the roles field on the 'create' view with this data:
$roles = Role::byLevel(Auth::user()->role()->level)->get();
I am also working on adding code to my validator that checks that the 'level' key doesn't contain a level higher than what the current user is allowed to pass, but the above code has things working for now and the validator is pretty far down the list at this point.
This probably isn't the most flexible solution, but it got the job done for me.
BTW - my solution above does not work with different guards and that is one of my favorite parts of this package. I'm sure it can be made to work with multiple guards, I just haven't had time to write and test an implementation.
Simply adding an additional where clause in createInsert where('guard', '=', $attributes['guard'] may be enough.
@robinsonryan wrote:
my solution above does not work with different guards and that is one of my favorite parts of this package.
Do you specifically require different roles/permissions defined down to the "per guard" detail?
If so, can you explain why that needs to be dynamic (database-stored) in your app, vs coded into model Policies (and the database is unaware of the specific guard used)?
@drbyte : i implemented it as i initially described in my post.. i added a level param to the role and added some specific check functions via Traits to the User model.. Pretty straight forward i guess - nothing fancy.. but for my use-case it was sufficient..
all the best
Thanks @johannesschobel
I would also like to see a "level" system implemented. This would allow you to make sure users are not allowed to modify other users of the same or higher level than them. RIght now the roles are generic, there isn't a higher order of them, it doesnt understand if "Manager" is higher than "Staff"
In most apps I've worked on that "started out with" a concept of numbered "levels" we realized that named permissions and roles were sufficient when we simply used policies to enforce additional logic related to "levels", meaning we no longer required the complexity of "levels" stored in the database and all the logic to enforce them that way.
Nevertheless, if you need a quick way to implement numbered "levels", the ultraware package can handle that.
Most helpful comment
While I appreciate the authors point on this one, I do think a role level is kinda essential if you are also building the UI to manage users.
If you have an application with roles like Super Admin, Admin and Editor, and you want to give admins the ability to edit other users, it would make sense to have a built in way to allow that without risking Admins creating or giving themselves Super Admin credentials.