typo3/access-control

TYPO3 访问控制组件。

0.1.1 2019-12-04 16:05 UTC

This package is auto-updated.

Last update: 2024-09-06 06:03:57 UTC


README

Build Coverage Code Quality

安装

使用composer在项目中安装此扩展

composer require typo3/access-control

概念

通过使用策略,用户可以通过策略获得访问权限。底层模型被称为基于属性的访问控制 (ABAC)。它使用布尔表达式来决定是否允许访问请求。此类请求通常包含资源操作主题环境属性。此扩展实现了一个基于Jiang, Hao & Bouabdallah, Ahmed (2017)的轻量级策略语言和评估框架。

策略结构由策略集策略规则组成。一个策略集是一组策略,它又包含一组规则。因为并非所有策略都与给定的请求相关,所以每个元素都包含一个目标的概念。它通过使用布尔表达式对属性设置约束来确定策略是否适用于请求。

如果访问请求满足目标,则策略是适用的。如果是这样,则评估其子项,并使用组合算法组合这些子项返回的结果。否则,策略将被跳过,而不会进一步检查其子项,并返回一个不适用决定

规则是能够生成最终决定的基本单元。一个规则的条件是一个更复杂的布尔表达式,它比其目标中指定的谓词进一步细化适用性,并且是可选的。如果请求满足规则的目标条件,则该规则适用于请求,并返回其效果作为其决定。否则,返回不适用

每个规则策略策略集都有一个唯一的标识符和义务,这些义务表示在授予或拒绝访问请求后要执行的操作。

属性

请求通常包含以下属性

要定义自己的属性,您必须从相应的类中派生它们

namespace App\Security\AccessControl\Attribute;

use TYPO3\AccessControl\Attribute\PrincipalAttribute;

class RoleAttribute extends PrincipalAttribute
{
  public function __construct(string $identifier)
  {
    parent::__construct($identifier);
  }
}

表达式

表达式用于决定策略是否适用于请求。因此,必须实现一个所谓的表达式解析器。例如,通过使用表达式语言组件

namespace App\Security\AccessControl\Expression;

use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use TYPO3\AccessControl\Expression\ResolverInterface;

class ExpressionLanguageResolver implements ResolverInterface
{
  private $expressionLanguage;

  public function __construct()
  {
    $this->expressionLanguage = new ExpressionLanguage();
    // register a custom function `hasAuthority`
    $this->expressionLanguage->register(
      'hasAuthority', function () {
        // not implemented, we only use the evaluator
      },
      function ($variables, ...$arguments) {
        if (count($arguments) == 1) {
          // checks if the subject has the given principal
          return isset($variables['subject']->principals[$arguments[0]]);
        }
        return false;
      }
    );
  }

  public function validate(string $expression): void
  {
    // only allow the attributes `subject`, `resource` and `action`
    $this->expressionLanguage->parse($expression, ['subject', 'resource', 'action']);
  }

  public function evaluate(string $expression, array $attributes): bool
  {
    return $this->expressionLanguage->evaluate($expression, $attributes);
  }
}

策略

策略可以声明性地定义。例如,使用YAML和策略工厂

---
description: 'Root policy set.'
algorithm: highestPriority
policies:
  Admin:
    target: 'hasAuthority("typo3:security:principal:admin")'
    description: 'Administrator policy'
    priority: 100
    rules:
      -
        effect: permit
  Default:
    description: 'Deny everything per default.'
    rules:
      -
        obligation:
          deny:
            Feedback: ['Access denied.']
use App\Security\AccessControl\Expression\ExpressionLanguageResolver;
use Symfony\Component\Yaml\Parser;
use TYPO3\AccessControl\Policy\PolicyFactory;

$resolver = new ExpressionLanguageResolver();
$factory = new PolicyFactory();
$parser = new Parser();

$policy = $factory->build(
  $parser->parseFile('/path/to/policies.yaml'),
  $resolver
);

策略集是一组策略集策略。它具有以下配置字段

具有与策略集类似的配置字段,策略是一组规则

策略集策略不同,规则不包含任何叶节点

策略可能会冲突,并为同一请求产生不同的决定。为此,提供了四种类型的组合算法。每种算法代表将多个本地决定组合成一个单一的全局决定的不同方式

请注意,对于所有这些组合算法,如果没有子项适用,则返回不适用

策略语言的架构也有正式描述,可以在这里找到。

授权

执行访问请求时,必须使用策略决策点。它评估所有策略,并返回一个决策,可以是允许拒绝不适用

use App\Security\AccessControl\Attribute\ActionAttribute;
use App\Security\AccessControl\Attribute\ResourceAttribute;
use Symfony\Component\EventDispatcher\EventDispatcher;
use TYPO3\AccessControl\Policy\PolicyDecisionPoint;
use TYPO3\AccessControl\Policy\PolicyInformationPoint;

$dispatcher = new EventDispatcher();

// creeates an policy information point
$policyInformationPoint = new PolicyInformationPoint(
  $dispatcher
);

// creates a policy decision point
$policyDecisionPoint = new PolicyDecisionPoint(
  $dispatcher,
  $policy,
  $policyInformationPoint
);

// perform an authorization request
$policyDecision = $policyDecisionPoint->authorize(
  [
    // concrete resource to access
    'resource' => new ResourceAttribute('identifier'),
    // concrete action on resource
    'action' => new ActionAttribute()
  ]
);

if (!$policyDecision->isApplicable()) {
  // access request is not applicable
}

// process determining policy rule
$determinigRule = $policyDecision->getRule();

foreach ($policyDecision->getObligations() as $obligation) {
  // process obligations
}

if ($policyDecision->getValue() === PolicyDecision::PERMIT)
  // access is granted
}

// access is denied otherwise

要接收在拒绝或授予访问请求后应执行的所有操作,必须使用事件\TYPO3\AccessControl\Event\PolicyDecisionEvent

namespace App\Security\AccessControl\EventListener;

use TYPO3\AccessControl\Event\PolicyDecisionEvent;

class PolicyDecisionListener
{
    public function __invoke(PolicyDecisionEvent $event)
    {
        // ...
    }
}

在访问请求之前为属性提供额外数据时,可以使用事件\TYPO3\AccessControl\Event\AttributeRetrievalEvent

namespace App\Security\AccessControl\EventListener;

use TYPO3\AccessControl\Event\AttributeRetrievalEvent;

class AttributeRetrievalListener
{
    public function __invoke(AttributeRetrievalEvent $event)
    {
        // ...
    }
}

为主体属性提供主体时,必须使用单独的事件\TYPO3\AccessControl\Event\SubjectRetrievalEvent

namespace App\Security\AccessControl\EventListener;

use TYPO3\AccessControl\Attribute\PrincipalAttribute;
use TYPO3\AccessControl\Event\SubjectRetrievalEvent;

class SubjectRetrievalListener
{
    public function __invoke(SubjectRetrievalEvent $event)
    {
        // Adds administrator principal
        $event->addPrincipal(new PrincipalAttribute('administrator'));
    }
}

设计原则

尽可能将授权逻辑作为策略的一部分。这样,它就可以进行审计和更改。由于性能或复杂性的原因,可能不可能这样做。那么,如果可能的话,建议扩展表达式语言以自定义函数。

开发

本扩展的开发是作为TYPO3 持久性倡议的一部分进行的。