1<?php
2// must be run within Dokuwiki
3if(!defined('DOKU_INC')) die();
4
5/**
6 * Authentication backend using generic SSO
7 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 * @author     Dominique Launay <dominique.launay AT cru.fr>
9 * @author     Olivier Salaun <olivier.salaun AT cru.fr>
10 * @author     Yoann Lecuyer <yoann.lecuyer AT cru.fr>
11 * @author     Etienne Meleard <etienne.meleard AT renater.fr>
12 **/
13
14class auth_plugin_genericsso extends DokuWiki_Auth_Plugin {
15    private $users = array();
16
17    public function __construct() {
18        parent::__construct();
19
20        $this->loadConfig();
21
22        $this->cando['external'] = true;
23        $this->cando['logout'] = true;
24        $this->success = false;
25
26        // check if the server configuration has correctly been done
27        $missing_conf = array();
28        foreach(array('emailAttribute', 'loginURL', 'logoutURL') as $k)
29            if(!array_key_exists($k, $this->conf) || !$this->conf[$k])
30                $missing_conf[] = $k;
31
32        if($missing_conf) {
33            msg('Your genericsso configuration is not fully set, missing parameters : '.implode(', ', $missing_conf), -1, '', '', MSG_ADMINS_ONLY);
34            return;
35        }
36
37        if(!array_key_exists('alwaysCheck', $this->conf))
38            $this->conf['alwaysCheck'] = false;
39
40        $this->success = true;
41    }
42
43    // Required
44    public function checkPass($user, $pass) {}
45
46    /**
47    * Return user info
48    **/
49    public function getUserData($user) {
50        if(is_null($this->users)) $this->loadUsers();
51        if(array_key_exists($user, $this->users)) return $this->users[$user]; // Cache
52        $this->users[$user] = array('name' => $user, 'mail' => $user, 'grps' => array());
53        return $this->users[$user];
54    }
55
56    /**
57    * Do all authentication
58    * @param   string  $user    Username
59    * @param   string  $pass    Cleartext Password
60    * @param   bool    $sticky  Cookie should not expire
61    * @return  bool             true on successful auth
62    */
63    public function trustExternal($user, $pass, $sticky=false) {
64        global $USERINFO;
65        global $ACT;
66        global $conf;
67
68        $do = array_key_exists('do', $_REQUEST) ? $_REQUEST['do'] : null;
69        $user = $this->getSSOEMail();
70
71        //Got a session already ?
72        if($this->hasSession()) {
73            if($this->conf['alwaysCheck'] && !$user) {
74                auth_logoff();
75                return false;
76            }
77            if($do == 'logout') $this->logOff(false); // Logout request ?
78            return true;
79        }else{ // No session, do the stuff
80            if($user) {
81                if($do == 'logout') $this->logOff(false); // Logout request ?
82
83                $data = $this->getUserData($user);
84                $this->setSession($user, $data['grps'], $data['mail'], $data['name']);
85                error_log('genericsso : authenticated user');
86                return true;
87            }else{
88                if($do == 'login') $this->logIn();
89                //error_log('genericsso : no email address to log in');
90                auth_logoff();
91                return false;
92            }
93        }
94    }
95
96    private function hasSession() {
97        if(is_null($_SESSION[DOKU_COOKIE])) return false;
98        if(!array_key_exists('auth', $_SESSION[DOKU_COOKIE]) || !$_SESSION[DOKU_COOKIE]['auth']) return false;
99        if(!array_key_exists('user', $_SESSION[DOKU_COOKIE]['auth']) || !$_SESSION[DOKU_COOKIE]['auth']['user']) return false;
100        global $USERINFO;
101        $USERINFO = $_SESSION[DOKU_COOKIE]['auth']['info'];
102        $_SERVER['REMOTE_USER'] = $_SESSION[DOKU_COOKIE]['auth']['user'];
103        return true;
104    }
105
106    // Create user session
107    private function setSession($user, $grps = null, $mail = null, $name = null) {
108        global $USERINFO;
109        $USERINFO['name'] = $name ? $name : $user;
110        $USERINFO['mail'] = $mail ? $mail : (mail_isvalid($user) ? $user : null);
111        $USERINFO['grps'] = is_array($grps) ? $grps : array();
112        $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
113        $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
114        $_SERVER['REMOTE_USER'] = $user;
115        return $_SESSION[DOKU_COOKIE];
116    }
117
118    // Get EMail from Shib env
119    private function getSSOEMail() {
120        if(!array_key_exists($this->conf['emailAttribute'], $_SERVER)) return null;
121        $mail = $_SERVER[$this->conf['emailAttribute']];
122        if(!$mail || !mail_isvalid($mail)) return null;
123        return $mail;
124    }
125
126    // Redirect for login
127    public function logIn() {
128        error_log('genericsso : redirect user for login to '.$this->conf['loginURL']);
129        header('Location: '.str_replace('{target}', wl(getId()), $this->conf['loginURL']));
130        exit;
131    }
132
133    // Redirect for logout
134    public function logOff($ignore = true) {
135        if($ignore) return;
136        auth_logoff();
137        error_log('genericsso : authenticated user redirected for logout to '.$this->conf['logoutURL']);
138        header('Location: '.str_replace('{target}', $_SERVER['HTTP_REFERER'], $this->conf['logoutURL']));
139        exit;
140    }
141
142    /**
143    * Load local user data
144    */
145    private function loadUsers(){
146        $this->users = array();
147        if(!@file_exists(DOKU_CONF.'users.auth.php')) return;
148        foreach(file(DOKU_CONF.'users.auth.php') as $line){
149            $line = trim(preg_replace('/#.*$/', '', $line)); //ignore comments
150            if(!$line) continue;
151            $row = split(':', $line,5);
152            $this->users[$row[0]] = array(
153                'pass' => $row[1],
154                'name' => urldecode($row[2]),
155                'mail' => $row[3],
156                'grps' => split(',', $row[4])
157            );
158        }
159    }
160}
161