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