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