lexus27/php-abac

基于属性的访问控制(ABAC)库

dev-master 2019-10-30 00:42 UTC

This package is not auto-updated.

Last update: 2024-09-15 01:35:24 UTC


README

注意!该组件目前不适合直接使用,由于存在以下问题 Issues,如果您愿意,可以为此组件的开发做出贡献,最终有望与其他框架和库集成。

定义

  • 上下文 - 在计算访问权限时信息来源。 (什么?对象,谁?用户,为什么?所采取的操作,哪里?环境数据)
  • 可计算 - 可以通过上下文计算的对象,可以获得计算结果,主观上指的是规则、策略或策略组中的一条。
  • 组合器 - 遍历多个可计算对象的方法,将子可计算对象的解决方案逐一组合,从而给出容器最终解决方案。
  • 聚合器 - 包含多个可计算对象的容器,使用组合器进行计算。
  • 目标 - 在聚合之前的一个抽象屏障,是条件的提供者。
  • 规则 - 具体计算解决方案的对象,也是单个条件的提供者。
  • 条件 - 给定两个值的比较,每个操作数的值可以是静态的或指向上下文的引用。
  • 策略 - 规则的聚合器,检查目标是否适用于上下文,然后执行进一步的聚合。
  • 策略组 - 策略的聚合器,检查目标是否适用于上下文,然后执行进一步的聚合。

可计算返回的状态

  • NOT_APPLICABLE (不适用) - 目标或规则的条件的上下文不匹配。
  • INDETERMINATE (未确定) - 错误阻止了决策的发现
  • PERMIT (允许) TRUE - 允许
  • DENY (拒绝) FALSE - 拒绝

关于提供访问权限的最终决策

最终决策只能是 PERMITDENY

  • 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)
    }
  }
}