Acl.php
<?php
namespace PHN\Model;
class Acl{
public $tree;
public $controller;
public $db;
public $cfg;
public $acl;
//predefined convention role names
const ROLE_GUEST='guest';
const ROLE_USER='user';
function __construct(&$cfg,$controller) {
$this->controller = &$controller;
$this->user = &$controller->user;
$this->cfg = &$cfg;
//convention role name
if (!isset($this->cfg['role_names'])) $this->cfg['role_names'] = array(
'guest'=>self::ROLE_GUEST,
'user'=>self::ROLE_USER,
);
if ($cfg['usedb']) //chỉ dùng db để load tree ... khi usedb=true ( mặc định = true )
{
if (isset($cfg['db']['connect'])) $this->db = $this->controller->db($cfg['db']['connect']);
else $this->db = $this->controller->db();
//connection error
if (!$this->db) {
$e = new \Exception("DB_SERVER_ERROR", 404);
throw $e;
}
else { //$this->db ready
$treeCfg=array(
'db'=>$this->db,
'tableMain'=>'acl_role',
'tableMainId'=>'role_id',
'tableMainCode'=>'role_code',
'tableMainName'=>'role_name',
'tablePath'=>'acl_closure'
);
$this->tree = new \PHN\Model\SQLClosureTree($treeCfg);
}
}
}
// <editor-fold defaultstate="collapsed" desc=" Role Tree Related Functions ">
/**
*
* @param type $role_code
* @param type $role_name
* @return int role_id
*/
public function addRole($role_code,$role_name,$parent=null)
{
$this->tree->append(array('code'=>$role_code,'name'=>$role_name),$parent);
}
public function deleteRole($role_code,$tree=true)
{
$this->tree->delete($role_code,$tree);
}
public function insertRole($role_code,$role_name,$sibling)
{
}
public function getRolePath($role_code)
{
return $this->tree->getPath($role_code);
}
public function replaceRoleParent($role_code,$parent_code)
{
$this->tree->replaceParent($role_code,$parent_code);
}
public function getRoleTree($role_code,$level=0)
{
return $this->tree->getChildren($role_code,$level);
}
public function getRoleTreeSorted($role_code)
{
return $this->tree->getTreeSorted($role_code);
}
// </editor-fold>
/**
* private
* get any user's acl , default is current user
* @param array/null $user <=> array('user_id'=>,'username'=>) if null then use controller->user[]
* @return array
* array(2) {
["role-pmses"]=>{
["user"]=>{
["pms_cfg_1"]=>1
}
["admin"]=>{
[0]=>
string(1) "*"
["pms-1"]=>
int(1)
}
["admin-sub2"]=>
array(1) {
["pms-1"]=>
int(1)
}
}
["roles"]=>
array(9) {
["user"]=>
int(2)
["admin"]=>
string(1) "3"
["admin-sub1"]=>
string(1) "4"
["admin-sub1-sub1"]=>
string(1) "7"
["admin-sub1-sub1-sub1"]=>8
}
}
*/
public function getAcl($user=null)
{
$cfg = &$this->cfg;
$my_roles =array(); //cumulative roles
$my_cfg_roles=array(); //roles from cfg
$my_role_pmses=array(); //cumulative role-pmses
if (empty($user)) {
$username = $this->controller->user['username'];
$user_id = $this->controller->user['user_id'];
}
else {
$username=$user['username'];
$user_id=$user['user_id'];
}
// <editor-fold defaultstate="collapsed" desc=" normalize some configs first ">
if (isset($cfg['role-pmses'])) {
foreach ($cfg['role-pmses'] as $key=>$value) {
if ($value =='*') $cfg['role-pmses'][$key] = array('*'=>1);
}
}
// </editor-fold>
//không đăng nhập thì thuộc nhóm guest
if (!$username) {
$my_cfg_roles[] = $cfg['role_names']['guest'];
}
//nếu đăng nhập tức có id thì mặc định thuộc nhóm user
else {
$my_cfg_roles[] = $cfg['role_names']['user'];
//dò tiếp xem còn thuộc nhóm gì trong cfg ?
if (isset($cfg['user-roles']) && isset($cfg['user-roles'][$username])) $my_cfg_roles = array_merge( $my_cfg_roles,$cfg['user-roles'][$username]);
}
//normalize my_cfg_roles as [role_code=>role_id]
$my_roles = array();
$my_role_ids = array();
foreach($my_cfg_roles as $role){
if (isset($cfg['roles']) && isset($cfg['roles'][$role])) $my_roles[$role] = $cfg['roles'][$role];
}
//đã xác định xong các nhóm từ cfg mà user thuộc về
//từ đó tìm pms tương ứng
foreach ($my_roles as $role=>$id)
{
if (isset($cfg['role-pmses'][$role]))
$my_role_pmses[$role] =$cfg['role-pmses'][$role];
}
//var_dump($my_roles,$my_role_pmses);exit;
//nếu user logged in thì mới dùng db để tìm roles/pms
if ($cfg['usedb']){
//try cache first
$this->db->table('acl_cache');
//chỉ lấy cache nếu user đã đăng nhập
if ($user_id) $cache=$this->db->getVal(array("select"=>"data","where"=>"user_id=$user_id"));
else $cache=null;
//nếu chưa có cache
if (!$cache){
//query db
$this->db->table('acl_role');
//nếu user đăng nhập thì
//lấy roles mà user_id này có từ trong db
if ($user_id) {
$roles =$this->db->getAll("
SELECT acl_role.role_id FROM acl_role
JOIN acl_user_role ON (acl_role.role_id=acl_user_role.role_id)
WHERE acl_user_role.user_id = $user_id
");
//nếu tồn tại roles trong db mà user này thuộc về thì tiến hành cummulative và merge
if (!empty($roles)) {
$cumulative_roles=array();
foreach ($roles as $role)
{
$path=$this->getRolePath($role['role_id']);
$cumulative_roles = array_merge($cumulative_roles,$path);
}
//echo "<hr>";
//var_dump($cumulative_roles); exit
//ex : 1; 1,4,7; 1,5
//chuyển sang dạng {'code'=>id} merge với my_roles trước đó từ cfg
//chuyển sang dạng key nên những id trùng lắp sẽ chỉ còn 1
foreach ($cumulative_roles as $role){
$role_code = $role['role_code'];
$my_roles[$role_code] = $role['role_id'] ;
//nếu role mới có 'pms' trong cfg trước đó thì update $my_role_pmses
if ( isset($cfg['role-pmses'][$role_code]))
$my_role_pmses[$role_code] =$cfg['role-pmses'][$role_code];
}
}//if (!empty($roles)
}//end if ($user_id)
//echo "<hr>";
//var_dump($my_roles);exit;
//var_dump($my_role_pmses);exit;
//chú ý ngay cả user không đăng nhập thì user vẫn thuộc role guest nên role_ids luôn tồn tại
$role_ids = implode(',', array_values($my_roles));
//echo "<hr>";
//var_dump($role_ids);
//tìm mọi pms cho role có trong danh sách cần tìm kể cả guest
$role_pmses = $this->db->getAll("
SELECT acl_role.role_id,acl_role.role_code,acl_pms.pms_code,acl_role_pms.allow
FROM acl_role
JOIN acl_role_pms ON (acl_role.role_id=acl_role_pms.role_id)
JOIN acl_pms ON (acl_role_pms.pms_id=acl_pms.pms_id)
WHERE acl_role_pms.role_id IN ($role_ids)
");
if (!empty($role_pmses)){
//convert to $code=>array(pms=>id)
$db_role_pmses=array();
foreach($role_pmses as $role)
{
$role_code = $role['role_code'];
if (!isset($db_role_pmses[$role_code])) $db_role_pmses[$role_code] = array();
$db_role_pmses[$role_code][$role['pms_code']] = (int) $role['allow'];
}
//merge
$my_role_pmses = array_merge_recursive($my_role_pmses,$db_role_pmses);
}
//chỉ lưu cache nếu user đã đăng nhập và my_roles có từ 2 roles trở lên vì mặc định user luôn thuộc
//role user nếu đăng nhập hoặc guest nếu chưa đăng nhập
//tức count(my_roles) luôn tối thiểu bằng 1
//hoặc pmses phải có ít nhất 1 quyền
if ($user_id && (count($my_roles) > 1 || count($my_role_pmses)) )
{
$cache = array(
'role-pmses'=>$my_role_pmses,
'roles'=>$my_roles
);
$this->db->table('acl_cache');
$this->db->insert(array("user_id"=>$user_id,
'data'=>serialize($cache),
'create_time'=>array('NOW()'),
'modify_time'=>array('NOW()')
));
}
}//if not cache
//có thể cache trong db nhưng là chuỗi rỗng/null tức ngầm hiểu user chỉ thuộc duy nhất là nhóm user
//mặc định mọi user có đăng ký ( tức có user_id ) đều thuộc user nên không cần lưu cache chi cho tốn kém
elseif (!empty($cache)) $cache = unserialize($cache);
if ($cache) return $cache;
else return array(
'role-pmses'=>$my_role_pmses,
'roles'=>$my_roles
);
}//end usedb
else{
return array(
'role-pmses'=>$my_role_pmses,
'roles'=>$my_roles
);
}
}
/**
*
* @return array
*/
public function getPms($user=null)
{
if (empty($user)) {
$username = $this->controller->user['username'];
$user_id = $this->controller->user['user_id'];
}
else {
$username=$user['username'];
$user_id=$user['user_id'];
}
$key = 'acl';
if (isset($user_id)) $key.=$user_id;
if (isset($_SESSION[$key])) $this->acl=&$_SESSION[$key];
else {
$acl=$this->getAcl($user);
$roles=&$acl['roles'];
$role_pmses=&$acl['role-pmses'];
$pmses=array();
$reg_pmses=array();
foreach ($acl['roles'] as $role=>$id)
{
if (isset($role_pmses[$role])) {
$pmses=array_merge($pmses,$role_pmses[$role]);
}
}
foreach ($pmses as $k=>$v)
{
if (preg_match("#^\^#", $k)) {
$reg_pmses[$k]=$v;
}
}
$this->acl=array(
'roles'=>$roles,
'pmses'=>$pmses
);
if (!empty($reg_pmses)) $this->acl['reg-pmses']=$reg_pmses;
//$_SESSION[$key]=$acl;
}
return $this->acl;
}
/**
* kiểm tra xem pms có khớp trong pms mask không ?
* @param type $pms
*/
private function matchPMS(&$pms,&$pmses)
{
if ( isset($pmses['pmses'][$pms]) &&$pmses['pmses'][$pms] ) return true;
elseif (isset($pmses['pmses']['*']) && !(isset($pmses['pmses'][$pms]) && !$pmses['pmses'][$pms])) return true;
elseif(isset($pmses['reg-pmses'])) {
foreach($pmses['reg-pmses'] as $k=>$v)
{
if (preg_match("#$k#",$pms)) return $v;
}
//not found
return false;
}
return false;
}
/**
*
* @param string $pms
* @param int $user_id, default using current logged user_id
* @return boolean
*/
public function allow($pms=null,$user=null)
{
if (empty($pms))
{
$pms=$this->controller->app->controllerName .'-'.$this->controller->app->controllerAction;
}
if (empty($user)) {
$username = $this->controller->user['username'];
$user_id = $this->controller->user['user_id'];
}
else {
$username=$user['username'];
$user_id=$user['user_id'];
}
$pmses=$this->getPms($user);
if (!is_array($pms)) {
return $this->matchPMS($pms,$pmses);
}
else{
foreach($pms as $p){
//chỉ cần phát hiện 1 pms không thỏa thì return false;
if (!$this->matchPMS($p,$pmses)) return false;
}
return true;
}
}
/**
* check if user belongs to role(s)
* @param string/array of string $role
* @param type $user_id
*/
public function hasRole($role,$user=null)
{
if (empty($user)) {
$username = $this->controller->user['username'];
$user_id = $this->controller->user['user_id'];
}
else {
$username=$user['username'];
$user_id=$user['user_id'];
}
$pmses=$this->getPms($user);
if (!is_array($role)) {
return isset($pmses['roles'][$role]);
}
else{
foreach($role as $r){
if (!isset($pmses['roles'][$r])) return false;
}
return true;
}
}
}
?>