Created
March 12, 2014 21:30
-
-
Save mindplay-dk/9516824 to your computer and use it in GitHub Desktop.
Authorization service and context
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Provides authorization services | |
*/ | |
class AuthorizationService | |
{ | |
/** @var AuthorizationRules */ | |
public $rules; | |
public function __construct(AuthorizationRules $rules) | |
{ | |
$this->rules = $rules; | |
} | |
/** | |
* Factory method: creates an AuthorizationContext | |
* | |
* @return AuthorizationContext | |
*/ | |
public function context($user_id) | |
{ | |
$context = new AuthorizationContext($this->rules); | |
$context->user_id = $user_id; | |
return $context; | |
} | |
} | |
/** | |
* Defines stateless (context free) authorization rules (business logic) | |
*/ | |
class AuthorizationRules | |
{ | |
/** @return bool */ | |
protected function isAdmin($user_id) | |
{ | |
return $user_id === 1; // user with ID 1 is the admin | |
} | |
/** @return bool */ | |
protected function isPostOwner($user_id, $post_id) | |
{ | |
// in real life we would load $post model and check $post->author_id or something. | |
return $post_id == 2 && $user_id == 2; // mock: user 2 can edit post 2 | |
} | |
/** @return bool */ | |
protected function postIsActive($post_id) | |
{ | |
} | |
/** @return bool */ | |
public function canEditPost($user_id, $post_id) | |
{ | |
return $this->isAdmin($user_id) | |
|| $this->isPostOwner($user_id, $post_id); | |
} | |
/** @return bool */ | |
public function canDeletePost($user_id, $post_id) | |
{ | |
return $this->isAdmin($user_id) | |
|| $this->isPostOwner($user_id, $post_id) | |
&& $this->postIsActive($post_id); | |
} | |
} | |
/** | |
* Model defines authorization parameters (as mutable state) | |
* | |
* Mirrors the shape of AuthorizationRules, but methods (typically) have no arguments | |
* and contain no logic - they simply delegate to the matching AuthorizationRule method. | |
*/ | |
class AuthorizationContext | |
{ | |
/** @var AuthorizationRules */ | |
public $rules; | |
/** @param int $user_id */ | |
public function __construct(AuthorizationRules $rules) | |
{ | |
$this->rules = $rules; | |
} | |
/** @var int */ | |
public $user_id; | |
/** @var int */ | |
public $post_id; | |
/** @return bool */ | |
public function canEditPost() | |
{ | |
return $this->rules->canEditPost($this->user_id, $this->post_id); | |
} | |
/** @return bool */ | |
public function canDeletePost() | |
{ | |
return $this->rules->canDeletePost($this->user_id, $this->post_id); | |
} | |
} | |
// TEST: | |
function test($name, $result) { | |
echo $result === true | |
? "- OK: $name\n" | |
: "- FAIL: $name\n"; | |
} | |
$auth = new AuthorizationService(new AuthorizationRules); | |
// direct checks via AuthorizationRules: | |
test('correctly initializes a new context', $auth->context(1)->user_id === 1); | |
test('can perform direct access control check', $auth->rules->canEditPost(1, 10)); | |
$context = $auth->context(1); // user 1 is admin | |
$context->post_id = 10; | |
test('can perform indirect access control check', $context->canEditPost() === true); // admin can edit any post | |
$context->user_id = 2; | |
$context->post_id = 2; | |
test('can perform indirect access control check', $context->canEditPost() === true); | |
// that all works | |
// now put your AuthorizationService somewhere central in your application | |
// then add a simple filter class or "before action" method to the controller in your favorite framework. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment