xref: /dokuwiki/lib/plugins/authpdo/auth.php (revision f64dbc90055403db700941e4691ea451bb971cef)
1<?php
2/**
3 * DokuWiki Plugin authpdo (Auth Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Andreas Gohr <andi@splitbrain.org>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class auth_plugin_authpdo extends DokuWiki_Auth_Plugin {
13
14    /** @var PDO */
15    protected $pdo;
16
17    /**
18     * Constructor.
19     */
20    public function __construct() {
21        parent::__construct(); // for compatibility
22
23        if(!class_exists('PDO')) {
24            $this->_debug('PDO extension for PHP not found.', -1, __LINE__);
25            $this->success = false;
26            return;
27        }
28
29        if(!$this->getConf('dsn')) {
30            $this->_debug('No DSN specified', -1, __LINE__);
31            $this->success = false;
32            return;
33        }
34
35        try {
36            $this->pdo = new PDO(
37                $this->getConf('dsn'),
38                $this->getConf('user'),
39                $this->getConf('pass'),
40                array(
41                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
42                )
43            );
44        } catch(PDOException $e) {
45            $this->_debug($e);
46            $this->success = false;
47            return;
48        }
49
50        // FIXME set capabilities accordingly
51        //$this->cando['addUser']     = false; // can Users be created?
52        //$this->cando['delUser']     = false; // can Users be deleted?
53        //$this->cando['modLogin']    = false; // can login names be changed?
54        //$this->cando['modPass']     = false; // can passwords be changed?
55        //$this->cando['modName']     = false; // can real names be changed?
56        //$this->cando['modMail']     = false; // can emails be changed?
57        //$this->cando['modGroups']   = false; // can groups be changed?
58        //$this->cando['getUsers']    = false; // can a (filtered) list of users be retrieved?
59        //$this->cando['getUserCount']= false; // can the number of users be retrieved?
60        //$this->cando['getGroups']   = false; // can a list of available groups be retrieved?
61        //$this->cando['external']    = false; // does the module do external auth checking?
62        //$this->cando['logout']      = true; // can the user logout again? (eg. not possible with HTTP auth)
63
64        // FIXME intialize your auth system and set success to true, if successful
65        $this->success = true;
66    }
67
68    /**
69     * Check user+password
70     *
71     * May be ommited if trustExternal is used.
72     *
73     * @param   string $user the user name
74     * @param   string $pass the clear text password
75     * @return  bool
76     */
77    public function checkPass($user, $pass) {
78
79        $data = $this->_selectUser($user);
80        if($data == false) return false;
81
82        if(isset($data['hash'])) {
83            // hashed password
84            $passhash = new PassHash();
85            return $passhash->verify_hash($pass, $data['hash']);
86        } else {
87            // clear text password in the database O_o
88            return ($pass == $data['clear']);
89        }
90    }
91
92    /**
93     * Return user info
94     *
95     * Returns info about the given user needs to contain
96     * at least these fields:
97     *
98     * name string  full name of the user
99     * mail string  email addres of the user
100     * grps array   list of groups the user is in
101     *
102     * @param   string $user the user name
103     * @param   bool $requireGroups whether or not the returned data must include groups
104     * @return array containing user data or false
105     */
106    public function getUserData($user, $requireGroups = true) {
107        $data = $this->_selectUser($user);
108        if($data == false) return false;
109
110        if($requireGroups) {
111
112        }
113
114        return $data;
115    }
116
117
118    /**
119     * Create a new User [implement only where required/possible]
120     *
121     * Returns false if the user already exists, null when an error
122     * occurred and true if everything went well.
123     *
124     * The new user HAS TO be added to the default group by this
125     * function!
126     *
127     * Set addUser capability when implemented
128     *
129     * @param  string $user
130     * @param  string $pass
131     * @param  string $name
132     * @param  string $mail
133     * @param  null|array $grps
134     * @return bool|null
135     */
136    //public function createUser($user, $pass, $name, $mail, $grps = null) {
137    // FIXME implement
138    //    return null;
139    //}
140
141    /**
142     * Modify user data [implement only where required/possible]
143     *
144     * Set the mod* capabilities according to the implemented features
145     *
146     * @param   string $user nick of the user to be changed
147     * @param   array $changes array of field/value pairs to be changed (password will be clear text)
148     * @return  bool
149     */
150    //public function modifyUser($user, $changes) {
151    // FIXME implement
152    //    return false;
153    //}
154
155    /**
156     * Delete one or more users [implement only where required/possible]
157     *
158     * Set delUser capability when implemented
159     *
160     * @param   array $users
161     * @return  int    number of users deleted
162     */
163    //public function deleteUsers($users) {
164    // FIXME implement
165    //    return false;
166    //}
167
168    /**
169     * Bulk retrieval of user data [implement only where required/possible]
170     *
171     * Set getUsers capability when implemented
172     *
173     * @param   int $start index of first user to be returned
174     * @param   int $limit max number of users to be returned
175     * @param   array $filter array of field/pattern pairs, null for no filter
176     * @return  array list of userinfo (refer getUserData for internal userinfo details)
177     */
178    //public function retrieveUsers($start = 0, $limit = -1, $filter = null) {
179    // FIXME implement
180    //    return array();
181    //}
182
183    /**
184     * Return a count of the number of user which meet $filter criteria
185     * [should be implemented whenever retrieveUsers is implemented]
186     *
187     * Set getUserCount capability when implemented
188     *
189     * @param  array $filter array of field/pattern pairs, empty array for no filter
190     * @return int
191     */
192    //public function getUserCount($filter = array()) {
193    // FIXME implement
194    //    return 0;
195    //}
196
197    /**
198     * Define a group [implement only where required/possible]
199     *
200     * Set addGroup capability when implemented
201     *
202     * @param   string $group
203     * @return  bool
204     */
205    //public function addGroup($group) {
206    // FIXME implement
207    //    return false;
208    //}
209
210    /**
211     * Retrieve groups [implement only where required/possible]
212     *
213     * Set getGroups capability when implemented
214     *
215     * @param   int $start
216     * @param   int $limit
217     * @return  array
218     */
219    //public function retrieveGroups($start = 0, $limit = 0) {
220    // FIXME implement
221    //    return array();
222    //}
223
224    /**
225     * Return case sensitivity of the backend
226     *
227     * When your backend is caseinsensitive (eg. you can login with USER and
228     * user) then you need to overwrite this method and return false
229     *
230     * @return bool
231     */
232    public function isCaseSensitive() {
233        return true;
234    }
235
236    /**
237     * Sanitize a given username
238     *
239     * This function is applied to any user name that is given to
240     * the backend and should also be applied to any user name within
241     * the backend before returning it somewhere.
242     *
243     * This should be used to enforce username restrictions.
244     *
245     * @param string $user username
246     * @return string the cleaned username
247     */
248    public function cleanUser($user) {
249        return $user;
250    }
251
252    /**
253     * Sanitize a given groupname
254     *
255     * This function is applied to any groupname that is given to
256     * the backend and should also be applied to any groupname within
257     * the backend before returning it somewhere.
258     *
259     * This should be used to enforce groupname restrictions.
260     *
261     * Groupnames are to be passed without a leading '@' here.
262     *
263     * @param  string $group groupname
264     * @return string the cleaned groupname
265     */
266    public function cleanGroup($group) {
267        return $group;
268    }
269
270    /**
271     * Check Session Cache validity [implement only where required/possible]
272     *
273     * DokuWiki caches user info in the user's session for the timespan defined
274     * in $conf['auth_security_timeout'].
275     *
276     * This makes sure slow authentication backends do not slow down DokuWiki.
277     * This also means that changes to the user database will not be reflected
278     * on currently logged in users.
279     *
280     * To accommodate for this, the user manager plugin will touch a reference
281     * file whenever a change is submitted. This function compares the filetime
282     * of this reference file with the time stored in the session.
283     *
284     * This reference file mechanism does not reflect changes done directly in
285     * the backend's database through other means than the user manager plugin.
286     *
287     * Fast backends might want to return always false, to force rechecks on
288     * each page load. Others might want to use their own checking here. If
289     * unsure, do not override.
290     *
291     * @param  string $user - The username
292     * @return bool
293     */
294    //public function useSessionCache($user) {
295    // FIXME implement
296    //}
297
298    /**
299     * Select data of a specified user
300     *
301     * @param $user
302     * @return bool|array
303     */
304    protected function _selectUser($user) {
305        $sql = $this->getConf('select-user');
306
307        try {
308            $sth = $this->pdo->prepare($sql);
309            $sth->execute(array(':user' => $user));
310            $result = $sth->fetchAll();
311            $sth->closeCursor();
312            $sth = null;
313        } catch(PDOException $e) {
314            $this->_debug($e);
315            $result = array();
316        }
317        $found = count($result);
318        if($found == 0) return false;
319
320        if($found > 1) {
321            $this->_debug('Found more than one matching user', -1, __LINE__);
322            return false;
323        }
324
325        $data = array_shift($result);
326        $dataok = true;
327
328        if(!isset($data['user'])) {
329            $this->_debug("Statement did not return 'user' attribute", -1, __LINE__);
330            $dataok = false;
331        }
332        if(!isset($data['hash']) && !isset($data['clear'])) {
333            $this->_debug("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__);
334            $dataok = false;
335        }
336        if(!isset($data['name'])) {
337            $this->_debug("Statement did not return 'name' attribute", -1, __LINE__);
338            $dataok = false;
339        }
340        if(!isset($data['mail'])) {
341            $this->_debug("Statement did not return 'mail' attribute", -1, __LINE__);
342            $dataok = false;
343        }
344
345        if(!$dataok) return false;
346        return $data;
347    }
348
349    /**
350     * Wrapper around msg() but outputs only when debug is enabled
351     *
352     * @param string|Exception $message
353     * @param int $err
354     * @param int $line
355     */
356    protected function _debug($message, $err = 0, $line = 0) {
357        if(!$this->getConf('debug')) return;
358        if(is_a($message, 'Exception')) {
359            $err = -1;
360            $line = $message->getLine();
361            $msg = $message->getMessage();
362        } else {
363            $msg = $message;
364        }
365
366        if(defined('DOKU_UNITTEST')) {
367            printf("\n%s, %s:%d\n", $msg, __FILE__, $line);
368        } else {
369            msg('authpdo: ' . $msg, $err, $line, __FILE__);
370        }
371    }
372}
373
374// vim:ts=4:sw=4:et:
375