xref: /dokuwiki/lib/plugins/authpdo/auth.php (revision 6459f496c8689d7960a4c338677ae020055d1038)
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