spareparts/overseer

Attribute-based authorization manager.

v0.1.1 2019-09-17 12:22 UTC

This package is auto-updated.

Last update: 2024-04-18 17:34:28 UTC


README

Action-based authorization manager

Quick disclaimer: This is pretty much a work in progress. At this point this is more of a "proof-of-concept" than working code. Though the logic is sound and I fully intend to finish this into an awesome 1.0 release.

Build Status Scrutinizer Code Quality Build Status Code Coverage

What is this and why should I care?

Overseer is an "action-based" auth manager, meaning it is based on authorizing possible "actions" (such as read, edit, delete, etc.) with given "subject" (such as Article, Product, Category etc.).

Overseer focuses on decoupling auth logic from the rest of the application. When solving problems as "user that is the owner of this product can edit it" other auth managers tend to wire the logic directly into the said product class or pile all possible actions (read, write, delete, ...) into one big method. Either way it breaks S of the SOLID principles (single responsibility principle) and that's where Overseer jumps in.

Basic building stones of Overseer are "voting assemblies", consisting of "voters". Each combination of action and subject can have (doesn't have to, though) its own voting assembly, thus separating concerns and responsibilities involved.

Installation

Composer

This is how we do it, boys.

composer require spareparts/overseer

Basic usage

Let's imagine we have an article site, and we want to make sure the admin can read the article always, while its author only unless it's not banned.

This is how we create the voting assembly for this specific subject and action. It contains four voters,

$assembly = new VotingAssembly(
	$subjectName = 'article',
	$actionName = 'read',
	$strategy = StrategyEnum::FIRST_VOTE_DECIDES(),
	$voters = [
		new RoleVoter(VotingDecisionEnum::ALLOWED(), 'admin'),
		new ClosureVoter(function (DummyArticle $article, IdentityContext $context) {
			// allow the owner to edit
			if ($subject->ownerId === $context->getId()) {
				return new SingleVoterResult(VotingDecisionEnum::ALLOWED());
			}
			return null;
		}),
		new ClosureVoter(function (DummyArticle $article) {
			// deny access if the article is banned
			if ($subject->isBanned()) {
				return new SingleVoterResult(VotingDecisionEnum::ALLOWED());
			}
			return null;
		}),
		new RoleVoter(VotingDecisionEnum::ALLOWED(), 'user'),
	]
);

$authorizationManager = new GenericVotingManager([
	// our article edit assembly
	$assembly,
	// other assemblies...
	// ...
]);

Now let's use it

$context = new IdentityContext($userId, $userRoles);
$authorized = $authorizationManager->vote('edit', $article, $context);
if ($authorized->getDecision() === VotingDecisionEnum::ALLOWED()) {
	// we can edit!
}