diff --git a/_test/cases/inc/auth_nameencode.test.php b/_test/cases/inc/auth_nameencode.test.php new file mode 100644 index 0000000000000000000000000000000000000000..6deb7de9f1b0e56c40b9699e4fcd4fbb11e94c1c --- /dev/null +++ b/_test/cases/inc/auth_nameencode.test.php @@ -0,0 +1,28 @@ +<?php + +require_once DOKU_INC.'inc/init.php'; +require_once DOKU_INC.'inc/auth.php'; + +class auth_nameencode_test extends UnitTestCase { + + function test_simple(){ + $in = 'hey$you'; + $out = 'hey%24you'; + $this->assertEqual(auth_nameencode($in),$out); + } + + function test_complex(){ + $in = 'hey $ you !$%! foo '; + $out = 'hey%20%24%20you%20%21%24%25%21%20foo%20'; + $this->assertEqual(auth_nameencode($in),$out); + } + + function test_complexutf8(){ + $in = 'häü $ yü !$%! foo '; + $out = 'häü%20%24%20yü%20%21%24%25%21%20foo%20'; + $this->assertEqual(auth_nameencode($in),$out); + } + +} + +//Setup VIM: ex: et ts=4 enc=utf-8 : diff --git a/conf/acl.auth.php.dist b/conf/acl.auth.php.dist index 3b074564843da2b4ee251eff15b267df0d7a42b2..3fa9741c5c34d1216b24a5043ccf3f07ab4d5623 100644 --- a/conf/acl.auth.php.dist +++ b/conf/acl.auth.php.dist @@ -2,7 +2,14 @@ # <?php exit()?> # Don't modify the lines above # -# Access Control +# Access Control Lists +# +# Editing this file by hand shouldn't be necessary. Use the ACL +# Manager interface instead. +# +# If your auth backend allows special char like spaces in groups +# or user names you need to urlencode them (only chars <128, leave +# UTF-8 multibyte chars as is) # # none 0 # read 1 diff --git a/inc/auth.php b/inc/auth.php index 6280cf1c1514934b9c8e23380e8f5ed7c8088d5f..79cae52e7034d1f91f3bc4c09101f196339fd7b3 100644 --- a/inc/auth.php +++ b/inc/auth.php @@ -264,16 +264,18 @@ function auth_aclcheck($id,$user,$groups){ # if no ACL is used always return upload rights if(!$conf['useacl']) return AUTH_UPLOAD; + $user = auth_nameencode($user); + //if user is superuser return 255 (acl_admin) if($conf['superuser'] == $user) { return AUTH_ADMIN; } //make sure groups is an array if(!is_array($groups)) $groups = array(); - //prepend groups with @ + //prepend groups with @ and nameencode $cnt = count($groups); for($i=0; $i<$cnt; $i++){ - $groups[$i] = '@'.$groups[$i]; + $groups[$i] = '@'.auth_nameencode($groups[$i]); } //if user is in superuser group return 255 (acl_admin) if(in_array($conf['superuser'], $groups)) { return AUTH_ADMIN; } @@ -350,6 +352,23 @@ function auth_aclcheck($id,$user,$groups){ return AUTH_NONE; } +/** + * Encode ASCII special chars + * + * Some auth backends allow special chars in their user and groupnames + * The special chars are encoded with this function. Only ASCII chars + * are encoded UTF-8 multibyte are left as is (different from usual + * urlencoding!). + * + * Decoding can be done with rawurldecode + * + * @author Andreas Gohr <gohr@cosmocode.de> + * @see rawurldecode() + */ +function auth_nameencode($name){ + return preg_replace('/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/e',"'%'.dechex(ord('\\1'))",$name); +} + /** * Create a pronouncable password * diff --git a/inc/auth/plain.class.php b/inc/auth/plain.class.php index 3ed0142620d79fcd4257607ea4be5f6ef6b0aa39..d3df1c0ddab1531938e98a3f7c36b7b4c67a227e 100644 --- a/inc/auth/plain.class.php +++ b/inc/auth/plain.class.php @@ -15,6 +15,8 @@ define('AUTH_USERFILE',DOKU_CONF.'users.auth.php'); // we only accept page ids for auth_plain if(isset($_REQUEST['u'])) $_REQUEST['u'] = cleanID($_REQUEST['u']); +if(isset($_REQUEST['acl_user'])) + $_REQUEST['acl_user'] = cleanID($_REQUEST['acl_user']); class auth_plain extends auth_basic { diff --git a/lib/plugins/acl/admin.php b/lib/plugins/acl/admin.php index 5dc62edabe3a4dda3f91cb4bd9a27623743358f0..7295141cdfd33872f6fe29a1b420eac1e2b40d38 100644 --- a/lib/plugins/acl/admin.php +++ b/lib/plugins/acl/admin.php @@ -8,7 +8,7 @@ if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'admin.php'); - + /** * All DokuWiki plugins to extend the admin function * need to inherit from this class @@ -16,12 +16,12 @@ require_once(DOKU_PLUGIN.'admin.php'); class admin_plugin_acl extends DokuWiki_Admin_Plugin { - function admin_plugin_acl(){ - $this->setupLocale(); - } + function admin_plugin_acl(){ + $this->setupLocale(); + } + - /** * return some info */ @@ -35,33 +35,33 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { 'url' => 'http://wiki.splitbrain.org/wiki:acl', ); } - + /** * return prompt for admin menu */ function getMenuText($language) { return $this->lang['admin_acl']; } - + /** * return sort order for position in admin menu */ function getMenuSort() { return 1; } - + /** * handle user request */ function handle() { global $AUTH_ACL; - + $cmd = $_REQUEST['acl_cmd']; $scope = $_REQUEST['acl_scope']; $type = $_REQUEST['acl_type']; $user = $_REQUEST['acl_user']; $perm = $_REQUEST['acl_perm']; - + if(is_array($perm)){ //use the maximum sort($perm); @@ -69,30 +69,30 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { }else{ $perm = 0; } - + //sanitize - $user = cleanID($user); + $user = auth_nameencode($user); if($type == '@') $user = '@'.$user; if($user == '@all') $user = '@ALL'; //special group! (now case insensitive) $perm = (int) $perm; if($perm > AUTH_DELETE) $perm = AUTH_DELETE; //FIXME sanitize scope!!! - + //nothing to do? if(empty($cmd) || empty($scope) || empty($user)) return; - - + + if($cmd == 'save'){ $this->admin_acl_del($scope, $user); - $this->admin_acl_add($scope, $user, $perm); + $this->admin_acl_add($scope, $user, $perm); }elseif($cmd == 'delete'){ $this->admin_acl_del($scope, $user); } - + // reload ACL config $AUTH_ACL = file(DOKU_CONF.'acl.auth.php'); } - + /** * ACL Output function * @@ -104,26 +104,26 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { */ function html() { global $ID; - + print $this->locale_xhtml('intro'); - + ptln('<div class="acladmin">'); ptln('<table class="inline">'); - + //new $this->admin_acl_html_new(); - + //current config $acls = $this->get_acl_config($ID); foreach ($acls as $id => $acl){ - $this->admin_acl_html_current($id,$acl); + $this->admin_acl_html_current($id,$acl); } - + ptln('</table>'); ptln('</div>'); } - - + + /** * Get matching ACL lines for a page * @@ -138,9 +138,9 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { */ function get_acl_config($id){ global $AUTH_ACL; - + $acl_config=array(); - + // match exact name $matches = preg_grep('/^'.$id.'\s+.*/',$AUTH_ACL); if(count($matches)){ @@ -151,7 +151,7 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]); } } - + $specific_found=array(); // match ns while(($id=getNS($id)) !== false){ @@ -166,7 +166,7 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { } } } - + //include *-config $matches = preg_grep('/^\*\s+.*/',$AUTH_ACL); if(count($matches)){ @@ -180,15 +180,15 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { } } } - + //sort //FIXME: better sort algo: first sort by key, then sort by first value krsort($acl_config, SORT_STRING); - + return($acl_config); } - - + + /** * adds new acl-entry to conf/acl.auth.php * @@ -196,19 +196,19 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { */ function admin_acl_add($acl_scope, $acl_user, $acl_level){ $acl_config = join("",file(DOKU_CONF.'acl.auth.php')); - + // max level for pagenames is edit if(strpos($acl_scope,'*') === false) { if($acl_level > AUTH_EDIT) $acl_level = AUTH_EDIT; } - + $new_acl = "$acl_scope\t$acl_user\t$acl_level\n"; - + $new_config = $acl_config.$new_acl; - + return io_saveFile(DOKU_CONF.'acl.auth.php', $new_config); } - + /** * remove acl-entry from conf/acl.auth.php * @@ -216,17 +216,17 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { */ function admin_acl_del($acl_scope, $acl_user){ $acl_config = file(DOKU_CONF.'acl.auth.php'); - + $acl_pattern = '^'.preg_quote($acl_scope,'/').'\s+'.$acl_user.'\s+[0-8].*$'; - + // save all non!-matching #FIXME invert is available from 4.2.0 only! $new_config = preg_grep("/$acl_pattern/", $acl_config, PREG_GREP_INVERT); - + return io_saveFile(DOKU_CONF.'acl.auth.php', join('',$new_config)); } - + // --- HTML OUTPUT FUNCTIONS BELOW --- // - + /** * print tablerows with the current permissions for one id * @@ -237,26 +237,26 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { $cur = $id; $ret = ''; $opt = array(); - + //prepare all options - + // current page $opt[] = array('key'=> $id, 'val'=> $id.' ('.$this->lang['page'].')'); - + // additional namespaces while(($id=getNS($id)) !== false){ $opt[] = array('key'=> $id.':*', 'val'=> $id.':* ('.$this->lang['namespace'].')'); } - + // the top namespace $opt[] = array('key'=> '*', 'val'=> '* ('.$this->lang['namespace'].')'); - + // set sel on second entry (current namespace) $opt[1]['sel'] = ' selected="selected"'; - + // flip options $opt = array_reverse($opt); - + // create HTML $att = array( 'name' => 'acl_scope', 'class' => 'edit', @@ -266,10 +266,10 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { $ret .= '<option value="'.$o['key'].'"'.$o['sel'].'>'.$o['val'].'</option>'; } $ret .= '</select>'; - + return $ret; } - + /** * print tablerows with the current permissions for one id * @@ -278,26 +278,26 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { */ function admin_acl_html_new(){ global $ID; - global $lang; - + global $lang; + // table headers ptln('<tr>',2); ptln(' <th class="leftalign" colspan="3">'.$this->lang['acl_new'].'</th>',2); ptln('</tr>',2); - + ptln('<tr>',2); - + ptln('<td class="centeralign" colspan="3">',4); - + ptln(' <form method="post" action="'.wl($ID).'">',4); ptln(' <input type="hidden" name="do" value="admin" />',4); ptln(' <input type="hidden" name="page" value="acl" />',4); ptln(' <input type="hidden" name="acl_cmd" value="save" />',4); - + //scope select ptln($this->lang['acl_perms'],4); - ptln($this->admin_acl_html_dropdown($ID),4); - + ptln($this->admin_acl_html_dropdown($ID),4); + $att = array( 'name' => 'acl_type', 'class' => 'edit', 'title' => $this->lang['acl_user'].'/'.$this->lang['acl_group']); @@ -305,22 +305,22 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { ptln(' <option value="@">'.$this->lang['acl_group'].'</option>',4); ptln(' <option value="">'.$this->lang['acl_user'].'</option>',4); ptln(' </select>',4); - + $att = array( 'name' => 'acl_user', 'type' => 'text', 'class' => 'edit', 'title' => $this->lang['acl_user'].'/'.$this->lang['acl_group']); ptln(' <input '.html_attbuild($att).' />',4); ptln(' <br />'); - + ptln( $this->admin_acl_html_checkboxes(0,false),8); - + ptln(' <input type="submit" class="edit" value="'.$lang['btn_save'].'" />',4); ptln(' </form>'); ptln('</td>',4); ptln('</tr>',2); } - + /** * print tablerows with the current permissions for one id * @@ -330,14 +330,14 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { function admin_acl_html_current($id,$permissions){ global $lang; global $ID; - + //is it a page? if(substr($id,-1) == '*'){ $ispage = false; }else{ $ispage = true; } - + // table headers ptln(' <tr>'); ptln(' <th class="leftalign" colspan="3">'); @@ -350,11 +350,12 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { ptln('<em>'.$id.'</em>',6); ptln(' </th>'); ptln(' </tr>'); - + sort($permissions); - + foreach ($permissions as $conf){ //userfriendly group/user display + $conf['name'] = rawurldecode($conf['name']); if(substr($conf['name'],0,1)=="@"){ $group = $this->lang['acl_group']; $name = substr($conf['name'],1); @@ -364,10 +365,10 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { $name = $conf['name']; $type = ''; } - + ptln('<tr>',2); - ptln('<td class="leftalign">'.$group.' '.$name.'</td>',4); - + ptln('<td class="leftalign">'.htmlspecialchars($group.' '.$name).'</td>',4); + // update form ptln('<td class="centeralign">',4); ptln(' <form method="post" action="'.wl($ID).'">',4); @@ -381,14 +382,14 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { ptln(' <input type="submit" class="edit" value="'.$lang['btn_update'].'" />',4); ptln(' </form>'); ptln('</td>',4); - - + + // deletion form - + $ask = $lang['del_confirm'].'\\n'; $ask .= $id.' '.$conf['name'].' '.$conf['perm']; ptln('<td class="centeralign">',4); - ptln(' <form method="post" action="'.wl($ID).'" onsubmit="return confirm(\''.$ask.'\')">',4); + ptln(' <form method="post" action="'.wl($ID).'" onsubmit="return confirm(\''.str_replace('\\\\n','\\n',addslashes($ask)).'\')">',4); ptln(' <input type="hidden" name="do" value="admin" />',4); ptln(' <input type="hidden" name="page" value="acl" />',4); ptln(' <input type="hidden" name="acl_cmd" value="delete" />',4); @@ -398,13 +399,13 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { ptln(' <input type="submit" class="edit" value="'.$lang['btn_delete'].'" />',4); ptln(' </form>',4); ptln('</td>',4); - + ptln('</tr>',2); } - + } - - + + /** * print the permission checkboxes * @@ -413,13 +414,13 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { */ function admin_acl_html_checkboxes($setperm,$ispage){ global $lang; - + static $label = 0; //number labels $ret = ''; - + foreach(array(AUTH_READ,AUTH_EDIT,AUTH_CREATE,AUTH_UPLOAD,AUTH_DELETE) as $perm){ $label += 1; - + //general checkbox attributes $atts = array( 'type' => 'checkbox', 'id' => 'pbox'.$label, @@ -429,7 +430,7 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { if($setperm >= $perm) $atts['checked'] = 'checked'; # if($perm > AUTH_READ) $atts['onchange'] = #FIXME JS to autoadd lower perms if($ispage && $perm > AUTH_EDIT) $atts['disabled'] = 'disabled'; - + //build code $ret .= '<label for="pbox'.$label.'" title="'.$this->lang['acl_perm'.$perm].'">'; $ret .= '<input '.html_attbuild($atts).' />'; @@ -438,5 +439,5 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin { } return $ret; } - + }