827 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			827 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * DokuWiki Plugin authpdo (Auth Component)
 | |
|  *
 | |
|  * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
 | |
|  * @author  Andreas Gohr <andi@splitbrain.org>
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * Class auth_plugin_authpdo
 | |
|  */
 | |
| class auth_plugin_authpdo extends DokuWiki_Auth_Plugin
 | |
| {
 | |
| 
 | |
|     /** @var PDO */
 | |
|     protected $pdo;
 | |
| 
 | |
|     /** @var null|array The list of all groups */
 | |
|     protected $groupcache = null;
 | |
| 
 | |
|     /**
 | |
|      * Constructor.
 | |
|      */
 | |
|     public function __construct()
 | |
|     {
 | |
|         parent::__construct(); // for compatibility
 | |
| 
 | |
|         if (!class_exists('PDO')) {
 | |
|             $this->debugMsg('PDO extension for PHP not found.', -1, __LINE__);
 | |
|             $this->success = false;
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (!$this->getConf('dsn')) {
 | |
|             $this->debugMsg('No DSN specified', -1, __LINE__);
 | |
|             $this->success = false;
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         try {
 | |
|             $this->pdo = new PDO(
 | |
|                 $this->getConf('dsn'),
 | |
|                 $this->getConf('user'),
 | |
|                 conf_decodeString($this->getConf('pass')),
 | |
|                 array(
 | |
|                     PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // always fetch as array
 | |
|                     PDO::ATTR_EMULATE_PREPARES => true, // emulating prepares allows us to reuse param names
 | |
|                     PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes
 | |
|                 )
 | |
|             );
 | |
|         } catch (PDOException $e) {
 | |
|             $this->debugMsg($e);
 | |
|             msg($this->getLang('connectfail'), -1);
 | |
|             $this->success = false;
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         // can Users be created?
 | |
|         $this->cando['addUser'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'select-user',
 | |
|                 'select-user-groups',
 | |
|                 'select-groups',
 | |
|                 'insert-user',
 | |
|                 'insert-group',
 | |
|                 'join-group'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can Users be deleted?
 | |
|         $this->cando['delUser'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'select-user',
 | |
|                 'select-user-groups',
 | |
|                 'select-groups',
 | |
|                 'leave-group',
 | |
|                 'delete-user'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can login names be changed?
 | |
|         $this->cando['modLogin'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'select-user',
 | |
|                 'select-user-groups',
 | |
|                 'update-user-login'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can passwords be changed?
 | |
|         $this->cando['modPass'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'select-user',
 | |
|                 'select-user-groups',
 | |
|                 'update-user-pass'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can real names be changed?
 | |
|         $this->cando['modName'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'select-user',
 | |
|                 'select-user-groups',
 | |
|                 'update-user-info:name'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can real email be changed?
 | |
|         $this->cando['modMail'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'select-user',
 | |
|                 'select-user-groups',
 | |
|                 'update-user-info:mail'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can groups be changed?
 | |
|         $this->cando['modGroups'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'select-user',
 | |
|                 'select-user-groups',
 | |
|                 'select-groups',
 | |
|                 'leave-group',
 | |
|                 'join-group',
 | |
|                 'insert-group'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can a filtered list of users be retrieved?
 | |
|         $this->cando['getUsers'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'list-users'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can the number of users be retrieved?
 | |
|         $this->cando['getUserCount'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'count-users'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         // can a list of available groups be retrieved?
 | |
|         $this->cando['getGroups'] = $this->checkConfig(
 | |
|             array(
 | |
|                 'select-groups'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         $this->success = true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Check user+password
 | |
|      *
 | |
|      * @param string $user the user name
 | |
|      * @param string $pass the clear text password
 | |
|      * @return  bool
 | |
|      */
 | |
|     public function checkPass($user, $pass)
 | |
|     {
 | |
| 
 | |
|         $userdata = $this->selectUser($user);
 | |
|         if ($userdata == false) return false;
 | |
| 
 | |
|         // password checking done in SQL?
 | |
|         if ($this->checkConfig(array('check-pass'))) {
 | |
|             $userdata['clear'] = $pass;
 | |
|             $userdata['hash'] = auth_cryptPassword($pass);
 | |
|             $result = $this->query($this->getConf('check-pass'), $userdata);
 | |
|             if ($result === false) return false;
 | |
|             return (count($result) == 1);
 | |
|         }
 | |
| 
 | |
|         // we do password checking on our own
 | |
|         if (isset($userdata['hash'])) {
 | |
|             // hashed password
 | |
|             $passhash = new \dokuwiki\PassHash();
 | |
|             return $passhash->verify_hash($pass, $userdata['hash']);
 | |
|         } else {
 | |
|             // clear text password in the database O_o
 | |
|             return ($pass === $userdata['clear']);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return user info
 | |
|      *
 | |
|      * Returns info about the given user needs to contain
 | |
|      * at least these fields:
 | |
|      *
 | |
|      * name string  full name of the user
 | |
|      * mail string  email addres of the user
 | |
|      * grps array   list of groups the user is in
 | |
|      *
 | |
|      * @param string $user the user name
 | |
|      * @param bool $requireGroups whether or not the returned data must include groups
 | |
|      * @return array|bool containing user data or false
 | |
|      */
 | |
|     public function getUserData($user, $requireGroups = true)
 | |
|     {
 | |
|         $data = $this->selectUser($user);
 | |
|         if ($data == false) return false;
 | |
| 
 | |
|         if (isset($data['hash'])) unset($data['hash']);
 | |
|         if (isset($data['clean'])) unset($data['clean']);
 | |
| 
 | |
|         if ($requireGroups) {
 | |
|             $data['grps'] = $this->selectUserGroups($data);
 | |
|             if ($data['grps'] === false) return false;
 | |
|         }
 | |
| 
 | |
|         return $data;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create a new User [implement only where required/possible]
 | |
|      *
 | |
|      * Returns false if the user already exists, null when an error
 | |
|      * occurred and true if everything went well.
 | |
|      *
 | |
|      * The new user HAS TO be added to the default group by this
 | |
|      * function!
 | |
|      *
 | |
|      * Set addUser capability when implemented
 | |
|      *
 | |
|      * @param string $user
 | |
|      * @param string $clear
 | |
|      * @param string $name
 | |
|      * @param string $mail
 | |
|      * @param null|array $grps
 | |
|      * @return bool|null
 | |
|      */
 | |
|     public function createUser($user, $clear, $name, $mail, $grps = null)
 | |
|     {
 | |
|         global $conf;
 | |
| 
 | |
|         if (($info = $this->getUserData($user, false)) !== false) {
 | |
|             msg($this->getLang('userexists'), -1);
 | |
|             return false; // user already exists
 | |
|         }
 | |
| 
 | |
|         // prepare data
 | |
|         if ($grps == null) $grps = array();
 | |
|         array_unshift($grps, $conf['defaultgroup']);
 | |
|         $grps = array_unique($grps);
 | |
|         $hash = auth_cryptPassword($clear);
 | |
|         $userdata = compact('user', 'clear', 'hash', 'name', 'mail');
 | |
| 
 | |
|         // action protected by transaction
 | |
|         $this->pdo->beginTransaction();
 | |
|         {
 | |
|             // insert the user
 | |
|             $ok = $this->query($this->getConf('insert-user'), $userdata);
 | |
|             if ($ok === false) goto FAIL;
 | |
|             $userdata = $this->getUserData($user, false);
 | |
|             if ($userdata === false) goto FAIL;
 | |
| 
 | |
|             // create all groups that do not exist, the refetch the groups
 | |
|             $allgroups = $this->selectGroups();
 | |
|             foreach ($grps as $group) {
 | |
|                 if (!isset($allgroups[$group])) {
 | |
|                     $ok = $this->addGroup($group);
 | |
|                     if ($ok === false) goto FAIL;
 | |
|                 }
 | |
|             }
 | |
|             $allgroups = $this->selectGroups();
 | |
| 
 | |
|             // add user to the groups
 | |
|             foreach ($grps as $group) {
 | |
|                 $ok = $this->joinGroup($userdata, $allgroups[$group]);
 | |
|                 if ($ok === false) goto FAIL;
 | |
|             }
 | |
|         }
 | |
|         $this->pdo->commit();
 | |
|         return true;
 | |
| 
 | |
|         // something went wrong, rollback
 | |
|         FAIL:
 | |
|         $this->pdo->rollBack();
 | |
|         $this->debugMsg('Transaction rolled back', 0, __LINE__);
 | |
|         msg($this->getLang('writefail'), -1);
 | |
|         return null; // return error
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Modify user data
 | |
|      *
 | |
|      * @param string $user nick of the user to be changed
 | |
|      * @param array $changes array of field/value pairs to be changed (password will be clear text)
 | |
|      * @return  bool
 | |
|      */
 | |
|     public function modifyUser($user, $changes)
 | |
|     {
 | |
|         // secure everything in transaction
 | |
|         $this->pdo->beginTransaction();
 | |
|         {
 | |
|             $olddata = $this->getUserData($user);
 | |
|             $oldgroups = $olddata['grps'];
 | |
|             unset($olddata['grps']);
 | |
| 
 | |
|             // changing the user name?
 | |
|             if (isset($changes['user'])) {
 | |
|                 if ($this->getUserData($changes['user'], false)) goto FAIL;
 | |
|                 $params = $olddata;
 | |
|                 $params['newlogin'] = $changes['user'];
 | |
| 
 | |
|                 $ok = $this->query($this->getConf('update-user-login'), $params);
 | |
|                 if ($ok === false) goto FAIL;
 | |
|             }
 | |
| 
 | |
|             // changing the password?
 | |
|             if (isset($changes['pass'])) {
 | |
|                 $params = $olddata;
 | |
|                 $params['clear'] = $changes['pass'];
 | |
|                 $params['hash'] = auth_cryptPassword($changes['pass']);
 | |
| 
 | |
|                 $ok = $this->query($this->getConf('update-user-pass'), $params);
 | |
|                 if ($ok === false) goto FAIL;
 | |
|             }
 | |
| 
 | |
|             // changing info?
 | |
|             if (isset($changes['mail']) || isset($changes['name'])) {
 | |
|                 $params = $olddata;
 | |
|                 if (isset($changes['mail'])) $params['mail'] = $changes['mail'];
 | |
|                 if (isset($changes['name'])) $params['name'] = $changes['name'];
 | |
| 
 | |
|                 $ok = $this->query($this->getConf('update-user-info'), $params);
 | |
|                 if ($ok === false) goto FAIL;
 | |
|             }
 | |
| 
 | |
|             // changing groups?
 | |
|             if (isset($changes['grps'])) {
 | |
|                 $allgroups = $this->selectGroups();
 | |
| 
 | |
|                 // remove membership for previous groups
 | |
|                 foreach ($oldgroups as $group) {
 | |
|                     if (!in_array($group, $changes['grps']) && isset($allgroups[$group])) {
 | |
|                         $ok = $this->leaveGroup($olddata, $allgroups[$group]);
 | |
|                         if ($ok === false) goto FAIL;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 // create all new groups that are missing
 | |
|                 $added = 0;
 | |
|                 foreach ($changes['grps'] as $group) {
 | |
|                     if (!isset($allgroups[$group])) {
 | |
|                         $ok = $this->addGroup($group);
 | |
|                         if ($ok === false) goto FAIL;
 | |
|                         $added++;
 | |
|                     }
 | |
|                 }
 | |
|                 // reload group info
 | |
|                 if ($added > 0) $allgroups = $this->selectGroups();
 | |
| 
 | |
|                 // add membership for new groups
 | |
|                 foreach ($changes['grps'] as $group) {
 | |
|                     if (!in_array($group, $oldgroups)) {
 | |
|                         $ok = $this->joinGroup($olddata, $allgroups[$group]);
 | |
|                         if ($ok === false) goto FAIL;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|         }
 | |
|         $this->pdo->commit();
 | |
|         return true;
 | |
| 
 | |
|         // something went wrong, rollback
 | |
|         FAIL:
 | |
|         $this->pdo->rollBack();
 | |
|         $this->debugMsg('Transaction rolled back', 0, __LINE__);
 | |
|         msg($this->getLang('writefail'), -1);
 | |
|         return false; // return error
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Delete one or more users
 | |
|      *
 | |
|      * Set delUser capability when implemented
 | |
|      *
 | |
|      * @param array $users
 | |
|      * @return  int    number of users deleted
 | |
|      */
 | |
|     public function deleteUsers($users)
 | |
|     {
 | |
|         $count = 0;
 | |
|         foreach ($users as $user) {
 | |
|             if ($this->deleteUser($user)) $count++;
 | |
|         }
 | |
|         return $count;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Bulk retrieval of user data [implement only where required/possible]
 | |
|      *
 | |
|      * Set getUsers capability when implemented
 | |
|      *
 | |
|      * @param int $start index of first user to be returned
 | |
|      * @param int $limit max number of users to be returned
 | |
|      * @param array $filter array of field/pattern pairs, null for no filter
 | |
|      * @return  array list of userinfo (refer getUserData for internal userinfo details)
 | |
|      */
 | |
|     public function retrieveUsers($start = 0, $limit = -1, $filter = null)
 | |
|     {
 | |
|         if ($limit < 0) $limit = 10000; // we don't support no limit
 | |
|         if (is_null($filter)) $filter = array();
 | |
| 
 | |
|         if (isset($filter['grps'])) $filter['group'] = $filter['grps'];
 | |
|         foreach (array('user', 'name', 'mail', 'group') as $key) {
 | |
|             if (!isset($filter[$key])) {
 | |
|                 $filter[$key] = '%';
 | |
|             } else {
 | |
|                 $filter[$key] = '%' . $filter[$key] . '%';
 | |
|             }
 | |
|         }
 | |
|         $filter['start'] = (int)$start;
 | |
|         $filter['end'] = (int)$start + $limit;
 | |
|         $filter['limit'] = (int)$limit;
 | |
| 
 | |
|         $result = $this->query($this->getConf('list-users'), $filter);
 | |
|         if (!$result) return array();
 | |
|         $users = array();
 | |
|         if (is_array($result)) {
 | |
|             foreach ($result as $row) {
 | |
|                 if (!isset($row['user'])) {
 | |
|                     $this->debugMsg("list-users statement did not return 'user' attribute", -1, __LINE__);
 | |
|                     return array();
 | |
|                 }
 | |
|                 $users[] = $this->getUserData($row['user']);
 | |
|             }
 | |
|         } else {
 | |
|             $this->debugMsg("list-users statement did not return a list of result", -1, __LINE__);
 | |
|         }
 | |
|         return $users;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return a count of the number of user which meet $filter criteria
 | |
|      *
 | |
|      * @param array $filter array of field/pattern pairs, empty array for no filter
 | |
|      * @return int
 | |
|      */
 | |
|     public function getUserCount($filter = array())
 | |
|     {
 | |
|         if (is_null($filter)) $filter = array();
 | |
| 
 | |
|         if (isset($filter['grps'])) $filter['group'] = $filter['grps'];
 | |
|         foreach (array('user', 'name', 'mail', 'group') as $key) {
 | |
|             if (!isset($filter[$key])) {
 | |
|                 $filter[$key] = '%';
 | |
|             } else {
 | |
|                 $filter[$key] = '%' . $filter[$key] . '%';
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         $result = $this->query($this->getConf('count-users'), $filter);
 | |
|         if (!$result || !isset($result[0]['count'])) {
 | |
|             $this->debugMsg("Statement did not return 'count' attribute", -1, __LINE__);
 | |
|         }
 | |
|         return (int)$result[0]['count'];
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create a new group with the given name
 | |
|      *
 | |
|      * @param string $group
 | |
|      * @return bool
 | |
|      */
 | |
|     public function addGroup($group)
 | |
|     {
 | |
|         $sql = $this->getConf('insert-group');
 | |
| 
 | |
|         $result = $this->query($sql, array(':group' => $group));
 | |
|         $this->clearGroupCache();
 | |
|         if ($result === false) return false;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Retrieve groups
 | |
|      *
 | |
|      * Set getGroups capability when implemented
 | |
|      *
 | |
|      * @param int $start
 | |
|      * @param int $limit
 | |
|      * @return  array
 | |
|      */
 | |
|     public function retrieveGroups($start = 0, $limit = 0)
 | |
|     {
 | |
|         $groups = array_keys($this->selectGroups());
 | |
|         if ($groups === false) return array();
 | |
| 
 | |
|         if (!$limit) {
 | |
|             return array_splice($groups, $start);
 | |
|         } else {
 | |
|             return array_splice($groups, $start, $limit);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Select data of a specified user
 | |
|      *
 | |
|      * @param string $user the user name
 | |
|      * @return bool|array user data, false on error
 | |
|      */
 | |
|     protected function selectUser($user)
 | |
|     {
 | |
|         $sql = $this->getConf('select-user');
 | |
| 
 | |
|         $result = $this->query($sql, array(':user' => $user));
 | |
|         if (!$result) return false;
 | |
| 
 | |
|         if (count($result) > 1) {
 | |
|             $this->debugMsg('Found more than one matching user', -1, __LINE__);
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         $data = array_shift($result);
 | |
|         $dataok = true;
 | |
| 
 | |
|         if (!isset($data['user'])) {
 | |
|             $this->debugMsg("Statement did not return 'user' attribute", -1, __LINE__);
 | |
|             $dataok = false;
 | |
|         }
 | |
|         if (!isset($data['hash']) && !isset($data['clear']) && !$this->checkConfig(array('check-pass'))) {
 | |
|             $this->debugMsg("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__);
 | |
|             $dataok = false;
 | |
|         }
 | |
|         if (!isset($data['name'])) {
 | |
|             $this->debugMsg("Statement did not return 'name' attribute", -1, __LINE__);
 | |
|             $dataok = false;
 | |
|         }
 | |
|         if (!isset($data['mail'])) {
 | |
|             $this->debugMsg("Statement did not return 'mail' attribute", -1, __LINE__);
 | |
|             $dataok = false;
 | |
|         }
 | |
| 
 | |
|         if (!$dataok) return false;
 | |
|         return $data;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Delete a user after removing all their group memberships
 | |
|      *
 | |
|      * @param string $user
 | |
|      * @return bool true when the user was deleted
 | |
|      */
 | |
|     protected function deleteUser($user)
 | |
|     {
 | |
|         $this->pdo->beginTransaction();
 | |
|         {
 | |
|             $userdata = $this->getUserData($user);
 | |
|             if ($userdata === false) goto FAIL;
 | |
|             $allgroups = $this->selectGroups();
 | |
| 
 | |
|             // remove group memberships (ignore errors)
 | |
|             foreach ($userdata['grps'] as $group) {
 | |
|                 if (isset($allgroups[$group])) {
 | |
|                     $this->leaveGroup($userdata, $allgroups[$group]);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             $ok = $this->query($this->getConf('delete-user'), $userdata);
 | |
|             if ($ok === false) goto FAIL;
 | |
|         }
 | |
|         $this->pdo->commit();
 | |
|         return true;
 | |
| 
 | |
|         FAIL:
 | |
|         $this->pdo->rollBack();
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Select all groups of a user
 | |
|      *
 | |
|      * @param array $userdata The userdata as returned by _selectUser()
 | |
|      * @return array|bool list of group names, false on error
 | |
|      */
 | |
|     protected function selectUserGroups($userdata)
 | |
|     {
 | |
|         global $conf;
 | |
|         $sql = $this->getConf('select-user-groups');
 | |
|         $result = $this->query($sql, $userdata);
 | |
|         if ($result === false) return false;
 | |
| 
 | |
|         $groups = array($conf['defaultgroup']); // always add default config
 | |
|         if (is_array($result)) {
 | |
|             foreach ($result as $row) {
 | |
|                 if (!isset($row['group'])) {
 | |
|                     $this->debugMsg("No 'group' field returned in select-user-groups statement", -1, __LINE__);
 | |
|                     return false;
 | |
|                 }
 | |
|                 $groups[] = $row['group'];
 | |
|             }
 | |
|         } else {
 | |
|             $this->debugMsg("select-user-groups statement did not return a list of result", -1, __LINE__);
 | |
|         }
 | |
| 
 | |
|         $groups = array_unique($groups);
 | |
|         sort($groups);
 | |
|         return $groups;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Select all available groups
 | |
|      *
 | |
|      * @return array|bool list of all available groups and their properties
 | |
|      */
 | |
|     protected function selectGroups()
 | |
|     {
 | |
|         if ($this->groupcache) return $this->groupcache;
 | |
| 
 | |
|         $sql = $this->getConf('select-groups');
 | |
|         $result = $this->query($sql);
 | |
|         if ($result === false) return false;
 | |
| 
 | |
|         $groups = array();
 | |
|         if (is_array($result)) {
 | |
|             foreach ($result as $row) {
 | |
|                 if (!isset($row['group'])) {
 | |
|                     $this->debugMsg("No 'group' field returned from select-groups statement", -1, __LINE__);
 | |
|                     return false;
 | |
|                 }
 | |
| 
 | |
|                 // relayout result with group name as key
 | |
|                 $group = $row['group'];
 | |
|                 $groups[$group] = $row;
 | |
|             }
 | |
|         } else {
 | |
|             $this->debugMsg("select-groups statement did not return a list of result", -1, __LINE__);
 | |
|         }
 | |
| 
 | |
|         ksort($groups);
 | |
|         return $groups;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Remove all entries from the group cache
 | |
|      */
 | |
|     protected function clearGroupCache()
 | |
|     {
 | |
|         $this->groupcache = null;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Adds the user to the group
 | |
|      *
 | |
|      * @param array $userdata all the user data
 | |
|      * @param array $groupdata all the group data
 | |
|      * @return bool
 | |
|      */
 | |
|     protected function joinGroup($userdata, $groupdata)
 | |
|     {
 | |
|         $data = array_merge($userdata, $groupdata);
 | |
|         $sql = $this->getConf('join-group');
 | |
|         $result = $this->query($sql, $data);
 | |
|         if ($result === false) return false;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Removes the user from the group
 | |
|      *
 | |
|      * @param array $userdata all the user data
 | |
|      * @param array $groupdata all the group data
 | |
|      * @return bool
 | |
|      */
 | |
|     protected function leaveGroup($userdata, $groupdata)
 | |
|     {
 | |
|         $data = array_merge($userdata, $groupdata);
 | |
|         $sql = $this->getConf('leave-group');
 | |
|         $result = $this->query($sql, $data);
 | |
|         if ($result === false) return false;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Executes a query
 | |
|      *
 | |
|      * @param string $sql The SQL statement to execute
 | |
|      * @param array $arguments Named parameters to be used in the statement
 | |
|      * @return array|int|bool The result as associative array for SELECTs, affected rows for others, false on error
 | |
|      */
 | |
|     protected function query($sql, $arguments = array())
 | |
|     {
 | |
|         $sql = trim($sql);
 | |
|         if (empty($sql)) {
 | |
|             $this->debugMsg('No SQL query given', -1, __LINE__);
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         // execute
 | |
|         $params = array();
 | |
|         $sth = $this->pdo->prepare($sql);
 | |
|         $result = false;
 | |
|         try {
 | |
|             // prepare parameters - we only use those that exist in the SQL
 | |
|             foreach ($arguments as $key => $value) {
 | |
|                 if (is_array($value)) continue;
 | |
|                 if (is_object($value)) continue;
 | |
|                 if ($key[0] != ':') $key = ":$key"; // prefix with colon if needed
 | |
|                 if (strpos($sql, $key) === false) continue; // skip if parameter is missing
 | |
| 
 | |
|                 if (is_int($value)) {
 | |
|                     $sth->bindValue($key, $value, PDO::PARAM_INT);
 | |
|                 } else {
 | |
|                     $sth->bindValue($key, $value);
 | |
|                 }
 | |
|                 $params[$key] = $value; //remember for debugging
 | |
|             }
 | |
| 
 | |
|             $sth->execute();
 | |
|             // only report last line's result
 | |
|             $hasnextrowset = true;
 | |
|             $currentsql = $sql;
 | |
|             while ($hasnextrowset) {
 | |
|                 if (strtolower(substr($currentsql, 0, 6)) == 'select') {
 | |
|                     $result = $sth->fetchAll();
 | |
|                 } else {
 | |
|                     $result = $sth->rowCount();
 | |
|                 }
 | |
|                 $semi_pos = strpos($currentsql, ';');
 | |
|                 if ($semi_pos) {
 | |
|                     $currentsql = trim(substr($currentsql, $semi_pos + 1));
 | |
|                 }
 | |
|                 try {
 | |
|                     $hasnextrowset = $sth->nextRowset(); // run next rowset
 | |
|                 } catch (PDOException $rowset_e) {
 | |
|                     $hasnextrowset = false; // driver does not support multi-rowset, should be executed in one time
 | |
|                 }
 | |
|             }
 | |
|         } catch (Exception $e) {
 | |
|             // report the caller's line
 | |
|             $trace = debug_backtrace();
 | |
|             $line = $trace[0]['line'];
 | |
|             $dsql = $this->debugSQL($sql, $params, !defined('DOKU_UNITTEST'));
 | |
|             $this->debugMsg($e, -1, $line);
 | |
|             $this->debugMsg("SQL: <pre>$dsql</pre>", -1, $line);
 | |
|         }
 | |
|         $sth->closeCursor();
 | |
|         $sth = null;
 | |
| 
 | |
|         return $result;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Wrapper around msg() but outputs only when debug is enabled
 | |
|      *
 | |
|      * @param string|Exception $message
 | |
|      * @param int $err
 | |
|      * @param int $line
 | |
|      */
 | |
|     protected function debugMsg($message, $err = 0, $line = 0)
 | |
|     {
 | |
|         if (!$this->getConf('debug')) return;
 | |
|         if (is_a($message, 'Exception')) {
 | |
|             $err = -1;
 | |
|             $msg = $message->getMessage();
 | |
|             if (!$line) $line = $message->getLine();
 | |
|         } else {
 | |
|             $msg = $message;
 | |
|         }
 | |
| 
 | |
|         if (defined('DOKU_UNITTEST')) {
 | |
|             printf("\n%s, %s:%d\n", $msg, __FILE__, $line);
 | |
|         } else {
 | |
|             msg('authpdo: ' . $msg, $err, $line, __FILE__);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Check if the given config strings are set
 | |
|      *
 | |
|      * @param string[] $keys
 | |
|      * @return  bool
 | |
|      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 | |
|      *
 | |
|      */
 | |
|     protected function checkConfig($keys)
 | |
|     {
 | |
|         foreach ($keys as $key) {
 | |
|             $params = explode(':', $key);
 | |
|             $key = array_shift($params);
 | |
|             $sql = trim($this->getConf($key));
 | |
| 
 | |
|             // check if sql is set
 | |
|             if (!$sql) return false;
 | |
|             // check if needed params are there
 | |
|             foreach ($params as $param) {
 | |
|                 if (strpos($sql, ":$param") === false) return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * create an approximation of the SQL string with parameters replaced
 | |
|      *
 | |
|      * @param string $sql
 | |
|      * @param array $params
 | |
|      * @param bool $htmlescape Should the result be escaped for output in HTML?
 | |
|      * @return string
 | |
|      */
 | |
|     protected function debugSQL($sql, $params, $htmlescape = true)
 | |
|     {
 | |
|         foreach ($params as $key => $val) {
 | |
|             if (is_int($val)) {
 | |
|                 $val = $this->pdo->quote($val, PDO::PARAM_INT);
 | |
|             } elseif (is_bool($val)) {
 | |
|                 $val = $this->pdo->quote($val, PDO::PARAM_BOOL);
 | |
|             } elseif (is_null($val)) {
 | |
|                 $val = 'NULL';
 | |
|             } else {
 | |
|                 $val = $this->pdo->quote($val);
 | |
|             }
 | |
|             $sql = str_replace($key, $val, $sql);
 | |
|         }
 | |
|         if ($htmlescape) $sql = hsc($sql);
 | |
|         return $sql;
 | |
|     }
 | |
| }
 | |
| 
 | |
| // vim:ts=4:sw=4:et:
 |