Extended Acls
bugs:issue[link1] in bugtrackerAuthor: WikiAdmin[link2]
1. Introduction
Introduction of ideaobject <- page
right <- privilege
role <- user
role <- group <- user
group -> organising users
role -> organising rights
2. database
2 of 2 Files accessible from this page :
acl_scheme.mwb[link3] | as workbench file | 8.7 KiB | 27.09.2010 19:03 | |
acl_scheme.png[link4] | new proposed ACL scheme | 18.4 KiB | 27.09.2010 19:03 |
Database Changes
- Required database changes to wacko_XYZ_table
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'
- add an table e.g. 'acl_static_object' and map your objects manually there
3. functions
#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; }
4. default values
5. 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 | ||
... |
6. Migration
######################################################## ## 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=" echo $this->_t('CategoriesSaveButton');" /> 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.'; } }
7. Related Links
8. Future Ideas
9. Feedback
- Please provide feedback
- [link1] https://wackowiki.org/bugs/view.php?id=issue
- [link2] https://wackowiki.org/doc/Users?profile=WikiAdmin
- [link3] https://wackowiki.org/doc/Dev/NewFeatures/ExtendedAcls/file?get=acl_scheme.mwb
- [link4] https://wackowiki.org/doc/Dev/NewFeatures/ExtendedAcls/file?get=acl_scheme.png