xref: /dokuwiki/lib/plugins/auth.php (revision ecd445c000e4e54bf7228890848222312cffd3e3)
1<?php
2// must be run within Dokuwiki
3if(!defined('DOKU_INC')) die();
4
5/**
6 * Auth Plugin Prototype
7 *
8 * foundation authorisation class
9 * all auth classes should inherit from this class
10 *
11 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
12 * @author    Chris Smith <chris@jalakai.co.uk>
13 * @author    Jan Schumann <js@jschumann-it.com>
14 */
15class DokuWiki_Auth_Plugin extends DokuWiki_Plugin {
16    public $success = true;
17
18    /**
19     * Possible things an auth backend module may be able to
20     * do. The things a backend can do need to be set to true
21     * in the constructor.
22     */
23    protected $cando = array(
24        'addUser'     => false, // can Users be created?
25        'delUser'     => false, // can Users be deleted?
26        'modLogin'    => false, // can login names be changed?
27        'modPass'     => false, // can passwords be changed?
28        'modName'     => false, // can real names be changed?
29        'modMail'     => false, // can emails be changed?
30        'modGroups'   => false, // can groups be changed?
31        'getUsers'    => false, // can a (filtered) list of users be retrieved?
32        'getUserCount'=> false, // can the number of users be retrieved?
33        'getGroups'   => false, // can a list of available groups be retrieved?
34        'external'    => false, // does the module do external auth checking?
35        'logout'      => true, // can the user logout again? (eg. not possible with HTTP auth)
36    );
37
38    /**
39     * Constructor.
40     *
41     * Carry out sanity checks to ensure the object is
42     * able to operate. Set capabilities in $this->cando
43     * array here
44     *
45     * Set $this->success to false if checks fail
46     *
47     * @author  Christopher Smith <chris@jalakai.co.uk>
48     */
49    public function __construct() {
50        // the base class constructor does nothing, derived class
51        // constructors do the real work
52    }
53
54    /**
55     * Capability check. [ DO NOT OVERRIDE ]
56     *
57     * Checks the capabilities set in the $this->cando array and
58     * some pseudo capabilities (shortcutting access to multiple
59     * ones)
60     *
61     * ususal capabilities start with lowercase letter
62     * shortcut capabilities start with uppercase letter
63     *
64     * @author  Andreas Gohr <andi@splitbrain.org>
65     * @param   string $cap the capability to check
66     * @return  bool
67     */
68    public function canDo($cap) {
69        switch($cap) {
70            case 'Profile':
71                // can at least one of the user's properties be changed?
72                return ($this->cando['modPass'] ||
73                    $this->cando['modName'] ||
74                    $this->cando['modMail']);
75                break;
76            case 'UserMod':
77                // can at least anything be changed?
78                return ($this->cando['modPass'] ||
79                    $this->cando['modName'] ||
80                    $this->cando['modMail'] ||
81                    $this->cando['modLogin'] ||
82                    $this->cando['modGroups'] ||
83                    $this->cando['modMail']);
84                break;
85            default:
86                // print a helping message for developers
87                if(!isset($this->cando[$cap])) {
88                    msg("Check for unknown capability '$cap' - Do you use an outdated Plugin?", -1);
89                }
90                return $this->cando[$cap];
91        }
92    }
93
94    /**
95     * Trigger the AUTH_USERDATA_CHANGE event and call the modification function. [ DO NOT OVERRIDE ]
96     *
97     * You should use this function instead of calling createUser, modifyUser or
98     * deleteUsers directly. The event handlers can prevent the modification, for
99     * example for enforcing a user name schema.
100     *
101     * @author Gabriel Birke <birke@d-scribe.de>
102     * @param string $type Modification type ('create', 'modify', 'delete')
103     * @param array  $params Parameters for the createUser, modifyUser or deleteUsers method. The content of this array depends on the modification type
104     * @return mixed Result from the modification function or false if an event handler has canceled the action
105     */
106    public function triggerUserMod($type, $params) {
107        $validTypes = array(
108            'create' => 'createUser',
109            'modify' => 'modifyUser',
110            'delete' => 'deleteUsers'
111        );
112        if(empty($validTypes[$type]))
113            return false;
114        $eventdata = array('type' => $type, 'params' => $params, 'modification_result' => null);
115        $evt       = new Doku_Event('AUTH_USER_CHANGE', $eventdata);
116        if($evt->advise_before(true)) {
117            $result                           = call_user_func_array(array($this, $validTypes[$type]), $params);
118            $evt->data['modification_result'] = $result;
119        }
120        $evt->advise_after();
121        unset($evt);
122        return $result;
123    }
124
125    /**
126     * Log off the current user [ OPTIONAL ]
127     *
128     * Is run in addition to the ususal logoff method. Should
129     * only be needed when trustExternal is implemented.
130     *
131     * @see     auth_logoff()
132     * @author  Andreas Gohr <andi@splitbrain.org>
133     */
134    public function logOff() {
135    }
136
137    /**
138     * Do all authentication [ OPTIONAL ]
139     *
140     * Set $this->cando['external'] = true when implemented
141     *
142     * If this function is implemented it will be used to
143     * authenticate a user - all other DokuWiki internals
144     * will not be used for authenticating, thus
145     * implementing the checkPass() function is not needed
146     * anymore.
147     *
148     * The function can be used to authenticate against third
149     * party cookies or Apache auth mechanisms and replaces
150     * the auth_login() function
151     *
152     * The function will be called with or without a set
153     * username. If the Username is given it was called
154     * from the login form and the given credentials might
155     * need to be checked. If no username was given it
156     * the function needs to check if the user is logged in
157     * by other means (cookie, environment).
158     *
159     * The function needs to set some globals needed by
160     * DokuWiki like auth_login() does.
161     *
162     * @see auth_login()
163     * @author  Andreas Gohr <andi@splitbrain.org>
164     *
165     * @param   string  $user    Username
166     * @param   string  $pass    Cleartext Password
167     * @param   bool    $sticky  Cookie should not expire
168     * @return  bool             true on successful auth
169     */
170    public function trustExternal($user, $pass, $sticky = false) {
171        /* some example:
172
173        global $USERINFO;
174        global $conf;
175        $sticky ? $sticky = true : $sticky = false; //sanity check
176
177        // do the checking here
178
179        // set the globals if authed
180        $USERINFO['name'] = 'FIXME';
181        $USERINFO['mail'] = 'FIXME';
182        $USERINFO['grps'] = array('FIXME');
183        $_SERVER['REMOTE_USER'] = $user;
184        $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
185        $_SESSION[DOKU_COOKIE]['auth']['pass'] = $pass;
186        $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
187        return true;
188
189        */
190    }
191
192    /**
193     * Check user+password [ MUST BE OVERRIDDEN ]
194     *
195     * Checks if the given user exists and the given
196     * plaintext password is correct
197     *
198     * May be ommited if trustExternal is used.
199     *
200     * @author  Andreas Gohr <andi@splitbrain.org>
201     * @param   string $user the user name
202     * @param   string $pass the clear text password
203     * @return  bool
204     */
205    public function checkPass($user, $pass) {
206        msg("no valid authorisation system in use", -1);
207        return false;
208    }
209
210    /**
211     * Return user info [ MUST BE OVERRIDDEN ]
212     *
213     * Returns info about the given user needs to contain
214     * at least these fields:
215     *
216     * name string  full name of the user
217     * mail string  email addres of the user
218     * grps array   list of groups the user is in
219     *
220     * @author  Andreas Gohr <andi@splitbrain.org>
221     * @param   string $user the user name
222     * @return  array containing user data or false
223     */
224    public function getUserData($user) {
225        if(!$this->cando['external']) msg("no valid authorisation system in use", -1);
226        return false;
227    }
228
229    /**
230     * Create a new User [implement only where required/possible]
231     *
232     * Returns false if the user already exists, null when an error
233     * occurred and true if everything went well.
234     *
235     * The new user HAS TO be added to the default group by this
236     * function!
237     *
238     * Set addUser capability when implemented
239     *
240     * @author  Andreas Gohr <andi@splitbrain.org>
241     * @param  string     $user
242     * @param  string     $pass
243     * @param  string     $name
244     * @param  string     $mail
245     * @param  null|array $grps
246     * @return bool|null
247     */
248    public function createUser($user, $pass, $name, $mail, $grps = null) {
249        msg("authorisation method does not allow creation of new users", -1);
250        return null;
251    }
252
253    /**
254     * Modify user data [implement only where required/possible]
255     *
256     * Set the mod* capabilities according to the implemented features
257     *
258     * @author  Chris Smith <chris@jalakai.co.uk>
259     * @param   string $user    nick of the user to be changed
260     * @param   array  $changes array of field/value pairs to be changed (password will be clear text)
261     * @return  bool
262     */
263    public function modifyUser($user, $changes) {
264        msg("authorisation method does not allow modifying of user data", -1);
265        return false;
266    }
267
268    /**
269     * Delete one or more users [implement only where required/possible]
270     *
271     * Set delUser capability when implemented
272     *
273     * @author  Chris Smith <chris@jalakai.co.uk>
274     * @param   array  $users
275     * @return  int    number of users deleted
276     */
277    public function deleteUsers($users) {
278        msg("authorisation method does not allow deleting of users", -1);
279        return false;
280    }
281
282    /**
283     * Return a count of the number of user which meet $filter criteria
284     * [should be implemented whenever retrieveUsers is implemented]
285     *
286     * Set getUserCount capability when implemented
287     *
288     * @author Chris Smith <chris@jalakai.co.uk>
289     * @param  array $filter array of field/pattern pairs, empty array for no filter
290     * @return int
291     */
292    public function getUserCount($filter = array()) {
293        msg("authorisation method does not provide user counts", -1);
294        return 0;
295    }
296
297    /**
298     * Bulk retrieval of user data [implement only where required/possible]
299     *
300     * Set getUsers capability when implemented
301     *
302     * @author  Chris Smith <chris@jalakai.co.uk>
303     * @param   int   $start     index of first user to be returned
304     * @param   int   $limit     max number of users to be returned
305     * @param   array $filter    array of field/pattern pairs, null for no filter
306     * @return  array list of userinfo (refer getUserData for internal userinfo details)
307     */
308    public function retrieveUsers($start = 0, $limit = -1, $filter = null) {
309        msg("authorisation method does not support mass retrieval of user data", -1);
310        return array();
311    }
312
313    /**
314     * Define a group [implement only where required/possible]
315     *
316     * Set addGroup capability when implemented
317     *
318     * @author  Chris Smith <chris@jalakai.co.uk>
319     * @param   string $group
320     * @return  bool
321     */
322    public function addGroup($group) {
323        msg("authorisation method does not support independent group creation", -1);
324        return false;
325    }
326
327    /**
328     * Retrieve groups [implement only where required/possible]
329     *
330     * Set getGroups capability when implemented
331     *
332     * @author  Chris Smith <chris@jalakai.co.uk>
333     * @param   int $start
334     * @param   int $limit
335     * @return  array
336     */
337    public function retrieveGroups($start = 0, $limit = 0) {
338        msg("authorisation method does not support group list retrieval", -1);
339        return array();
340    }
341
342    /**
343     * Return case sensitivity of the backend [OPTIONAL]
344     *
345     * When your backend is caseinsensitive (eg. you can login with USER and
346     * user) then you need to overwrite this method and return false
347     *
348     * @return bool
349     */
350    public function isCaseSensitive() {
351        return true;
352    }
353
354    /**
355     * Sanitize a given username [OPTIONAL]
356     *
357     * This function is applied to any user name that is given to
358     * the backend and should also be applied to any user name within
359     * the backend before returning it somewhere.
360     *
361     * This should be used to enforce username restrictions.
362     *
363     * @author Andreas Gohr <andi@splitbrain.org>
364     * @param string $user username
365     * @return string the cleaned username
366     */
367    public function cleanUser($user) {
368        return $user;
369    }
370
371    /**
372     * Sanitize a given groupname [OPTIONAL]
373     *
374     * This function is applied to any groupname that is given to
375     * the backend and should also be applied to any groupname within
376     * the backend before returning it somewhere.
377     *
378     * This should be used to enforce groupname restrictions.
379     *
380     * Groupnames are to be passed without a leading '@' here.
381     *
382     * @author Andreas Gohr <andi@splitbrain.org>
383     * @param  string $group groupname
384     * @return string the cleaned groupname
385     */
386    public function cleanGroup($group) {
387        return $group;
388    }
389
390    /**
391     * Check Session Cache validity [implement only where required/possible]
392     *
393     * DokuWiki caches user info in the user's session for the timespan defined
394     * in $conf['auth_security_timeout'].
395     *
396     * This makes sure slow authentication backends do not slow down DokuWiki.
397     * This also means that changes to the user database will not be reflected
398     * on currently logged in users.
399     *
400     * To accommodate for this, the user manager plugin will touch a reference
401     * file whenever a change is submitted. This function compares the filetime
402     * of this reference file with the time stored in the session.
403     *
404     * This reference file mechanism does not reflect changes done directly in
405     * the backend's database through other means than the user manager plugin.
406     *
407     * Fast backends might want to return always false, to force rechecks on
408     * each page load. Others might want to use their own checking here. If
409     * unsure, do not override.
410     *
411     * @param  string $user - The username
412     * @author Andreas Gohr <andi@splitbrain.org>
413     * @return bool
414     */
415    public function useSessionCache($user) {
416        global $conf;
417        return ($_SESSION[DOKU_COOKIE]['auth']['time'] >= @filemtime($conf['cachedir'].'/sessionpurge'));
418    }
419
420    /**
421     * loadConfig()
422     * merges the plugin's default settings with any local settings
423     * this function is automatically called through getConf()
424     */
425    function loadConfig() {
426        global $conf;
427
428        parent::loadConfig();
429
430        $this->conf['debug']          = $conf['debug'];
431        $this->conf['useacl']         = $conf['useacl'];
432        $this->conf['disableactions'] = $conf['disableactions'];
433        $this->conf['autopasswd']     = $conf['autopasswd'];
434        $this->conf['passcrypt']      = $conf['ssha'];
435    }
436}
437