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