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