1<?php
2
3/**
4 * CAS authentication plugin
5 *
6 * @licence   GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @author    Xylle, Fabian Bircher
8 * @version   0.0.3
9 *
10 */
11
12use dokuwiki\Extension\AuthPlugin;
13
14
15class auth_plugin_authssocas extends AuthPlugin
16{
17    /**
18     * @var array|mixed
19     */
20    private array $options = array();
21
22    private ?string $logfileuser = null;
23
24    public function __construct()
25    {
26        global $conf;
27        parent::__construct();
28        require_once __DIR__ . '/vendor/autoload.php';
29
30        // Vérifie si la classe phpCAS existe
31        if (!class_exists('phpCAS')) {
32            msg("CAS err: phpCAS class not found.", -1);
33            $this->success = false;
34            return;
35        }
36        // Vérifie si l'extension curl existe
37        if (!extension_loaded("curl")) {
38            msg("CAS err: CURL php extension not found.", -1);
39            $this->success = false;
40            return;
41        }
42        // Définition des capacités de l'extension d'authentification
43        $this->cando['external'] = true;
44//        $this->cando['login'] = true;
45//        $this->cando['logout'] = true;
46
47        // Création d'un journal des connexions, si un fichier est défini.
48        if ($this->getConf('logfileuser')) {
49            $this->logfileuser = $conf['logdir'] . "/" . $this->getConf('logfileuser');
50        }
51        if (!is_null($this->logfileuser) and !@is_readable($this->logfileuser)) {
52            if (!fopen($this->logfileuser, 'a')) {
53                msg("plainCAS: The CAS log users file could not be opened.", -1);
54                $this->success = false;
55            }
56        }
57
58
59        // Chargement des options
60        $this->options['debug'] = $this->getConf('debug');
61        $this->options['group_attribut'] = $this->getConf('group_attribut');
62        $this->options['handlelogoutrequest'] = $this->getConf('handlelogoutrequest');
63        $this->options['handlelogoutrequestTrustedHosts'] = $this->getConf('handlelogoutrequestTrustedHosts');
64        $this->options['mail_attribut'] = $this->getConf('mail_attribut');
65        $this->options['name_attribut'] = $this->getConf('name_attribut');
66        $this->options['port'] = $this->getConf('port');
67        $this->options['samlValidate'] = $this->getConf('samlValidate');
68        $this->options['server'] = $this->getConf('server');
69        $this->options['rootcas'] = $this->getConf('rootcas');
70        $this->options['uid_attribut'] = $this->getConf('uid_attribut');
71        $this->options['cacert'] = $this->getConf('cacert');
72
73        $server_version = CAS_VERSION_2_0;
74        if ($this->getOption("samlValidate")) {
75            $server_version = SAML_VERSION_1_1;
76        }
77
78        if ($this->getOption("debug")) {
79            phpCAS::setLogger();
80            phpCAS::setVerbose(true);
81        }
82
83        if (!DOKU_BASE == "/") {
84            $service_base_url = str_replace(DOKU_BASE, "", DOKU_URL);
85        } else {
86            $service_base_url = DOKU_URL;
87        }
88
89        // Configuration du client CAS
90        phpCAS::client(
91            $server_version,
92            $this->getOption('server'),
93            (int)$this->getOption('port'),
94            $this->getOption('rootcas'),
95            $service_base_url
96        );
97
98        if ($this->getConf('autologin')) {
99            phpCAS::setCacheTimesForAuthRecheck(-1);
100        } else {
101            phpCAS::setCacheTimesForAuthRecheck(1);
102        }
103
104        // Gestion de l'autorité de certification du certificat du serveur CAS pour la bibliothèque php_curl
105        $cas_cacert_file = DOKU_CONF . 'authssocas.cacert.pem';
106        if ($this->getOption('cacert')) {
107            if (!io_saveFile($cas_cacert_file, $this->getOption('cacert'))) {
108                msg('The ' . $cas_cacert_file . ' file is not writable. Please inform the Wiki-Admin', -1);
109            }
110            phpCAS::setCasServerCACert($cas_cacert_file);
111        } else {
112            phpCAS::setNoCasServerValidation();
113        }
114
115        // Gestion de la déconnexion sur le serveur CAS
116        if ($this->getOption('handlelogoutrequest')) {
117            phpCAS::handleLogoutRequests(true, $this->getOption('handlelogoutrequestTrustedHosts'));
118        } else {
119            phpCAS::handleLogoutRequests(false);
120        }
121    }
122
123    /**
124     *
125     * Récupère les options
126     *  Transforme en tableau les URL de notification de la déconnexion pour les serveurs CAS
127     *
128     * @param $optionName
129     * @return array|mixed|string[]|null
130     */
131    private function getOption($optionName)
132    {
133        if (isset($this->options[$optionName])) {
134            switch ($optionName) {
135                case 'handlelogoutrequestTrustedHosts':
136                    $arr = explode(',', $this->options[$optionName]);
137                    foreach ($arr as $key => $item) {
138                        $arr[$key] = trim($item);
139                    }
140                    return $arr;
141                default:
142                    return $this->options[$optionName];
143            }
144        }
145        return NULL;
146    }
147
148    /**
149     *
150     * Transfert de la demande de connexion au serveur CAS
151     *
152     * @return void
153     * @noinspection PhpUnused
154     */
155    public function logIn()
156    {
157        global $ID;
158        $login_url = DOKU_URL . 'doku.php?id=' . $ID;
159
160        phpCAS::setFixedServiceURL($login_url);
161        phpCAS::forceAuthentication();
162    }
163
164    /**
165     *
166     * Déconnexion de l'utilisateur avec prise en compte de la déconnexion générale du CAS
167     *
168     * @return void
169     */
170    public function logOff(): void
171    {
172        global $ID;
173
174        @session_start();
175        session_destroy();
176        if ($this->getOption('handlelogoutrequest')) {
177            $logout_url = DOKU_URL . 'doku.php?id=' . $ID;
178            @phpCAS::logoutWithRedirectService($logout_url);
179        } else {
180            phpCAS::handleLogoutRequests();
181            unset($_SESSION);
182        }
183    }
184
185    public function trustExternal($user, $pass, $sticky = false): bool
186    {
187        global $USERINFO;
188
189        if (!empty($_SESSION[DOKU_COOKIE]['auth']['info'])) {
190            $USERINFO['name'] = $_SESSION[DOKU_COOKIE]['auth']['info']['name'];
191            $USERINFO['mail'] = $_SESSION[DOKU_COOKIE]['auth']['info']['mail'];
192            $USERINFO['grps'] = $_SESSION[DOKU_COOKIE]['auth']['info']['grps'];
193            $_SERVER['REMOTE_USER'] = $_SESSION[DOKU_COOKIE]['auth']['user'];
194            return true;
195        }
196
197        if (phpCAS::isAuthenticated() or ($this->getOption('autologin') and phpCAS::checkAuthentication())) {
198
199            $USERINFO = $this->cas_user_attributes(phpCAS::getAttributes());
200            $this->auth_log($USERINFO['uid']);
201            $_SESSION[DOKU_COOKIE]['auth']['user'] = $USERINFO['uid'];
202            $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
203            $_SERVER['REMOTE_USER'] = $USERINFO['uid'];
204            return true;
205        }
206
207        return false;
208    }
209
210    /**
211     *
212     * Renvoi les informations de l'utilisateur fournit par le CAS
213     *
214     * @param $attributes
215     * @return array
216     */
217    private function cas_user_attributes($attributes): array
218    {
219        return array(
220            'uid' => $attributes[$this->getOption('uid_attribut')],
221            'name' => $attributes[$this->getOption('name_attribut')],
222            'mail' => $attributes[$this->getOption('mail_attribut')],
223            'grps' => $attributes[$this->getOption('group_attribut')],
224        );
225    }
226
227    /**
228     *
229     * Log user connection if the log file is defined
230     *
231     * format : DATE|TIME|USER
232     *
233     * @param $user
234     * @return void
235     */
236    private function auth_log($user): void
237    {
238        if (!is_null($this->logfileuser)) {
239
240            $date = (new DateTime('now'))->format('Ymd|H:i:s');
241
242            $userline = $date . "|" . $user . PHP_EOL;
243            if (!io_saveFile($this->logfileuser, $userline, true)) {
244                msg($this->getLang('writefail'), -1);
245            }
246        }
247    }
248
249}
250