dev-master
9999999-devAuthorisation library
MIT
The Development Requires
by Adam Nicholson
0.1
0.1.0.0Authorisation library
MIT
The Development Requires
by Adam Nicholson
Wallogit.com
2017 © Pedro Peláez
Authorisation library
Authorization package for PHP., (*1)
"Roles" are things that users of your application can do. These are represented simply as text strings, and are usually things like "EDIT_ORDERS", "VIEW_LOGS", etc., (*2)
"Identities" represent the users of your application. Like roles, they are represented as a string. An identity could be some account specific key like an email address or user ID, or it could be something completely different like "console" or "API", or even "anonymous"., (*3)
Keeping this mental separation between accounts and identities is key - Judge doesn't have any knowledge of, or care about, accounts or users., (*4)
To get started with some basic role-based auth, there is no setup required. Just instantiate Judge, and allow an an identity access to a role:, (*5)
$judge = new Judge();
// Grant access to edit orders
$judge->allow('adam@example.com', 'EDIT_ORDERS');
$judge->check('adam@example.com', 'EDIT_ORDERS'); // true
// Revoke access to edit orders
$judge->deny('adam@example.com', 'EDIT_ORDERS');
$judge->check('adam@example.com', 'EDIT_ORDERS'); // false
Simple enough? Good, now let's go a little deeper., (*6)
Simple role-based permissions suit a lot of use cases, but often you hit their limits quickly when an application starts to grow. Say you have a role for "EDIT_ORDERS" which applies to all orders, but you need a single identity to have access to only edit 1 specific order. How would you do this?, (*7)
In Judge, the way you would achieve this is by using role "contexts". A role context is a third parameter you can pass to allow(), deny() and check() to add more specificity to your rule. In practice, a context is usually used for unique identifiers such as an order ID:, (*8)
$judge->allow('adam@example.com', 'EDIT_ORDERS', '5');
$judge->check('adam@example.com', 'EDIT_ORDERS', '5'); // true
The benefit of role contexts is due to their ability to inherit rules. If an rule has not been explicity granted/revoked for the exact role+context combination, then Judge will fallback to check if the identity has been granted/revoked for that role without the context:, (*9)
$judge = new Judge();
// Grant access to edit all orders
$judge->allow('adam@example.com', 'EDIT_ORDERS');
// Override the above, revoking access specifically to order "10"
$judge->deny('adam@example.com', 'EDIT_ORDERS', '10');
$judge->check('adam@example.com', 'EDIT_ORDERS'); // true
$judge->check('adam@example.com', 'EDIT_ORDERS', '5'); // true
$judge->check('adam@example.com', 'EDIT_ORDERS', '10'); // false
Roles & contexts together can solve many auth requirements, but larger systems may need more complex inheritance rules. Here is where we introduce role "parents"., (*10)
Configuring a role parent looks like this:, (*11)
$judge->getRepository()->addRole('{ROLE}', '{PARENT_ROLE}');
If you call check() but the identity has not been explicitly granted or revoked access to the role, but that role has a defined parent, then we fallback to check if a rule exists for the parent:, (*12)
$judge = new Judge();
// Setup the "EDIT_ORDERS" role with the parent "ORDERS"
$judge->getRepository()->addRole('EDIT_ORDERS', 'ORDERS');
// Grant access to ORDERS and all child roles
$judge->allow('adam@example.com', 'ORDERS');
$judge->check('adam@example', 'EDIT_ORDERS'); // true
You can have as many levels in your role parent hierarchy as you like:, (*13)
$judge = new Judge();
$repo = $judge->getRepository();
$repo->addRole('DELETE_ORDERS', 'CHANGE_ORDERS');
$repo->addRole('CHANGE_ORDERS', 'VIEW_ORDERS');
$repo->addRole('VIEW_ORDERS', 'ORDERS');
// Grant access to ORDERS and all children
$judge->allow('adam@example.com', 'ORDERS');
$judge->check('adam@example', 'EDIT_ORDERS'); // true
// Override the above for access to CHANGE_ORDERS and its children recursively
$judge->deny('adam@example.com', 'CHANGE_ORDERS');
$judge->check('adam@example', 'ORDERS'); // true
$judge->check('adam@example', 'VIEW_ORDERS'); // true
$judge->check('adam@example', 'CHANGE_ORDERS'); // false
$judge->check('adam@example', 'DELETE_ORDERS'); // false
Identity inheritence works just like role inheritence:, (*14)
$judge = new Judge();
$repo = $judge->getRepository();
$repo->addIdentity('adam', 'customer_service');
$judge->allow('customer_service', 'EDIT_ORDERS');
$judge->check('adam', 'EDIT_ORDERS'); // true
Role contexts, role parents, and identity parents all stack, working together to give a truly robust authorisation system., (*15)
In order of precidence:, (*16)
, (*17)
Roles:, (*18)
| Role | ParentRole |
|---|---|
| ORDERS_EDIT | ORDERS_VIEW |
| ORDERS_VIEW | ORDERS |
| ORDERS | null |
Identities:, (*19)
| Identity | ParentIdentity |
|---|---|
| adam | customer_service |
| paul | customer_service |
| customer_service | null |
Rules:, (*20)
| Identity | Role | Context | State |
|---|---|---|---|
| adam | ORDERS_EDIT | 5 | GRANTED |
| customer_service | ORDERS | null |
GRANTED |
| paul | ORDERS_VIEW | 5 | REVOKED |
The main problem with all of the examples so far is that roles & identities are not persisted across requests - meaning you'd need to reconfigure Judge to understand your identity/role hierarchies, and allow/deny all the relevant permissions, on every request., (*21)
These rules/roles/identities can be persisted between requests by using a Judge\Repository\Repository, which can be passed to Judge::__construct() as the first argument., (*22)
Judge ships with a number of Judge\Repository\Repository implementations to use out of the box:, (*23)
PDORepository : Store data to a PDO compatible DB, like MySQL, SQLite, PostgreSQLFlatbaseRepository : Store to a flat file database
ArrayRepository : Store data in an array for the duration of this requestIf you do not pass a
Repositoryto Judge's constructor, thenArrayRepositorywill be used by default., (*24)
Store data to a PDO compatible DB, like MySQL, SQLite, PostgreSQL, (*25)
$pdo = new PDO('mysql:host=db;dbname=site', 'root');
$repo = new Judge\Repository\PDORepository($pdo);
$judge = new Judge\Judge($repo);
You may wish to use the PDORepository in conjunction with the LazyRepositoryWrapper, so that you do not have to instantiate a PDO connection until it is actually requested., (*26)
$repo = new Judge\Repository\LazyRepositoryWrapper(function () {
$pdo = new PDO('mysql:host=db;dbname=site', 'root');
return new Judge\Repository\PDORepository($pdo);
});
$judge = new Judge\Judge($repo);
Store data in an in-memory array for the duration of this process., (*27)
$judge = new Judge\Judge(new ArrayRepository);
Store to a flat file database., (*28)
$storage = new Flatbase\Storage\Filesystem('/some/storage/path');
$flatbase = new Flatbase\Flatbase($storage);
$repo = new Judge\Repository\FlatbaseRepository($pdo);
$judge = new Judge\Judge($repo);
Authorisation library
MIT
Authorisation library
MIT