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