我很遗憾地说 Lock 目前没有得到维护。目前我无法提供支持或接受新的贡献。其他优先事项使我无法将应有的工作投入到 Lock 中。最终我会尝试再次开始工作,但不幸的是我无法说出具体时间。我感谢所有贡献者和用户。
-- 干燥
Lock 是一个灵活的、基于驱动程序的Acl包,适用于PHP 5.4+ 。
由 Dries Vints 创建。感谢 BeatSwitch,这一切才得以实现。灵感来自 Matthew Machuga 的权威。杰里·洛的标志。
Lock
:主题的 acl 实例。该软件包当前附带CallerLock
和RoleLock
Caller
:可以有权执行某些操作的身份对象Driver
:权限的存储系统,可以是静态的,也可以是持久的Permission
:权限包含一个操作和一个可选(唯一)资源。可以是Restriction
,也可以是Privilege
Restriction
:限制拒绝您执行操作(在可选资源上)Privilege
:特权允许您执行操作(在可选资源上)Action
:操作是允许或拒绝您执行的操作Resource
:资源可以是您可以对其执行一项或多项操作的对象。它可以通过其唯一标识符来定位某种类型的资源或特定资源Role
:一个角色还可以拥有多个权限。一个调用者可以有多个角色。角色可以继承其他角色的权限Lock 与其他 acl 包的不同之处在于它试图提供最灵活的方式来处理多个权限调用者和存储权限。
通过使用 Lock 的Caller
合约,您可以设置多个身份的权限。
Driver
合约允许以一种简单的方式将权限存储到持久或静态存储系统。此包附带一个默认的静态ArrayDriver
。查看下面的列表,了解更多已为您准备的驱动程序。或者通过实施Driver
合约来构建您自己的驱动程序合约。
您可以通过手动传递资源的类型和(可选)标识符来设置和检查资源的权限,或者您可以将Resource
契约实现到您的对象上,以便您可以更轻松地传递它们以进行锁定。
Manager
提供了一种简单的方法来实例化新的Lock
实例、设置操作别名或注册角色。
如果您需要特定于框架的实现,请选择下面已准备好的驱动程序之一。
通过 Composer 安装此包。
$ composer require beatswitch/lock
每个有权执行某些操作的身份都必须实现BeatSwitchLockCallersCaller
合约。 Caller
合约通过要求调用者返回其类型和唯一标识符来识别调用者。让我们看下面的一个例子。
<?php
use BeatSwitch Lock Callers Caller ;
class User implements Caller
{
public function getCallerType ()
{
return ' users ' ;
}
public function getCallerId ()
{
return $ this -> id ;
}
public function getCallerRoles ()
{
return [ ' editor ' , ' publisher ' ];
}
}
通过添加getCallerType
函数,我们可以通过唯一的类型来识别一组调用者。如果我们在某个时候想要对另一组调用者设置权限,我们可以轻松地在另一个对象上实现合约。
<?php
use BeatSwitch Lock Callers Caller ;
class Organization implements Caller
{
public function getCallerType ()
{
return ' organizations ' ;
}
public function getCallerId ()
{
return $ this -> id ;
}
public function getCallerRoles ()
{
return [ ' enterprise ' ];
}
}
因此,我们可以通过驱动程序轻松检索特定调用者类型的权限。
如果您想预先配置所有权限,您可以使用软件包附带的静态ArrayDriver
。这允许您在应用程序运行之前设置调用者的权限列表。
use BeatSwitch Lock Drivers ArrayDriver ;
use BeatSwitch Lock Lock ;
use BeatSwitch Lock Manager ;
// Create a new Manager instance.
$ manager = new Manager ( new ArrayDriver ());
// Instantiate a new Lock instance for an object which implements the Caller contract.
$ lock = $ manager -> caller ( $ caller );
// Set some permissions.
$ lock -> allow ( ' manage_settings ' );
$ lock -> allow ( ' create ' , ' events ' );
// Use the Lock instance to validate permissions on the given caller.
$ lock -> can ( ' manage_settings ' ); // true: can manage settings
$ lock -> can ( ' create ' , ' events ' ); // true: can create events
$ lock -> cannot ( ' update ' , ' events ' ); // true: cannot update events
$ lock -> can ( ' delete ' , ' events ' ); // false: cannot delete events
使用持久驱动程序允许您将权限存储到持久存储层并在运行时调整它们。例如,如果您实现 Laravel 5 驱动程序,它将使用 Laravel 的数据库组件将权限存储到数据库中。通过创建您自己的 UI,您可以轻松附加此包中的 acl 功能,以创建例如不同用户具有不同权限的用户管理系统。
让我们看一下一个非常基本的用户管理控制器,看看它是如何完成的。我们假设我们使用 Laravel DB 驱动程序获得了一个引导锁管理器实例。
<?php
use BeatSwitch Lock Manager ;
class UserManagementController extends BaseController
{
protected $ lockManager ;
public function __construct ( Manager $ lockManager )
{
$ this -> lockManager = $ lockManager ;
}
public function togglePermission ()
{
$ userId = Input:: get ( ' user ' );
$ action = Input:: get ( ' action ' );
$ resource = Input:: get ( ' resource ' );
$ user = User:: find ( $ userId );
$ this -> lockManager -> caller ( $ user )-> toggle ( $ action , $ resource );
return Redirect:: route ( ' user_management ' );
}
}
每次使用togglePermission
方法时,都会切换用户对给定操作和资源类型的权限。
您可以allow
或deny
呼叫者执行某些操作。以下是设置和检查权限的几种方法。
允许调用者创建一切。
$ lock -> allow ( ' create ' );
$ lock -> can ( ' create ' ); // true
允许呼叫者仅创建帖子。
$ lock -> allow ( ' create ' , ' posts ' );
$ lock -> can ( ' create ' ); // false
$ lock -> can ( ' create ' , ' posts ' ); // true
允许呼叫者仅编辑 ID 为 5 的特定帖子。
$ lock -> allow ( ' edit ' , ' posts ' , 5 );
$ lock -> can ( ' edit ' ); // false
$ lock -> can ( ' edit ' , ' posts ' ); // false
$ lock -> can ( ' edit ' , ' posts ' , 5 ); // true
允许呼叫者编辑所有帖子,但拒绝编辑 id 为 5 的帖子。
$ lock -> allow ( ' edit ' , ' posts ' );
$ lock -> deny ( ' edit ' , ' posts ' , 5 );
$ lock -> can ( ' edit ' , ' posts ' ); // true
$ lock -> can ( ' edit ' , ' posts ' , 5 ); // false
切换权限的值。
$ lock -> allow ( ' create ' );
$ lock -> can ( ' create ' ); // true
$ lock -> toggle ( ' create ' );
$ lock -> can ( ' create ' ); // false
您可以一次允许或拒绝多个操作,也可以一次检查多个操作。
$ lock -> allow ([ ' create ' , ' edit ' ], ' posts ' );
$ lock -> can ( ' create ' , ' posts ' ); // true
$ lock -> can ([ ' create ' , ' edit ' ], ' posts ' ); // true
$ lock -> can ([ ' create ' , ' delete ' ], ' posts ' ); // false
您可以轻松清除一组特定操作和资源组合的权限。
$ lock -> allow ([ ' create ' , ' edit ' ], ' posts ' );
$ lock -> clear ( ' edit ' , ' posts ' );
$ lock -> can ( ' edit ' , ' posts ' ); // false
$ lock -> can ( ' create ' , ' posts ' ); // true
您也可以只清除锁定实例的所有权限。
$ lock -> allow ( ' manage-posts ' );
$ lock -> allow ([ ' create ' , ' edit ' ], ' users ' );
$ lock -> clear ();
$ lock -> can ( ' manage-posts ' ); // false
$ lock -> can ( ' create ' , ' users ' ); // false
要将多个操作分组并一次设置所有操作,您可能需要设置操作别名。
$ lock -> alias ( ' manage ' , [ ' create ' , ' read ' , ' delete ' ]);
$ lock -> allow ( ' manage ' , ' posts ' );
$ lock -> can ( ' manage ' , ' posts ' ); // true
$ lock -> can ( ' create ' , ' posts ' ); // true
$ lock -> can ( ' delete ' , ' posts ' , 1 ); // true
$ lock -> can ( ' update ' , ' posts ' ); // false
您可以通过将all
通配符作为锁定实例上的操作传递来轻松设置具有所有权限的调用者。
$ lock -> allow ( ' all ' );
现在,每个“can”方法调用都会对该调用者验证为 true。
Lock 提供了一种使用角色的简单方法。您可以使用开箱即用的角色,但如果您想使用继承,则需要将角色注册到管理器实例。
$ manager -> setRole ( ' guest ' );
$ manager -> setRole ( ' user ' , ' guest ' ); // "user" will inherit all permissions from "guest"
或者一次注册多个角色。
$ manager -> setRole ([ ' editor ' , ' admin ' ], ' user ' ); // "editor" and "admin" will inherit all permissions from "user".
我们来设置一些权限,看看如何解决。
// Allow a guest to read everything.
$ manager -> role ( ' guest ' )-> allow ( ' guest ' , ' read ' );
// Allow a user to create posts.
$ manager -> role ( ' user ' )-> allow ( ' create ' , ' posts ' );
// Allow an editor and admin to publish posts.
$ manager -> role ( ' editor ' )-> allow ( ' publish ' , ' posts ' );
$ manager -> role ( ' admin ' )-> allow ( ' publish ' , ' posts ' );
// Allow an admin to delete posts.
$ manager -> role ( ' admin ' )-> allow ( ' delete ' , ' posts ' );
// Let's assume our caller has the role of "editor" and check some permissions.
$ lock = $ manager -> caller ( $ caller );
$ lock -> can ( ' read ' ); // true
$ lock -> can ( ' delete ' , ' posts ' ); // false
$ lock -> can ( ' publish ' ); // false: we can't publish everything, just posts.
$ lock -> can ([ ' create ' , ' publish ' ], ' posts ' ); // true
您需要注意的是,调用者级别的权限会取代角色级别的权限。让我们看看它是如何工作的。
我们的调用者将具有用户角色。
$ manager -> caller ( $ caller )-> allow ( ' create ' , ' posts ' );
// Notice that we don't need to set the role in the
// manager first if we don't care about inheritance.
$ manager -> role ( ' user ' )-> deny ( ' user ' , ' create ' , ' posts ' );
$ manager -> caller ( $ caller )-> can ( ' create ' , ' posts ' ); // true: the user has explicit permission to create posts.
条件实际上是断言,是您可以为权限设置的额外检查。您可以传递一个数组作为allow
和deny
的最后一个参数。所有条件都必须实现BeatSwitchLockPermissionsCondition
接口。
警告:请注意,当前条件仅适用于静态驱动程序。
让我们设置一个条件。
<?php
use BeatSwitch Lock Lock ;
use BeatSwitch Lock Permissions Condition ;
use BeatSwitch Lock Permissions Permission ;
use BeatSwitch Lock Resources Resource ;
use Illuminate Auth AuthManager ;
class LoggedInCondition implements Condition
{
/**
* The Laravel AuthManager instance
*
* @var IlluminateAuthAuthManager
*/
protected $ auth ;
/**
* @param IlluminateAuthAuthManager $auth
*/
public function __construct ( AuthManager $ auth )
{
$ this -> auth = $ auth ;
}
/**
* Assert if the condition is correct
*
* @param BeatSwitchLockLock $lock The current Lock instance that's being used
* @param BeatSwitchLockPermissionsPermission $permission The Permission that's being checked
* @param string $action The action passed to the can or cannot method
* @param BeatSwitchLockResourcesResource|null $resource The resource passed to the can or cannot method
* @return bool
*/
public function assert ( Lock $ lock , Permission $ permission , $ action , Resource $ resource = null )
{
// Condition will succeed if the user is logged in.
return $ this -> auth -> check ();
}
}
现在让我们看看在设置权限时这是如何工作的。
$ condition = App:: make ( ' LoggedInCondition ' );
$ lock -> allow ( ' create ' , ' posts ' , null , $ condition );
$ lock -> can ( ' create ' , ' posts ' ); // true if logged in, otherwise false.
您还可以传递多个条件。
$ lock -> allow ( ' create ' , ' posts ' , null , [ $ falseCondition , $ trueCondition ]);
$ lock -> can ( ' create ' , ' posts ' ); // false: there's at least one false condition
您可以传递任意数量的条件,但它们都需要成功才能获得许可。
如果您愿意,也可以使用回调。
$ lock -> allow ( ' create ' , ' posts ' , null , function ( $ lock , $ permission , $ action , $ resource = null ) {
return false ;
});
$ lock -> can ( ' create ' , ' posts ' ); // false because the callback returns false.
如果您想检索允许或拒绝执行特定操作的资源列表,您可以在Lock
实例上使用allowed
和denied
方法。
$ lock -> allow ( ' update ' , ' users ' , 1 );
$ lock -> allow ( ' update ' , ' users ' , 2 );
$ lock -> allow ( ' update ' , ' users ' , 3 );
$ lock -> deny ( ' update ' , ' users ' , 2 );
$ lock -> allowed ( ' update ' , ' users ' ); // Returns [1, 3];
$ lock -> denied ( ' update ' , ' users ' ); // Returns [2];
请记住,您只能从已设置权限的资源中检索 ID。未通过Lock注册的资源将不会被归还。
您可以通过实现BeatSwitchLockLockAware
特征轻松地将 acl 功能添加到您的调用者或角色。
<?php
use BeatSwitch Lock Callers Caller ;
use BeatSwitch Lock LockAware ;
class Organization implements Caller
{
use LockAware;
public function getCallerType ()
{
return ' organizations ' ;
}
public function getCallerId ()
{
return $ this -> id ;
}
public function getCallerRoles ()
{
return [ ' enterprise ' ];
}
}
现在我们需要设置它的锁实例。
$ caller -> setLock ( $ lock );
现在,您的调用者可以对其自身使用所有锁定方法。
$ caller -> can ( ' create ' , ' posts ' );
$ caller -> allow ( ' edit ' , ' pages ' );
如果您有一个实现LockAware
特征的调用者,但尚未引导调用者的锁实例,那么您可以使用管理器的makeCallerLockAware
方法轻松地使调用者锁定。
$ caller = $ manager -> makeCallerLockAware ( $ caller );
现在您的调用者将能够使用LockAware
方法。角色也有类似的方法。
$ role = $ manager -> makeRoleLockAware ( ' guest ' );
这将引导一个SimpleRole
对象,该对象已经带有LockAware
特征。
以下方法都可以在BeatSwitchLockLock
实例上调用。
检查当前调用者是否有权执行某些操作。
can(
string|array $action,
string|BeatSwitchLockResourcesResource $resource = null,
int $resourceId = null
)
检查当前调用者是否禁止执行某些操作。
cannot(
string|array $action,
string|BeatSwitchLockResourcesResource $resource = null,
int $resourceId = null
)
为调用者设置Privilege
以允许其执行某些操作。删除任何匹配限制。
allow(
string|array $action,
string|BeatSwitchLockResourcesResource $resource = null,
int $resourceId = null,
BeatSwitchLockPermissionsCondition[] $conditions = []
)
对调用者设置Restriction
权限以阻止其执行某些操作。删除任何匹配的权限。
deny(
string|array $action,
string|BeatSwitchLockResourcesResource $resource = null,
int $resourceId = null,
BeatSwitchLockPermissionsCondition[] $conditions = []
)
切换给定权限的值。
toggle(
string|array $action,
string|BeatSwitchLockResourcesResource $resource = null,
int $resourceId = null
)
返回允许主体执行给定操作的给定资源类型数组中的所有 id。
allowed(
string|array $action,
string|BeatSwitchLockResourcesResource $resourceType
)
返回给定资源类型数组中的所有 id,主题被拒绝对其执行给定操作。
denied(
string|array $action,
string|BeatSwitchLockResourcesResource $resourceType
)
以下方法都可以在BeatSwitchLockManager
实例上调用。
返回调用者的BeatSwitchLockLock
实例。
caller(
BeatSwitchLockCallersCaller $caller
)
返回角色的BeatSwitchLockLock
实例。
role(
BeatSwitchLockRolesRole $role
)
为一项或多项操作添加别名。
alias(
string $name,
string|array $actions
)
设置一个或多个角色以及一个可选角色以继承权限。
setRole(
string|array $name,
string $inherit = null
)
为实现LockAware
特征的调用者设置锁实例。返回设置了锁实例的调用者。
makeCallerLockAware(
BeatSwitchLockCallersCaller $caller
)
为实现LockAware
特征的角色设置锁实例。返回设置了锁实例的角色。
makeRoleLockAware(
BeatSwitchLockRolesRole|string $role
)
您可以通过实现BeatSwitchLockDriversDriver
合约轻松构建驱动程序。下面我们将演示如何使用 Laravel 的 Eloquent ORM 作为我们的存储机制来创建我们自己的持久驱动程序。
我们假设我们有一个CallerPermission
模型类,至少包含以下数据库列:
caller_type
(varchar,100)caller_id
(int, 11)type
(varchar,10)action
(varchar,100)resource_type
(varchar,100,可为空)resource_id
(int,11,可为空)我们有一个具有以下数据库列的RolePermission
模型:
role
(varchar,100)type
(varchar,10)action
(varchar,100)resource_type
(varchar,100,可为空)resource_id
(int,11,可为空)让我们看看下面驱动程序的完整实现。请注意,对于getCallerPermissions
方法,我们使用PermissionFactory
类来轻松映射数据并从中创建Permission
对象。 PermissionFactory
的createFromData
方法将接受数组和对象。
<?php
use BeatSwitch Lock Callers Caller ;
use BeatSwitch Lock Drivers Driver ;
use BeatSwitch Lock Permissions Permission ;
use BeatSwitch Lock Permissions PermissionFactory ;
use BeatSwitch Lock Roles Role ;
use CallerPermission ;
use RolePermission ;
class EloquentDriver implements Driver
{
/**
* Returns all the permissions for a caller
*
* @param BeatSwitchLockCallersCaller $caller
* @return BeatSwitchLockPermissionsPermission[]
*/
public function getCallerPermissions ( Caller $ caller )
{
$ permissions = CallerPermission:: where ( ' caller_type ' , $ caller -> getCallerType ())
-> where ( ' caller_id ' , $ caller -> getCallerId ())
-> get ();
return PermissionFactory:: createFromData ( $ permissions -> toArray ());
}
/**
* Stores a new permission into the driver for a caller
*
* @param BeatSwitchLockCallersCaller $caller
* @param BeatSwitchLockPermissionsPermission
* @return void
*/
public function storeCallerPermission ( Caller $ caller , Permission $ permission )
{
$ eloquentPermission = new CallerPermission ;
$ eloquentPermission -> caller_type = $ caller -> getCallerType ();
$ eloquentPermission -> caller_id = $ caller -> getCallerId ();
$ eloquentPermission -> type = $ permission -> getType ();
$ eloquentPermission -> action = $ permission -> getAction ();
$ eloquentPermission -> resource_type = $ permission -> getResourceType ();
$ eloquentPermission -> resource_id = $ permission -> getResourceId ();
$ eloquentPermission -> save ();
}
/**
* Removes a permission from the driver for a caller
*
* @param BeatSwitchLockCallersCaller $caller
* @param BeatSwitchLockPermissionsPermission
* @return void
*/
public function removeCallerPermission ( Caller $ caller , Permission $ permission )
{
CallerPermission:: where ( ' caller_type ' , $ caller -> getCallerType ())
-> where ( ' caller_id ' , $ caller -> getCallerId ())
-> where ( ' type ' , $ permission -> getType ())
-> where ( ' action ' , $ permission -> getAction ())
-> where ( ' resource_type ' , $ permission -> getResourceType ())
-> where ( ' resource_id ' , $ permission -> getResourceId ())
-> delete ();
}
/**
* Checks if a permission is stored for a user
*
* @param BeatSwitchLockCallersCaller $caller
* @param BeatSwitchLockPermissionsPermission
* @return bool
*/
public function hasCallerPermission ( Caller $ caller , Permission $ permission )
{
return ( bool ) CallerPermission:: where ( ' caller_type ' , $ caller -> getCallerType ())
-> where ( ' caller_id ' , $ caller -> getCallerId ())
-> where ( ' type ' , $ permission -> getType ())
-> where ( ' action ' , $ permission -> getAction ())
-> where ( ' resource_type ' , $ permission -> getResourceType ())
-> where ( ' resource_id ' , $ permission -> getResourceId ())
-> first ();
}
/**
* Returns all the permissions for a role
*
* @param BeatSwitchLockRolesRole $role
* @return BeatSwitchLockPermissionsPermission[]
*/
public function getRolePermissions ( Role $ role )
{
$ permissions = RolePermission:: where ( ' role ' , $ role -> getRoleName ())-> get ();
return PermissionFactory:: createFromData ( $ permissions -> toArray ());
}
/**
* Stores a new permission for a role
*
* @param BeatSwitchLockRolesRole $role
* @param BeatSwitchLockPermissionsPermission
* @return void
*/
public function storeRolePermission ( Role $ role , Permission $ permission )
{
$ eloquentPermission = new RolePermission ;
$ eloquentPermission -> role = $ role -> getRoleName ();
$ eloquentPermission -> type = $ permission -> getType ();
$ eloquentPermission -> action = $ permission -> getAction ();
$ eloquentPermission -> resource_type = $ permission -> getResourceType ();
$ eloquentPermission -> resource_id = $ permission -> getResourceId ();
$ eloquentPermission -> save ();
}
/**
* Removes a permission for a role
*
* @param BeatSwitchLockRolesRole $role
* @param BeatSwitchLockPermissionsPermission
* @return void
*/
public function removeRolePermission ( Role $ role , Permission $ permission )
{
RolePermission:: where ( ' role ' , $ role -> getRoleName ())
-> where ( ' type ' , $ permission -> getType ())
-> where ( ' action ' , $ permission -> getAction ())
-> where ( ' resource_type ' , $ permission -> getResourceType ())
-> where ( ' resource_id ' , $ permission -> getResourceId ())
-> delete ();
}
/**
* Checks if a permission is stored for a role
*
* @param BeatSwitchLockRolesRole $role
* @param BeatSwitchLockPermissionsPermission
* @return bool
*/
public function hasRolePermission ( Role $ role , Permission $ permission )
{
return ( bool ) RolePermission:: where ( ' role ' , $ role -> getRoleName ())
-> where ( ' type ' , $ permission -> getType ())
-> where ( ' action ' , $ permission -> getAction ())
-> where ( ' resource_type ' , $ permission -> getResourceType ())
-> where ( ' resource_id ' , $ permission -> getResourceId ())
-> first ();
}
}
请注意,当我们尝试存储权限时,我们不会检查权限是否已存在。您无需担心这一点,因为这一切都已在Lock
实例中为您完成。
现在我们有了一个支持存储调用者和角色权限的驱动程序。
您可以轻松确保驱动程序按预期工作。如果您正在构建持久驱动程序,则可以通过创建扩展PersistentDriverTestCase
类的 PHPUnit 测试来轻松测试它。
<?php
use BeatSwitch Lock Tests PersistentDriverTestCase ;
class EloquentDriverTest extends PersistentDriverTestCase
{
public function setUp ()
{
// Don't forget to reset your DB here.
// Bootstrap your driver.
$ this -> driver = new EloquentDriver ();
parent :: setUp ();
}
}
这就是您所需要的! PersistentDriverTestCase
包含确保驱动程序按预期工作所需的所有测试。因此,如果所有这些测试都通过,那么您的驱动程序设置正确。不需要模拟任何东西,这是一个纯粹的集成测试用例。当然,在上面这个具体示例中,要使 Eloquent 工作,您需要引导 Laravel。使用像 sqlite 这样的数据库将是测试驱动程序的最佳方法。
锁此时未维护。
该软件包目前由 Dries Vints 维护。
如果您有任何疑问,请随时在问题中提问。
请参阅贡献文件了解详细信息。
您可以在我们的变更日志文件中查看每个版本的变更列表。
麻省理工学院许可证。请参阅许可证文件以获取更多信息。