Skip to content

Instantly share code, notes, and snippets.

@liverbool
Created February 20, 2014 12:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save liverbool/9112651 to your computer and use it in GitHub Desktop.
Save liverbool/9112651 to your computer and use it in GitHub Desktop.
Sample Acl
<?php
namespace JP\Authen;
/**
* we are start with deny concept
* all usergroup init with deny (not allow)
*
* mulitple usergroup
* allow + allow = allow
* deny + deny = deny
* allow + deny = allow
* null = deny
*
* group & user
* if user privilate has set to allow|deny by specify each user don't check there group
* if user's privilate is an inherite|null check there group
*
* @note static property prevent store in session
* @note static not serilize by php
*/
class Acl {
const ALLOW = 'a';
const DENY = 'd';
const RULE_GLOBAL = '*';
const RULE_LOGON = 'logon';
const NODE_ROOT = 'site';
/**
* RESTRICT
* disallow type
*/
const RESTRICT_FILTER = 1;
const RESTRICT_RULE = 2;
const RESTRICT_METHOD = 3;
const RESTRICT_LOGON = 4;
/**
* @var $RESTRICT int restrict type
*/
var $RESTRICT = 0;
var $RESTRICT_RULE = NULL;
var $RESTRICT_CASE = 0;
var $RESTRICT_CASES = array(
1 => ''
,2 => ''
,3 => ''
,4 => ''
,5 => ''
,6 => 'ยังไม่ได้รับการกำหนดสิทธิการใช้งาน'
,7 => ''
,8 => ''
,9 => ''
);
var $IS_ALLOW = TRUE;
var $IS_LOGON = FALSE;
var $IS_MAGIC = FALSE;
static private $_scope;
static private $_rule_keys = array(
'method', 'filter', 'rules'
);
static public $instance;
static public $node;
static public $user;
/**
* @array
*/
static public $acl_rules = array();
/**
* @array
*/
static public $acl_rules_access = array();
public $acr_lists;
/**
* reset prevois check
*/
public function reset(){
$this->RESTRICT = 0;
$this->RESTRICT_RULE = NULL;
$this->RESTRICT_CASE = 0;
$this->IS_ALLOW = TRUE;
$this->IS_LOGON = FALSE;
$this->IS_MAGIC = FALSE;
}
public function set_node($node){
self::$node = $node;
}
public function set_scope($scope){
self::$_scope = $scope;
}
public function get_scope(){
return self::$_scope;
}
public function set_logon($state){
$this->IS_LOGON = $state;
}
public function set_magic($state){
$this->IS_MAGIC = $state;
}
public function add_rules($node, $rules) {
if(empty($rules)) return;
if(empty($node)) $node = self::NODE_ROOT;
self::$acl_rules = array_unique_merge_key(self::$acl_rules, array($node => $rules));
}
public function add_rules_access($node, $access_rules) {
if(empty($access_rules)) return;
if(empty($node)) $node = self::NODE_ROOT;
self::$acl_rules_access = array_unique_merge_key(self::$acl_rules_access, array($node => $access_rules));
}
public function rules($node, $task){
if(empty(self::$acl_rules_access[$node])) return null;
// methods are you mapping of current node (class) and inherite root node
$rules_access = array_merge((array) self::$acl_rules_access[$node], (array) self::$acl_rules_access[self::NODE_ROOT]);
// * rule
$global = array();
if(!empty($rules_access[self::RULE_GLOBAL]))
$global = $rules_access[self::RULE_GLOBAL];
// rule of current task (method)
$rule = array();
if(!empty($rules_access[$task]))
$rule = $rules_access[$task];
// build current task rules
$task_rules = array();
foreach(self::$_rule_keys as $key){
if($key == 'method') {
$task_rules[$key] = empty($rule[$key]) ? $global[$key] : $rule[$key];
} else {
// remove space
$g = isset($global[$key]) ? preg_replace('/ /', '', $global[$key]) : null;
$r = isset($rule[$key]) ? preg_replace('/ /', '', $rule[$key]) : null;
if($key == 'rules' && isset(self::$acl_rules[$node])) {
if($rule[$key] === false){
// reset rule if user define NULL
$r = null;
$g = null;
}else{
// is now * mean with-in current node (current class)
$rs = implode(',', array_keys((array) self::$acl_rules[$node]));
if($g == '*') $g = $rs;
if($r == '*') $r = $rs;
}
}
// remove multiple ,,
$val = preg_replace('/,{2,}/', ',', sprintf('%s,%s', $g, $r));
$val = preg_replace('/,$/', '', $val);
$val = preg_replace('/^,/', '', $val);
$task_rules[$key] = $val;
}
}
if(!empty($task_rules['rules']))
$task_rules['rules'] = array_unique(explode(',', $task_rules['rules']));
return (object) array(
//'path' => sprintf('%s.%s', $node, $task),
//'global' => $global,
//'rule' => $rule,
'rule' => $task_rules
);
}
public function rules_access($task){
if(empty(self::$node)) self::$node = self::NODE_ROOT;
$task_rule = $this->rules(self::$node, $task);
// this task not set any rule
if(empty($task_rule->rule)){
$this->IS_ALLOW = TRUE;
return TRUE;
}
$rules = (object) $task_rule->rule;
// 1. check method
// @todo inherite check parent allow method
if(!$this->is_method($rules->method)) {
$this->RESTRICT = self::RESTRICT_METHOD;
$this->IS_ALLOW = FALSE;
$this->RESTRICT_CASE = 1;
return FALSE;
}
// fast login check optimize unnessecery check
if(in_array(self::RULE_LOGON, (array) $rules->rules) && $this->IS_LOGON === FALSE) {
$this->IS_ALLOW = FALSE;
$this->RESTRICT = self::RESTRICT_LOGON;
$this->RESTRICT_CASE = 3;
return FALSE;
}
if(empty($rules->rules)) {
if(!empty($rules->filter)) {
if($this->_filter($rules->filter) === FALSE){
$this->IS_ALLOW = FALSE;
$this->RESTRICT = self::RESTRICT_FILTER;
$this->RESTRICT_CASE = 2;
return FALSE;
}
}
$this->IS_ALLOW = TRUE;
return TRUE;
}
// not assign any rule
if(empty($this->acr_lists)) {
$this->RESTRICT = self::RESTRICT_RULE;
$this->IS_ALLOW = FALSE;
$this->RESTRICT_CASE = 4;
return FALSE;
}
$user = $this->acr_lists['user'];
$groups = $this->acr_lists['groups'];
// not assign any rule
if(empty($user) && empty($groups)) {
$this->RESTRICT = self::RESTRICT_RULE;
$this->IS_ALLOW = FALSE;
$this->RESTRICT_CASE = 5;
return FALSE;
}
$user = isset($user[self::$node]) ? $user[self::$node] : null;
$groups = isset($groups[self::$node]) ? $groups[self::$node] : null;
// empty node rule
if(empty($user) && empty($groups)
&& (in_array('logon', $rules->rules) ? count($rules->rules) > 1 : count($rules->rules))) {
$this->RESTRICT = self::RESTRICT_RULE;
$this->IS_ALLOW = FALSE;
$this->RESTRICT_CASE = 6;
return FALSE;
}
// 2. check rules
if(!$this->rules_access_check($rules->rules, $user, $groups, self::$node)){
$this->RESTRICT = self::RESTRICT_RULE;
$this->IS_ALLOW = FALSE;
$this->RESTRICT_CASE = 7;
return FALSE;
}
// 3. check filter
if(!empty($rules->filter)) {
if($this->_filter($rules->filter) === FALSE){
$this->RESTRICT = self::RESTRICT_FILTER;
$this->IS_ALLOW = FALSE;
$this->RESTRICT_CASE = 8;
return FALSE;
}
}
$this->IS_ALLOW = TRUE;
return TRUE;
}
private function _filter($filter){
if(empty(self::$_scope) && function_exists($filter)){
if(call_user_func($filter) === FALSE){
return false;
}
}
if(!empty(self::$_scope)){
if(call_user_func(array(self::$_scope, $filter)) === FALSE){
return false;
}
}
return true;
}
/**
* @param array $rules
* @param array $user
* @param array $groups
* @param string $node
* @return bool
*/
public function rules_access_check($rules, $user, $groups, $node){
$travals = array();
$deny_flag = false;
$rule = null;
foreach($rules as $rule) {
if($rule === self::RULE_LOGON) continue;
// check rules from user
if($user)
$r = $user[$rule];
// else from user group
if(empty($r) && $groups)
$r = $groups[$rule];
// if not any assign rule, store it for try to travel in top node
if(empty($r)) {
$travals[] = $rule;
} else {
if($r->acl_access == self::DENY) {
$deny_flag = true;
$this->RESTRICT_RULE = $rule;
break;
}
}
}
if($deny_flag)
return false;
if(empty($travals))
return true;
$nodes = explode('.', $node);
// can't find in any node
//if(!count($nodes)) {
if(count($nodes) < 2) {
$this->RESTRICT_RULE = $rule;
return false;
}
if(count($nodes) == 1){
$node = $nodes[0];
}else{
array_pop($nodes);
$node = implode('.', $nodes);
}
$user = $this->acr_lists['user'][$node];
$groups = $this->acr_lists['groups'][$node];
// try to find in top node
return $this->rules_access_check($travals, $user, $groups, $node);
}
public function required($rules, $node = null) {
if($this->IS_MAGIC === true) return true;
/**
* @var array $user
* @var array $groups
*/
$user = $this->acr_lists['user'];
$groups = $this->acr_lists['groups'];
// not assign any rule
if(empty($user) && empty($groups)) {
return false;
}
$rules = preg_replace(array('/ /','/,{2,}/'), array('',','), $rules);
$rules = array_unique(explode(',', $rules));
// make sure !!
if(empty($rules)) {
return true;
}
if(empty($node)) $node = self::$node;
$user = isset($user[$node]) ? $user[$node] : null;
$groups = isset($groups[$node]) ? $groups[$node] : null;
return $this->rules_access_check($rules, $user, $groups, $node);
}
/**
* check the rule(s) you have gant (allow) of node
* this method you can use any where
*/
public function can($rules, $node = null){
if($this->IS_MAGIC === true) return true;
return $this->required($rules, $node);
}
/**
* check task is required some rule that you have
* this feature you MUST config $acl_rules_access in you node to matching object (task) and rule (permiss)
*/
public function is_allow($task, $node = null){
if($this->IS_MAGIC === true) return true;
if(!empty($node)) $this->set_node($node);
return $this->rules_access($task);
}
public function is_method($method){
if($method === '*' || empty($method)) return true;
return strtolower($_SERVER['REQUEST_METHOD']) === strtolower($method);
}
function acr() {
if($this->acr_lists) return $this->acr_lists;
$this->acr_lists = array();
$acr_user = $this->acr_user();
$acr_group = array_merge(
(array) $this->acr_groups(),
(array) $this->acr_globals()
);
$this->_add_acr('user', $acr_user);
$this->_add_acr('groups', $acr_group);
return $this->acr_lists;
}
public function acr_add($path, $key, $value) {
$path = explode('.', $path);
$acr =& $this->acr_lists;
foreach($path as $pt){
if(empty($acr[$pt])){
$acr[$pt] = array();
}
$acr =& $acr[$pt];
}
$acr[$key] = $value;
}
/**
* @param null $user
* @return JAcl
*/
static function getInstance($user = null){
if(self::$instance) return self::$instance;
self::$instance = new JAcl();
if($user) self::$instance->init($user);
return self::$instance;
}
function init($user){
$this->set_user($user);
$this->acr();
}
function set_user($user){
self::$user = $user;
}
function acr_user(){
return $this->_load_acr(self::$user->user_id, 'u');
}
function acr_groups(){
$dbo =& JFactory::getDBO();
$dbo->setQuery(array(
"SELECT user_group_id"
,"FROM ##_users_groups_map"
,"WHERE user_id=". self::$user->user_id
));
$gids = $dbo->loadResultArray();
if(empty($gids)) return null;
return $this->_load_acr($gids, 'g', self::$user->bch_id);
}
function acr_globals(){
$dbo =& JFactory::getDBO();
$recs = $dbo->Table('##_users_groups')
->findAll('user_group_is_user=? OR user_group_is_admin=?', 1, 1);
if(empty($recs)) return null;
$ids = array();
foreach($recs as $rec){
$ids[] = $rec['user_group_id'];
}
return $this->_load_acr($ids, 'g');
}
private function _load_acr($ids, $type, $bch = null) {
$dbo =& JFactory::getDBO();
$dbo->setQuery(array(
"SELECT * FROM ##_users_acl"
,"WHERE acl_acr_type='{$type}'"
,is_array($ids) ? ("AND acl_acr_id IN(". implode(',', $ids) .")") : "AND acl_acr_id={$ids}"
,$bch ? "AND acl_bch_id={$bch}" : ''
));
return $dbo->loadObjectList();
}
private function _add_acr($key, $data) {
if(empty($this->acr_lists[$key]))
$this->acr_lists[$key] = array();
if(!empty($data)) {
foreach($data as $rec){
$this->acr_lists[$key][$rec->acl_aco][$rec->acl_rule] = (object) array(
'acl_rule' => $rec->acl_rule,
'acl_access' => $rec->acl_access
);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment