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