Skip to content

Commit

Permalink
Merge pull request #11 from dtkahl/1.0
Browse files Browse the repository at this point in the history
merge changes from 1.0 to master
  • Loading branch information
dantodev authored May 25, 2017
2 parents f372b90 + 5e57957 commit 82fbad4
Show file tree
Hide file tree
Showing 11 changed files with 561 additions and 113 deletions.
156 changes: 156 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,159 @@
[![Build Status](https://travis-ci.org/dtkahl/php-access-control.svg?branch=master)](https://travis-ci.org/dtkahl/php-access-control)

# PHP access control

This package provides an access control (right management) system based on user, roles, rights and objects.


### User

The main reason of this package is to prove whether the user has a specific right or not.

A user can have a right on different ways:

- through a global role
- through an object role

### Roles

A role is a defined set of rights and can extend another existing role. Roles can be assigned on a global scope or on a specific object.


### Objects

An user can have a specific role for a specific object. As example: The User "John" has the role "author" on the object "BlogPost".

A object can be any class that implements `ObjectInterface`. As example it could be the Eloquent model class `BlogPost`.


## Installation

Install with [Composer](http://getcomposer.org):
```
composer require dtkahl/php-array-tools
```


## Usage

### create User(s)

This is not really a big deal. You just need a class that implements the `UserAccessInterface`.

This requires you to implement one method:

- `getGlobalRoles` - Returns an array of role names (strings) you want your user to have. The way you store this information is completely up to you.


### create Objects

This is a step you can skip if you only want to implement global rights. If you wan to have object roles and rights you have to implement the `ObjectInterface` in your objects class.

This requires three methods:

- `getObjectIdentifier` - Returns an identifier as string. This is used to find the right rights in the later defined roles
- `getUserRoles` - Returns an array of role names (string) that the given user (parameter of this method) have in relation to this object instance. The way you store this information is completely up to you.
- `getRelatedObjects` - Returns an array of related objects (which also have to implement the interface). This is used for inheritance. As example: The User is allowed to delete a BlogComment because the BlogComment is related to the BlogPost for which the user has the role "author"


### define roles an rights

This could take place anywhere in your application but needs to be done before checking rights. The best place could be inside a dependency injection container.

**Defining a role**

```php
$role_member = new AccessRole(
"member", // role name
[
"access", // rights as string
"blog_post.edit" // you can namespace rights with a dot, this is often used for object identifier
"blog_post" => ["view", "create"] // instead of a dot you can also use an array to create namespaces
],
);
```

Namespaces are new with issue #10 and new release 2.0.0

**Extending a role**

```php
$role_admin = new AccessRole(
"admin", // role name
["do_admin_stuff"], // global rights as array
[],
$role_member // extend the member role so you dont have to specify all rights a second time
);
```

**define an object**

```php
$object_blog = new AccessObject(
"blog", // identifier of an object
[$role_author, $role_subscriber] // array of object related roles
);
```

**create the judge instance**

```php
$judge = new Judge(
[$role_member], // array of all defined global roles
[$object_blog, $object_comment], // array of all defined objects
$user // optional, default user to check rights for
);
```

## The Judge class

This is the main class to check rights or roles. You normally want let your dependency container to return a instance of this class.

It has the following public methods:

### `registerRole($role)`

Register a new global role for the Judge instance.

### `registerObject($object)`

Register a new object for the Judge instance.

### `setUser($user)`

Set the default user for the Judge instance.

### `checkRight($rights, $object = null, $user = null)`

Throws `NotAllowedException` if the user do not have the given right(s).

If given object is null it only checks global rights.
If given user is null it uses the default user.

**Example:**
```php
$comment = BlogComment::find('1');
$judge->checkRight('edit', $comment); // check if the default user is allowed to edit a specific comment
```

### `hasRight($rights, $object = null, $user = null)`

This is a proxy for `checkRights()` but instead of throwing an exception it only return true or false.

### `checkRole`

Throws `NotALlowedException` if the user do not have the given role.

If given object is null it only checks global roles.
If given user is null it uses the default user.
If parameter `check_extend_roles` is true (default: false) the given role is also checked against the extend roles of the roles the user has. (see issue #9)

**Example:**
```php
$comment = BlogComment::find('1');
$judge->checkRole('creator', $comment); // check if the default user is the creator of this comment
```

### `hasRight`

This is a proxy for `checkRole()` but instead of throwing an exception it only return true or false.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"license": "MIT",
"require": {
"php": ">=5.6",
"dtkahl/php-simple-config": "^1.0"
"dtkahl/php-array-tools": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "5.2.*"
Expand Down
50 changes: 50 additions & 0 deletions src/AccessObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php namespace Dtkahl\AccessControl;

use Dtkahl\ArrayTools\Map;

class AccessObject
{
private $identifier;
private $object_roles;

/**
* AccessObject constructor.
* @param string $identifier
* @param AccessRole[] $object_roles
*/
public function __construct($identifier, array $object_roles)
{
$this->identifier = $identifier;
$this->object_roles = new Map();

foreach ($object_roles as $role) {
$this->registerRole($role);
}
}

/**
* @return string
*/
public function getIdentifier()
{
return $this->identifier;
}

/**
* @param AccessRole $role
* @return $this
*/
public function registerRole(AccessRole $role)
{
$this->object_roles->set($role->getIdentifier(), $role);
return $this;
}

/**
* @return Map
*/
public function getRoles()
{
return $this->object_roles;
}
}
69 changes: 69 additions & 0 deletions src/AccessRole.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php namespace Dtkahl\AccessControl;

class AccessRole
{
private $identifier;
private $rights = [];
private $extended_role;

/**
* AccessRole constructor.
* @param string $identifier
* @param array $rights
* @param AccessRole|null $extended_role
*/
public function __construct($identifier, array $rights, AccessRole $extended_role = null)
{
$this->identifier = $identifier;
$this->rights = $this->prepareRights($rights);
$this->extended_role = $extended_role;
}

private function prepareRights($rights)
{
$prepared = [];
foreach ($rights as $key=>$value) {
if (is_array($value)) {
foreach ($value as $item) {
$prepared[] = "$key.$item";
}
} else {
$prepared[] = $value;
}
}
return $prepared;
}

/**
* @return string
*/
public function getIdentifier()
{
return $this->identifier;
}

/**
* @param $rights
* @param AccessObject|null $access_object
* @throws AllowedException
* @throws NotAllowedException
*/
public function checkRight($rights, AccessObject $access_object = null)
{
$rights = (array) $rights;

if (count(array_intersect($rights, $this->rights)) == count($rights)) {
throw new AllowedException;
}

if ($this->extended_role instanceof AccessRole) {
$this->extended_role->checkRight($rights, $access_object);
}
}

public function getExtendedRole()
{
return $this->extended_role;
}

}
13 changes: 13 additions & 0 deletions src/AllowedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php namespace Dtkahl\AccessControl;

class AllowedException extends \Exception
{
/**
* No reason to start sweating honey :)
*
* If this "exception" is thrown everything is alright. The Judge catches it, so it should never get outside.
*
* I know this is sort of an anti pattern but it seams to be the best solution for this specific use case.
* Lets call it a "Sucception" - credits to Michael Betka for this name ;)
*/
}
Loading

0 comments on commit 82fbad4

Please sign in to comment.