Skip to content
Snippets Groups Projects
Commit 6c2bb100 authored by Andreas Gohr's avatar Andreas Gohr
Browse files

Allow non-ID names in ACLs

Some auth backends allow special chars like whitespaces in user and group
names. This made problems with the existing ACL checks and ACL manager.
This patch makes the ACL system work with these cases by (url)encoding all
special chars below 128.

darcs-hash:20060302101850-6e07b-14bda9dbdb3528904325419b35bb9eddb0d1dde3.gz
parent a18f748f
No related branches found
No related tags found
No related merge requests found
<?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 :
......@@ -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
......
......@@ -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
*
......
......@@ -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 {
......
......@@ -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;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment