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