tobscure/许可

该软件包已被放弃且不再维护。没有建议的替代软件包。
该软件包最新版本(dev-master)没有可用的许可信息。

使用 Eloquent 实现强大、灵活、关系型权限。

dev-master 2015-05-29 05:01 UTC

This package is auto-updated.

Last update: 2022-02-01 12:37:35 UTC


README

使用 Eloquent 实现强大、灵活、关系型权限。

Permissible 允许您将权限定义为独立的逻辑子句,可以直接对模型数据进行评估,或编译到 Fluent 查询的 WHERE 子句中。

理由

有时,权限可能很复杂。想象一下这样一个场景:有 讨论,每个讨论都有许多 帖子。以下是我们的权限逻辑

  • 只有当用户有权查看讨论时,他们才有权查看帖子。
  • 用户只能查看他们发起的讨论。

级联开始构建:只有当用户发起的讨论时,他们才能查看帖子。

那么我们如何判断一个帖子是否可以查看?显然的解决方案可能是这样做

class Post extends Eloquent
{
    public function discussion()
    {
        return $this->belongsTo('Discussion');
    }

    public function canView()
    {
        return $this->discussion->canView();
    }
}

class Discussion extends Eloquent
{
    public function canView()
    {
        return $this->start_user_id == Auth::user()->id;
    }
}

太好了!现在我们可以调用 $post->canView() 来判断帖子是否可以查看。但这在所有情况下都不适用。想象一下我们在搜索帖子

$results = Post::with('discussion')
    ->where('content', 'like', '%hello%')
    ->take(20)
    ->get();

假设我们得到了请求的 20 个结果,数据库中还有更多我们没有得到。但现在我们必须过滤它们,只保留我们可以查看的帖子

$results = $results->filter(function ($post) {
    return $post->canView();
});

现在我们可能只有 10 个、5 个,甚至 0 个!我们不能将这些呈现给用户——他们想要一整页的结果,而且没有任何合理的理由说明他们不应该得到他们想要的。 显然,我们需要在搜索查询中过滤掉用户无法查看的帖子。

好的,那么我们试试这个

$viewableDiscussions = function ($query) {
    $query->select('id')
          ->from('discussions')
          ->where('start_user_id', Auth::user()->id);
};

$results = Post::where('content', 'LIKE', '%hello%')
    ->whereIn('discussion_id', $viewableDiscussions)
    ->take(20)
    ->get();

太好了,问题解决了,对吧?嗯,是的,但是现在我们在 Discussion 模型的 canView 方法和我们的子查询中 重复了权限逻辑

当然,我们可以将子查询逻辑移动到 scopeCanView 方法中,但是逻辑仍然是重复的——canView 逻辑的副本评估模型数据,而 scopeCanView 逻辑的副本向查询添加 WHERE 子句以过滤其结果。 这是完全相同的逻辑,只是形式不同!当权限变得非常复杂时,这种重复是痛苦的。

Permissible 使得处理此类场景变得非常容易。权限由无知的条件子句定义,可以直接对模型数据进行评估,或编译到查询的 WHERE 子句中以过滤结果。

安装

通过 Composer

"tobscure/permissible": "*"

使用

检查权限

在您希望启用权限检查的任何模型中,只需包含 Tobscure\Permissible\Permissible 特性。很简单!

use Tobscure\Permissible\Permissible;

class Discussion extends Eloquent
{
    use Permissible;
}

此特性提供了 canscopeWhereCan 方法,因此您可以这样做

// Check if a user has permission to view a certain discussion
$discussion = Discussion::find(1);
if (! $discussion->can($user, 'view')) {
    echo 'permission denied';
}

// List all of the discussions that a user has permission to view
$discussions = Discussion::whereCan($user, 'view')->get();

然而,如果没有定义任何条件来授予权限,这些将没有太大作用,因为 最初所有权限都被拒绝

授予权限

为了定义一个授予权限的条件,特性为我们提供了一个静态的 grant 方法。我们可以在启动模型时调用此方法。第一个参数是权限的名称;第二个是一个闭包,它接受一个 Tobscure\Permissible\Condition\Builder 对象和一个 $user 对象。

Condition.Builder 类与 Laravel 的查询构建器非常相似;您将熟悉 wherewhereNullwhereNotNullwhereInwhereNotIn 方法,以及所有这些方法的 or 变体。

public static function boot()
{
    parent::boot();

    static::grant('view', function ($grant, $user) {
        $grant->where('start_user_id', $user->id);
    });
}

权限条件

除了这些静态的 where 条件外,您还可以定义依赖于另一个权限或关系权限的条件。这可以通过使用 whereCanwhereCannot 方法来完成。(这些方法也有 or 变体。)

$grant->whereCan('edit')
      ->orWhereCan('view', 'user');

存在条件

您还可以通过将闭包传递给 whereExistswhereNotExists 来定义检查数据库记录是否存在或不存在的情况。除了查询构建器外,此闭包还接受一个表示模型 ID 的值;如果条件是针对模型数据进行评估的,则它将是一个占位符(?),如果条件是添加到查询中,则它将是列名。

$grant->whereExists(function ($query, $id) use ($user) {
    return $query->select(DB::raw(1))
        ->from('discussions_private')
        ->where('user_id', $user->id)
        ->whereRaw("discussion_id = $id");
});

授予多个权限

您可以通过传递一个权限名称数组或完全省略权限参数来使用相同的逻辑授予多个权限。传递给闭包的第三个参数是被评估的权限名称。

要始终授予权限,无论具体实体如何,您可以从回调中返回 true。

static::grant(['view', 'edit'], function ($grant, $user) {
    return $user->isAdmin();
});

需要条件

如果为某个权限定义的任一条件集被满足,则授予该权限。因此,我们可以在运行时动态添加新的授权条件。

// Users can view discussions if they are started by themselves
static::grant('view', function ($grant, $user) {
    $grant->where('start_user_id', $user->id);
});

// Users can also view discussions if they are started by their mothers
static::grant('view', function ($grant, $user) {
    $grant->where('start_user_id', $user->mother->id);
});

但是,如果在运行时需要让用户无法查看一年以上的任何讨论,无论它们是由谁发起的,我们必须使用 check 方法添加 必需 条件。

// Users can view discussions ONLY if they are less than a year old
static::check('view', function ($check, $user) {
    $oneYearAgo = strtotime('-1 year', time());
    $check->where('start_time', '<', $oneYearAgo);
});

为了授予权限,必须满足使用 check 方法定义的所有条件(如果有)。

总结一下,为了授予权限,必须满足两套条件

  1. 至少必须满足一个 授权 条件。
  2. 必须满足所有 检查 条件。