cura/user-policy-bundle

安装次数: 1,630

依赖项: 0

建议者: 0

安全: 0

星标: 0

关注者: 3

分支: 0

公开问题: 0

类型:symfony-bundle

1.2.0 2024-01-11 08:43 UTC

This package is not auto-updated.

Last update: 2024-09-19 11:57:35 UTC


README

这个包受到GeekCell 用户策略包的启发,并针对 Cura4You 面临的使用场景进行了适配。

示例

假设你正在开发一个视频平台,用户只有拥有高级订阅或剩余上传配额时才允许上传。

use Cura\UserPolicyBundle\Policy\AbilityNotSupportedRejection;
use Cura\UserPolicyBundle\Policy\Rejected;
use Cura\UserPolicyBundle\Policy\Granted;
use Cura\UserPolicyBundle\Policy\RejectionReason;

#[AsPolicy(Video::class)]
/** @implements Policy<User, Video> */
class VideoPolicy implements Policy
{
    public function __construct(
        private readonly QuotaService $quotaService,
    ) {
    }

    public function canDo(
        $instance,
        string $ability,
        $subject,
        mixed ...$arguments
    ): Granted|Rejected {
        return match ($ability) {
            'upload' => $this->upload($instance),
            default => new Rejected(new AbilityNotSupportedRejection())
        };
    }
    
    public function upload(User $user): Granted
    {
        if ($user->hasPremiumSubscription()) {
            return new Granted();
        }
        
        if ($this->quotaService->getRemainingUserUploads($user) > 0) {
            return new Granted();
        }
        
        return new Rejected(new RejectionReason('User has no premium subscription or remaining quote'));
    }
}
use Cura\UserPolicyBundle\Policy\Granted;

class VideoController extends AbstractController
{
    #[Route('/videos/new_upload')]
    public function create(): Response
    {
        if ($this->getUser()->can('upload', Video::class) instanceof Granted) {
            // Proceed with upload ...
        }

        $this->createAccessDeniedException('Operation not allowed.');
    }
}

非常好,不是吗?业务逻辑封装在策略类中,可以直接从用户对象中神奇地查询

安装

要使用此包,请在 Composer 中要求它

composer require cura/user-policy-bundle

安装后,在您的 config/services.yaml 中添加以下行

services:

    # Add these lines below to your services.yaml

    _instanceof:
        Cura\UserPolicyBundle\Contracts\Policy:
            tags: ['cura.user_policy.policy']

这些行对于 Symfony 自动发现您应用程序中定义的策略至关重要。或者,策略可以手动配置,甚至可以根据名称猜测,但这些方法不推荐使用。

现在将 PolicyResolverTrait 特性添加到您的用户类中。

<?php

namespace App\Security;

use Cura\UserPolicyBundle\Trait\PolicyResolverTrait;
class User
{
    use PolicyResolverTrait;
}

现在您可以开始了。

编写策略

一个基本策略看起来像这样

<?php

namespace App\Security\Policy;

use App\Entity\Book;
use App\Security\User;
use Cura\UserPolicyBundle\Policy\AbilityNotSupportedRejection;
use Cura\UserPolicyBundle\Policy\Granted;use Cura\UserPolicyBundle\Policy\Rejected;
use GeekCell\UserPolicyBundle\Contracts\Policy;
use GeekCell\UserPolicyBundle\Support\Attribute\AsPolicy;

#[AsPolicy(Book::class)]
/** @implements Policy<User, Book> */
class BookPolicy implements Policy
{
    public function canDo(
        $instance,
        string $ability,
        $subject,
        mixed ...$arguments
    ): Granted|Rejected {
      return match ($ability) {  
        'create' => $this->create($instance),
        'update' => $this->update($instance, $subject),
        'delete' => $this->delete($instance, $subject, $arguments),
        default => new Rejected(new AbilityNotSupportedRejection());
      }
    }
    
    public function create(User $user): Granted|Rejected
    {
        // ...
    }

    public function update(User $user, Book $book): Granted|Rejected
    {
        // ...
    }

    public function delete(User $user, Book $book, mixed $someArguments): Granted|Rejected
    {
        // ...
    }
}

让我们来分解它

  • 策略必须实现 Cura\UserPolicyBundle\Contracts\Policy 接口。
  • 使用 #[AsPolicy] 属性将策略关联到主题。
  • 使用 /** @implements Policy<InstanceClass, SubjectClass> */ 注释策略以帮助静态分析/自动完成
  • 策略方法可以具有任意名称,即它们不受 CRUD 操作的限制。
    • canDo 函数始终接收使用该特质的对象作为第一个参数,接着是能力(如 createupdate 等),主题(作为类字符串或对象实例)以及传递给 can 函数的任何其他参数

由于 canDo 函数必须始终返回 GrantedRejected 的实例,因此上面示例中的默认匹配臂始终返回一个 AbilityNotSupported "拒绝"。我们建议您也这样做。

灵感