thedevdojo / voyager

Voyager - The Missing Laravel Admin
https://voyager.devdojo.com
MIT License
11.78k stars 2.67k forks source link

A means to prevent users with non-admin roles from granting admin role to other users #3852

Open artchik opened 5 years ago

artchik commented 5 years ago

Version information

Description of problem

The user with the role of admin (Administrator) can do everything. In my application, I need a one level "down" administrator (I call him/her a site admin), that can do a lot of the things that the admin can do including creating other users. I was able to set this up fine. But what I don't want this site admin to be able to do is give the role of admin to new users they create or existing ones that they edit.

While, I know I can create my local policy, and modify the editRoles method to disallow role editing if this not an admin, this is not what I need. I still want this "site admin" to grant users any other roles, but the role of admin.

Proposed solution

Allow only users with the role of "admin" to grant the role admin to new or existing users. Any other roles should not be able to grant the role admin to anyone even if they are allowed to browse Admin and perform other admin functions.

Alternatives considered

An alternative could be a more complex solution where each role can have a field indicating whether or not they can grant admin role. Or, to go even further, for each role, allow to specify the roles they can grant.

Vas-P commented 5 years ago

If possible, please, could you explain me how did you make "site admin". Step by step, or with screenshots. Thank you.

artchik commented 5 years ago

I created the "site admin" in the following manner:

  1. Create a new role called site admin

  2. Granted this role permissions to a. Browse Admin 2019-01-01 21_07_59-edit role

b. Manipulate users 2019-01-01 21_10_04-edit role

  1. Granted this role additional BREAD permissions that are relevant to my app.
wvffle commented 5 years ago

In my app I've edited views/vendor/voyager/users/edit-add.blade.php and added a check to skip those fields if current users role is not admin.

Just between @foreach($dataTypeRows as $row) and <!-- GET THE DISPLAY OPTIONS --> add:

@foreach($dataTypeRows as $row)

    @php
      if(!Auth::user()->hasRole('admin') && $row->field == 'user_belongsto_role_relationship') {
        continue;
      }

      if(!Auth::user()->hasRole('admin') && $row->field == 'user_belongstomany_role_relationship') {
        continue;
      }
    @endphp

    <!-- GET THE DISPLAY OPTIONS -->

But I'd rather see a better solution ;/

emptynick commented 5 years ago

Why don't you create a policy (eg. App\Policies\UserPolicy)

<?php

namespace App\Policies;

use TCG\Voyager\Contracts\User;
use TCG\Voyager\Policies\UserPolicy as Base;

class UserPolicy extends Base
{
    public function editRoles(User $user, $model)
    {
        return parent::editRoles($user, $model) && $anotherCondition;
    }
}

and set it as the default-policy for the user-bread?

wvffle commented 5 years ago

@emptynick, I couldn't manage to get policies working so I had to work around this.

yaknan123 commented 5 years ago

In my app I've edited views/vendor/voyager/users/edit-add.blade.php and added a check to skip those fields if current users role is not admin.

Just between @foreach($dataTypeRows as $row) and <!-- GET THE DISPLAY OPTIONS --> add:

@foreach($dataTypeRows as $row)

    @php
      if(!Auth::user()->hasRole('admin') && $row->field == 'user_belongsto_role_relationship') {
        continue;
      }

      if(!Auth::user()->hasRole('admin') && $row->field == 'user_belongstomany_role_relationship') {
        continue;
      }
    @endphp

    <!-- GET THE DISPLAY OPTIONS -->

But I'd rather see a better solution ;/

@wvffle I like your solution to this issue. So far I have not seen any straight forward way to restrict fields in voyager admin. However, when I use your method, all the fields that are restricted return null to the database even when there were preexisting values in those fields. How can I ensure that I do not get null errors from restricted fields.

inkbear commented 4 years ago

I added a column to the roles table for "rank" with admin = 0 and staff at 40. Then changed the Role model in Voyager to compare the rank of the current user with the list. Users can set permissions for those of lower ranks to other lower ranks e.g. "site admin" can make visitor "staff".

public function permissions()
    { 
        // This bit filters the role options to only those levels below the user's own role.
        static::addGlobalScope('rank', function (Builder $builder) {
            $MyRole = \Auth::user()->role->rank;
            // \Log::info('Role: ' . $MyRole);
            $builder->where('rank', '>', $MyRole);
        });
        return $this->belongsToMany(Voyager::modelClass('Permission'));
    }