1<?php 2/** 3 * ACL administration functions 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Frank Schubert <frank@schokilade.de> 7 */ 8if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 9if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 10require_once(DOKU_PLUGIN.'admin.php'); 11 12/** 13 * All DokuWiki plugins to extend the admin function 14 * need to inherit from this class 15 */ 16class admin_plugin_acl extends DokuWiki_Admin_Plugin { 17 18 19 function admin_plugin_acl(){ 20 $this->setupLocale(); 21 } 22 23 24 25 /** 26 * return some info 27 */ 28 function getInfo(){ 29 return array( 30 'author' => 'Frank Schubert', 31 'email' => 'frank@schokilade.de', 32 'date' => '2005-08-08', 33 'name' => 'ACL', 34 'desc' => 'Manage Page Access Control Lists', 35 'url' => 'http://wiki.splitbrain.org/wiki:acl', 36 ); 37 } 38 39 /** 40 * return prompt for admin menu 41 */ 42 function getMenuText($language) { 43 return $this->lang['admin_acl']; 44 } 45 46 /** 47 * return sort order for position in admin menu 48 */ 49 function getMenuSort() { 50 return 1; 51 } 52 53 /** 54 * handle user request 55 */ 56 function handle() { 57 global $AUTH_ACL; 58 59 $cmd = $_REQUEST['acl_cmd']; 60 $scope = $_REQUEST['acl_scope']; 61 $type = $_REQUEST['acl_type']; 62 $user = $_REQUEST['acl_user']; 63 $perm = $_REQUEST['acl_perm']; 64 65 if(is_array($perm)){ 66 //use the maximum 67 sort($perm); 68 $perm = array_pop($perm); 69 }else{ 70 $perm = 0; 71 } 72 73 //sanitize 74 $user = cleanID($user); 75 if($type == '@') $user = '@'.$user; 76 if($user == '@all') $user = '@ALL'; //special group! (now case insensitive) 77 $perm = (int) $perm; 78 if($perm > AUTH_DELETE) $perm = AUTH_DELETE; 79 //FIXME sanitize scope!!! 80 81 //nothing to do? 82 if(empty($cmd) || empty($scope) || empty($user)) return; 83 84 85 if($cmd == 'save'){ 86 $this->admin_acl_del($scope, $user); 87 $this->admin_acl_add($scope, $user, $perm); 88 }elseif($cmd == 'delete'){ 89 $this->admin_acl_del($scope, $user); 90 } 91 92 // reload ACL config 93 $AUTH_ACL = file(DOKU_CONF.'acl.auth.php'); 94 } 95 96 /** 97 * ACL Output function 98 * 99 * print a table with all significant permissions for the 100 * current id 101 * 102 * @author Frank Schubert <frank@schokilade.de> 103 * @author Andreas Gohr <andi@splitbrain.org> 104 */ 105 function html() { 106 global $ID; 107 108 print $this->locale_xhtml('intro'); 109 110 ptln('<div class="acladmin">'); 111 ptln('<table class="inline">'); 112 113 //new 114 $this->admin_acl_html_new(); 115 116 //current config 117 $acls = $this->get_acl_config($ID); 118 foreach ($acls as $id => $acl){ 119 $this->admin_acl_html_current($id,$acl); 120 } 121 122 ptln('</table>'); 123 ptln('</div>'); 124 } 125 126 127 /** 128 * Get matching ACL lines for a page 129 * 130 * $ID is pagename, reads matching lines from $AUTH_ACL, 131 * also reads acls from namespace 132 * returns multi-array with key=pagename and value=array(user, acl) 133 * 134 * @todo Fix comment to make sense 135 * @todo should this moved to auth.php? 136 * @todo can this be combined with auth_aclcheck to avoid duplicate code? 137 * @author Frank Schubert <frank@schokilade.de> 138 */ 139 function get_acl_config($id){ 140 global $AUTH_ACL; 141 142 $acl_config=array(); 143 144 // match exact name 145 $matches = preg_grep('/^'.$id.'\s+.*/',$AUTH_ACL); 146 if(count($matches)){ 147 foreach($matches as $match){ 148 $match = preg_replace('/#.*$/','',$match); //ignore comments 149 $acl = preg_split('/\s+/',$match); 150 //0 is pagename, 1 is user, 2 is acl 151 $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]); 152 } 153 } 154 155 $specific_found=array(); 156 // match ns 157 while(($id=getNS($id)) !== false){ 158 $matches = preg_grep('/^'.$id.':\*\s+.*/',$AUTH_ACL); 159 if(count($matches)){ 160 foreach($matches as $match){ 161 $match = preg_replace('/#.*$/','',$match); //ignore comments 162 $acl = preg_split('/\s+/',$match); 163 //0 is pagename, 1 is user, 2 is acl 164 $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]); 165 $specific_found[]=$acl[1]; 166 } 167 } 168 } 169 170 //include *-config 171 $matches = preg_grep('/^\*\s+.*/',$AUTH_ACL); 172 if(count($matches)){ 173 foreach($matches as $match){ 174 $match = preg_replace('/#.*$/','',$match); //ignore comments 175 $acl = preg_split('/\s+/',$match); 176 // only include * for this user if not already found in ns 177 if(!in_array($acl[1], $specific_found)){ 178 //0 is pagename, 1 is user, 2 is acl 179 $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]); 180 } 181 } 182 } 183 184 //sort 185 //FIXME: better sort algo: first sort by key, then sort by first value 186 krsort($acl_config, SORT_STRING); 187 188 return($acl_config); 189 } 190 191 192 /** 193 * adds new acl-entry to conf/acl.auth.php 194 * 195 * @author Frank Schubert <frank@schokilade.de> 196 */ 197 function admin_acl_add($acl_scope, $acl_user, $acl_level){ 198 $acl_config = join("",file(DOKU_CONF.'acl.auth.php')); 199 200 // max level for pagenames is edit 201 if(strpos($acl_scope,'*') === false) { 202 if($acl_level > AUTH_EDIT) $acl_level = AUTH_EDIT; 203 } 204 205 $new_acl = "$acl_scope\t$acl_user\t$acl_level\n"; 206 207 $new_config = $acl_config.$new_acl; 208 209 return io_saveFile(DOKU_CONF.'acl.auth.php', $new_config); 210 } 211 212 /** 213 * remove acl-entry from conf/acl.auth.php 214 * 215 * @author Frank Schubert <frank@schokilade.de> 216 */ 217 function admin_acl_del($acl_scope, $acl_user){ 218 $acl_config = file(DOKU_CONF.'acl.auth.php'); 219 220 $acl_pattern = '^'.preg_quote($acl_scope,'/').'\s+'.$acl_user.'\s+[0-8].*$'; 221 222 // save all non!-matching #FIXME invert is available from 4.2.0 only! 223 $new_config = preg_grep("/$acl_pattern/", $acl_config, PREG_GREP_INVERT); 224 225 return io_saveFile(DOKU_CONF.'acl.auth.php', join('',$new_config)); 226 } 227 228 // --- HTML OUTPUT FUNCTIONS BELOW --- // 229 230 /** 231 * print tablerows with the current permissions for one id 232 * 233 * @author Frank Schubert <frank@schokilade.de> 234 * @author Andreas Gohr <andi@splitbrain.org> 235 */ 236 function admin_acl_html_dropdown($id){ 237 $cur = $id; 238 $ret = ''; 239 $opt = array(); 240 241 //prepare all options 242 243 // current page 244 $opt[] = array('key'=> $id, 'val'=> $id.' ('.$this->lang['page'].')'); 245 246 // additional namespaces 247 while(($id=getNS($id)) !== false){ 248 $opt[] = array('key'=> $id.':*', 'val'=> $id.':* ('.$this->lang['namespace'].')'); 249 } 250 251 // the top namespace 252 $opt[] = array('key'=> '*', 'val'=> '* ('.$this->lang['namespace'].')'); 253 254 // set sel on second entry (current namespace) 255 $opt[1]['sel'] = ' selected="selected"'; 256 257 // flip options 258 $opt = array_reverse($opt); 259 260 // create HTML 261 $att = array( 'name' => 'acl_scope', 262 'class' => 'edit', 263 'title' => $this->lang['page'].'/'.$this->lang['namespace']); 264 $ret .= '<select '.html_attbuild($att).'>'; 265 foreach($opt as $o){ 266 $ret .= '<option value="'.$o['key'].'"'.$o['sel'].'>'.$o['val'].'</option>'; 267 } 268 $ret .= '</select>'; 269 270 return $ret; 271 } 272 273 /** 274 * print tablerows with the current permissions for one id 275 * 276 * @author Frank Schubert <frank@schokilade.de> 277 * @author Andreas Gohr <andi@splitbrain.org> 278 */ 279 function admin_acl_html_new(){ 280 global $ID; 281 global $lang; 282 283 // table headers 284 ptln('<tr>',2); 285 ptln(' <th class="leftalign" colspan="3">'.$this->lang['acl_new'].'</th>',2); 286 ptln('</tr>',2); 287 288 ptln('<tr>',2); 289 290 ptln('<td class="centeralign" colspan="3">',4); 291 292 ptln(' <form method="post" action="'.wl($ID).'">',4); 293 ptln(' <input type="hidden" name="do" value="admin" />',4); 294 ptln(' <input type="hidden" name="page" value="acl" />',4); 295 ptln(' <input type="hidden" name="acl_cmd" value="save" />',4); 296 297 //scope select 298 ptln($this->lang['acl_perms'],4); 299 ptln($this->admin_acl_html_dropdown($ID),4); 300 301 $att = array( 'name' => 'acl_type', 302 'class' => 'edit', 303 'title' => $this->lang['acl_user'].'/'.$this->lang['acl_group']); 304 ptln(' <select '.html_attbuild($att).'>',4); 305 ptln(' <option value="@">'.$this->lang['acl_group'].'</option>',4); 306 ptln(' <option value="">'.$this->lang['acl_user'].'</option>',4); 307 ptln(' </select>',4); 308 309 $att = array( 'name' => 'acl_user', 310 'type' => 'text', 311 'class' => 'edit', 312 'title' => $this->lang['acl_user'].'/'.$this->lang['acl_group']); 313 ptln(' <input '.html_attbuild($att).' />',4); 314 ptln(' <br />'); 315 316 ptln( $this->admin_acl_html_checkboxes(0,false),8); 317 318 ptln(' <input type="submit" class="edit" value="'.$lang['btn_save'].'" />',4); 319 ptln(' </form>'); 320 ptln('</td>',4); 321 ptln('</tr>',2); 322 } 323 324 /** 325 * print tablerows with the current permissions for one id 326 * 327 * @author Frank Schubert <frank@schokilade.de> 328 * @author Andreas Gohr <andi@splitbrain.org> 329 */ 330 function admin_acl_html_current($id,$permissions){ 331 global $lang; 332 global $ID; 333 334 //is it a page? 335 if(substr($id,-1) == '*'){ 336 $ispage = false; 337 }else{ 338 $ispage = true; 339 } 340 341 // table headers 342 ptln(' <tr>'); 343 ptln(' <th class="leftalign" colspan="3">'); 344 ptln($this->lang['acl_perms'],6); 345 if($ispage){ 346 ptln($this->lang['page'],6); 347 }else{ 348 ptln($this->lang['namespace'],6); 349 } 350 ptln('<em>'.$id.'</em>',6); 351 ptln(' </th>'); 352 ptln(' </tr>'); 353 354 sort($permissions); 355 356 foreach ($permissions as $conf){ 357 //userfriendly group/user display 358 if(substr($conf['name'],0,1)=="@"){ 359 $group = $this->lang['acl_group']; 360 $name = substr($conf['name'],1); 361 $type = '@'; 362 }else{ 363 $group = $this->lang['acl_user']; 364 $name = $conf['name']; 365 $type = ''; 366 } 367 368 ptln('<tr>',2); 369 ptln('<td class="leftalign">'.$group.' '.$name.'</td>',4); 370 371 // update form 372 ptln('<td class="centeralign">',4); 373 ptln(' <form method="post" action="'.wl($ID).'">',4); 374 ptln(' <input type="hidden" name="do" value="admin" />',4); 375 ptln(' <input type="hidden" name="page" value="acl" />',4); 376 ptln(' <input type="hidden" name="acl_cmd" value="save" />',4); 377 ptln(' <input type="hidden" name="acl_scope" value="'.formtext($id).'" />',4); 378 ptln(' <input type="hidden" name="acl_type" value="'.$type.'" />',4); 379 ptln(' <input type="hidden" name="acl_user" value="'.formtext($name).'" />',4); 380 ptln( $this->admin_acl_html_checkboxes($conf['perm'],$ispage),8); 381 ptln(' <input type="submit" class="edit" value="'.$lang['btn_update'].'" />',4); 382 ptln(' </form>'); 383 ptln('</td>',4); 384 385 386 // deletion form 387 388 $ask = $lang['del_confirm'].'\\n'; 389 $ask .= $id.' '.$conf['name'].' '.$conf['perm']; 390 ptln('<td class="centeralign">',4); 391 ptln(' <form method="post" action="'.wl($ID).'" onsubmit="return confirm(\''.$ask.'\')">',4); 392 ptln(' <input type="hidden" name="do" value="admin" />',4); 393 ptln(' <input type="hidden" name="page" value="acl" />',4); 394 ptln(' <input type="hidden" name="acl_cmd" value="delete" />',4); 395 ptln(' <input type="hidden" name="acl_scope" value="'.formtext($id).'" />',4); 396 ptln(' <input type="hidden" name="acl_type" value="'.$type.'" />',4); 397 ptln(' <input type="hidden" name="acl_user" value="'.formtext($name).'" />',4); 398 ptln(' <input type="submit" class="edit" value="'.$lang['btn_delete'].'" />',4); 399 ptln(' </form>',4); 400 ptln('</td>',4); 401 402 ptln('</tr>',2); 403 } 404 405 } 406 407 408 /** 409 * print the permission checkboxes 410 * 411 * @author Frank Schubert <frank@schokilade.de> 412 * @author Andreas Gohr <andi@splitbrain.org> 413 */ 414 function admin_acl_html_checkboxes($setperm,$ispage){ 415 global $lang; 416 417 static $label = 0; //number labels 418 $ret = ''; 419 420 foreach(array(AUTH_READ,AUTH_EDIT,AUTH_CREATE,AUTH_UPLOAD,AUTH_DELETE) as $perm){ 421 $label += 1; 422 423 //general checkbox attributes 424 $atts = array( 'type' => 'checkbox', 425 'id' => 'pbox'.$label, 426 'name' => 'acl_perm[]', 427 'value' => $perm ); 428 //dynamic attributes 429 if($setperm >= $perm) $atts['checked'] = 'checked'; 430 # if($perm > AUTH_READ) $atts['onchange'] = #FIXME JS to autoadd lower perms 431 if($ispage && $perm > AUTH_EDIT) $atts['disabled'] = 'disabled'; 432 433 //build code 434 $ret .= '<label for="pbox'.$label.'" title="'.$this->lang['acl_perm'.$perm].'">'; 435 $ret .= '<input '.html_attbuild($atts).' />'; 436 $ret .= $this->lang['acl_perm'.$perm]; 437 $ret .= "</label>\n"; 438 } 439 return $ret; 440 } 441 442} 443