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