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