xref: /dokuwiki/lib/plugins/authpdo/auth.php (revision e19be5160bbe04352d6ae60d2294855d246d0dde)
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;
68*e19be516SAndreas 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])) {
1685de3a6a5SAndreas Gohr                    $ok = $this->_insertGroup($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])) {
2514fb8dfabSAndreas Gohr                        $ok = $this->_insertGroup($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    /**
280*e19be516SAndreas 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     */
287*e19be516SAndreas Gohr    public function deleteUsers($users) {
288*e19be516SAndreas Gohr        $count = 0;
289*e19be516SAndreas Gohr        foreach($users as $user) {
290*e19be516SAndreas Gohr            if($this->_deleteUser($user)) $count++;
291*e19be516SAndreas Gohr        }
292*e19be516SAndreas Gohr        return $count;
293*e19be516SAndreas 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     */
305f64dbc90SAndreas Gohr    //public function retrieveUsers($start = 0, $limit = -1, $filter = null) {
306f64dbc90SAndreas Gohr    // FIXME implement
307f64dbc90SAndreas Gohr    //    return array();
308f64dbc90SAndreas Gohr    //}
309f64dbc90SAndreas Gohr
310f64dbc90SAndreas Gohr    /**
311f64dbc90SAndreas Gohr     * Return a count of the number of user which meet $filter criteria
312f64dbc90SAndreas Gohr     * [should be implemented whenever retrieveUsers is implemented]
313f64dbc90SAndreas Gohr     *
314f64dbc90SAndreas Gohr     * Set getUserCount capability when implemented
315f64dbc90SAndreas Gohr     *
316f64dbc90SAndreas Gohr     * @param  array $filter array of field/pattern pairs, empty array for no filter
317f64dbc90SAndreas Gohr     * @return int
318f64dbc90SAndreas Gohr     */
319f64dbc90SAndreas Gohr    //public function getUserCount($filter = array()) {
320f64dbc90SAndreas Gohr    // FIXME implement
321f64dbc90SAndreas Gohr    //    return 0;
322f64dbc90SAndreas Gohr    //}
323f64dbc90SAndreas Gohr
324f64dbc90SAndreas Gohr    /**
325f64dbc90SAndreas Gohr     * Define a group [implement only where required/possible]
326f64dbc90SAndreas Gohr     *
327f64dbc90SAndreas Gohr     * Set addGroup capability when implemented
328f64dbc90SAndreas Gohr     *
329f64dbc90SAndreas Gohr     * @param   string $group
330f64dbc90SAndreas Gohr     * @return  bool
331f64dbc90SAndreas Gohr     */
332f64dbc90SAndreas Gohr    //public function addGroup($group) {
333f64dbc90SAndreas Gohr    // FIXME implement
334f64dbc90SAndreas Gohr    //    return false;
335f64dbc90SAndreas Gohr    //}
336f64dbc90SAndreas Gohr
337f64dbc90SAndreas Gohr    /**
3385de3a6a5SAndreas Gohr     * Retrieve groups
339f64dbc90SAndreas Gohr     *
340f64dbc90SAndreas Gohr     * Set getGroups capability when implemented
341f64dbc90SAndreas Gohr     *
342f64dbc90SAndreas Gohr     * @param   int $start
343f64dbc90SAndreas Gohr     * @param   int $limit
344f64dbc90SAndreas Gohr     * @return  array
345f64dbc90SAndreas Gohr     */
3465de3a6a5SAndreas Gohr    public function retrieveGroups($start = 0, $limit = 0) {
3475de3a6a5SAndreas Gohr        $groups = array_keys($this->_selectGroups());
3485de3a6a5SAndreas Gohr        if($groups === false) return array();
349f64dbc90SAndreas Gohr
3505de3a6a5SAndreas Gohr        if(!$limit) {
3515de3a6a5SAndreas Gohr            return array_splice($groups, $start);
3525de3a6a5SAndreas Gohr        } else {
3535de3a6a5SAndreas Gohr            return array_splice($groups, $start, $limit);
354f64dbc90SAndreas Gohr        }
355f64dbc90SAndreas Gohr    }
356f64dbc90SAndreas Gohr
357f64dbc90SAndreas Gohr    /**
358f64dbc90SAndreas Gohr     * Select data of a specified user
359f64dbc90SAndreas Gohr     *
3605de3a6a5SAndreas Gohr     * @param string $user the user name
3615de3a6a5SAndreas Gohr     * @return bool|array user data, false on error
362f64dbc90SAndreas Gohr     */
363f64dbc90SAndreas Gohr    protected function _selectUser($user) {
364f64dbc90SAndreas Gohr        $sql = $this->getConf('select-user');
365f64dbc90SAndreas Gohr
3665de3a6a5SAndreas Gohr        $result = $this->_query($sql, array(':user' => $user));
36770a89417SAndreas Gohr        if(!$result) return false;
368f64dbc90SAndreas Gohr
36970a89417SAndreas Gohr        if(count($result) > 1) {
370f64dbc90SAndreas Gohr            $this->_debug('Found more than one matching user', -1, __LINE__);
371f64dbc90SAndreas Gohr            return false;
372f64dbc90SAndreas Gohr        }
373f64dbc90SAndreas Gohr
374f64dbc90SAndreas Gohr        $data = array_shift($result);
375f64dbc90SAndreas Gohr        $dataok = true;
376f64dbc90SAndreas Gohr
377f64dbc90SAndreas Gohr        if(!isset($data['user'])) {
378f64dbc90SAndreas Gohr            $this->_debug("Statement did not return 'user' attribute", -1, __LINE__);
379f64dbc90SAndreas Gohr            $dataok = false;
380f64dbc90SAndreas Gohr        }
381f64dbc90SAndreas Gohr        if(!isset($data['hash']) && !isset($data['clear'])) {
382f64dbc90SAndreas Gohr            $this->_debug("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__);
383f64dbc90SAndreas Gohr            $dataok = false;
384f64dbc90SAndreas Gohr        }
385f64dbc90SAndreas Gohr        if(!isset($data['name'])) {
386f64dbc90SAndreas Gohr            $this->_debug("Statement did not return 'name' attribute", -1, __LINE__);
387f64dbc90SAndreas Gohr            $dataok = false;
388f64dbc90SAndreas Gohr        }
389f64dbc90SAndreas Gohr        if(!isset($data['mail'])) {
390f64dbc90SAndreas Gohr            $this->_debug("Statement did not return 'mail' attribute", -1, __LINE__);
391f64dbc90SAndreas Gohr            $dataok = false;
392f64dbc90SAndreas Gohr        }
393f64dbc90SAndreas Gohr
394f64dbc90SAndreas Gohr        if(!$dataok) return false;
395f64dbc90SAndreas Gohr        return $data;
396f64dbc90SAndreas Gohr    }
397f64dbc90SAndreas Gohr
398f64dbc90SAndreas Gohr    /**
399*e19be516SAndreas Gohr     * Delete a user after removing all their group memberships
400*e19be516SAndreas Gohr     *
401*e19be516SAndreas Gohr     * @param string $user
402*e19be516SAndreas Gohr     * @return bool true when the user was deleted
403*e19be516SAndreas Gohr     */
404*e19be516SAndreas Gohr    protected function _deleteUser($user) {
405*e19be516SAndreas Gohr        $this->pdo->beginTransaction();
406*e19be516SAndreas Gohr        {
407*e19be516SAndreas Gohr            $userdata = $this->getUserData($user);
408*e19be516SAndreas Gohr            if($userdata === false) goto FAIL;
409*e19be516SAndreas Gohr            $allgroups = $this->_selectGroups();
410*e19be516SAndreas Gohr
411*e19be516SAndreas Gohr            // remove group memberships (ignore errors)
412*e19be516SAndreas Gohr            foreach($userdata['grps'] as $group) {
413*e19be516SAndreas Gohr                $this->_leaveGroup($userdata, $allgroups[$group]);
414*e19be516SAndreas Gohr            }
415*e19be516SAndreas Gohr
416*e19be516SAndreas Gohr            $ok = $this->_query($this->getConf('delete-user'), $userdata);
417*e19be516SAndreas Gohr            if($ok === false) goto FAIL;
418*e19be516SAndreas Gohr        }
419*e19be516SAndreas Gohr        $this->pdo->commit();
420*e19be516SAndreas Gohr        return true;
421*e19be516SAndreas Gohr
422*e19be516SAndreas Gohr        FAIL:
423*e19be516SAndreas Gohr        $this->pdo->rollBack();
424*e19be516SAndreas Gohr        return false;
425*e19be516SAndreas Gohr    }
426*e19be516SAndreas Gohr
427*e19be516SAndreas Gohr    /**
42870a89417SAndreas Gohr     * Select all groups of a user
42970a89417SAndreas Gohr     *
43070a89417SAndreas Gohr     * @param array $userdata The userdata as returned by _selectUser()
4315de3a6a5SAndreas Gohr     * @return array|bool list of group names, false on error
43270a89417SAndreas Gohr     */
43370a89417SAndreas Gohr    protected function _selectUserGroups($userdata) {
43470a89417SAndreas Gohr        global $conf;
43570a89417SAndreas Gohr        $sql = $this->getConf('select-user-groups');
4365de3a6a5SAndreas Gohr        $result = $this->_query($sql, $userdata);
4375de3a6a5SAndreas Gohr        if($result === false) return false;
43870a89417SAndreas Gohr
43970a89417SAndreas Gohr        $groups = array($conf['defaultgroup']); // always add default config
4405de3a6a5SAndreas Gohr        foreach($result as $row) {
4415de3a6a5SAndreas Gohr            if(!isset($row['group'])) {
4425de3a6a5SAndreas Gohr                $this->_debug("No 'group' field returned in select-user-groups statement");
4435de3a6a5SAndreas Gohr                return false;
4445de3a6a5SAndreas Gohr            }
44570a89417SAndreas Gohr            $groups[] = $row['group'];
44670a89417SAndreas Gohr        }
44770a89417SAndreas Gohr
44870a89417SAndreas Gohr        $groups = array_unique($groups);
44970a89417SAndreas Gohr        sort($groups);
45070a89417SAndreas Gohr        return $groups;
45170a89417SAndreas Gohr    }
45270a89417SAndreas Gohr
45370a89417SAndreas Gohr    /**
4545de3a6a5SAndreas Gohr     * Select all available groups
4555de3a6a5SAndreas Gohr     *
4565de3a6a5SAndreas Gohr     * @todo this should be cached
4575de3a6a5SAndreas Gohr     * @return array|bool list of all available groups and their properties
4585de3a6a5SAndreas Gohr     */
4595de3a6a5SAndreas Gohr    protected function _selectGroups() {
4605de3a6a5SAndreas Gohr        $sql = $this->getConf('select-groups');
4615de3a6a5SAndreas Gohr        $result = $this->_query($sql);
4625de3a6a5SAndreas Gohr        if($result === false) return false;
4635de3a6a5SAndreas Gohr
4645de3a6a5SAndreas Gohr        $groups = array();
4655de3a6a5SAndreas Gohr        foreach($result as $row) {
4665de3a6a5SAndreas Gohr            if(!isset($row['group'])) {
4675de3a6a5SAndreas Gohr                $this->_debug("No 'group' field returned from select-groups statement", -1, __LINE__);
4685de3a6a5SAndreas Gohr                return false;
4695de3a6a5SAndreas Gohr            }
4705de3a6a5SAndreas Gohr
4715de3a6a5SAndreas Gohr            // relayout result with group name as key
4725de3a6a5SAndreas Gohr            $group = $row['group'];
4735de3a6a5SAndreas Gohr            $groups[$group] = $row;
4745de3a6a5SAndreas Gohr        }
4755de3a6a5SAndreas Gohr
4765de3a6a5SAndreas Gohr        ksort($groups);
4775de3a6a5SAndreas Gohr        return $groups;
4785de3a6a5SAndreas Gohr    }
4795de3a6a5SAndreas Gohr
4805de3a6a5SAndreas Gohr    /**
4815de3a6a5SAndreas Gohr     * Create a new group with the given name
4825de3a6a5SAndreas Gohr     *
4835de3a6a5SAndreas Gohr     * @param string $group
4845de3a6a5SAndreas Gohr     * @return bool
4855de3a6a5SAndreas Gohr     */
4865de3a6a5SAndreas Gohr    protected function _insertGroup($group) {
4875de3a6a5SAndreas Gohr        $sql = $this->getConf('insert-group');
4885de3a6a5SAndreas Gohr
4895de3a6a5SAndreas Gohr        $result = $this->_query($sql, array(':group' => $group));
4905de3a6a5SAndreas Gohr        if($result === false) return false;
4915de3a6a5SAndreas Gohr        return true;
4925de3a6a5SAndreas Gohr    }
4935de3a6a5SAndreas Gohr
4945de3a6a5SAndreas Gohr    /**
4954fb8dfabSAndreas Gohr     * Adds the user to the group
4965de3a6a5SAndreas Gohr     *
4975de3a6a5SAndreas Gohr     * @param array $userdata all the user data
4985de3a6a5SAndreas Gohr     * @param array $groupdata all the group data
4995de3a6a5SAndreas Gohr     * @return bool
5005de3a6a5SAndreas Gohr     */
5015de3a6a5SAndreas Gohr    protected function _joinGroup($userdata, $groupdata) {
5025de3a6a5SAndreas Gohr        $data = array_merge($userdata, $groupdata);
5035de3a6a5SAndreas Gohr        $sql = $this->getConf('join-group');
5045de3a6a5SAndreas Gohr        $result = $this->_query($sql, $data);
5055de3a6a5SAndreas Gohr        if($result === false) return false;
5065de3a6a5SAndreas Gohr        return true;
5075de3a6a5SAndreas Gohr    }
5085de3a6a5SAndreas Gohr
5095de3a6a5SAndreas Gohr    /**
5104fb8dfabSAndreas Gohr     * Removes the user from the group
5114fb8dfabSAndreas Gohr     *
5124fb8dfabSAndreas Gohr     * @param array $userdata all the user data
5134fb8dfabSAndreas Gohr     * @param array $groupdata all the group data
5144fb8dfabSAndreas Gohr     * @return bool
5154fb8dfabSAndreas Gohr     */
5164fb8dfabSAndreas Gohr    protected function _leaveGroup($userdata, $groupdata) {
5174fb8dfabSAndreas Gohr        $data = array_merge($userdata, $groupdata);
5184fb8dfabSAndreas Gohr        $sql = $this->getConf('leave-group');
5194fb8dfabSAndreas Gohr        $result = $this->_query($sql, $data);
5204fb8dfabSAndreas Gohr        if($result === false) return false;
5214fb8dfabSAndreas Gohr        return true;
5224fb8dfabSAndreas Gohr    }
5234fb8dfabSAndreas Gohr
5244fb8dfabSAndreas Gohr    /**
52570a89417SAndreas Gohr     * Executes a query
52670a89417SAndreas Gohr     *
52770a89417SAndreas Gohr     * @param string $sql The SQL statement to execute
52870a89417SAndreas Gohr     * @param array $arguments Named parameters to be used in the statement
5295de3a6a5SAndreas Gohr     * @return array|bool The result as associative array, false on error
53070a89417SAndreas Gohr     */
5315de3a6a5SAndreas Gohr    protected function _query($sql, $arguments = array()) {
5325de3a6a5SAndreas Gohr        if(empty($sql)) {
5335de3a6a5SAndreas Gohr            $this->_debug('No SQL query given', -1, __LINE__);
5345de3a6a5SAndreas Gohr            return false;
5355de3a6a5SAndreas Gohr        }
5365de3a6a5SAndreas Gohr
53770a89417SAndreas Gohr        // prepare parameters - we only use those that exist in the SQL
53870a89417SAndreas Gohr        $params = array();
53970a89417SAndreas Gohr        foreach($arguments as $key => $value) {
54070a89417SAndreas Gohr            if(is_array($value)) continue;
54170a89417SAndreas Gohr            if(is_object($value)) continue;
54270a89417SAndreas Gohr            if($key[0] != ':') $key = ":$key"; // prefix with colon if needed
54370a89417SAndreas Gohr            if(strpos($sql, $key) !== false) $params[$key] = $value;
54470a89417SAndreas Gohr        }
54570a89417SAndreas Gohr
54670a89417SAndreas Gohr        // execute
54770a89417SAndreas Gohr        $sth = $this->pdo->prepare($sql);
5485de3a6a5SAndreas Gohr        try {
54970a89417SAndreas Gohr            $sth->execute($params);
55070a89417SAndreas Gohr            $result = $sth->fetchAll();
5515de3a6a5SAndreas Gohr        } catch(Exception $e) {
5524fb8dfabSAndreas Gohr            // report the caller's line
5534fb8dfabSAndreas Gohr            $trace = debug_backtrace();
5544fb8dfabSAndreas Gohr            $line = $trace[0]['line'];
5555de3a6a5SAndreas Gohr            $dsql = $this->_debugSQL($sql, $params, !defined('DOKU_UNITTEST'));
5564fb8dfabSAndreas Gohr            $this->_debug($e, -1, $line);
5574fb8dfabSAndreas Gohr            $this->_debug("SQL: <pre>$dsql</pre>", -1, $line);
55870a89417SAndreas Gohr            $result = false;
5595de3a6a5SAndreas Gohr        } finally {
56070a89417SAndreas Gohr            $sth->closeCursor();
56170a89417SAndreas Gohr            $sth = null;
56270a89417SAndreas Gohr        }
56370a89417SAndreas Gohr
5645de3a6a5SAndreas Gohr        return $result;
5655de3a6a5SAndreas Gohr    }
56670a89417SAndreas Gohr
56770a89417SAndreas Gohr    /**
568f64dbc90SAndreas Gohr     * Wrapper around msg() but outputs only when debug is enabled
569f64dbc90SAndreas Gohr     *
570f64dbc90SAndreas Gohr     * @param string|Exception $message
571f64dbc90SAndreas Gohr     * @param int $err
572f64dbc90SAndreas Gohr     * @param int $line
573f64dbc90SAndreas Gohr     */
574f64dbc90SAndreas Gohr    protected function _debug($message, $err = 0, $line = 0) {
575f64dbc90SAndreas Gohr        if(!$this->getConf('debug')) return;
576f64dbc90SAndreas Gohr        if(is_a($message, 'Exception')) {
577f64dbc90SAndreas Gohr            $err = -1;
578f64dbc90SAndreas Gohr            $msg = $message->getMessage();
5794fb8dfabSAndreas Gohr            if(!$line) $line = $message->getLine();
580f64dbc90SAndreas Gohr        } else {
581f64dbc90SAndreas Gohr            $msg = $message;
582f64dbc90SAndreas Gohr        }
583f64dbc90SAndreas Gohr
584f64dbc90SAndreas Gohr        if(defined('DOKU_UNITTEST')) {
585f64dbc90SAndreas Gohr            printf("\n%s, %s:%d\n", $msg, __FILE__, $line);
586f64dbc90SAndreas Gohr        } else {
587f64dbc90SAndreas Gohr            msg('authpdo: ' . $msg, $err, $line, __FILE__);
588f64dbc90SAndreas Gohr        }
589f64dbc90SAndreas Gohr    }
5905de3a6a5SAndreas Gohr
5915de3a6a5SAndreas Gohr    /**
5925de3a6a5SAndreas Gohr     * Check if the given config strings are set
5935de3a6a5SAndreas Gohr     *
5945de3a6a5SAndreas Gohr     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
5955de3a6a5SAndreas Gohr     *
5965de3a6a5SAndreas Gohr     * @param   string[] $keys
5975de3a6a5SAndreas Gohr     * @return  bool
5985de3a6a5SAndreas Gohr     */
5995de3a6a5SAndreas Gohr    protected function _chkcnf($keys) {
6005de3a6a5SAndreas Gohr        foreach($keys as $key) {
6015de3a6a5SAndreas Gohr            if(!$this->getConf($key)) return false;
6025de3a6a5SAndreas Gohr        }
6035de3a6a5SAndreas Gohr
6045de3a6a5SAndreas Gohr        return true;
6055de3a6a5SAndreas Gohr    }
6065de3a6a5SAndreas Gohr
6075de3a6a5SAndreas Gohr    /**
6085de3a6a5SAndreas Gohr     * create an approximation of the SQL string with parameters replaced
6095de3a6a5SAndreas Gohr     *
6105de3a6a5SAndreas Gohr     * @param string $sql
6115de3a6a5SAndreas Gohr     * @param array $params
6125de3a6a5SAndreas Gohr     * @param bool $htmlescape Should the result be escaped for output in HTML?
6135de3a6a5SAndreas Gohr     * @return string
6145de3a6a5SAndreas Gohr     */
6155de3a6a5SAndreas Gohr    protected function _debugSQL($sql, $params, $htmlescape = true) {
6165de3a6a5SAndreas Gohr        foreach($params as $key => $val) {
6175de3a6a5SAndreas Gohr            if(is_int($val)) {
6185de3a6a5SAndreas Gohr                $val = $this->pdo->quote($val, PDO::PARAM_INT);
6195de3a6a5SAndreas Gohr            } elseif(is_bool($val)) {
6205de3a6a5SAndreas Gohr                $val = $this->pdo->quote($val, PDO::PARAM_BOOL);
6215de3a6a5SAndreas Gohr            } elseif(is_null($val)) {
6225de3a6a5SAndreas Gohr                $val = 'NULL';
6235de3a6a5SAndreas Gohr            } else {
6245de3a6a5SAndreas Gohr                $val = $this->pdo->quote($val);
6255de3a6a5SAndreas Gohr            }
6265de3a6a5SAndreas Gohr            $sql = str_replace($key, $val, $sql);
6275de3a6a5SAndreas Gohr        }
6285de3a6a5SAndreas Gohr        if($htmlescape) $sql = hsc($sql);
6295de3a6a5SAndreas Gohr        return $sql;
6305de3a6a5SAndreas Gohr    }
631f64dbc90SAndreas Gohr}
632f64dbc90SAndreas Gohr
633f64dbc90SAndreas Gohr// vim:ts=4:sw=4:et:
634