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

时间:2020-06-26 10:42:35

标签: php laravel laravel-6

我有一个位置模型,其中包含两个属性:ID和Name。

要编辑此模型,我设置了以下路线:

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是通过permission_user表映射的包含ID和名称的模型。

我已设置以下权限:

  • edit_los_angeles
  • edit_new_york
  • edit_boston
  • 其他人的权限过多而不是相关位置

经过漫长的讨论之后,我的实际问题是:

如何将这些权限与位置编辑相关?

我面临的问题是,不允许给定用户编辑所有位置,而只能允许编辑一个位置。只有具有权限edit_los_angeles的用户才能被允许编辑名称为Location的{​​{1}}。
因此,我无法将此权限归类为Los Angeles之类的权限,并将其添加到我的路由edit_location中。

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

->middleware('can:edit_location')

...显然这行不通。

您将如何解决这一难题? :-)
也许我做的事情完全错了,这样做有更好的Laravel方法吗?

非常感谢您的提前帮助!

我正在使用Laravel 6.0:-)

2 个答案:

答案 0 :(得分:1)

如果您有基于位置关系的要求,那么您将需要在数据中捕获该关系。一个很好的起点是添加一个特定于这些编辑权限的数据透视表。考虑具有location_permissionsuser_id的表location_id。然后,您可以修改或添加权限中间件,以在拥有特定用户和位置后检查此表中的记录。

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

实现的关键点可能是通过此新的数据透视表在用户模型上定义与位置的关系。

我建议然后添加一个附加方法,该方法将新的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定义的是正确的

答案 1 :(得分:1)

我的工作方法有两个假设,在控制器中使用模型绑定(无论如何都应该这样做)。其次,位置和所需的权限之间必须存在联系,类似于您建议的内容。

您的控制器功能将如下所示。添加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,
];

现在您的表单请求中使用了authorize方法中的策略。从这里开始,您处于请求上下文中,可以访问$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);
}