Created
February 20, 2014 12:42
-
-
Save liverbool/9112651 to your computer and use it in GitHub Desktop.
Sample Acl
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 | |
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