lexus27 / php-abac
基于属性的访问控制(ABAC)库
This package is not auto-updated.
Last update: 2024-09-15 01:35:24 UTC
README
注意!该组件目前不适合直接使用,由于存在以下问题 Issues,如果您愿意,可以为此组件的开发做出贡献,最终有望与其他框架和库集成。
定义
- 上下文 - 在计算访问权限时信息来源。 (什么?对象,谁?用户,为什么?所采取的操作,哪里?环境数据)
- 可计算 - 可以通过上下文计算的对象,可以获得计算结果,主观上指的是规则、策略或策略组中的一条。
- 组合器 - 遍历多个可计算对象的方法,将子可计算对象的解决方案逐一组合,从而给出容器最终解决方案。
- 聚合器 - 包含多个可计算对象的容器,使用组合器进行计算。
- 目标 - 在聚合之前的一个抽象屏障,是条件的提供者。
- 规则 - 具体计算解决方案的对象,也是单个条件的提供者。
- 条件 - 给定两个值的比较,每个操作数的值可以是静态的或指向上下文的引用。
- 策略 - 规则的聚合器,检查目标是否适用于上下文,然后执行进一步的聚合。
- 策略组 - 策略的聚合器,检查目标是否适用于上下文,然后执行进一步的聚合。
可计算返回的状态
- NOT_APPLICABLE (不适用) - 目标或规则的条件的上下文不匹配。
- INDETERMINATE (未确定) - 错误阻止了决策的发现
- PERMIT (允许) TRUE - 允许
- DENY (拒绝) FALSE - 拒绝
关于提供访问权限的最终决策
最终决策只能是 PERMIT 或 DENY
-
NOT_APPLICABLE - 这意味着没有任何可计算与上下文匹配,因此最终值将根据基础管理器(允许(Permit)或拒绝(Deny))分配
-
INDETERMINATE - 倾向于拒绝,因为无法在计算或完成阶段结束时计算出所有内容。
-
NOT_APPLICABLE - 可能从 NULL 值解释,因为 NOT_APPLICABLE 可以由容器(最终)重定义
-
INDETERMINATE - 可以通过异常实现
基本定义示例
<?php
use Jungle\User\AccessControl\Adapter\PolicyAdater\Memory as MemoryPolicyAdapter;
use Jungle\User\AccessControl\Context;
use Jungle\User\AccessControl\Manager;
use Jungle\User\AccessControl\Matchable;
include './loader.php';
$manager = new Manager();
$resolver = new Matchable\Resolver\ConditionResolver();
$manager->setConditionResolver($resolver);
// .........$combiner_set declaration
$manager->setCombiner('delegate', new Matchable\Combiner($combiner_set['delegate']));
$manager->setCombiner('delegate_same', new Matchable\Combiner($combiner_set['delegate_same']));
$manager->setCombiner('same', new Matchable\Combiner($combiner_set['same']));
$manager->setCombiner('same_only', new Matchable\Combiner($combiner_set['same_only']));
$manager->setCombiner('same_by_same', new Matchable\Combiner($combiner_set['same_by_same']));
$manager->setCombiner('permit_by_permit', new Matchable\Combiner($combiner_set['permit_by_permit']));
$manager->setCombiner('deny_by_deny', new Matchable\Combiner($combiner_set['deny_by_deny']));
$manager->setCombiner('dispute', new Matchable\Combiner($combiner_set['dispute']));
$manager->setCombiner('dispute_all', new Matchable\Combiner($combiner_set['dispute_all']));
$manager->setDefaultCombiner('dispute_all');
$manager->setMainCombiner('dispute_all');
$manager->setDefaultEffect(Matchable::PERMIT);
$manager->setSameEffect(Matchable::DENY);
// .........$aggregator main declaration
// ............$context main declaration
$manager->setAggregator($aggregator);
$manager->setContext($context);
访问策略集示例
$aggregator = new MemoryPolicyAdapter(null);
$aggregator->build([
'policies' => [[
'effect' => true,
'name' => 'Работа с Записками',
'target' => [
'all_of' => '[object::class] = Note',
],
'combiner' => 'delegate',
'rules' => [[
'condition' => '[user.group] = Administrators'
],[
'condition' => '[object.owner_id] = [user.id]'
],[
'condition' => '[object.public] = true'
]]
],[
'effect' => true,
'name' => 'Администраторы',
'combiner' => 'dispute',
'obligation' => function(){
echo 'Вседозволенные Администраторы';
},
'target' => [
'all_of' => [
'[user.group] = Administrators',
]
],
],[
'effect' => false,
'name' => 'Анонимы',
'combiner' => 'dispute',
'target' => [
'all_of' => [
'[object::class] = Document',
'[user.group] = Anonymous',
]
],
'obligation' => function(){
echo 'Мы запрещаем работать здесь анонимам';
},
'policies' => [[
'effect' => true,
'rules' => [[
'condition' => '[action.name] = Read'
]],
'obligation' => function(){
echo 'Просматривать можно';
},
],[
'effect' => true,
'rules' => [[
'condition' => '[scope.time.week_day] in [TIME.WORK_DAYS]'
]],
'obligation' => function(){
echo 'Слава богу будние дни';
},
]],
],[
'effect' => false,
'name' => 'CurrentToken',
'obligation' => function(){
echo 'По токену Есть ограничения';
},
'combiner' => 'same_by_same',
'policies' => [[
'effect' => true,
'target' => [ 'all_of' => [ '[user.group] = Administrators', ], ],
'obligation' => function(){
echo 'Администраторам можно';
},
],[
'effect' => true,
'target' => [ 'all_of' => [ '[action.name] = Read', ], ],
'obligation' => function(){
echo 'Просмотр для токенов доступен';
},
],[
'effect' => true,
'combiner' => 'same_by_same',
'rules' => [[ 'condition' => '[scope.time.week_day] in [TIME.WORK_DAYS]' ]],
'obligation' => function(){
echo 'В рабочие дни токен работает';
},
]],
],[
'name' => 'Deny',
'effect' => false,
'target' => [
'all_of' => [
'[user.group] != Administrators'
]
],
'obligation' => function(){
echo 'Технические работы';
},
]]
]);
上下文
$context = new Context();
$context->setProperties([
'user' => [
'id' => 1,
'name' => 'John',
'login' => 'john.mail@site.com',
'group' => 'Anonymous',
'email' => 'john.mail@site.com',
'photo' => '/user/123223/avatar.jpg'
],
'route' => [
'module' => null,
'controller' => null,
'action' => null,
'params' => null
],
'client' => [
'request' => & $_REQUEST,
'server' => & $_SERVER,
'cookies' => & $_COOKIE,
'session' => & $_SESSION
]
], true);
组合器和行为
需要仔细选择组合器,以适应授权操作的任务。
输入
聚合器使用组合器来计算自己的解决方案。
聚合器基于效果。
聚合器可能是不可用的,这是通过组合子可计算来计算的。
预期的组合原则
- А.
delegate
将第一个可用的子解决方案委派为聚合器的计算结果 - Б.
delegate_same
类似 [A],但聚合器始终是可用的,在子不可用的情况下,其效果将优先。 - В.
same_only
如果至少有一个子等于聚合器的效果,并且没有相反的效果,则聚合器的解决方案 - Г.
same_soft
类似 [B],但允许不可用
组合原则的结构
组合原则建立在逐步搜索上,每次搜索新的可计算对象,并根据其解决方案进行反应,以提前或最终给出最终解决方案
反应由以下事件定义
"不适用"时的行为
"未定义"时的行为
"允许"时的行为
"禁止"时的行为
数组中的示例
占位符
{same}
- 替换策略容器的效果{!same}
- 如果不等于策略容器的效果{current}
- 当前规则的效果
配置数组的键描述
-
default
- 默认效果,在策略容器中遍历规则之前设置 -
empty
- 如果策略容器为空且不包含规则 -
applicable
- 在 applicable 元素上的行为check
- IF 检查check
- 可以用作 switch case case case- 在这种情况下,值应该是一个索引数组,其中每个元素都将作为规则(类似于
applicable
)
- 在这种情况下,值应该是一个索引数组,其中每个元素都将作为规则(类似于
early
- 结束组合而不继续遍历effect
- 设置效果,例如{current}
-
not_applicable
- 在 not_applicable 上的行为(类似于applicable
) -
deny
- 在 deny 元素上的行为(类似于applicable
) -
permit
- 在 permit 元素上的行为(类似于applicable
)
示例
$combiner_set = [
'delegate' => [
'default' => 'not_applicable',
'applicable' => [
'early' => true,
'effect' => '{current}'
],
],
'delegate_same' => [
'default' => '{same}',
'applicable' => [
'early' => true,
'effect' => '{current}'
],
],
'dispute' => [
'default' => '{same}',
'empty' => '{same}',
'applicable' => [
'check' => '{!same}',
'early' => true,
'effect' => '{current}'
],
],
'dispute_all' => [
'default' => '{same}',
'empty' => '{same}',
'applicable' => [
'check' => [[
'check' => '{!same}',
'effect' => '{current}'
],[
'check' => '{same}',
'early' => true,
'effect' => '{current}'
]]
],
],
'permit_by_permit' => [
'return_only' => 'permit',
'default' => 'not_applicable',
'empty' => 'not_applicable',
'deny' => [
'early' => true,
'effect' => 'not_applicable'
],
'permit' => [
'effect' => '{current}'
],
],
'deny_by_deny' => [
'return_only' => 'deny',
'default' => 'not_applicable',
'empty' => 'not_applicable',
'deny' => [
'effect' => '{current}'
],
'permit' => [
'early' => true,
'effect' => 'not_applicable'
],
],
'same_by_same' => [
'default' => 'not_applicable',
'empty' => 'not_applicable',
'applicable' => [
'check' => [[
'check' => '{!same}',
'early' => true,
'effect' => 'not_applicable'
],[
'check' => '{same}',
'effect' => '{same}'
]]
],
],
'same' => [
'default' => '{same}',
'empty' => '{same}',
'applicable' => [
'check' => '{!same}',
'early' => true,
'effect' => '{current}'
],
],
'same_only' => [
'default'=> '{same}',
'not_applicable' => [
'effect' => '{current}'
],
'applicable' => [
'check' => '{!same}',
'early' => true,
'effect' => '{current}'
]
],
];
原则被解释、重新思考并按以下文章实施
ORM 和 ABAC
ABAC 提供了一种在直接从数据库中检索之前检查对象访问权限的机会,通过预先准备 WHERE 条件,这些条件必须与 ABAC 管理员的所需决策相匹配。因此,选择结果将是满足所需效果的实体。所有这些通过从条件中收集谓词来完成。
示例
$object = new Context\ObjectAccessor([
'class' => 'Note',
'phantom' => [
'owner_id' => 2,
],
'predicate_effect' = true,
]);
$result = $manager->enforce('Read',$object, true);
if($result->isAllowed() === $object->getPredicateEffect()){
echo '<p><pre>';
var_dump($object->getSelectConditions());
echo '</pre></p>';
}else{
echo '<p><pre>';
var_dump($result->getEffect());
echo '</pre></p>';
}
结果将包含可添加到后续查询准则中的 WHERE 兼容表达式
array(3) {
[0]=>
array(3) {
[0]=>
string(6) "public"
[1]=>
string(1) "="
[2]=>
bool(true)
}
[1]=>
string(3) "AND"
[2]=>
array(1) {
[0]=>
array(3) {
[0]=>
string(8) "owner_id"
[1]=>
string(1) "="
[2]=>
int(2)
}
}
}