tatter/permits

CodeIgniter 4的模型权限处理

v3.0.0-rc.2 2022-02-03 16:03 UTC

README

CodeIgniter 4的模型权限处理

Coverage Status

快速开始

  1. 使用Composer安装: > composer require tatter/permits
  2. 将特性添加到你的模型中
class BlogModel extends Model
{
    use PermitsTrait;
  1. 使用CRUDL动词检查访问权限: if ($blogs->mayCreate()) ...

特性

Permits解决了对象权限管理中的常见问题:"此用户能否添加/更改/删除此项目?" 此库通过添加CRUDL风格的动词到你的模型类,为你的模型类提供了对象级别的访问权限。

安装

通过Composer轻松安装以利用CodeIgniter 4的自动加载功能,并始终保持最新状态

composer require tatter/permits

或者,通过下载源文件并将目录添加到app/Config/Autoload.php来手动安装。

Permits需要Composer提供codeigniter4/authentication-implementation,如CodeIgniter身份验证指南中所述。你必须安装并配置一个支持的包,以便Permits能够了解你的用户。

配置(可选)

可以通过扩展其配置文件来改变库的默认行为。将**examples/Permits.php复制到app/Config/,并遵循注释中的说明。如果没有在app/Config中找到配置文件,库将使用其自身的。

配置文件包括一组$default访问级别,你可以修改你自己的版本。你打算使用的每个模型都应该有一个与它的$table属性对应的Config属性,其中包含需要从默认值调整的属性。

配置完成后,只需在模型中包含PermitsTrait即可启用这些方法

use CodeIgniter\Model;
use Tatter\Permits\Traits\PermitsTrait;

class FruitModel extends Model
{
    use PermitsTrait;
...

用法

有两种类型的权限:显式和推断。两者都依赖于这个常见的CRUDL动词集合

  • 创建:创建新项目
  • 读取:查看单个项目
  • 更新:更改单个项目
  • 删除:删除单个项目
  • 列表:查看所有项目的索引
  • 管理员:无论其他权限如何,都可以执行上述任何操作

使用相应的模型动词来检查访问权限

if (! $model->mayCreate()) {
    return redirect()->back()->with('error', 'You do not have permission to do that!');
}

$item = $model->find($id);
if (! $model->mayUpdate($item)) {
    return redirect()->back()->with('error', 'You can only update your own items!');
}

PermitsTrait将根据当前登录用户(如果有的话)检查访问权限,但你也可以传递一个明确的用户ID来检查

if (! $model->mayAdmin($userId)) {
    log_message('debug', "User #{$userId} attempted to access item administration.");
}

显式

显式权限通过你的授权库授予用户或组。 Permits使用Tatter\Users与用户记录交互并确定显式权限。如果你的认证包不支持Users自动发现,请确保阅读文档

在检查显式权限时,Permits使用"table.verb"格式。例如,如果你的项目包含一个BlogModel,你希望允许博客编辑员编辑任何人的博客条目,你将"blogs.edit"分配给"editors"组。

注意:显式权限始终优先于推断权限。

推断

推断权限使用配置(见上文)来确定任何单个用户对项目或项目组的访问权限。可以应用于每个动词的有四种访问级别(这些是 Tatter\Permits\Config\Permits 上的常量)

  • NOBODY:禁止所有访问,除非有明确的权限
  • OWNERS:需要经过验证的用户拥有该物品
  • OWNERS:需要经过验证的用户拥有该物品
  • USERS:需要任何经过验证的用户
  • ANYBODY:允许任何人,无论是否经过验证或拥有

除了访问级别之外,每个模型都应该配置如何确定物品所有权。在您表的单个属性Config上设置所需的以下值

  • userKey:在物品或其连接表中用户ID的字段
  • pivotKey:在连接表中物品ID的字段
  • pivotTable:将物品与所有者连接的表

示例

您的Web应用程序包含一个内容管理系统,允许站点所有者登录并更新网站的各个部分。这包括一个博客部分,它有自己的模型、控制器和视图。任何访问您网站的访客都可以创建账户并提交博客条目,但必须在“上线”前进行审批。作为您这样优秀的开发者,您决定使用 Tatter\Permits 来管理博客条目的访问权限。

Permits 需要一个 认证实现;在这个例子中,我们将使用 Shield

首先,我们需要确保我们的认证包已经准备好并设置了正确的权限。这可能会因包而异,但 Shield 使用 配置文件 定义组和权限。我们将保留现有的组并添加一个新的“编辑者”组。 app/Config/AuthGroups.php

    public $groups = [
        'superadmin' => [
            'title'       => 'Super Admin',
            'description' => 'Complete control of the site.',
        ],
        'admin' => [
            'title'       => 'Admin',
            'description' => 'Day to day administrators of the site.',
        ],
        'developer' => [
            'title'       => 'Developer',
            'description' => 'Site programmers.',
        ],
        'user' => [
            'title'       => 'User',
            'description' => 'General users of the site. Often customers.',
        ],
        'beta' => [
            'title'       => 'Beta User',
            'description' => 'Has access to beta-level features.',
        ],
        'editor' => [
            'title'       => 'Blog Editors',
            'description' => 'Has access to all blog entries.',
        ],
    ];

我们想给一些组明确的博客管理权限,所以在同一个文件中,我们添加了一个新的以格式"{table}.{verb}"的权限

