LoginSignup
2
0

More than 3 years have passed since last update.

Laravel Backpack with Bouncer for Access Control

Last updated at Posted at 2019-11-17

Recently I was in a project that uses laravel bouncer with backpack 4. Here is how it was done after you have installed laravel backpack and bouncer in your project

Step 1: Create a Role model that extends \Silber\Bouncer\Database\Role
This need to be done as Laravel Backpack requires the model to use Backpack\CRUD\app\Models\Traits\CrudTrait.

You may create an Ability model that extends \Silber\Bouncer\Database\Ability as well for the same reasons. Although I am not doing it for reasons that will be clearer later.

Step 2: Now, let's create some roles and ability with laravel tinker

Bouncer::allow('super-admin')->everything();
Bouncer::allow('super-reader')->to('view')->everything();
Bouncer::allow('super-reader')->to('viewAny')->everything();

$admin = User::first();
Bouncer::assign('super-admin')->to($admin);

$reader = User::find(2);
Bouncer::assign('super-reader')->to($reader);

Step 3: Let's look at the database now. The roles and abilities table are self explanatory. But the permissions table is worth a closer look. It is a pivot (many to many) table between roles and abilities. Notice how the entity_type is roles and not Silber\Bouncer\Database\Role.

The reason for that is because the Bouncer package has declared relation morph map for both its Role and Ability model.

This will cause us some issues later, as we wanted it to refer to our Role model in App\Models\Role. I will reveal an easy fix for it in a later step.

Step 5: Let's create our RoleCrudController. It should reference the Role model we created in App\Models.

Step 6. Add the appropriate fields to create and update a Role. For me I have chosen to only allow the updating of the abilities a Role is associated with. As I do not want an admin user to edit the role name or ability name and potentially break the permissions check in my code.

Below is how I implemented my setupUpdateOperation method of my RoleCrudController. If you have your own Ability model class that extends Bouncer Ability class, please reference your model class instead.

protected function setupUpdateOperation()
{
        $currentRole = $this->crud->getCurrentEntry();

        if (backpack_user()->cannot('update', $currentRole)) {
            $this->crud->denyAccess(['update']);
        }

        $this->crud->setValidation(UpdateRequest::class);
        $this->crud->addFields([
            [
                'name' => 'name',
                'type' => 'custom_html',
                'value' => '<strong>Role: </strong>'.$this->crud->getCurrentEntry()->name,
            ],
            [
                'name' => 'abilities',
                'type' => 'checklist',
                'entity' => 'abilities',
                'attribute' => 'title',
                'model' => Silber\Bouncer\Database\Ability::class,
                'pivot' => true,
            ],
        ]);
    }

Step 7: Click edit on a Role record and look at how the checklist field looks like. You will notice nothing is checked. That is because the checklist field is checking the ability against App\Model\Role but using Bouncer code, we assigned the abilities to Silber\Bouncer\Database\Role. (Remember step 3?)

To solve this is simple. In your AppServiceProvider boot method. Tell Bouncer to use your Role model instead of theirs. While we are at it let's define the relation morphMap for our other models such as the User model too. This will save us from other troubles later. You may learn more about relationships morph map here.


Relation::morphMap([
    'users' => App\Models\User::class,
]);
// App\Models\Role::class will now map to 'roles' in entity_type column 
// instead of Bouncer Role model
BouncerFacade::useRoleModel(App\Models\Role::class);

Step 8. Now, what's left is to add a checklist_dependency field to our UserCrudController. Below is an example for my UserCrudController setupUpdateOperation() method:


if (backpack_user()->isA('super-admin')) {
            $this->crud->addFields([
                [
                    'label'     => 'Roles and Abilities',
                    'field_unique_name' => 'user_role_ability',
                    'type'      => 'checklist_dependency',
                    'name'      => ['roles', 'abilities'],
                    'subfields' => [
                        'primary'   => [
                            'label'            => 'Roles',
                            'name'             => 'roles',
                            'entity'           => 'roles',
                            'entity_secondary' => 'abilities',
                            'attribute'        => 'title',
                            'model'            => App\Models\Role::class,
                            'pivot'            => true,
                            'number_columns'   => 3,
                        ],
                        'secondary' => [
                            'label'          => 'Abilities',
                            'name'           => 'abilities',
                            'entity'         => 'abilities',
                            'entity_primary' => 'roles',
                            'attribute'      => 'title',
                            'model'          => Silber\Bouncer\Database\Ability::class,
                            'pivot'          => true,
                            'number_columns' => 3,
                        ],
                    ],
                ],
            ]);
        }

I'm not sure if checking for the super-admin role here specifically is good since it is usually best practice to check for a specific ability but for now I'm going with a specific check for the super-admin role in case a super-admin accidentally assign a givePermission ability to a lower tier Role in edit Role.

That's the end of this tutorial. I hope it help anyone who wanted to use Bouncer with Backpack.

Update: It appears I didn't handle the case of forbidden permissions. I will keep this in mind and update this article again next time when I thought of a way.

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0