0

I'm trying to make this policy stuff work, and i have followed the documentation. But it doesn't seem like the policy code is not even run.

I have Role model. And i created RolePolicy. The thing i want to do in the policy is to ensure that the role with the ID of 1 never-ever gets updated or deleted.

My RolePolicy looks like this:

<?php

namespace App\Policies;

use App\Models\Role;
use Illuminate\Support\Facades\Response;

class RolePolicy
{
    /**
     * Determine whether the user can update the model.
     *
     * @param  \App\User  $user
     * @param  \App\Models\Role  $role
     * @return mixed
     */
    public function update(Role $role)
    {
        return $role->id === 1
        ? Response::deny('Cannot change super-admin role')
        : Response::allow();
    }

    /**
     * Determine whether the user can delete the model.
     *
     * @param  \App\User  $user
     * @param  \App\Models\Role  $role
     * @return mixed
     */
    public function delete(Role $role)
    {
        return $role->id === 1
        ? Response::deny('Cannot delete super-admin role')
        : Response::allow();
    }
}

I even tried to do a dd() inside both delete and update method in the policy, but when i try to delete/update the model with the ID of 1, nothing happens. The dd wont run, nor will the response in the current code above.

I have registered the policy in the AuthServiceProvider where i also have this gate to give the super-admin all the permissions.

<?php

namespace App\Providers;

use App\Models\Role;
use App\Policies\RolePolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Role::class => RolePolicy::class
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        // Implicitly grant "Super Admin" role all permissions
        // This works in the app by using gate-related functions like auth()->user->can() and @can()
        Gate::before(function($user, $ability) {
            return $user->hasRole('super-admin') ? true : null;
        });
    }
}

Here is also my RoleController method for updating the Role model:

/**
 * Edit role
 *
 * @param Edit $request
 * @param Role $role
 * @return void
 */
public function postEdit(Edit $request, Role $role)
{
    # Validation checks happens in the FormRequest
    # Session flash also happens in FormRequest

    # Update model
    $role->update([
        'name' => $request->name
    ]);

    # Sync permissions
    $permissions = Permission::whereIn('name', $request->input('permissions', []))->get();
    $role->syncPermissions($permissions);

    return redirect(route('dashboard.roles.edit.get', ['role' => $role->id]))->with('success', 'Changes saved');
}

Does the gate i use to give all permissions have anything to do with the policy not running? Or what am i doing wrong here?

Thanks in advance if anyone can point me in the right direction.

Kaizokupuffball
  • 2,703
  • 8
  • 38
  • 58

2 Answers2

0

The User model that is included with your Laravel application includes two helpful methods for authorizing actions: can and cant. The can method receives the action you wish to authorize and the relevant model. For example, let's determine if a user is authorized to update a given Role model:

if ($user->can('update', $role)) {
    //
}

If a policy is registered for the given model, the can method will automatically call the appropriate policy and return the boolean result. If no policy is registered for the model, the can method will attempt to call the Closure based Gate matching the given action name.

Via Controller Helpers

In addition to helpful methods provided to the User model, Laravel provides a helpful authorize method to any of your controllers which extend the App\Http\Controllers\Controller base class. Like the can method, this method accepts the name of the action you wish to authorize and the relevant model. If the action is not authorized, the authorize method will throw an Illuminate\Auth\Access\AuthorizationException, which the default Laravel exception handler will convert to an HTTP response with a 403 status code:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Role;
use Illuminate\Http\Request;

class RoleController extends Controller
{
    /**
     * Update the given role.
     *
     * @param  Request  $request
     * @param  role  $role
     * @return Response
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function update(Request $request, Role $role)
    {
        $this->authorize('update', $role);

        // The current user can update the role...
    }
}
MrEduar
  • 1,784
  • 10
  • 22
  • Okay so after trying this now, i still can't get it to work. I tried to see if i could use `$user->can('update', $role)` in the controller, and i still can update it for some reason. Same with `$this->authorize(...)`, same result. It's as if the policy is just ignored. – Kaizokupuffball Aug 10 '20 at 14:08
0

The Gate::before method in the AuthServiceProvider was the problem. Removed this and rewrote the permissions, policies and some gates to get the error messages from the policies.

Decided to give the role super-admin the permission * and check for this with $user->can() and middleware .....->middlware('can:*') and everything is working now.

Kaizokupuffball
  • 2,703
  • 8
  • 38
  • 58