允许根据设置的权限编辑模型以及模型在 Laravel 中的 属性 值

Allow edit of Model based on a set permission as well as Model's property value in Laravel

我有一个位置模型,它包含两个属性:ID 和名称。

为了编辑这个模型,我设置了这条路线:

Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit');

我设置了非常简单的权限:在 AuthServiceProvider 中,我在 boot 方法中检查以下

Gate::before(function ($user, $permission) {
    if ($user->permissions->pluck('name')->contains($permission)) {
        return true;
    }
});

其中 permission 是一个包含 ID 和名称的模型,通过 permission_user table.

映射

我设置了这些权限:

说了这么多,我的实际问题是:

如何将这些权限绑定到编辑位置?

我面临的问题是,不允许给定用户编辑所有位置,但可能只允许编辑一个位置。只有具有 edit_los_angeles 权限的用户才可以编辑名为 Los Angeles.
Location 所以我不能将其分组为一个权限,如 edit_location 并将其添加到我的路由 ->middleware('can:edit_location').

相反,我需要这样的东西,我想:

Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_los_angeles');
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_new_york');
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_boston');

...显然这行不通。

你会用什么方法来解决这个难题? :-)
也许我做的事情完全错了,还有更好的方法 Laravel- 做这件事的方法?

非常感谢您的提前帮助!

我正在使用 Laravel 6.0 :-)

如果您有基于位置关系的需求,则需要在数据中捕获该关系。一个好的起点是添加一个特定于这些编辑权限的枢轴 table。考虑 table、location_permissionsuser_idlocation_id。一旦您拥有特定的用户和位置,您就可以修改或添加权限中间件来检查此 table 中的记录。

编辑:回答关于中间件实现的问题,

实施的关键可能会通过此新枢轴定义用户模型与位置的关系来解决 table。

然后我建议添加一个额外的方法,该方法使用 locations 与模型的新关系

public function canEditLocation(Location $location): bool { 

   return $this->locations
               ->where('location_id', '=', $location->id)
               ->count() > 0; 
    }

实际的中间件是这样的:

public function handle($request, Closure $next, $location)
    {
        if (! $request->user()->canEditLocation($location)) {
            \handle failed permission as appropriate here.
        }

        return $next($request);
    }

我的中间件参数知识是生锈的,但我相信在 https://laravel.com/docs/master/middleware#middleware-parameters

中定义的是正确的

我的工作方法有两个假设,在控制器中使用模型绑定(无论如何你都应该这样做)。其次,位置和它需要的权限之间需要有一种关系,类似于你建议的 slug。

你的控制器函数看起来像这样。添加 FormRequest 是执行此逻辑的好方法。

class LocationController {
     public function edit(EditLocationRequest $request, Location $location) { // implicit model binding
         ...
     }
}

为了方便使用,我也做一个策略

class LocationPolicy
{
    public function edit(User $user, Location $location) {
        return $user->permissions->pluck('name')
            ->contains($location->permission_slug); // assuming we have a binding
    }
}

记得在AuthServiceProvider.php中注册政策。

protected $policies = [
    Location::class => LocationPolicy::class,
];

现在在您的表单请求中使用授权方法中的策略。从这里您处于请求上下文中,您可以访问 $this->user() 上的用户,并且您可以访问所有在其名称上绑定模型的模型,例如 $this->location.

class EditLocationRequest
{
    public function authorize(): bool
    {
        return $this->user()->can('edit', $this->location);
    }
}

现在您应该只能有一个路由定义。

Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit');

编辑

在没有表单请求的情况下,如果您使用特征 AuthorizesRequests,您可以执行以下操作。这将抛出 AuthorizationException 失败。

use AuthorizesRequests;

public function edit() {
    $this->authorize('edit', $location);
}