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:
 |