1<?php 2// must be run within Dokuwiki 3if(!defined('DOKU_INC')) die(); 4 5/** 6 * Auth Plugin Prototype 7 * 8 * foundation authorisation class 9 * all auth classes should inherit from this class 10 * 11 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 12 * @author Chris Smith <chris@jalakai.co.uk> 13 * @author Jan Schumann <js@jschumann-it.com> 14 */ 15class DokuWiki_Auth_Plugin extends DokuWiki_Plugin { 16 public $success = true; 17 18 /** 19 * Possible things an auth backend module may be able to 20 * do. The things a backend can do need to be set to true 21 * in the constructor. 22 */ 23 protected $cando = array( 24 'addUser' => false, // can Users be created? 25 'delUser' => false, // can Users be deleted? 26 'modLogin' => false, // can login names be changed? 27 'modPass' => false, // can passwords be changed? 28 'modName' => false, // can real names be changed? 29 'modMail' => false, // can emails be changed? 30 'modGroups' => false, // can groups be changed? 31 'getUsers' => false, // can a (filtered) list of users be retrieved? 32 'getUserCount'=> false, // can the number of users be retrieved? 33 'getGroups' => false, // can a list of available groups be retrieved? 34 'external' => false, // does the module do external auth checking? 35 'logout' => true, // can the user logout again? (eg. not possible with HTTP auth) 36 ); 37 38 /** 39 * Constructor. 40 * 41 * Carry out sanity checks to ensure the object is 42 * able to operate. Set capabilities in $this->cando 43 * array here 44 * 45 * Set $this->success to false if checks fail 46 * 47 * @author Christopher Smith <chris@jalakai.co.uk> 48 */ 49 public function __construct() { 50 // the base class constructor does nothing, derived class 51 // constructors do the real work 52 } 53 54 /** 55 * Capability check. [ DO NOT OVERRIDE ] 56 * 57 * Checks the capabilities set in the $this->cando array and 58 * some pseudo capabilities (shortcutting access to multiple 59 * ones) 60 * 61 * ususal capabilities start with lowercase letter 62 * shortcut capabilities start with uppercase letter 63 * 64 * @author Andreas Gohr <andi@splitbrain.org> 65 * @param string $cap the capability to check 66 * @return bool 67 */ 68 public function canDo($cap) { 69 switch($cap) { 70 case 'Profile': 71 // can at least one of the user's properties be changed? 72 return ($this->cando['modPass'] || 73 $this->cando['modName'] || 74 $this->cando['modMail']); 75 break; 76 case 'UserMod': 77 // can at least anything be changed? 78 return ($this->cando['modPass'] || 79 $this->cando['modName'] || 80 $this->cando['modMail'] || 81 $this->cando['modLogin'] || 82 $this->cando['modGroups'] || 83 $this->cando['modMail']); 84 break; 85 default: 86 // print a helping message for developers 87 if(!isset($this->cando[$cap])) { 88 msg("Check for unknown capability '$cap' - Do you use an outdated Plugin?", -1); 89 } 90 return $this->cando[$cap]; 91 } 92 } 93 94 /** 95 * Trigger the AUTH_USERDATA_CHANGE event and call the modification function. [ DO NOT OVERRIDE ] 96 * 97 * You should use this function instead of calling createUser, modifyUser or 98 * deleteUsers directly. The event handlers can prevent the modification, for 99 * example for enforcing a user name schema. 100 * 101 * @author Gabriel Birke <birke@d-scribe.de> 102 * @param string $type Modification type ('create', 'modify', 'delete') 103 * @param array $params Parameters for the createUser, modifyUser or deleteUsers method. The content of this array depends on the modification type 104 * @return mixed Result from the modification function or false if an event handler has canceled the action 105 */ 106 public function triggerUserMod($type, $params) { 107 $validTypes = array( 108 'create' => 'createUser', 109 'modify' => 'modifyUser', 110 'delete' => 'deleteUsers' 111 ); 112 if(empty($validTypes[$type])) 113 return false; 114 $eventdata = array('type' => $type, 'params' => $params, 'modification_result' => null); 115 $evt = new Doku_Event('AUTH_USER_CHANGE', $eventdata); 116 if($evt->advise_before(true)) { 117 $result = call_user_func_array(array($this, $validTypes[$type]), $params); 118 $evt->data['modification_result'] = $result; 119 } 120 $evt->advise_after(); 121 unset($evt); 122 return $result; 123 } 124 125 /** 126 * Log off the current user [ OPTIONAL ] 127 * 128 * Is run in addition to the ususal logoff method. Should 129 * only be needed when trustExternal is implemented. 130 * 131 * @see auth_logoff() 132 * @author Andreas Gohr <andi@splitbrain.org> 133 */ 134 public function logOff() { 135 } 136 137 /** 138 * Do all authentication [ OPTIONAL ] 139 * 140 * Set $this->cando['external'] = true when implemented 141 * 142 * If this function is implemented it will be used to 143 * authenticate a user - all other DokuWiki internals 144 * will not be used for authenticating, thus 145 * implementing the checkPass() function is not needed 146 * anymore. 147 * 148 * The function can be used to authenticate against third 149 * party cookies or Apache auth mechanisms and replaces 150 * the auth_login() function 151 * 152 * The function will be called with or without a set 153 * username. If the Username is given it was called 154 * from the login form and the given credentials might 155 * need to be checked. If no username was given it 156 * the function needs to check if the user is logged in 157 * by other means (cookie, environment). 158 * 159 * The function needs to set some globals needed by 160 * DokuWiki like auth_login() does. 161 * 162 * @see auth_login() 163 * @author Andreas Gohr <andi@splitbrain.org> 164 * 165 * @param string $user Username 166 * @param string $pass Cleartext Password 167 * @param bool $sticky Cookie should not expire 168 * @return bool true on successful auth 169 */ 170 public function trustExternal($user, $pass, $sticky = false) { 171 /* some example: 172 173 global $USERINFO; 174 global $conf; 175 $sticky ? $sticky = true : $sticky = false; //sanity check 176 177 // do the checking here 178 179 // set the globals if authed 180 $USERINFO['name'] = 'FIXME'; 181 $USERINFO['mail'] = 'FIXME'; 182 $USERINFO['grps'] = array('FIXME'); 183 $_SERVER['REMOTE_USER'] = $user; 184 $_SESSION[DOKU_COOKIE]['auth']['user'] = $user; 185 $_SESSION[DOKU_COOKIE]['auth']['pass'] = $pass; 186 $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; 187 return true; 188 189 */ 190 } 191 192 /** 193 * Check user+password [ MUST BE OVERRIDDEN ] 194 * 195 * Checks if the given user exists and the given 196 * plaintext password is correct 197 * 198 * May be ommited if trustExternal is used. 199 * 200 * @author Andreas Gohr <andi@splitbrain.org> 201 * @param string $user the user name 202 * @param string $pass the clear text password 203 * @return bool 204 */ 205 public function checkPass($user, $pass) { 206 msg("no valid authorisation system in use", -1); 207 return false; 208 } 209 210 /** 211 * Return user info [ MUST BE OVERRIDDEN ] 212 * 213 * Returns info about the given user needs to contain 214 * at least these fields: 215 * 216 * name string full name of the user 217 * mail string email addres of the user 218 * grps array list of groups the user is in 219 * 220 * @author Andreas Gohr <andi@splitbrain.org> 221 * @param string $user the user name 222 * @return array containing user data or false 223 */ 224 public function getUserData($user) { 225 if(!$this->cando['external']) msg("no valid authorisation system in use", -1); 226 return false; 227 } 228 229 /** 230 * Create a new User [implement only where required/possible] 231 * 232 * Returns false if the user already exists, null when an error 233 * occurred and true if everything went well. 234 * 235 * The new user HAS TO be added to the default group by this 236 * function! 237 * 238 * Set addUser capability when implemented 239 * 240 * @author Andreas Gohr <andi@splitbrain.org> 241 * @param string $user 242 * @param string $pass 243 * @param string $name 244 * @param string $mail 245 * @param null|array $grps 246 * @return bool|null 247 */ 248 public function createUser($user, $pass, $name, $mail, $grps = null) { 249 msg("authorisation method does not allow creation of new users", -1); 250 return null; 251 } 252 253 /** 254 * Modify user data [implement only where required/possible] 255 * 256 * Set the mod* capabilities according to the implemented features 257 * 258 * @author Chris Smith <chris@jalakai.co.uk> 259 * @param string $user nick of the user to be changed 260 * @param array $changes array of field/value pairs to be changed (password will be clear text) 261 * @return bool 262 */ 263 public function modifyUser($user, $changes) { 264 msg("authorisation method does not allow modifying of user data", -1); 265 return false; 266 } 267 268 /** 269 * Delete one or more users [implement only where required/possible] 270 * 271 * Set delUser capability when implemented 272 * 273 * @author Chris Smith <chris@jalakai.co.uk> 274 * @param array $users 275 * @return int number of users deleted 276 */ 277 public function deleteUsers($users) { 278 msg("authorisation method does not allow deleting of users", -1); 279 return false; 280 } 281 282 /** 283 * Return a count of the number of user which meet $filter criteria 284 * [should be implemented whenever retrieveUsers is implemented] 285 * 286 * Set getUserCount capability when implemented 287 * 288 * @author Chris Smith <chris@jalakai.co.uk> 289 * @param array $filter array of field/pattern pairs, empty array for no filter 290 * @return int 291 */ 292 public function getUserCount($filter = array()) { 293 msg("authorisation method does not provide user counts", -1); 294 return 0; 295 } 296 297 /** 298 * Bulk retrieval of user data [implement only where required/possible] 299 * 300 * Set getUsers capability when implemented 301 * 302 * @author Chris Smith <chris@jalakai.co.uk> 303 * @param int $start index of first user to be returned 304 * @param int $limit max number of users to be returned 305 * @param array $filter array of field/pattern pairs, null for no filter 306 * @return array list of userinfo (refer getUserData for internal userinfo details) 307 */ 308 public function retrieveUsers($start = 0, $limit = -1, $filter = null) { 309 msg("authorisation method does not support mass retrieval of user data", -1); 310 return array(); 311 } 312 313 /** 314 * Define a group [implement only where required/possible] 315 * 316 * Set addGroup capability when implemented 317 * 318 * @author Chris Smith <chris@jalakai.co.uk> 319 * @param string $group 320 * @return bool 321 */ 322 public function addGroup($group) { 323 msg("authorisation method does not support independent group creation", -1); 324 return false; 325 } 326 327 /** 328 * Retrieve groups [implement only where required/possible] 329 * 330 * Set getGroups capability when implemented 331 * 332 * @author Chris Smith <chris@jalakai.co.uk> 333 * @param int $start 334 * @param int $limit 335 * @return array 336 */ 337 public function retrieveGroups($start = 0, $limit = 0) { 338 msg("authorisation method does not support group list retrieval", -1); 339 return array(); 340 } 341 342 /** 343 * Return case sensitivity of the backend [OPTIONAL] 344 * 345 * When your backend is caseinsensitive (eg. you can login with USER and 346 * user) then you need to overwrite this method and return false 347 * 348 * @return bool 349 */ 350 public function isCaseSensitive() { 351 return true; 352 } 353 354 /** 355 * Sanitize a given username [OPTIONAL] 356 * 357 * This function is applied to any user name that is given to 358 * the backend and should also be applied to any user name within 359 * the backend before returning it somewhere. 360 * 361 * This should be used to enforce username restrictions. 362 * 363 * @author Andreas Gohr <andi@splitbrain.org> 364 * @param string $user username 365 * @return string the cleaned username 366 */ 367 public function cleanUser($user) { 368 return $user; 369 } 370 371 /** 372 * Sanitize a given groupname [OPTIONAL] 373 * 374 * This function is applied to any groupname that is given to 375 * the backend and should also be applied to any groupname within 376 * the backend before returning it somewhere. 377 * 378 * This should be used to enforce groupname restrictions. 379 * 380 * Groupnames are to be passed without a leading '@' here. 381 * 382 * @author Andreas Gohr <andi@splitbrain.org> 383 * @param string $group groupname 384 * @return string the cleaned groupname 385 */ 386 public function cleanGroup($group) { 387 return $group; 388 } 389 390 /** 391 * Check Session Cache validity [implement only where required/possible] 392 * 393 * DokuWiki caches user info in the user's session for the timespan defined 394 * in $conf['auth_security_timeout']. 395 * 396 * This makes sure slow authentication backends do not slow down DokuWiki. 397 * This also means that changes to the user database will not be reflected 398 * on currently logged in users. 399 * 400 * To accommodate for this, the user manager plugin will touch a reference 401 * file whenever a change is submitted. This function compares the filetime 402 * of this reference file with the time stored in the session. 403 * 404 * This reference file mechanism does not reflect changes done directly in 405 * the backend's database through other means than the user manager plugin. 406 * 407 * Fast backends might want to return always false, to force rechecks on 408 * each page load. Others might want to use their own checking here. If 409 * unsure, do not override. 410 * 411 * @param string $user - The username 412 * @author Andreas Gohr <andi@splitbrain.org> 413 * @return bool 414 */ 415 public function useSessionCache($user) { 416 global $conf; 417 return ($_SESSION[DOKU_COOKIE]['auth']['time'] >= @filemtime($conf['cachedir'].'/sessionpurge')); 418 } 419} 420