    public $permissions = [
        'admin.access'        => 'Can access the sites admin area',
        'admin.settings'      => 'Can access the main site settings',
        'users.manage-admins' => 'Can manage other admins',
        'users.create'        => 'Can create new non-admin users',
        'users.edit'          => 'Can edit existing non-admin users',
        'users.delete'        => 'Can delete existing non-admin users',
        'beta.access'         => 'Can access beta-level features',
        'blogs.admin'         => 'Allows all access to blog model operations',
    ];

最后,我们将在同一个文件中添加新的权限到我们想要添加权限的组中

    public $matrix = [
        'superadmin' => [
            'admin.*',
            'users.*',
            'beta.*',
            'blogs.*',
        ],
        'admin' => [
            'admin.access',
            'users.create',
            'users.edit',
            'users.delete',
            'beta.access',
            'blogs.admin',
        ],
        'developer' => [
            'admin.access',
            'admin.settings',
            'users.create',
            'users.edit',
            'beta.access',
        ],
        'user' => [],
        'beta' => [
            'beta.access',
        ],
        'editor' => [
            'blogs.admin',
        ],
    ];

这就是第三方授权配置的全部内容!接下来是 Permits - 我们首先需要在Config文件中设置权限。我们可以保留默认设置,并添加自己的属性。 app/Config/Permits.php

    /*
     * @var array<string,mixed>
     */
    public $blogs = [
        'admin'      => self::NOBODY,
        'create'     => self::USERS,
        'list'       => self::USERS,
        'read'       => self::OWNERS,
        'update'     => self::OWNERS,
        'delete'     => self::OWNERS,
        'userKey'    => 'user_id',
        'pivotKey'   => null,
        'pivotTable' => null,
    ];
}

让我们分解一下。

  1. 第一个权限,“admin”:我们在上面的认证包中已经赋予了明确的权利,因此我们不希望其他人有权访问,所以是 NOBODY。明确的权限具有优先级,所以我们的“superadmin”、“admin”和“editor”组仍然可以完全访问。

  2. 接下来是“list”和“create”:两者都可供 USERS - 即,任何登录的用户使用。他们将能够创建新的条目并查看其他人的条目列表。

  3. 然而,“read”、“update”和“delete”都限制为 OWNERS - 即,经过验证的用户只能点击他们自己的条目来阅读和修改内容。

  4. 最后,我们需要一种让 Permits 来决定“这是谁的所有权”的方法。在这种情况下,我们设置了“userKey”,但留空了连接属性 - 这意味着,我们的 blogs 表有一个名为 user_id 的字段,该字段与创建博客的用户ID相对应。

在更复杂的设置中,如果多个用户被分配到多个博客,我们可能有一个连接表,在这种情况下,我们也会将“pivotTable”设置为类似 blogs_users 的值,并将“pivotKey”设置为类似 blog_id 的值。

配置完成!集成最后一部分是向博客模型添加我们的特质,这将处理激活我们的访问动词。 app/Models/BlogModel.php

use App\Entities\Blog;
use CodeIgniter\Model;
use Tatter\Permits\Traits\PermitsTrait;

class BlogModel extends Model
{
    use PermitsTrait;

    protected $table      = 'blogs';
    protected $primaryKey = 'id';
    protected $returnType = Blog::class;
...
}

集成完成!现在您可以开始在使用代码中使用 Permits。让我们为我们的博客创建一个控制器,并在常规代码之前添加一些权限检查。app/Controllers/Blogs.php

<?php

namespace App\Controllers;

use App\Models\BlogModel;
use CodeIgniter\HTTP\RedirectResponse;

class Blogs extends BaseController
{
    /**
     * @var BlogModel
     */
    protected $model;

    /**
     * Preloads the model.
     */
    public function __construct()
    {
        $this->model = model(BlogModel::class);
    }

    /**
     * Displays the list of approved blogs
     * for all visitors of the website.
     */
    public function index(): string
    {
        return view('blogs/public', [
            'blogs' => $this->model->findAll(),
        ]);
    }
    
    /**
     * Displays blogs eligible for updating
     * based on the authenticated user (handled
     * by our authentication Filter).
     */
    public function manage(): string
    {
        // Admin access sees all blogs, otherwise limit to the current user
        if (! $this->model->mayAdmin()) {
            $this->model->where('user_id', user_id());
        }

        return view('blogs/manage', [
            'blogs' => $this->model->findAll(),
        ]);
    }
    
    /**
     * Shows a single blog with options
     * to update or delete.
     *
     * @return RedirectResponse|string
     */
    public function edit($blogId)
    {
        // Verify the blog
        if (empty($blogId) || null === $blog = $this->model->find($blogId)) {
            return redirect()->back()->with('error', 'Could not find that blog entry.');
        }

        // Check access
        if (! $this->model->mayUpdate($blog)) {
            return redirect()->back()->with('error', 'You do not have permission to do that.');
        }

        return view('blogs/edit', [
            'blog' => $blog,
        ]);
    }
...

希望从这里您能理解!对于喜欢使控制器更轻量级的开发者,您甚至可以将这些检查中的某些放入过滤器中。

扩展

CRUDL风格的函数只是一个起点!您的模型可以覆盖这些内置函数或添加新的函数,这些函数可以利用库的结构和方法。查看源代码库中的代码以获取如何利用显式和隐式权限的灵感。