1f64dbc90SAndreas Gohr<?php 2f64dbc90SAndreas Gohr/** 3f64dbc90SAndreas Gohr * DokuWiki Plugin authpdo (Auth Component) 4f64dbc90SAndreas Gohr * 5f64dbc90SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6f64dbc90SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 7f64dbc90SAndreas Gohr */ 8f64dbc90SAndreas Gohr 9f64dbc90SAndreas Gohr// must be run within Dokuwiki 10f64dbc90SAndreas Gohrif(!defined('DOKU_INC')) die(); 11f64dbc90SAndreas Gohr 12f64dbc90SAndreas Gohrclass auth_plugin_authpdo extends DokuWiki_Auth_Plugin { 13f64dbc90SAndreas Gohr 14f64dbc90SAndreas Gohr /** @var PDO */ 15f64dbc90SAndreas Gohr protected $pdo; 16f64dbc90SAndreas Gohr 17f64dbc90SAndreas Gohr /** 18f64dbc90SAndreas Gohr * Constructor. 19f64dbc90SAndreas Gohr */ 20f64dbc90SAndreas Gohr public function __construct() { 21f64dbc90SAndreas Gohr parent::__construct(); // for compatibility 22f64dbc90SAndreas Gohr 23f64dbc90SAndreas Gohr if(!class_exists('PDO')) { 24f64dbc90SAndreas Gohr $this->_debug('PDO extension for PHP not found.', -1, __LINE__); 25f64dbc90SAndreas Gohr $this->success = false; 26f64dbc90SAndreas Gohr return; 27f64dbc90SAndreas Gohr } 28f64dbc90SAndreas Gohr 29f64dbc90SAndreas Gohr if(!$this->getConf('dsn')) { 30f64dbc90SAndreas Gohr $this->_debug('No DSN specified', -1, __LINE__); 31f64dbc90SAndreas Gohr $this->success = false; 32f64dbc90SAndreas Gohr return; 33f64dbc90SAndreas Gohr } 34f64dbc90SAndreas Gohr 35f64dbc90SAndreas Gohr try { 36f64dbc90SAndreas Gohr $this->pdo = new PDO( 37f64dbc90SAndreas Gohr $this->getConf('dsn'), 38f64dbc90SAndreas Gohr $this->getConf('user'), 39f64dbc90SAndreas Gohr $this->getConf('pass'), 40f64dbc90SAndreas Gohr array( 4170a89417SAndreas Gohr PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // always fetch as array 4270a89417SAndreas Gohr PDO::ATTR_EMULATE_PREPARES => true, // emulating prepares allows us to reuse param names 435de3a6a5SAndreas Gohr PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes 44f64dbc90SAndreas Gohr ) 45f64dbc90SAndreas Gohr ); 46f64dbc90SAndreas Gohr } catch(PDOException $e) { 47f64dbc90SAndreas Gohr $this->_debug($e); 48f64dbc90SAndreas Gohr $this->success = false; 49f64dbc90SAndreas Gohr return; 50f64dbc90SAndreas Gohr } 51f64dbc90SAndreas Gohr 52f64dbc90SAndreas Gohr // FIXME set capabilities accordingly 53f64dbc90SAndreas Gohr //$this->cando['addUser'] = false; // can Users be created? 54f64dbc90SAndreas Gohr //$this->cando['delUser'] = false; // can Users be deleted? 55f64dbc90SAndreas Gohr //$this->cando['modLogin'] = false; // can login names be changed? 56f64dbc90SAndreas Gohr //$this->cando['modPass'] = false; // can passwords be changed? 57f64dbc90SAndreas Gohr //$this->cando['modName'] = false; // can real names be changed? 58f64dbc90SAndreas Gohr //$this->cando['modMail'] = false; // can emails be changed? 59f64dbc90SAndreas Gohr //$this->cando['modGroups'] = false; // can groups be changed? 60f64dbc90SAndreas Gohr //$this->cando['getUsers'] = false; // can a (filtered) list of users be retrieved? 61f64dbc90SAndreas Gohr //$this->cando['getUserCount']= false; // can the number of users be retrieved? 62f64dbc90SAndreas Gohr //$this->cando['getGroups'] = false; // can a list of available groups be retrieved? 63f64dbc90SAndreas Gohr //$this->cando['external'] = false; // does the module do external auth checking? 64f64dbc90SAndreas Gohr //$this->cando['logout'] = true; // can the user logout again? (eg. not possible with HTTP auth) 65f64dbc90SAndreas Gohr 66f64dbc90SAndreas Gohr // FIXME intialize your auth system and set success to true, if successful 67f64dbc90SAndreas Gohr $this->success = true; 68e19be516SAndreas Gohr 69f64dbc90SAndreas Gohr } 70f64dbc90SAndreas Gohr 71f64dbc90SAndreas Gohr /** 72f64dbc90SAndreas Gohr * Check user+password 73f64dbc90SAndreas Gohr * 74f64dbc90SAndreas Gohr * @param string $user the user name 75f64dbc90SAndreas Gohr * @param string $pass the clear text password 76f64dbc90SAndreas Gohr * @return bool 77f64dbc90SAndreas Gohr */ 78f64dbc90SAndreas Gohr public function checkPass($user, $pass) { 79f64dbc90SAndreas Gohr 80f64dbc90SAndreas Gohr $data = $this->_selectUser($user); 81f64dbc90SAndreas Gohr if($data == false) return false; 82f64dbc90SAndreas Gohr 83f64dbc90SAndreas Gohr if(isset($data['hash'])) { 84f64dbc90SAndreas Gohr // hashed password 85f64dbc90SAndreas Gohr $passhash = new PassHash(); 86f64dbc90SAndreas Gohr return $passhash->verify_hash($pass, $data['hash']); 87f64dbc90SAndreas Gohr } else { 88f64dbc90SAndreas Gohr // clear text password in the database O_o 89f64dbc90SAndreas Gohr return ($pass == $data['clear']); 90f64dbc90SAndreas Gohr } 91f64dbc90SAndreas Gohr } 92f64dbc90SAndreas Gohr 93f64dbc90SAndreas Gohr /** 94f64dbc90SAndreas Gohr * Return user info 95f64dbc90SAndreas Gohr * 96f64dbc90SAndreas Gohr * Returns info about the given user needs to contain 97f64dbc90SAndreas Gohr * at least these fields: 98f64dbc90SAndreas Gohr * 99f64dbc90SAndreas Gohr * name string full name of the user 100f64dbc90SAndreas Gohr * mail string email addres of the user 101f64dbc90SAndreas Gohr * grps array list of groups the user is in 102f64dbc90SAndreas Gohr * 103f64dbc90SAndreas Gohr * @param string $user the user name 104f64dbc90SAndreas Gohr * @param bool $requireGroups whether or not the returned data must include groups 105f64dbc90SAndreas Gohr * @return array containing user data or false 106f64dbc90SAndreas Gohr */ 107f64dbc90SAndreas Gohr public function getUserData($user, $requireGroups = true) { 108f64dbc90SAndreas Gohr $data = $this->_selectUser($user); 109f64dbc90SAndreas Gohr if($data == false) return false; 110f64dbc90SAndreas Gohr 11170a89417SAndreas Gohr if(isset($data['hash'])) unset($data['hash']); 11270a89417SAndreas Gohr if(isset($data['clean'])) unset($data['clean']); 113f64dbc90SAndreas Gohr 11470a89417SAndreas Gohr if($requireGroups) { 11570a89417SAndreas Gohr $data['grps'] = $this->_selectUserGroups($data); 1165de3a6a5SAndreas Gohr if($data['grps'] === false) return false; 117f64dbc90SAndreas Gohr } 118f64dbc90SAndreas Gohr 119f64dbc90SAndreas Gohr return $data; 120f64dbc90SAndreas Gohr } 121f64dbc90SAndreas Gohr 122f64dbc90SAndreas Gohr /** 123f64dbc90SAndreas Gohr * Create a new User [implement only where required/possible] 124f64dbc90SAndreas Gohr * 125f64dbc90SAndreas Gohr * Returns false if the user already exists, null when an error 126f64dbc90SAndreas Gohr * occurred and true if everything went well. 127f64dbc90SAndreas Gohr * 128f64dbc90SAndreas Gohr * The new user HAS TO be added to the default group by this 129f64dbc90SAndreas Gohr * function! 130f64dbc90SAndreas Gohr * 131f64dbc90SAndreas Gohr * Set addUser capability when implemented 132f64dbc90SAndreas Gohr * 133f64dbc90SAndreas Gohr * @param string $user 1345de3a6a5SAndreas Gohr * @param string $clear 135f64dbc90SAndreas Gohr * @param string $name 136f64dbc90SAndreas Gohr * @param string $mail 137f64dbc90SAndreas Gohr * @param null|array $grps 138f64dbc90SAndreas Gohr * @return bool|null 139f64dbc90SAndreas Gohr */ 1405de3a6a5SAndreas Gohr public function createUser($user, $clear, $name, $mail, $grps = null) { 1415de3a6a5SAndreas Gohr global $conf; 1425de3a6a5SAndreas Gohr 1435de3a6a5SAndreas Gohr if(($info = $this->getUserData($user, false)) !== false) { 1445de3a6a5SAndreas Gohr msg($this->getLang('userexists'), -1); 1455de3a6a5SAndreas Gohr return false; // user already exists 1465de3a6a5SAndreas Gohr } 1475de3a6a5SAndreas Gohr 1485de3a6a5SAndreas Gohr // prepare data 1495de3a6a5SAndreas Gohr if($grps == null) $grps = array(); 1505de3a6a5SAndreas Gohr $grps[] = $conf['defaultgroup']; 1515de3a6a5SAndreas Gohr $grps = array_unique($grps); 1525de3a6a5SAndreas Gohr $hash = auth_cryptPassword($clear); 1535de3a6a5SAndreas Gohr $userdata = compact('user', 'clear', 'hash', 'name', 'mail'); 1545de3a6a5SAndreas Gohr 1555de3a6a5SAndreas Gohr // action protected by transaction 1565de3a6a5SAndreas Gohr $this->pdo->beginTransaction(); 1575de3a6a5SAndreas Gohr { 1585de3a6a5SAndreas Gohr // insert the user 1595de3a6a5SAndreas Gohr $ok = $this->_query($this->getConf('insert-user'), $userdata); 1605de3a6a5SAndreas Gohr if($ok === false) goto FAIL; 1615de3a6a5SAndreas Gohr $userdata = $this->getUserData($user, false); 1625de3a6a5SAndreas Gohr if($userdata === false) goto FAIL; 1635de3a6a5SAndreas Gohr 1645de3a6a5SAndreas Gohr // create all groups that do not exist, the refetch the groups 1655de3a6a5SAndreas Gohr $allgroups = $this->_selectGroups(); 1665de3a6a5SAndreas Gohr foreach($grps as $group) { 1675de3a6a5SAndreas Gohr if(!isset($allgroups[$group])) { 168*6459f496SAndreas Gohr $ok = $this->addGroup($group); 1695de3a6a5SAndreas Gohr if($ok === false) goto FAIL; 1705de3a6a5SAndreas Gohr } 1715de3a6a5SAndreas Gohr } 1725de3a6a5SAndreas Gohr $allgroups = $this->_selectGroups(); 1735de3a6a5SAndreas Gohr 1745de3a6a5SAndreas Gohr // add user to the groups 1755de3a6a5SAndreas Gohr foreach($grps as $group) { 1765de3a6a5SAndreas Gohr $ok = $this->_joinGroup($userdata, $allgroups[$group]); 1775de3a6a5SAndreas Gohr if($ok === false) goto FAIL; 1785de3a6a5SAndreas Gohr } 1795de3a6a5SAndreas Gohr } 1805de3a6a5SAndreas Gohr $this->pdo->commit(); 1815de3a6a5SAndreas Gohr return true; 1825de3a6a5SAndreas Gohr 1835de3a6a5SAndreas Gohr // something went wrong, rollback 1845de3a6a5SAndreas Gohr FAIL: 1855de3a6a5SAndreas Gohr $this->pdo->rollBack(); 1865de3a6a5SAndreas Gohr $this->_debug('Transaction rolled back', 0, __LINE__); 1875de3a6a5SAndreas Gohr return null; // return error 1885de3a6a5SAndreas Gohr } 189f64dbc90SAndreas Gohr 190f64dbc90SAndreas Gohr /** 1914fb8dfabSAndreas Gohr * Modify user data 192f64dbc90SAndreas Gohr * 193f64dbc90SAndreas Gohr * @param string $user nick of the user to be changed 194f64dbc90SAndreas Gohr * @param array $changes array of field/value pairs to be changed (password will be clear text) 195f64dbc90SAndreas Gohr * @return bool 196f64dbc90SAndreas Gohr */ 1974fb8dfabSAndreas Gohr public function modifyUser($user, $changes) { 1984fb8dfabSAndreas Gohr // secure everything in transaction 1994fb8dfabSAndreas Gohr $this->pdo->beginTransaction(); 2004fb8dfabSAndreas Gohr { 2014fb8dfabSAndreas Gohr $olddata = $this->getUserData($user); 2024fb8dfabSAndreas Gohr $oldgroups = $olddata['grps']; 2034fb8dfabSAndreas Gohr unset($olddata['grps']); 2044fb8dfabSAndreas Gohr 2054fb8dfabSAndreas Gohr // changing the user name? 2064fb8dfabSAndreas Gohr if(isset($changes['user'])) { 2074fb8dfabSAndreas Gohr if($this->getUserData($changes['user'], false)) goto FAIL; 2084fb8dfabSAndreas Gohr $params = $olddata; 2094fb8dfabSAndreas Gohr $params['newlogin'] = $changes['user']; 2104fb8dfabSAndreas Gohr 2114fb8dfabSAndreas Gohr $ok = $this->_query($this->getConf('update-user-login'), $params); 2124fb8dfabSAndreas Gohr if($ok === false) goto FAIL; 2134fb8dfabSAndreas Gohr } 2144fb8dfabSAndreas Gohr 2154fb8dfabSAndreas Gohr // changing the password? 2164fb8dfabSAndreas Gohr if(isset($changes['pass'])) { 2174fb8dfabSAndreas Gohr $params = $olddata; 2184fb8dfabSAndreas Gohr $params['clear'] = $changes['pass']; 2194fb8dfabSAndreas Gohr $params['hash'] = auth_cryptPassword($changes['pass']); 2204fb8dfabSAndreas Gohr 2214fb8dfabSAndreas Gohr $ok = $this->_query($this->getConf('update-user-pass'), $params); 2224fb8dfabSAndreas Gohr if($ok === false) goto FAIL; 2234fb8dfabSAndreas Gohr } 2244fb8dfabSAndreas Gohr 2254fb8dfabSAndreas Gohr // changing info? 2264fb8dfabSAndreas Gohr if(isset($changes['mail']) || isset($changes['name'])) { 2274fb8dfabSAndreas Gohr $params = $olddata; 2284fb8dfabSAndreas Gohr if(isset($changes['mail'])) $params['mail'] = $changes['mail']; 2294fb8dfabSAndreas Gohr if(isset($changes['name'])) $params['name'] = $changes['name']; 2304fb8dfabSAndreas Gohr 2314fb8dfabSAndreas Gohr $ok = $this->_query($this->getConf('update-user-info'), $params); 2324fb8dfabSAndreas Gohr if($ok === false) goto FAIL; 2334fb8dfabSAndreas Gohr } 2344fb8dfabSAndreas Gohr 2354fb8dfabSAndreas Gohr // changing groups? 2364fb8dfabSAndreas Gohr if(isset($changes['grps'])) { 2374fb8dfabSAndreas Gohr $allgroups = $this->_selectGroups(); 2384fb8dfabSAndreas Gohr 2394fb8dfabSAndreas Gohr // remove membership for previous groups 2404fb8dfabSAndreas Gohr foreach($oldgroups as $group) { 2414fb8dfabSAndreas Gohr if(!in_array($group, $changes['grps'])) { 2424fb8dfabSAndreas Gohr $ok = $this->_leaveGroup($olddata, $allgroups[$group]); 2434fb8dfabSAndreas Gohr if($ok === false) goto FAIL; 2444fb8dfabSAndreas Gohr } 2454fb8dfabSAndreas Gohr } 2464fb8dfabSAndreas Gohr 2474fb8dfabSAndreas Gohr // create all new groups that are missing 2484fb8dfabSAndreas Gohr $added = 0; 2494fb8dfabSAndreas Gohr foreach($changes['grps'] as $group) { 2504fb8dfabSAndreas Gohr if(!isset($allgroups[$group])) { 251*6459f496SAndreas Gohr $ok = $this->addGroup($group); 2524fb8dfabSAndreas Gohr if($ok === false) goto FAIL; 2534fb8dfabSAndreas Gohr $added++; 2544fb8dfabSAndreas Gohr } 2554fb8dfabSAndreas Gohr } 2564fb8dfabSAndreas Gohr // reload group info 2574fb8dfabSAndreas Gohr if($added > 0) $allgroups = $this->_selectGroups(); 2584fb8dfabSAndreas Gohr 2594fb8dfabSAndreas Gohr // add membership for new groups 2604fb8dfabSAndreas Gohr foreach($changes['grps'] as $group) { 2614fb8dfabSAndreas Gohr if(!in_array($group, $oldgroups)) { 2624fb8dfabSAndreas Gohr $ok = $this->_joinGroup($olddata, $allgroups[$group]); 2634fb8dfabSAndreas Gohr if($ok === false) goto FAIL; 2644fb8dfabSAndreas Gohr } 2654fb8dfabSAndreas Gohr } 2664fb8dfabSAndreas Gohr } 2674fb8dfabSAndreas Gohr 2684fb8dfabSAndreas Gohr } 2694fb8dfabSAndreas Gohr $this->pdo->commit(); 2704fb8dfabSAndreas Gohr return true; 2714fb8dfabSAndreas Gohr 2724fb8dfabSAndreas Gohr // something went wrong, rollback 2734fb8dfabSAndreas Gohr FAIL: 2744fb8dfabSAndreas Gohr $this->pdo->rollBack(); 2754fb8dfabSAndreas Gohr $this->_debug('Transaction rolled back', 0, __LINE__); 2764fb8dfabSAndreas Gohr return false; // return error 2774fb8dfabSAndreas Gohr } 278f64dbc90SAndreas Gohr 279f64dbc90SAndreas Gohr /** 280e19be516SAndreas Gohr * Delete one or more users 281f64dbc90SAndreas Gohr * 282f64dbc90SAndreas Gohr * Set delUser capability when implemented 283f64dbc90SAndreas Gohr * 284f64dbc90SAndreas Gohr * @param array $users 285f64dbc90SAndreas Gohr * @return int number of users deleted 286f64dbc90SAndreas Gohr */ 287e19be516SAndreas Gohr public function deleteUsers($users) { 288e19be516SAndreas Gohr $count = 0; 289e19be516SAndreas Gohr foreach($users as $user) { 290e19be516SAndreas Gohr if($this->_deleteUser($user)) $count++; 291e19be516SAndreas Gohr } 292e19be516SAndreas Gohr return $count; 293e19be516SAndreas Gohr } 294f64dbc90SAndreas Gohr 295f64dbc90SAndreas Gohr /** 296f64dbc90SAndreas Gohr * Bulk retrieval of user data [implement only where required/possible] 297f64dbc90SAndreas Gohr * 298f64dbc90SAndreas Gohr * Set getUsers capability when implemented 299f64dbc90SAndreas Gohr * 300f64dbc90SAndreas Gohr * @param int $start index of first user to be returned 301f64dbc90SAndreas Gohr * @param int $limit max number of users to be returned 302f64dbc90SAndreas Gohr * @param array $filter array of field/pattern pairs, null for no filter 303f64dbc90SAndreas Gohr * @return array list of userinfo (refer getUserData for internal userinfo details) 304f64dbc90SAndreas Gohr */ 305*6459f496SAndreas Gohr public function retrieveUsers($start = 0, $limit = -1, $filter = null) { 306*6459f496SAndreas Gohr if($limit < 0) $limit = 10000; // we don't support no limit 307*6459f496SAndreas Gohr if(is_null($filter)) $filter = array(); 308*6459f496SAndreas Gohr 309*6459f496SAndreas Gohr foreach(array('user','name','mail','group') as $key) { 310*6459f496SAndreas Gohr if(!isset($filter[$key])) { 311*6459f496SAndreas Gohr $filter[$key] = '%'; 312*6459f496SAndreas Gohr } else { 313*6459f496SAndreas Gohr $filter[$key] = '%'.$filter[$key].'%'; 314*6459f496SAndreas Gohr } 315*6459f496SAndreas Gohr } 316*6459f496SAndreas Gohr $filter['start'] = $start; 317*6459f496SAndreas Gohr $filter['end'] = $start + $limit; 318*6459f496SAndreas Gohr $filter['limit'] = $limit; 319*6459f496SAndreas Gohr 320*6459f496SAndreas Gohr $result = $this->_query($this->getConf('list-users'), $filter); 321*6459f496SAndreas Gohr if(!$result) return array(); 322*6459f496SAndreas Gohr $users = array(); 323*6459f496SAndreas Gohr foreach($result as $row) { 324*6459f496SAndreas Gohr if(!isset($row['user'])) { 325*6459f496SAndreas Gohr $this->_debug("Statement did not return 'user' attribute", -1, __LINE__); 326*6459f496SAndreas Gohr return array(); 327*6459f496SAndreas Gohr } 328*6459f496SAndreas Gohr $users[] = $row['user']; 329*6459f496SAndreas Gohr } 330*6459f496SAndreas Gohr return $users; 331*6459f496SAndreas Gohr } 332f64dbc90SAndreas Gohr 333f64dbc90SAndreas Gohr /** 334f64dbc90SAndreas Gohr * Return a count of the number of user which meet $filter criteria 335f64dbc90SAndreas Gohr * 336f64dbc90SAndreas Gohr * @param array $filter array of field/pattern pairs, empty array for no filter 337f64dbc90SAndreas Gohr * @return int 338f64dbc90SAndreas Gohr */ 339*6459f496SAndreas Gohr public function getUserCount($filter = array()) { 340*6459f496SAndreas Gohr if(is_null($filter)) $filter = array(); 341*6459f496SAndreas Gohr 342*6459f496SAndreas Gohr foreach(array('user','name','mail','group') as $key) { 343*6459f496SAndreas Gohr if(!isset($filter[$key])) { 344*6459f496SAndreas Gohr $filter[$key] = '%'; 345*6459f496SAndreas Gohr } else { 346*6459f496SAndreas Gohr $filter[$key] = '%'.$filter[$key].'%'; 347*6459f496SAndreas Gohr } 348*6459f496SAndreas Gohr } 349*6459f496SAndreas Gohr 350*6459f496SAndreas Gohr $result = $this->_query($this->getConf('count-users'), $filter); 351*6459f496SAndreas Gohr if(!$result || !isset($result[0]['count'])) { 352*6459f496SAndreas Gohr $this->_debug("Statement did not return 'count' attribute", -1, __LINE__); 353*6459f496SAndreas Gohr } 354*6459f496SAndreas Gohr return isset($result[0]['count']); 355*6459f496SAndreas Gohr } 356f64dbc90SAndreas Gohr 357f64dbc90SAndreas Gohr /** 358*6459f496SAndreas Gohr * Create a new group with the given name 359f64dbc90SAndreas Gohr * 360f64dbc90SAndreas Gohr * @param string $group 361f64dbc90SAndreas Gohr * @return bool 362f64dbc90SAndreas Gohr */ 363*6459f496SAndreas Gohr public function addGroup($group) { 364*6459f496SAndreas Gohr $sql = $this->getConf('insert-group'); 365*6459f496SAndreas Gohr 366*6459f496SAndreas Gohr $result = $this->_query($sql, array(':group' => $group)); 367*6459f496SAndreas Gohr if($result === false) return false; 368*6459f496SAndreas Gohr return true; 369*6459f496SAndreas Gohr } 370f64dbc90SAndreas Gohr 371f64dbc90SAndreas Gohr /** 3725de3a6a5SAndreas Gohr * Retrieve groups 373f64dbc90SAndreas Gohr * 374f64dbc90SAndreas Gohr * Set getGroups capability when implemented 375f64dbc90SAndreas Gohr * 376f64dbc90SAndreas Gohr * @param int $start 377f64dbc90SAndreas Gohr * @param int $limit 378f64dbc90SAndreas Gohr * @return array 379f64dbc90SAndreas Gohr */ 3805de3a6a5SAndreas Gohr public function retrieveGroups($start = 0, $limit = 0) { 3815de3a6a5SAndreas Gohr $groups = array_keys($this->_selectGroups()); 3825de3a6a5SAndreas Gohr if($groups === false) return array(); 383f64dbc90SAndreas Gohr 3845de3a6a5SAndreas Gohr if(!$limit) { 3855de3a6a5SAndreas Gohr return array_splice($groups, $start); 3865de3a6a5SAndreas Gohr } else { 3875de3a6a5SAndreas Gohr return array_splice($groups, $start, $limit); 388f64dbc90SAndreas Gohr } 389f64dbc90SAndreas Gohr } 390f64dbc90SAndreas Gohr 391f64dbc90SAndreas Gohr /** 392f64dbc90SAndreas Gohr * Select data of a specified user 393f64dbc90SAndreas Gohr * 3945de3a6a5SAndreas Gohr * @param string $user the user name 3955de3a6a5SAndreas Gohr * @return bool|array user data, false on error 396f64dbc90SAndreas Gohr */ 397f64dbc90SAndreas Gohr protected function _selectUser($user) { 398f64dbc90SAndreas Gohr $sql = $this->getConf('select-user'); 399f64dbc90SAndreas Gohr 4005de3a6a5SAndreas Gohr $result = $this->_query($sql, array(':user' => $user)); 40170a89417SAndreas Gohr if(!$result) return false; 402f64dbc90SAndreas Gohr 40370a89417SAndreas Gohr if(count($result) > 1) { 404f64dbc90SAndreas Gohr $this->_debug('Found more than one matching user', -1, __LINE__); 405f64dbc90SAndreas Gohr return false; 406f64dbc90SAndreas Gohr } 407f64dbc90SAndreas Gohr 408f64dbc90SAndreas Gohr $data = array_shift($result); 409f64dbc90SAndreas Gohr $dataok = true; 410f64dbc90SAndreas Gohr 411f64dbc90SAndreas Gohr if(!isset($data['user'])) { 412f64dbc90SAndreas Gohr $this->_debug("Statement did not return 'user' attribute", -1, __LINE__); 413f64dbc90SAndreas Gohr $dataok = false; 414f64dbc90SAndreas Gohr } 415f64dbc90SAndreas Gohr if(!isset($data['hash']) && !isset($data['clear'])) { 416f64dbc90SAndreas Gohr $this->_debug("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__); 417f64dbc90SAndreas Gohr $dataok = false; 418f64dbc90SAndreas Gohr } 419f64dbc90SAndreas Gohr if(!isset($data['name'])) { 420f64dbc90SAndreas Gohr $this->_debug("Statement did not return 'name' attribute", -1, __LINE__); 421f64dbc90SAndreas Gohr $dataok = false; 422f64dbc90SAndreas Gohr } 423f64dbc90SAndreas Gohr if(!isset($data['mail'])) { 424f64dbc90SAndreas Gohr $this->_debug("Statement did not return 'mail' attribute", -1, __LINE__); 425f64dbc90SAndreas Gohr $dataok = false; 426f64dbc90SAndreas Gohr } 427f64dbc90SAndreas Gohr 428f64dbc90SAndreas Gohr if(!$dataok) return false; 429f64dbc90SAndreas Gohr return $data; 430f64dbc90SAndreas Gohr } 431f64dbc90SAndreas Gohr 432f64dbc90SAndreas Gohr /** 433e19be516SAndreas Gohr * Delete a user after removing all their group memberships 434e19be516SAndreas Gohr * 435e19be516SAndreas Gohr * @param string $user 436e19be516SAndreas Gohr * @return bool true when the user was deleted 437e19be516SAndreas Gohr */ 438e19be516SAndreas Gohr protected function _deleteUser($user) { 439e19be516SAndreas Gohr $this->pdo->beginTransaction(); 440e19be516SAndreas Gohr { 441e19be516SAndreas Gohr $userdata = $this->getUserData($user); 442e19be516SAndreas Gohr if($userdata === false) goto FAIL; 443e19be516SAndreas Gohr $allgroups = $this->_selectGroups(); 444e19be516SAndreas Gohr 445e19be516SAndreas Gohr // remove group memberships (ignore errors) 446e19be516SAndreas Gohr foreach($userdata['grps'] as $group) { 447e19be516SAndreas Gohr $this->_leaveGroup($userdata, $allgroups[$group]); 448e19be516SAndreas Gohr } 449e19be516SAndreas Gohr 450e19be516SAndreas Gohr $ok = $this->_query($this->getConf('delete-user'), $userdata); 451e19be516SAndreas Gohr if($ok === false) goto FAIL; 452e19be516SAndreas Gohr } 453e19be516SAndreas Gohr $this->pdo->commit(); 454e19be516SAndreas Gohr return true; 455e19be516SAndreas Gohr 456e19be516SAndreas Gohr FAIL: 457e19be516SAndreas Gohr $this->pdo->rollBack(); 458e19be516SAndreas Gohr return false; 459e19be516SAndreas Gohr } 460e19be516SAndreas Gohr 461e19be516SAndreas Gohr /** 46270a89417SAndreas Gohr * Select all groups of a user 46370a89417SAndreas Gohr * 46470a89417SAndreas Gohr * @param array $userdata The userdata as returned by _selectUser() 4655de3a6a5SAndreas Gohr * @return array|bool list of group names, false on error 46670a89417SAndreas Gohr */ 46770a89417SAndreas Gohr protected function _selectUserGroups($userdata) { 46870a89417SAndreas Gohr global $conf; 46970a89417SAndreas Gohr $sql = $this->getConf('select-user-groups'); 4705de3a6a5SAndreas Gohr $result = $this->_query($sql, $userdata); 4715de3a6a5SAndreas Gohr if($result === false) return false; 47270a89417SAndreas Gohr 47370a89417SAndreas Gohr $groups = array($conf['defaultgroup']); // always add default config 4745de3a6a5SAndreas Gohr foreach($result as $row) { 4755de3a6a5SAndreas Gohr if(!isset($row['group'])) { 4765de3a6a5SAndreas Gohr $this->_debug("No 'group' field returned in select-user-groups statement"); 4775de3a6a5SAndreas Gohr return false; 4785de3a6a5SAndreas Gohr } 47970a89417SAndreas Gohr $groups[] = $row['group']; 48070a89417SAndreas Gohr } 48170a89417SAndreas Gohr 48270a89417SAndreas Gohr $groups = array_unique($groups); 48370a89417SAndreas Gohr sort($groups); 48470a89417SAndreas Gohr return $groups; 48570a89417SAndreas Gohr } 48670a89417SAndreas Gohr 48770a89417SAndreas Gohr /** 4885de3a6a5SAndreas Gohr * Select all available groups 4895de3a6a5SAndreas Gohr * 4905de3a6a5SAndreas Gohr * @todo this should be cached 4915de3a6a5SAndreas Gohr * @return array|bool list of all available groups and their properties 4925de3a6a5SAndreas Gohr */ 4935de3a6a5SAndreas Gohr protected function _selectGroups() { 4945de3a6a5SAndreas Gohr $sql = $this->getConf('select-groups'); 4955de3a6a5SAndreas Gohr $result = $this->_query($sql); 4965de3a6a5SAndreas Gohr if($result === false) return false; 4975de3a6a5SAndreas Gohr 4985de3a6a5SAndreas Gohr $groups = array(); 4995de3a6a5SAndreas Gohr foreach($result as $row) { 5005de3a6a5SAndreas Gohr if(!isset($row['group'])) { 5015de3a6a5SAndreas Gohr $this->_debug("No 'group' field returned from select-groups statement", -1, __LINE__); 5025de3a6a5SAndreas Gohr return false; 5035de3a6a5SAndreas Gohr } 5045de3a6a5SAndreas Gohr 5055de3a6a5SAndreas Gohr // relayout result with group name as key 5065de3a6a5SAndreas Gohr $group = $row['group']; 5075de3a6a5SAndreas Gohr $groups[$group] = $row; 5085de3a6a5SAndreas Gohr } 5095de3a6a5SAndreas Gohr 5105de3a6a5SAndreas Gohr ksort($groups); 5115de3a6a5SAndreas Gohr return $groups; 5125de3a6a5SAndreas Gohr } 5135de3a6a5SAndreas Gohr 5145de3a6a5SAndreas Gohr 5155de3a6a5SAndreas Gohr /** 5164fb8dfabSAndreas Gohr * Adds the user to the group 5175de3a6a5SAndreas Gohr * 5185de3a6a5SAndreas Gohr * @param array $userdata all the user data 5195de3a6a5SAndreas Gohr * @param array $groupdata all the group data 5205de3a6a5SAndreas Gohr * @return bool 5215de3a6a5SAndreas Gohr */ 5225de3a6a5SAndreas Gohr protected function _joinGroup($userdata, $groupdata) { 5235de3a6a5SAndreas Gohr $data = array_merge($userdata, $groupdata); 5245de3a6a5SAndreas Gohr $sql = $this->getConf('join-group'); 5255de3a6a5SAndreas Gohr $result = $this->_query($sql, $data); 5265de3a6a5SAndreas Gohr if($result === false) return false; 5275de3a6a5SAndreas Gohr return true; 5285de3a6a5SAndreas Gohr } 5295de3a6a5SAndreas Gohr 5305de3a6a5SAndreas Gohr /** 5314fb8dfabSAndreas Gohr * Removes the user from the group 5324fb8dfabSAndreas Gohr * 5334fb8dfabSAndreas Gohr * @param array $userdata all the user data 5344fb8dfabSAndreas Gohr * @param array $groupdata all the group data 5354fb8dfabSAndreas Gohr * @return bool 5364fb8dfabSAndreas Gohr */ 5374fb8dfabSAndreas Gohr protected function _leaveGroup($userdata, $groupdata) { 5384fb8dfabSAndreas Gohr $data = array_merge($userdata, $groupdata); 5394fb8dfabSAndreas Gohr $sql = $this->getConf('leave-group'); 5404fb8dfabSAndreas Gohr $result = $this->_query($sql, $data); 5414fb8dfabSAndreas Gohr if($result === false) return false; 5424fb8dfabSAndreas Gohr return true; 5434fb8dfabSAndreas Gohr } 5444fb8dfabSAndreas Gohr 5454fb8dfabSAndreas Gohr /** 54670a89417SAndreas Gohr * Executes a query 54770a89417SAndreas Gohr * 54870a89417SAndreas Gohr * @param string $sql The SQL statement to execute 54970a89417SAndreas Gohr * @param array $arguments Named parameters to be used in the statement 5505de3a6a5SAndreas Gohr * @return array|bool The result as associative array, false on error 55170a89417SAndreas Gohr */ 5525de3a6a5SAndreas Gohr protected function _query($sql, $arguments = array()) { 5535de3a6a5SAndreas Gohr if(empty($sql)) { 5545de3a6a5SAndreas Gohr $this->_debug('No SQL query given', -1, __LINE__); 5555de3a6a5SAndreas Gohr return false; 5565de3a6a5SAndreas Gohr } 5575de3a6a5SAndreas Gohr 55870a89417SAndreas Gohr // prepare parameters - we only use those that exist in the SQL 55970a89417SAndreas Gohr $params = array(); 56070a89417SAndreas Gohr foreach($arguments as $key => $value) { 56170a89417SAndreas Gohr if(is_array($value)) continue; 56270a89417SAndreas Gohr if(is_object($value)) continue; 56370a89417SAndreas Gohr if($key[0] != ':') $key = ":$key"; // prefix with colon if needed 56470a89417SAndreas Gohr if(strpos($sql, $key) !== false) $params[$key] = $value; 56570a89417SAndreas Gohr } 56670a89417SAndreas Gohr 56770a89417SAndreas Gohr // execute 56870a89417SAndreas Gohr $sth = $this->pdo->prepare($sql); 5695de3a6a5SAndreas Gohr try { 57070a89417SAndreas Gohr $sth->execute($params); 57170a89417SAndreas Gohr $result = $sth->fetchAll(); 5725de3a6a5SAndreas Gohr } catch(Exception $e) { 5734fb8dfabSAndreas Gohr // report the caller's line 5744fb8dfabSAndreas Gohr $trace = debug_backtrace(); 5754fb8dfabSAndreas Gohr $line = $trace[0]['line']; 5765de3a6a5SAndreas Gohr $dsql = $this->_debugSQL($sql, $params, !defined('DOKU_UNITTEST')); 5774fb8dfabSAndreas Gohr $this->_debug($e, -1, $line); 5784fb8dfabSAndreas Gohr $this->_debug("SQL: <pre>$dsql</pre>", -1, $line); 57970a89417SAndreas Gohr $result = false; 5805de3a6a5SAndreas Gohr } finally { 58170a89417SAndreas Gohr $sth->closeCursor(); 58270a89417SAndreas Gohr $sth = null; 58370a89417SAndreas Gohr } 58470a89417SAndreas Gohr 5855de3a6a5SAndreas Gohr return $result; 5865de3a6a5SAndreas Gohr } 58770a89417SAndreas Gohr 58870a89417SAndreas Gohr /** 589f64dbc90SAndreas Gohr * Wrapper around msg() but outputs only when debug is enabled 590f64dbc90SAndreas Gohr * 591f64dbc90SAndreas Gohr * @param string|Exception $message 592f64dbc90SAndreas Gohr * @param int $err 593f64dbc90SAndreas Gohr * @param int $line 594f64dbc90SAndreas Gohr */ 595f64dbc90SAndreas Gohr protected function _debug($message, $err = 0, $line = 0) { 596f64dbc90SAndreas Gohr if(!$this->getConf('debug')) return; 597f64dbc90SAndreas Gohr if(is_a($message, 'Exception')) { 598f64dbc90SAndreas Gohr $err = -1; 599f64dbc90SAndreas Gohr $msg = $message->getMessage(); 6004fb8dfabSAndreas Gohr if(!$line) $line = $message->getLine(); 601f64dbc90SAndreas Gohr } else { 602f64dbc90SAndreas Gohr $msg = $message; 603f64dbc90SAndreas Gohr } 604f64dbc90SAndreas Gohr 605f64dbc90SAndreas Gohr if(defined('DOKU_UNITTEST')) { 606f64dbc90SAndreas Gohr printf("\n%s, %s:%d\n", $msg, __FILE__, $line); 607f64dbc90SAndreas Gohr } else { 608f64dbc90SAndreas Gohr msg('authpdo: ' . $msg, $err, $line, __FILE__); 609f64dbc90SAndreas Gohr } 610f64dbc90SAndreas Gohr } 6115de3a6a5SAndreas Gohr 6125de3a6a5SAndreas Gohr /** 6135de3a6a5SAndreas Gohr * Check if the given config strings are set 6145de3a6a5SAndreas Gohr * 6155de3a6a5SAndreas Gohr * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 6165de3a6a5SAndreas Gohr * 6175de3a6a5SAndreas Gohr * @param string[] $keys 6185de3a6a5SAndreas Gohr * @return bool 6195de3a6a5SAndreas Gohr */ 6205de3a6a5SAndreas Gohr protected function _chkcnf($keys) { 6215de3a6a5SAndreas Gohr foreach($keys as $key) { 6225de3a6a5SAndreas Gohr if(!$this->getConf($key)) return false; 6235de3a6a5SAndreas Gohr } 6245de3a6a5SAndreas Gohr 6255de3a6a5SAndreas Gohr return true; 6265de3a6a5SAndreas Gohr } 6275de3a6a5SAndreas Gohr 6285de3a6a5SAndreas Gohr /** 6295de3a6a5SAndreas Gohr * create an approximation of the SQL string with parameters replaced 6305de3a6a5SAndreas Gohr * 6315de3a6a5SAndreas Gohr * @param string $sql 6325de3a6a5SAndreas Gohr * @param array $params 6335de3a6a5SAndreas Gohr * @param bool $htmlescape Should the result be escaped for output in HTML? 6345de3a6a5SAndreas Gohr * @return string 6355de3a6a5SAndreas Gohr */ 6365de3a6a5SAndreas Gohr protected function _debugSQL($sql, $params, $htmlescape = true) { 6375de3a6a5SAndreas Gohr foreach($params as $key => $val) { 6385de3a6a5SAndreas Gohr if(is_int($val)) { 6395de3a6a5SAndreas Gohr $val = $this->pdo->quote($val, PDO::PARAM_INT); 6405de3a6a5SAndreas Gohr } elseif(is_bool($val)) { 6415de3a6a5SAndreas Gohr $val = $this->pdo->quote($val, PDO::PARAM_BOOL); 6425de3a6a5SAndreas Gohr } elseif(is_null($val)) { 6435de3a6a5SAndreas Gohr $val = 'NULL'; 6445de3a6a5SAndreas Gohr } else { 6455de3a6a5SAndreas Gohr $val = $this->pdo->quote($val); 6465de3a6a5SAndreas Gohr } 6475de3a6a5SAndreas Gohr $sql = str_replace($key, $val, $sql); 6485de3a6a5SAndreas Gohr } 6495de3a6a5SAndreas Gohr if($htmlescape) $sql = hsc($sql); 6505de3a6a5SAndreas Gohr return $sql; 6515de3a6a5SAndreas Gohr } 652f64dbc90SAndreas Gohr} 653f64dbc90SAndreas Gohr 654f64dbc90SAndreas Gohr// vim:ts=4:sw=4:et: 655