xref: /plugin/authchained/auth.php (revision d9c5261fdaa794cf555ad2b2e2173640d505630f)
1<?php
2// must be run within Dokuwiki
3if(!defined('DOKU_INC')) die();
4
5/**
6* Chained authentication backend
7*
8* @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
9* @author     Philipp Neuser <pneuser@physik.fu-berlin.de>
10* @author     Christian Marg <marg@rz.tu-clausthal.de>
11*
12* Based on "Chained authentication backend"
13* by Grant Gardner <grant@lastweekend.com.au>
14* see https://www.dokuwiki.org/auth:ggauth
15*
16*/
17class auth_plugin_authchained extends DokuWiki_Auth_Plugin {
18    public $success = true;
19    //array with authentication plugins
20    protected $chained_plugins = array();
21    protected $chained_auth = NULL;
22    protected $usermanager_auth = NULL;
23
24    /**
25    * Constructor.
26    *
27    * Loads all configured plugins or the authentication plugin of the
28    * logged in user.
29    *
30    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
31    * @author  Christian Marg <marg@rz.tu-clausthal.de>
32    */
33    public function __construct() {
34        global $conf;
35        // call parent
36        #      parent::__constructor();
37
38        //check if there is already an authentication plugin selected
39        if(     isset($_SESSION[DOKU_COOKIE]['plugin']['authchained']['module']) &&
40                !empty($_SESSION[DOKU_COOKIE]['plugin']['authchained']['module']) ) {
41
42            //get previously selected authentication plugin
43            $this->chained_auth =& plugin_load('auth',$_SESSION[DOKU_COOKIE]['plugin']['authchained']['module']);
44            if ( is_null($this->chained_auth) || !$this->chained_auth->success ) {
45                $this->success = false;
46            }
47        } else {
48                        print_r($this->getConf('authtypes'));
49            //get authentication plugins
50            if($this->getConf('authtypes')){
51                foreach(explode(":",$this->getConf('authtypes')) as $tmp_plugin){
52                    $tmp_class =& plugin_load('auth',$tmp_plugin);
53
54                    if ( !is_null($tmp_class) || $tmp_class->success ) {
55                        $tmp_module = array($tmp_plugin,$tmp_class);
56                        array_push($this->chained_plugins, $tmp_module);
57                    } else {
58                        msg("Problem constructing $tmp_plugin",-1);
59                        $this->success = false;
60                    }
61                }
62            } else {
63                $success = false;
64            }
65        }
66
67        // If defined, instantiate usermanager authtype.
68        // No need to check for duplicates, "plugin_load" does that for us.
69        if($this->getConf('usermanager_authtype')){
70            $this->usermanager_auth =& plugin_load('auth',$this->getConf('usermanager_authtype'));
71            if(is_null($this->usermanager_auth) || !$this->usermanager_auth->success ) {
72                    msg("Problem constructing usermanager authtype: ".$this->getConf('usermanager_authtype'),-1);
73                    $this->success = false;
74            }
75        } else {
76            $this->usermanager_auth =& $this->chained_auth;
77        }
78
79        //debug
80        //      print_r($chained_plugins);
81    }
82
83    /**
84    * Forwards the authentication to configured authplugins.
85    * Returns true, if the usermanager authtype has the capability and no user
86    * is logged in.
87    *
88    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
89    * @author  Christian Marg <marg@rz.tu-clausthal.de>
90    * @param   string $cap the capability to check
91    * @return  bool
92    */
93    public function canDo($cap) {
94        global $ACT;
95        #      print_r($cap);
96        if(is_null($this->chained_auth)) {
97            if (!is_null($this->usermanager_auth)) {
98                return $this->usermanager_auth->canDo($cap);
99            } else {
100                return parent::canDo($cap);
101            }
102        } else {
103            switch($cap) {
104                case 'Profile':
105                case 'logoff':
106                    //Depends on current user.
107                    return $this->chained_auth->canDo($cap);
108                case 'UserMod':
109                case 'addUser':
110                case 'delUser':
111                case 'getUsers':
112                case 'getUserCount':
113                case 'getGroups':
114                    //Depends on the auth for use with user manager
115                    return $this->usermanager_auth->canDo($cap);
116                case 'modPass':
117                case 'modName':
118                case 'modLogin':
119                case 'modGroups':
120                case 'modMail':
121                    /**
122                    * Use request attributes to guess whether we are in the Profile or UserManager
123                    * and return the appropriate auth capabilities
124                    */
125                    if ($ACT == "admin" && $_REQUEST['page']=="usermanager") {
126                        return $this->usermanager_auth->canDo($cap);
127                    } else {
128                        // assume we want profile info.
129                        return $this->chained_auth->canDo($cap);
130                    }
131// I don't know how to handle "external" in this context yet.
132// Is it in any way sensible to mix regular auth with external auth?
133//                case 'external':
134//                    //We are external if one of the chains is valid for external use
135//                    return $this->trustExternal($_REQUEST['u'],$_REQUEST['p'],$_REQUEST['r']);
136                default:
137                    //Everything else (false)
138                    return parent::canDo($cap);
139            }
140            #echo "canDo $cap ".$this->chained_auth->canDo($cap)."\n";
141        }
142    }
143
144    /**
145    * Forwards the result of the auth plugin of the logged in user and
146    * unsets our session variable.
147    * @see     auth_logoff()
148    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de
149    * @author  Christian Marg <marg@rz.tu-clausthal.de>
150    */
151    public function logOff() {
152        if(!is_null($this->chained_auth))
153            $this->chained_auth->logOff();
154        unset($_SESSION[DOKU_COOKIE]['plugin']['authchained']['module']);
155    }
156
157    /**
158    * Do all authentication [ OPTIONAL ]
159    * If the current plugin is external, be external.
160    *
161    * @see     auth_login()
162    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
163    * @author  Christian Marg <marg@rz.tu-clausthal.de>
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        if(!is_null($this->chained_auth) && $this->chained_auth->canDo('external'))
172            $this->chained_auth->trustExternal($user, $pass, $sticky);
173    }
174
175    /**
176    * Check user+password [ MUST BE OVERRIDDEN ]
177    *
178    * Checks if the given user exists in one of the plugins and checks
179    * against the given password. The first plugin returning true becomes
180    * auth plugin of the user session.
181    *
182    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de
183    * @author  Christian Marg <marg@rz.tu-clausthal.de>
184    * @param   string $user the user name
185    * @param   string $pass the clear text password
186    * @return  bool
187    */
188    public function checkPass($user, $pass) {
189        //debug
190        //print_r($this->chained_plugins);
191        if(is_null($this->chained_auth)) {
192            foreach($this->chained_plugins as $module) {
193                if($module[1]->canDo('external')) {
194                    if($module[1]->trustExternal($user, $pass)) {
195                        $_SESSION[DOKU_COOKIE]['plugin']['authchained']['module'] = $module[0];
196                        $this->chained_auth = $module[1];
197                        return true;
198                    } else {
199                        if($module[1]->checkPass($user, $pass)) {
200                            $_SESSION[DOKU_COOKIE]['plugin']['authchained']['module'] = $module[0];
201                            $this->chained_auth = $module[1];
202                            return true;
203                        }
204                    }
205                } else {
206                    if($module[1]->checkPass($user, $pass)) {
207                        $_SESSION[DOKU_COOKIE]['plugin']['authchained']['module'] = $module[0];
208                        $this->this->chained_auth = $module[1];
209                        return true;
210                    }
211                }
212            }
213        } else {
214            return $this->chained_auth->checkPass($user, $pass);
215        }
216        return false;
217    }
218
219    /**
220    * Forwards the result of the auth plugin of the logged in user or
221    * checks all plugins if the users exists. The first plugin returning
222    * data is used.
223    *
224    * name string  full name of the user
225    * mail string  email addres of the user
226    * grps array   list of groups the user is in
227    *
228    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
229    * @author  Christian Marg <marg@rz.tu-clausthal.de>
230    * @param   string $user the user name
231    * @return  array containing user data or false
232    */
233    public function getUserData($user) {
234        //if(!$this->cando['external']) msg("no valid authorisation system in use", -1);
235        //       echo "TESTSETEST";
236        if(is_null($this->chained_auth)) {
237            foreach($this->chained_plugins as $module) {
238                $tmp_array = $module[1]->getUserData($user);
239                if(!is_bool($tmp_array))
240                    $tmp_chk_arr =array_filter($tmp_array);
241                if(!empty($tmp_chk_arr) && $tmp_array)
242                    return $tmp_array;
243            }
244            return false;
245        } else {
246            return $this->chained_auth->getUserData($user);
247        }
248    }
249
250    /**
251    * Forwards the result of the auth plugin of the logged in user or
252    * returns null.
253    *
254    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
255    * @author  Christian Marg <marg@rz.tu-clausthal.de>
256    * @param   string     $user
257    * @param   string     $pass
258    * @param   string     $name
259    * @param   string     $mail
260    * @param   null|array $grps
261    * @return  bool|null
262    */
263    public function createUser($user, $pass, $name, $mail, $grps = null) {
264        if(!is_null($this->usermanager_auth) && $this->canDo('addUser')) {
265            return $this->usermanager_auth->createUser($user, $pass, $name, $mail, $grps);
266        } else {
267            msg("authorisation method does not allow creation of new users", -1);
268            return null;
269        }
270    }
271
272    /**
273    * Forwards the result of the auth plugin of the logged in user or
274    * returns false
275    *
276    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
277    * @author  Christian Marg <marg@rz.tu-clausthal.de>
278    * @param   string $user    nick of the user to be changed
279    * @param   array  $changes array of field/value pairs to be changed (password will be clear text)
280    * @return  bool
281    */
282    public function modifyUser($user, $changes) {
283        if(!is_null($this->usermanager_auth) && $this->canDo('UserMod') ) {
284            return $this->usermanager_auth->modifyUser($user, $changes);
285        } else {
286            msg("authorisation method does not allow modifying of user data", -1);
287            return null;
288        }
289    }
290
291    /**
292    * Forwards the result of the auth plugin of the logged in user or
293    * returns false
294    *
295    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
296    * @author  Christian Marg <marg@rz.tu-clausthal.de>
297    * @param   array  $users
298    * @return  int    number of users deleted
299    */
300    public function deleteUsers($users) {
301        if(!is_null($this->usermanager_auth) && $this->canDo('delUser') ) {
302            return $this->usermanager_auth->deleteUsers($users);
303        }else{
304            msg("authorisation method does not allow deleting of users", -1);
305            return false;
306        }
307    }
308
309    /**
310    * Forwards the result of the auth plugin of the logged in user or
311    * returns 0
312    *
313    * @author Philipp Neuser <pneuser@physik.fu-berlin.de>
314    * @author Christian Marg <marg@rz.tu-clausthal.de>
315    * @param  array $filter array of field/pattern pairs, empty array for no filter
316    * @return int
317    */
318    public function getUserCount($filter = array()) {
319        if(!is_null($this->usermanager_auth) && $this->canDo('getUserCount') ){
320            return $this->usermanager_auth->getUserCount($filter);
321        } else {
322            msg("authorisation method does not provide user counts", -1);
323            return 0;
324        }
325    }
326
327    /**
328    * Forwards the result of the auth plugin of the logged in user or
329    * returns empty array
330    *
331    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
332    * @author  Christian Marg <marg@rz.tu-clausthal.de>
333    * @param   int   $start     index of first user to be returned
334    * @param   int   $limit     max number of users to be returned
335    * @param   array $filter    array of field/pattern pairs, null for no filter
336    * @return  array list of userinfo (refer getUserData for internal userinfo details)
337    */
338    public function retrieveUsers($start = 0, $limit = -1, $filter = null) {
339        if(!is_null($this->usermanager_auth) && $this->canDo('getUsers') ) {
340            //msg("RetrieveUsers is using ".get_class($this->usermanager_auth));
341            return $this->usermanager_auth->retrieveUsers($start, $limit, $filter);
342        } else {
343            msg("authorisation method does not support mass retrievals", -1);
344            return array();
345        }
346    }
347
348    /**
349    * Forwards the result of the auth plugin of the logged in user or
350    * returns false
351    *
352    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
353    * @author  Christian Marg <marg@rz.tu-clausthal.de>
354    * @param   string $group
355    * @return  bool
356    */
357    public function addGroup($group) {
358        if(!is_null($this->usermanager_auth) && $this->canDo('addGroup') ) {
359            return $this->usermanager_auth->addGroup($group);
360        } else {
361            msg("authorisation method does not support independent group creation", -1);
362            return false;
363        }
364    }
365
366    /**
367    * Forwards the result of the auth plugin of the logged in user or
368    * returns empty array
369    *
370    * @author  Philipp Neuser <pneuser@physik.fu-berlin.de>
371    * @author  Christian Marg <marg@rz.tu-clausthal.de>
372    * @param   int $start
373    * @param   int $limit
374    * @return  array
375    */
376    public function retrieveGroups($start = 0, $limit = 0) {
377        if(!is_null($this->usermanager_auth) && $this->canDo('getGroups') ) {
378                return $this->usermanager_auth->retrieveGroups($start,$limit);
379        } else {
380            msg("authorisation method does not support group list retrieval", -1);
381            return array();
382        }
383    }
384
385    /**
386    * Forwards the result of the auth plugin of the logged in user or
387    * returns true
388    *
389    * @return bool
390    */
391    public function isCaseSensitive() {
392        if(is_null($this->chained_auth))
393            return parent::isCaseSensitive();
394        else
395            return $this->chained_auth->isCaseSensitive();
396    }
397
398    /**
399    * Sanitize a given username [OPTIONAL]
400    * Forwards the result of the auth plugin of the logged in user or
401    * returns false
402    *
403    *
404    * @author Philipp Neuser <pneuser@physik.fu-berlin.de>
405    * @author Christian Marg <marg@rz.tu-clausthal.de>
406    * @param  string $user username
407    * @return string the cleaned username
408    */
409    public function cleanUser($user) {
410        global $ACT;
411        //print_r($this->chained_auth);
412        if ($ACT == "admin" && $_REQUEST['page']=="usermanager") {
413            if(!is_null($this->usermanager_auth))
414                return $this->usermanager_auth->cleanUser($user);
415        } else {
416            if(!is_null($this->chained_auth))
417                return $this->chained_auth->cleanUser($user);
418        }
419        return parent::cleanUser($user);
420    }
421
422    /**
423    * Sanitize a given groupname [OPTIONAL]
424    * Forwards the result of the auth plugin of the logged in user or
425    * returns false
426    *
427    * @author Philipp Neuser <pneuser@physik.fu-berlin.de>
428    * @author Christian Marg <marg@rz.tu-clausthal.de>
429    * @param  string $group groupname
430    * @return string the cleaned groupname
431    */
432    public function cleanGroup($group) {
433        global $ACT;
434        if ($ACT == "admin" && $_REQUEST['page']=="usermanager") {
435            if(!is_null($this->usermanager_auth))
436                return $this->usermanager_auth->cleanGroup($group);
437        } else {
438            if(!is_null($this->chained_auth))
439                return $this->chained_auth->cleanGroup($group);
440        }
441        return parent::cleanGroup($group);
442    }
443
444
445    public function useSessionCache($user) {
446        global $conf;
447        if(is_null($this->chained_auth))
448            return parent::useSessionCache($user);
449        else
450            return $this->chained_auth->useSessionCache($user);
451    }
452
453}
454