2017 © Pedro Peláez
 

library judge

Authorisation library

image

adamnicholson/judge

Authorisation library

  • Monday, February 12, 2018
  • by adamnicholson
  • Repository
  • 1 Watchers
  • 0 Stars
  • 731 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 0 Forks
  • 2 Open issues
  • 2 Versions
  • 35 % Grown

The README.md

Judge

Authorization package for PHP., (*1)

Getting Started

Roles & Identities

"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)

Role Contexts

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

Role Inheritence

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

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

Putting it all together

Role contexts, role parents, and identity parents all stack, working together to give a truly robust authorisation system., (*15)

In order of precidence:, (*16)

  1. Exact match of the identity-role-context combination passed
  2. Parent identities with the same role & context
  3. The identity + role passed without any context
  4. Parent identities with the same role without any context
  5. Parent roles with the original identity
  6. Parent roles with parent identities

Logic Flow

, (*17)

The Data Structure

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

Persistence

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, PostgreSQL
  • FlatbaseRepository : Store to a flat file database
  • ArrayRepository : Store data in an array for the duration of this request

If you do not pass a Repository to Judge's constructor, then ArrayRepository will be used by default., (*24)

PDORepository

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);

ArrayRepository

Store data in an in-memory array for the duration of this process., (*27)

$judge = new Judge\Judge(new ArrayRepository);

FlatbaseRepository

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);

The Versions

12/02 2018

dev-master

9999999-dev

Authorisation library

  Sources   Download

MIT

The Development Requires

by Adam Nicholson

23/05 2017

0.1

0.1.0.0

Authorisation library

  Sources   Download

MIT

The Development Requires

by Adam Nicholson