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