我很遺憾地說 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 維護。
如果您有任何疑問,請隨時在問題中提問。
請參閱貢獻文件以了解詳細資訊。
您可以在我們的變更日誌檔案中查看每個版本的變更清單。
麻省理工學院許可證。請參閱許可證文件以獲取更多資訊。