1<?php 2/** 3 * DokuWiki Plugin authpdo (Auth Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12class auth_plugin_authpdo extends DokuWiki_Auth_Plugin { 13 14 /** @var PDO */ 15 protected $pdo; 16 17 /** 18 * Constructor. 19 */ 20 public function __construct() { 21 parent::__construct(); // for compatibility 22 23 if(!class_exists('PDO')) { 24 $this->_debug('PDO extension for PHP not found.', -1, __LINE__); 25 $this->success = false; 26 return; 27 } 28 29 if(!$this->getConf('dsn')) { 30 $this->_debug('No DSN specified', -1, __LINE__); 31 $this->success = false; 32 return; 33 } 34 35 try { 36 $this->pdo = new PDO( 37 $this->getConf('dsn'), 38 $this->getConf('user'), 39 $this->getConf('pass'), 40 array( 41 PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 42 ) 43 ); 44 } catch(PDOException $e) { 45 $this->_debug($e); 46 $this->success = false; 47 return; 48 } 49 50 // FIXME set capabilities accordingly 51 //$this->cando['addUser'] = false; // can Users be created? 52 //$this->cando['delUser'] = false; // can Users be deleted? 53 //$this->cando['modLogin'] = false; // can login names be changed? 54 //$this->cando['modPass'] = false; // can passwords be changed? 55 //$this->cando['modName'] = false; // can real names be changed? 56 //$this->cando['modMail'] = false; // can emails be changed? 57 //$this->cando['modGroups'] = false; // can groups be changed? 58 //$this->cando['getUsers'] = false; // can a (filtered) list of users be retrieved? 59 //$this->cando['getUserCount']= false; // can the number of users be retrieved? 60 //$this->cando['getGroups'] = false; // can a list of available groups be retrieved? 61 //$this->cando['external'] = false; // does the module do external auth checking? 62 //$this->cando['logout'] = true; // can the user logout again? (eg. not possible with HTTP auth) 63 64 // FIXME intialize your auth system and set success to true, if successful 65 $this->success = true; 66 } 67 68 /** 69 * Check user+password 70 * 71 * May be ommited if trustExternal is used. 72 * 73 * @param string $user the user name 74 * @param string $pass the clear text password 75 * @return bool 76 */ 77 public function checkPass($user, $pass) { 78 79 $data = $this->_selectUser($user); 80 if($data == false) return false; 81 82 if(isset($data['hash'])) { 83 // hashed password 84 $passhash = new PassHash(); 85 return $passhash->verify_hash($pass, $data['hash']); 86 } else { 87 // clear text password in the database O_o 88 return ($pass == $data['clear']); 89 } 90 } 91 92 /** 93 * Return user info 94 * 95 * Returns info about the given user needs to contain 96 * at least these fields: 97 * 98 * name string full name of the user 99 * mail string email addres of the user 100 * grps array list of groups the user is in 101 * 102 * @param string $user the user name 103 * @param bool $requireGroups whether or not the returned data must include groups 104 * @return array containing user data or false 105 */ 106 public function getUserData($user, $requireGroups = true) { 107 $data = $this->_selectUser($user); 108 if($data == false) return false; 109 110 if($requireGroups) { 111 112 } 113 114 return $data; 115 } 116 117 118 /** 119 * Create a new User [implement only where required/possible] 120 * 121 * Returns false if the user already exists, null when an error 122 * occurred and true if everything went well. 123 * 124 * The new user HAS TO be added to the default group by this 125 * function! 126 * 127 * Set addUser capability when implemented 128 * 129 * @param string $user 130 * @param string $pass 131 * @param string $name 132 * @param string $mail 133 * @param null|array $grps 134 * @return bool|null 135 */ 136 //public function createUser($user, $pass, $name, $mail, $grps = null) { 137 // FIXME implement 138 // return null; 139 //} 140 141 /** 142 * Modify user data [implement only where required/possible] 143 * 144 * Set the mod* capabilities according to the implemented features 145 * 146 * @param string $user nick of the user to be changed 147 * @param array $changes array of field/value pairs to be changed (password will be clear text) 148 * @return bool 149 */ 150 //public function modifyUser($user, $changes) { 151 // FIXME implement 152 // return false; 153 //} 154 155 /** 156 * Delete one or more users [implement only where required/possible] 157 * 158 * Set delUser capability when implemented 159 * 160 * @param array $users 161 * @return int number of users deleted 162 */ 163 //public function deleteUsers($users) { 164 // FIXME implement 165 // return false; 166 //} 167 168 /** 169 * Bulk retrieval of user data [implement only where required/possible] 170 * 171 * Set getUsers capability when implemented 172 * 173 * @param int $start index of first user to be returned 174 * @param int $limit max number of users to be returned 175 * @param array $filter array of field/pattern pairs, null for no filter 176 * @return array list of userinfo (refer getUserData for internal userinfo details) 177 */ 178 //public function retrieveUsers($start = 0, $limit = -1, $filter = null) { 179 // FIXME implement 180 // return array(); 181 //} 182 183 /** 184 * Return a count of the number of user which meet $filter criteria 185 * [should be implemented whenever retrieveUsers is implemented] 186 * 187 * Set getUserCount capability when implemented 188 * 189 * @param array $filter array of field/pattern pairs, empty array for no filter 190 * @return int 191 */ 192 //public function getUserCount($filter = array()) { 193 // FIXME implement 194 // return 0; 195 //} 196 197 /** 198 * Define a group [implement only where required/possible] 199 * 200 * Set addGroup capability when implemented 201 * 202 * @param string $group 203 * @return bool 204 */ 205 //public function addGroup($group) { 206 // FIXME implement 207 // return false; 208 //} 209 210 /** 211 * Retrieve groups [implement only where required/possible] 212 * 213 * Set getGroups capability when implemented 214 * 215 * @param int $start 216 * @param int $limit 217 * @return array 218 */ 219 //public function retrieveGroups($start = 0, $limit = 0) { 220 // FIXME implement 221 // return array(); 222 //} 223 224 /** 225 * Return case sensitivity of the backend 226 * 227 * When your backend is caseinsensitive (eg. you can login with USER and 228 * user) then you need to overwrite this method and return false 229 * 230 * @return bool 231 */ 232 public function isCaseSensitive() { 233 return true; 234 } 235 236 /** 237 * Sanitize a given username 238 * 239 * This function is applied to any user name that is given to 240 * the backend and should also be applied to any user name within 241 * the backend before returning it somewhere. 242 * 243 * This should be used to enforce username restrictions. 244 * 245 * @param string $user username 246 * @return string the cleaned username 247 */ 248 public function cleanUser($user) { 249 return $user; 250 } 251 252 /** 253 * Sanitize a given groupname 254 * 255 * This function is applied to any groupname that is given to 256 * the backend and should also be applied to any groupname within 257 * the backend before returning it somewhere. 258 * 259 * This should be used to enforce groupname restrictions. 260 * 261 * Groupnames are to be passed without a leading '@' here. 262 * 263 * @param string $group groupname 264 * @return string the cleaned groupname 265 */ 266 public function cleanGroup($group) { 267 return $group; 268 } 269 270 /** 271 * Check Session Cache validity [implement only where required/possible] 272 * 273 * DokuWiki caches user info in the user's session for the timespan defined 274 * in $conf['auth_security_timeout']. 275 * 276 * This makes sure slow authentication backends do not slow down DokuWiki. 277 * This also means that changes to the user database will not be reflected 278 * on currently logged in users. 279 * 280 * To accommodate for this, the user manager plugin will touch a reference 281 * file whenever a change is submitted. This function compares the filetime 282 * of this reference file with the time stored in the session. 283 * 284 * This reference file mechanism does not reflect changes done directly in 285 * the backend's database through other means than the user manager plugin. 286 * 287 * Fast backends might want to return always false, to force rechecks on 288 * each page load. Others might want to use their own checking here. If 289 * unsure, do not override. 290 * 291 * @param string $user - The username 292 * @return bool 293 */ 294 //public function useSessionCache($user) { 295 // FIXME implement 296 //} 297 298 /** 299 * Select data of a specified user 300 * 301 * @param $user 302 * @return bool|array 303 */ 304 protected function _selectUser($user) { 305 $sql = $this->getConf('select-user'); 306 307 try { 308 $sth = $this->pdo->prepare($sql); 309 $sth->execute(array(':user' => $user)); 310 $result = $sth->fetchAll(); 311 $sth->closeCursor(); 312 $sth = null; 313 } catch(PDOException $e) { 314 $this->_debug($e); 315 $result = array(); 316 } 317 $found = count($result); 318 if($found == 0) return false; 319 320 if($found > 1) { 321 $this->_debug('Found more than one matching user', -1, __LINE__); 322 return false; 323 } 324 325 $data = array_shift($result); 326 $dataok = true; 327 328 if(!isset($data['user'])) { 329 $this->_debug("Statement did not return 'user' attribute", -1, __LINE__); 330 $dataok = false; 331 } 332 if(!isset($data['hash']) && !isset($data['clear'])) { 333 $this->_debug("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__); 334 $dataok = false; 335 } 336 if(!isset($data['name'])) { 337 $this->_debug("Statement did not return 'name' attribute", -1, __LINE__); 338 $dataok = false; 339 } 340 if(!isset($data['mail'])) { 341 $this->_debug("Statement did not return 'mail' attribute", -1, __LINE__); 342 $dataok = false; 343 } 344 345 if(!$dataok) return false; 346 return $data; 347 } 348 349 /** 350 * Wrapper around msg() but outputs only when debug is enabled 351 * 352 * @param string|Exception $message 353 * @param int $err 354 * @param int $line 355 */ 356 protected function _debug($message, $err = 0, $line = 0) { 357 if(!$this->getConf('debug')) return; 358 if(is_a($message, 'Exception')) { 359 $err = -1; 360 $line = $message->getLine(); 361 $msg = $message->getMessage(); 362 } else { 363 $msg = $message; 364 } 365 366 if(defined('DOKU_UNITTEST')) { 367 printf("\n%s, %s:%d\n", $msg, __FILE__, $line); 368 } else { 369 msg('authpdo: ' . $msg, $err, $line, __FILE__); 370 } 371 } 372} 373 374// vim:ts=4:sw=4:et: 375