This is an old revision of Dev/NewFeatures/ExtendedAcls from 03.12.2016 11:06 edited by WikiAdmin.
View source for Extended Acls
bugs:issue in bugtracker Author: ((user:WikiAdmin WikiAdmin)) {{toc numerate=1}} ===Introduction=== Introduction of idea object <- page right <- privilege role <- user role <- group <- user group -> organising users role -> organising rights ===database=== file:acl_scheme.png {{files}} Database Changes * Required database changes to wacko_XYZ_table %%(hl sql) CREATE TABLE IF NOT EXISTS `wacko_acl` ( `acl_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `object_id` int(10) unsigned DEFAULT NULL, `object_type_id` int(2) unsigned DEFAULT NULL, `object_right_id` int(2) unsigned DEFAULT NULL, PRIMARY KEY (`acl_id`), UNIQUE KEY `idx_acl` (`object_id`,`object_type_id`,`object_right_id`) ) ENGINE=MyISAM; CREATE TABLE IF NOT EXISTS `wacko_acl_privilege` ( `acl_privilege_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `acl_id` int(11) DEFAULT NULL, `grant_type_id` int(11) DEFAULT NULL, `grant_id` int(11) DEFAULT NULL, `deny` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`acl_privilege_id`), UNIQUE KEY `idx_privilege` (`acl_id`,`grant_type_id`,`grant_id`) ) ENGINE=MyISAM; CREATE TABLE IF NOT EXISTS `wacko_acl_right` ( `acl_right_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `object_right` varchar(45) DEFAULT NULL, PRIMARY KEY (`acl_right_id`) ) ENGINE=MyISAM; INSERT INTO `wacko_acl_right` (`acl_right_id`, `object_right`) VALUES (1, 'read'), (2, 'comment'), (3, 'write'), (4, 'add'), (5, 'remove'); CREATE TABLE IF NOT EXISTS `wacko_acl_type` ( `acl_type_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `object_type` varchar(45) DEFAULT NULL, PRIMARY KEY (`acl_type_id`) ) ENGINE=MyISAM; INSERT INTO `wacko_acl_type` (`acl_type_id`, `object_type`) VALUES (1, 'page'); %% %% $alter_acl_r5_4_0 = "ALTER TABLE {$pref}acl ADD acl_right_id INT(10) UNSIGNED NOT NULL AFTER page_id"; $alter_acl_r5_4_1 = "ALTER TABLE {$pref}acl DROP privilege"; $update_acl_r5_4_0 = "UPDATE {$pref}acl AS acl, (SELECT acl_right_id, acl_right FROM {$pref}acl_right) AS acl_right SET acl.acl_right_id = acl_right.acl_right_id WHERE acl.privilege = acl_right.acl_right"; %% Configuration * $_var indicates something Implementation Log Issues: * static objects * add an table e.g. 'acl_static_object' and map your objects manually there * acl_type = 'static' ===functions=== %%(hl php) <?php #function save_acl($object_id, $object_type, $object_right, $acl_privileges) function save_acl($page_id, $privilege, $list) { #if ($acl = $this->load_acl($object_id, $object_type, $object_right, 0, 0, 0)) if ($this->load_acl($page_id, $privilege, 0, 0, 0)) { $this->query( "UPDATE ".$this->config['table_prefix']."acl SET ". "list = '".quote($this->dblink, trim(str_replace("\r", '', $list)))."' ". "WHERE page_id = '".quote($this->dblink, $page_id)."' ". "AND privilege = '".quote($this->dblink, $privilege)."' "); /*foreach ($acl_privileges as $acl_privilege) { $this->query( "UPDATE ".$this->config['table_prefix']."acl_privilege SET ". "grant_type_id = '".quote($this->dblink, $list)."' ". "grant_id = '".quote($this->dblink, $list)."' ". "deny = '".quote($this->dblink, $list)."' ". "WHERE acl_id = '".quote($this->dblink, $page_id)."' ". "AND privilege = '".quote($this->dblink, $privilege)."' "); }*/ } else { $this->query( "INSERT INTO ".$this->config['table_prefix']."acl SET ". "list = '".quote($this->dblink, trim(str_replace("\r", '', $list)))."', ". "page_id = '".quote($this->dblink, $page_id)."', ". "privilege = '".quote($this->dblink, $privilege)."'"); } } #function get_cached_acl($object_id, $object_type, $object_right, $use_defaults) function get_cached_acl($page_id, $privilege, $use_defaults) { #if (isset( $this->acl_cache[$page_id.'#'.$object_type.'#'.$object_right.'#'.$use_defaults] )) if (isset( $this->acl_cache[$page_id.'#'.$privilege.'#'.$use_defaults] )) { #return $this->acl_cache[$page_id.'#'.$object_type.'#'.$object_right.'#'.$use_defaults]; return $this->acl_cache[$page_id.'#'.$privilege.'#'.$use_defaults]; } else { return ''; } } // $acl array must reflect acls table row structure #function cache_acl($object_id, $object_type, $object_right, $use_defaults, $acl_privilege) function cache_acl($page_id, $privilege, $use_defaults, $acl) { #$this->acl_cache[$object_id.'#'.$object_type.'#'.$object_right.'#'.$use_defaults] = $acl_privilege; $this->acl_cache[$page_id.'#'.$privilege.'#'.$use_defaults] = $acl; } # function load_acl($object_id, $object_type, $object_right, $use_defaults = 1, $use_cache = 1, $use_parent = 1, $new_tag = '') function load_acl($page_id, $privilege, $use_defaults = 1, $use_cache = 1, $use_parent = 1, $new_tag = '') { if (!isset($acl)) { $acl = ''; } if ($use_cache && $use_parent) { #if ($cached_acl = $this->get_cached_acl($object_id, $object_type, $object_right, $use_defaults)) if ($cached_acl = $this->get_cached_acl($page_id, $privilege, $use_defaults)) { $acl = $cached_acl; } } if (!$acl) { #if ($cached_acl = $this->get_cached_acl($object_id, $object_type, $object_right, $use_defaults)) if ($cached_acl = $this->get_cached_acl($page_id, $privilege, $use_defaults)) { $acl = $cached_acl; } if (!$acl) { if (!$new_tag) { $acl = $this->load_single( "SELECT * ". "FROM ".$this->config['table_prefix']."acl ". "WHERE page_id = '".quote($this->dblink, $page_id)."' ". "AND privilege = '".quote($this->dblink, $privilege)."' ". "LIMIT 1"); /* $acl = $this->load_single( "SELECT a.* ". "FROM ".$this->config['table_prefix']."acl a ". "INNER JOIN ".$this->config['table_prefix']."acl_type t ON (t.object_type_id = a.object_type_id) ". "INNER JOIN ".$this->config['table_prefix']."acl_right r ON (r.object_right_id = a.object_right_id) ". "WHERE a.object_id = '".quote($this->dblink, $object_id)."' ". "AND t.object_type = '".quote($this->dblink, $object_type)."' ". "AND r.object_type = '".quote($this->dblink, $object_right)."' ". "LIMIT 1"); $acl_privilege = $this->load_all( "SELECT * ". "FROM ".$this->config['table_prefix']."acl_privilege ". "WHERE acl_id = '".quote($this->dblink, $acl['acl_id'])."' "); */ } // if still no acl, use config defaults if (!$acl && $use_defaults) { // First look for parent ACL, so that clusters/subpages // work correctly. if (!empty($page_id)) { $tag = strtolower($this->get_page_tag_by_id($page_id)); } else { // new page which is to be created $tag = strtolower($new_tag); } if ( strstr($tag, '/') ) { $parent_tag = preg_replace('/^(.*)\\/([^\\/]+)$/', '$1', $tag); // By letting it fetch defaults, it will automatically recurse // up the tree of parent pages... fetching the ACL on the root // page if necessary. $parent_id = $this->get_page_id($parent_tag); #$acl = $this->load_acl($parent_id, $object_type, $object_right, 1); $acl = $this->load_acl($parent_id, $privilege, 1); } // if still no acl, use config defaults if (!$acl) { $acl = array( #'object_id' => $object_id, 'page_id' => $page_id, #'object_type' => $object_type, #'object_right' => $object_right, 'privilege' => $privilege, 'list' => $this->config['default_'.$privilege.'_acl'], 'time' => date('YmdHis'), 'default' => 1 ); } } #$this->cache_acl($object_id, $object_type, $object_right, $use_defaults, $acl_privilege); $this->cache_acl($page_id, $privilege, $use_defaults, $acl); } } // probably we want only $acl_privilege and we can skip $acl because we do not need it #return array($acl, $acl_privilege); #return $acl_privilege; return $acl; } function load_acl_new($object_id, $object_type, $object_right, $use_defaults = 1, $use_cache = 1, $use_parent = 1, $new_tag = '') { if (!isset($acl)) { $acl = ''; } if ($use_cache && $use_parent) { if ($cached_acl = $this->get_cached_acl($object_id, $object_type, $object_right, $use_defaults)) { $acl = $cached_acl; } } if (!$acl) { if ($cached_acl = $this->get_cached_acl($object_id, $object_type, $object_right, $use_defaults)) { $acl = $cached_acl; } if (!$acl) { if (!$new_tag) { $acl = $this->load_single( "SELECT a.* ". "FROM ".$this->config['table_prefix']."acl a ". "INNER JOIN ".$this->config['table_prefix']."acl_type t ON (t.object_type_id = a.object_type_id) ". "INNER JOIN ".$this->config['table_prefix']."acl_right r ON (r.object_right_id = a.object_right_id) ". "WHERE a.object_id = '".quote($this->dblink, $object_id)."' ". "AND t.object_type = '".quote($this->dblink, $object_type)."' ". "AND r.object_right = '".quote($this->dblink, $object_right)."' ". "LIMIT 1"); $acl_privilege = $this->load_all( "SELECT * ". "FROM ".$this->config['table_prefix']."acl_privilege ". "WHERE acl_id = '".quote($this->dblink, $acl['acl_id'])."' "); } // if still no acl, use config defaults if (!$acl && $use_defaults) { // First look for parent ACL, so that clusters/subpages // work correctly. if (!empty($page_id)) { $tag = strtolower($this->get_page_tag_by_id($page_id)); } else { // new page which is to be created $tag = strtolower($new_tag); } if ( strstr($tag, '/') ) { $parent_tag = preg_replace('/^(.*)\\/([^\\/]+)$/', '$1', $tag); // By letting it fetch defaults, it will automatically recurse // up the tree of parent pages... fetching the ACL on the root // page if necessary. $parent_id = $this->get_page_id($parent_tag); $acl = $this->load_acl($parent_id, $object_type, $object_right, 1); } // if still no acl, use config defaults if (!$acl) { $acl = array( 'object_id' => $object_id, 'object_type' => $object_type, 'object_right' => $object_right, 'list' => $this->config['default_'.$object_type.'_'.$object_right.'_acl'], 'time' => date('YmdHis'), 'default' => 1 ); } } $this->cache_acl($object_id, $object_type, $object_right, $use_defaults, $acl_privilege); } } // probably we want only $acl_privilege and we can skip $acl because we do not need it #return array($acl, $acl_privilege); return $acl_privilege; } // returns true if $user (defaults to the current user) has access to $privilege on $page_tag (defaults to the current page) #function has_access($object_type, $object_right, $object_id = '', $user = '', $use_parent = 1) function has_access($privilege, $page_id = '', $user = '', $use_parent = 1) { if ($user == '') { $user_name = strtolower($this->get_user_name()); } else if ($user == GUEST) { $user_name = GUEST; } else { $user_name = $user; } if (!$page_id = trim($page_id)) { $page_id = $this->page['page_id']; } // if still no page_id, use tag if (empty($page_id)) { // new page which is to be created $new_tag = $this->tag; } else { $new_tag = ''; } if ($privilege == 'write') { $use_parent = 0; } // load acl #$acl = $this->load_acl($object_id, $object_type, $object_right, 1, 1, $use_parent, $new_tag); $acl = $this->load_acl($page_id, $privilege, 1, 1, $use_parent, $new_tag); $this->_acl = $acl; // if current user is owner or admin, return true. they can do anything! if ($user == '' && $user_name != GUEST) { if ($this->user_is_owner($page_id) || $this->is_admin()) { return true; } } return $this->check_acl($user_name, $acl['list'], true); } // ... // REMOVALS # function remove_acls(object_id, $cluster = false) function remove_acls($tag, $cluster = false) { if (!$tag) { return false; } $this->query( "DELETE a.* ". "FROM ".$this->config['table_prefix']."acl a ". "LEFT JOIN ".$this->config['table_prefix']."page p ". "ON (a.page_id = p.page_id) ". "WHERE p.tag ".($cluster === true ? "LIKE" : "=")." '".quote($this->dblink, $tag.($cluster === true ? "/%" : ""))."' "); /*if ($acls = $this->load_all( "SELECT a.acl_id ". "FROM ".$this->config['table_prefix']."acl a ". "LEFT JOIN ".$this->config['table_prefix']."page p ". "INNER JOIN ".$this->config['table_prefix']."acl_type t ON (a.object_type_id = t.object_type_id) ". "ON (a.object_id = p.page_id) ". "WHERE p.tag ".($cluster === true ? "LIKE" : "=")." '".quote($this->dblink, $tag.($cluster === true ? "/%" : ""))."' ". "AND t.object_type = 'page'"))// later we will have also other objects than page { foreach ($acls as $acl) { // delete acl $this->query( "DELETE a.* ". "FROM ".$this->config['table_prefix']."acl a ". "WHERE a.acl_id = '".quote($this->dblink, $acl['acl_id'])."' "); // delete acl privilege $this->query( "DELETE a.* ". "FROM ".$this->config['table_prefix']."acl_privilege a ". "WHERE a.acl_id = '".quote($this->dblink, $acl['acl_id'])."' "); } }*/ return true; } ?> %% ===default values=== ===GUI=== - write - [] group [] user -- upload -- [Delete] [Add] ---- set permissions select a role [......] [user/group] [search] -> result [add] set permissions for this role #| || (default / extra) |allow |deny|| ||write | | || ||read | | || ||upload | | || ||create | | || ||comment | | || ||properties | | || ||permissions | | || ||approve | | || ||publish | | || ||... | | || |# ===Migration=== %%(hl php) <?php ######################################################## ## MIGRATE ACLs to new scheme ## ######################################################## if ($this->is_admin()) { if (!isset($_POST['migrate_acls'])) { echo "<h3>3. Migrates acls to new scheme:</h3>"; echo $this->form_open(); ?> <input type="submit" name="migrate_acls" value="<?php echo $this->_t('CategoriesSaveButton');?>" /> <?php echo $this->form_close(); } // migrate acls to new acl and acl_privilege table else if (isset($_POST['migrate_acls'])) { // load old ACLs $_acls = $this->load_all( "SELECT page_id, privilege, list ". "FROM {$this->config['table_prefix']}acl_old "); $old_acl_count = count($_acls); foreach ($_acls as $_acl) { echo $_acl['privilege'].'<br />'; // get object_right_id (e.g. 'write' -> 1, 'read' -> 2) $_object_right_id = $this->load_single( "SELECT acl_right_id ". "FROM {$this->config['table_prefix']}acl_right ". "WHERE object_right = '{$_acl['privilege']}' "); $object_right_id = $_object_right_id['acl_right_id']; // get object_type_id (e.g. 'page' -> 1) / there is only 'page' so far $_object_type_id = $this->load_single( "SELECT acl_type_id ". "FROM {$this->config['table_prefix']}acl_type ". "WHERE object_type = 'page' "); $object_type_id = $_object_type_id['acl_type_id']; // INSERT rights in 'acl' table $sql = "INSERT INTO {$this->config['table_prefix']}acl (object_id, object_type_id, object_right_id) VALUES ('{$_acl['page_id']}', '{$object_type_id}', '{$object_right_id}')"; $this->query($sql); // get new created $acl_id $acl_id = $this->load_single( "SELECT acl_id ". "FROM {$this->config['table_prefix']}acl ". "WHERE object_id = '{$_acl['page_id']}' ". "AND object_type_id = '{$object_type_id}' ". "AND object_right_id = '{$object_right_id}' "); $acl_id = $acl_id['acl_id']; // get user and group privileges $privileges = explode("\n", $_acl['list']); $this->debug_print_r($privileges); foreach ($privileges as $privilege) { if (!empty($privilege)) { $grant_id = ''; $grant_type_id = ''; $deny = ''; #$privilege = (string)$privilege; // look for '!' prefix, if true set $deny to true and remove it if ($privilege[0] == '!') { $deny = 1; $privilege = substr($privilege, 1); } else { $deny = 0; } echo $privilege.'<br />'; // is group? // 1. default groups // 1.1 Everybody if ($privilege == '*') { $_grant_id = $this->load_single( "SELECT group_id ". "FROM {$this->config['table_prefix']}group ". "WHERE group_name = 'Everybody' "); $grant_id = $_grant_id['group_id']; $grant_type_id = 1; } // 1.2 Registered else if ($privilege == '$') { $_grant_id = $this->load_single( "SELECT group_id ". "FROM {$this->config['table_prefix']}group ". "WHERE group_name = 'Registered' "); $grant_id = $_grant_id['group_id']; $grant_type_id = 1; } // 1.3 Admins else if ($privilege == 'Admins') { $_grant_id = $this->load_single( "SELECT group_id ". "FROM {$this->config['table_prefix']}group ". "WHERE group_name = 'Admins' "); $grant_id = $_grant_id['group_id']; $grant_type_id = 1; } else { // 2. non default groups if (!isset($this->groups)) { $_groups = $this->load_all( "SELECT group_name ". "FROM {$this->config['table_prefix']}group "); foreach ($_groups as $_group) { $groups[] = $_group['group_name']; } $this->groups = $groups; } $this->debug_print_r($groups); if (in_array($privilege, $this->groups)) { $grant_id = $this->load_single( "SELECT group_id ". "FROM {$this->config['table_prefix']}group ". "WHERE group_name = '{$privilege}' "); $grant_id = $grant_id['group_id']; $grant_type_id = 1; } else { // 3. users if (!isset($this->users)) { $_users = $this->load_all( "SELECT user_name ". "FROM {$this->config['table_prefix']}user "); foreach ($_users as $_user) { $users[] = $_user['user_name']; } $this->users = $users; } $this->debug_print_r($users); if (in_array($privilege, $this->users)) { $_grant_id = $this->load_single( "SELECT user_id ". "FROM {$this->config['table_prefix']}user ". "WHERE user_name = '{$privilege}' "); $grant_id = $_grant_id['user_id']; $grant_type_id = 2; } } } // INSERT privileges in 'acl_privilege' table $sql = "INSERT INTO {$this->config['table_prefix']}acl_privilege (acl_id, grant_type_id, grant_id, deny) VALUES ('{$acl_id}', '{$grant_type_id}', '{$grant_id}', '{$deny}')"; $this->query($sql); } } } echo '<br />'.$old_acl_count.' acl and '.$privilege_count.' privilege settings inserted.'; } } ?> %% ===Related Links=== ===Future Ideas=== ===Feedback=== * Please provide feedback