1<?php 2/** 3 * Permissioninfo: Displays group and user information and the permissions of users and groups 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Gabriel Birke <gb@birke-software.de> 7 */ 8 9if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 10if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 11require_once(DOKU_PLUGIN.'admin.php'); 12 13/** 14 * All DokuWiki plugins to extend the admin function 15 * need to inherit from this class 16 */ 17class admin_plugin_permissioninfo extends DokuWiki_Admin_Plugin { 18 19 /** 20 * If information about which user is in which group is displayed 21 */ 22 var $show_group_info = true; 23 24 /** 25 * return sort order for position in admin menu 26 */ 27 function getMenuSort() { 28 return 145; 29 } 30 31 /** 32 * handle user request 33 */ 34 function handle() { 35 global $conf; 36 global $auth; 37 global $INPUT; 38 $this->auth = $auth; 39 40 // If the auth class can't list users or groups, retrieve user and group information from ACL 41 if($this->auth->canDo('getUsers')) 42 { 43 $getUserFunc = array($this->auth, 'retrieveUsers'); 44 } 45 else 46 { 47 // Can't determine user/group association from ACL 48 $this->show_group_info = false; 49 $getUserFunc = array($this, "_getUsersFromACL"); 50 } 51 if($this->auth->canDo('getGroups')) 52 $getGroupFunc = array($this->auth, 'retrieveGroups'); 53 else 54 $getGroupFunc = array($this, '_getGroupsFromACL'); 55 // Collect user and group names 56 $this->users = call_user_func($getUserFunc); 57 $this->groups = call_user_func($getGroupFunc); 58 ksort($this->groups); 59 60 // Get permissions for each group and set the data in $this->aclGroupPermissions 61 $this->_aclGroupPermissions(); 62 63 // Get explicit user permissions from ACL and set the data in $this->aclUserPermissions 64 $this->_aclUserPermissions(); 65 66 // Associate groups with users and set the data in $this->group2user 67 $this->_group2user(); 68 69 // If we show permissions for an individual user, collect its permissions 70 if($INPUT->has('show') && $INPUT->has('user')) 71 { 72 $this->_userPermissions($INPUT->str('user')); 73 } 74 } 75 76 /** 77 * output Overview page with groups or permissionpage for individual user, all 78 * depending on $_REQUEST['show'] 79 */ 80 function html() { 81 global $INPUT; 82 switch($INPUT->str('show','overview')) 83 { 84 case 'userpermissions': 85 $this->_showUserPermissions(); 86 break; 87 case 'overview': 88 default: 89 $this->_groupOverview(); 90 } 91 } 92 93 /** 94 * Shows an overview for users in groups and permissions assigned to groups 95 */ 96 function _groupOverview() 97 { 98 $id = cleanID($this->getLang('menu')); 99 ptln('<h1><a name="'.$id.'" id="'.$id.'">'.$this->getLang('menu')."</a></h1>"); 100 echo $this->locale_xhtml('help'); 101 foreach($this->groups as $gname => $g) 102 { 103 // container for group information 104 ptln('<section class="piContainer">'); 105 // print group header 106 ptln('<header>', 2); 107 ptln("<h2>".hsc($gname)."</h2>", 4); 108 ptln('</header>', 2); 109 110 ptln('<div class="content">', 2); 111 112 // print acl settings for this group 113 ptln('<header>'.$this->getLang('permissions').'</header>', 4); 114 $this->_permissionTable($this->aclGroupPermissions[$gname], "permissions".$gname); 115 116 // print users in group 117 if(!empty($this->group2user[$gname])) { 118 ptln('<header>'.$this->getLang('users').'</header>', 4); 119 ptln('<div class="users">', 4); 120 foreach($this->group2user[$gname] as $u) 121 { 122 $url = wl($ID, array( 123 'do' => "admin", 124 'page' => $this->getPluginName(), 125 'show' => 'userpermissions', 126 'user' => $u 127 )); 128 $u_enc = auth_nameencode($u); 129 $lnk = '<a href="'.$url.'" title="'.hsc($u).'" '.(!empty($this->aclUserPermissions[$u_enc])?'class="special"':"").'>'; 130 ptln($lnk. hsc($this->users[$u]['name']) .'</a>', 6); 131 } 132 ptln(' </div>'); 133 } 134 135 // close content div 136 ptln('</div>', 2); 137 138 //end container 139 ptln('</section>'); 140 } 141 } 142 143 /** 144 * Show permissions for individual user, highlight permissions that were 145 * assigned explicitly to this user. 146 */ 147 function _showUserPermissions() 148 { 149 $head = sprintf($this->getLang('pi_permissionfor'), hsc($this->username)); 150 $id = cleanID($head); 151 ptln('<h1><a name="'.$id.'" id="'.$id.'">'.$head."</a></h1>"); 152 echo $this->locale_xhtml('help_userpermissions'); 153 154 // Link to Overview 155 $url =wl($ID, array( 156 'do' => "admin", 157 'page' => $this->getPluginName(), 158 'show' => 'overview' 159 )); 160 ptln('<p class="piToOverview"><a href="'.$url.'">'.$this->getLang('pi_to_overview')."</a></p>"); 161 162 ptln('<div class="piContainer">'); 163 $this->_permissionTable($this->userPermissions, 'Userpermissions'); 164 ptln('</div>'); 165 } 166 167 /** 168 * Print permissions for a user or group 169 * @param array $acldata namespace/page_name=>permission pairs 170 * @param string $id ID for the div that surrounds the table 171 */ 172 function _permissionTable($acldata) 173 { 174 $displayed_permissions = array( 175 AUTH_READ, 176 AUTH_EDIT, 177 AUTH_CREATE, 178 AUTH_UPLOAD, 179 AUTH_DELETE 180 ); 181 ptln(" <div class='permissions'>"); 182 if(empty($acldata)) 183 { 184 ptln(" <p>".$this->getLang('pi_no_permissions_found').'</p>'); 185 ptln(" </div>"); 186 return; 187 } 188 ptln(" <table>"); 189 $s = "<tr><th>".$this->getLang('pi_resource')."</th>"; 190 foreach($displayed_permissions as $p) 191 $s .= "<th>".$this->getLang('acl_perm'.$p)."</td>"; 192 ptln($s."</tr>",6); 193 194 195 $even = false; 196 foreach($acldata as $item => $perm) 197 { 198 $additional_class = empty($this->explicitUserPermissions[$item]) ? "" : " explicitUserPermission"; 199 ptln('<tr class="'.($even?"even":"odd").$additional_class.'">', 6); 200 if(preg_match('/\*\s*$/', $item)) 201 ptln('<td class="piItemNS">'.$item.'</td>', 9); 202 else 203 ptln('<td class="piItemPage">'.$item.'</td>',9); 204 foreach($displayed_permissions as $p) 205 { 206 if($p & $perm) 207 ptln('<td class="piAllowed">X</td>', 9); 208 else 209 ptln('<td class="piDenied">-</td>', 9); 210 } 211 $even = !$even; 212 } 213 ptln(" </table>"); 214 ptln(" </div>"); 215 } 216 217 218 /** 219 * This just gets a very rudimentary user and not very useful user list - 220 * only users who have special permissions in the ACL are listed. 221 * @return array This array is structured similar to the array returned by an auth class. 222 */ 223 function _getUsersFromACL() 224 { 225 global $AUTH_ACL; 226 $users = array(); 227 foreach($AUTH_ACL as $a) 228 { 229 // Don't parse comments 230 if(preg_match('/^#/', $a)) 231 continue; 232 if(preg_match('/^[^\s]+\s([^@\s]+)/', $a, $matches)) 233 { 234 $usr_arr = array('name' => $matches[1], 'grps' => array()); 235 $users[$matches[1]] = $usr_arr; 236 } 237 } 238 return $users; 239 } 240 241 /** 242 * This function retrieves group names from the acl file. 243 * Since none of the existing auth classes supports groups, I don't know 244 * what output to expect from them. I assume a two-dimensional hash similar 245 * to that from Auth->retrieveUsers. 246 * @return array 247 */ 248 function _getGroupsFromACL() 249 { 250 global $AUTH_ACL; 251 $groups = array(); 252 foreach($AUTH_ACL as $a) 253 { 254 // Don't parse comments 255 if(preg_match('/^#/', $a)) 256 continue; 257 if(preg_match('/^[^\s]+\s@([^\s]+)/', $a, $matches)) 258 { 259 $grp_arr = array('name' => $matches[1]); 260 $groups[urldecode($matches[1])] = $grp_arr; 261 } 262 } 263 return $groups; 264 } 265 266 /** 267 * sets $this->aclGroupPermissions in the form of a[groupname][namespace/page_name]=permission 268 */ 269 function _aclGroupPermissions() 270 { 271 $AUTH_ACL = $this->_auth_loadACL(); //without %USER% replacement 272 $gp = array(); 273 foreach($AUTH_ACL as $a) 274 { 275 // Don't parse comments 276 if(preg_match('/^#/', $a)) 277 continue; 278 if(preg_match('/^([^\s]+)\s@([^\s]+)\s(\d+)/', $a, $matches)) 279 { 280 $gp[$matches[2]][$matches[1]] = $matches[3]; 281 } 282 } 283 $this->aclGroupPermissions = array(); 284 foreach($gp as $grpname => $permissions) 285 { 286 ksort($permissions); 287 $this->aclGroupPermissions[urldecode($grpname)] = $permissions; 288 } 289 } 290 291 /** 292 * sets $this->aclUserPermissions in the form of a[username][namespace/page_name]=permission 293 */ 294 function _aclUserPermissions() 295 { 296 global $AUTH_ACL; 297 $up = array(); 298 foreach($AUTH_ACL as $a) 299 { 300 // Don't parse comments 301 if(preg_match('/^#/', $a)) 302 continue; 303 if(preg_match('/^([^\s]+)\s([^@\s]+)\s(\d+)/', $a, $matches)) 304 { 305 $up[$matches[2]][$matches[1]] = $matches[3]; 306 } 307 } 308 $this->aclUserPermissions = array(); 309 foreach($up as $usrname => $permissions) 310 { 311 ksort($permissions); 312 $this->aclUserPermissions[$usrname] = $permissions; 313 } 314 } 315 316 /** 317 * Build an Array in $this->group2user that associates user names with users 318 * The users are sorted by last name 319 */ 320 function _group2user() 321 { 322 $g2u = array(); 323 foreach(array_keys($this->groups) as $g) 324 $g2u[$g] = array(); 325 foreach($this->users as $username => $properties) 326 { 327 foreach($properties['grps'] as $grpname) 328 $g2u[$grpname][$username] = array_pop(explode(' ', $properties['name'])); // Store last name of user here for Sorting 329 } 330 $this->group2user = array(); 331 foreach($g2u as $grpname => $users) 332 { 333 // Sort users in each group by last name 334 asort($users); 335 $this->group2user[$grpname] = array_keys($users); 336 } 337 } 338 339 /** 340 * Collects permission data for an individual user from the ACL. It 341 * collects permission data from the groups of the user and from the 342 * explicitly assigned permissions for the user. 343 * The data is stored in the form of two arrays: 344 * $this->userPermissions Namespace/Page => Permission pairs 345 * $this->explicitUserPermissions Namespace/Page => Permission pairs 346 */ 347 function _userPermissions($username) 348 { 349 // Build regular expression for the username an its groups 350 $userdata = $this->auth->getUserData($username); 351 $this->username = $userdata['name']; 352 $search_string = preg_quote(auth_nameencode($username), '/'); 353 foreach($userdata['grps'] as $g) 354 $search_string .= '|@'.preg_quote(auth_nameencode($g), '/'); 355 $perm_regex = '/^([^\s]+)\s('.$search_string.')\s(\d+)/'; 356 // Search through permissions 357 $AUTH_ACL = $this->_auth_loadACL(); //without user replacement 358 $up = array(); 359 // $for_user holds permissions that are assigned explicitly to the user 360 $for_user = array(); 361 foreach($AUTH_ACL as $a) 362 { 363 // Don't parse comments 364 if(preg_match('/^#/', $a)) 365 continue; 366 if(preg_match($perm_regex, $a, $matches)) 367 { 368 $ns = str_replace('%USER%',auth_nameencode($username),$matches[1]); //replace %USER% with username 369 $up[$ns] = (empty($up[$matches[1]])?0:$up[$matches[1]]) | $matches[3]; 370 if(substr($matches[2], 0, 1) != "@") 371 $for_user[$ns] = $matches[3]; 372 } 373 } 374 ksort($up); 375 ksort($for_user); 376 $this->userPermissions = $up; 377 $this->explicitUserPermissions = $for_user; 378 } 379 /** 380 * Loads the ACL setup 381 * 382 * copyed from inc/auth -> auth_loadACL() 383 * - removed substitute of user wildcard 384 */ 385 function _auth_loadACL() { 386 global $config_cascade; 387 global $USERINFO; 388 389 if(!is_readable($config_cascade['acl']['default'])) return array(); 390 391 $acl = file($config_cascade['acl']['default']); 392 393 $out = array(); 394 foreach($acl as $line) { 395 $line = trim($line); 396 if(empty($line) || ($line{0} == '#')) continue; // skip blank lines & comments 397 list($id,$rest) = preg_split('/[ \t]+/',$line,2); 398 399 // substitute group wildcard (its 1:m) 400 if(strstr($line, '%GROUP%')){ 401 // if user is not logged in, grps is empty, no output will be added (i.e. skipped) 402 foreach((array) $USERINFO['grps'] as $grp){ 403 $nid = str_replace('%GROUP%',cleanID($grp),$id); 404 $nrest = str_replace('%GROUP%','@'.auth_nameencode($grp),$rest); 405 $out[] = "$nid\t$nrest"; 406 } 407 } else { 408 $out[] = "$id\t$rest"; 409 } 410 } 411 412 return $out; 413 } 414} 415 416