Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Authentication Plugin: CAS Authentication
19
 *
20
 * Authentication using CAS (Central Authentication Server).
21
 *
22
 * @author Martin Dougiamas
23
 * @author Jerome GUTIERREZ
24
 * @author Iñaki Arenaza
25
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
26
 * @package auth_cas
27
 */
28
 
29
defined('MOODLE_INTERNAL') || die();
30
 
31
require_once($CFG->dirroot.'/auth/ldap/auth.php');
32
require_once($CFG->dirroot.'/auth/cas/CAS/vendor/autoload.php');
33
require_once($CFG->dirroot.'/auth/cas/CAS/vendor/apereo/phpcas/source/CAS.php');
34
 
35
/**
36
 * CAS authentication plugin.
37
 */
38
class auth_plugin_cas extends auth_plugin_ldap {
39
 
40
    /**
41
     * Constructor.
42
     */
43
    public function __construct() {
44
        $this->authtype = 'cas';
45
        $this->roleauth = 'auth_cas';
46
        $this->errorlogtag = '[AUTH CAS] ';
47
        $this->init_plugin($this->authtype);
48
    }
49
 
50
    /**
51
     * Old syntax of class constructor. Deprecated in PHP7.
52
     *
53
     * @deprecated since Moodle 3.1
54
     */
55
    public function auth_plugin_cas() {
56
        debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
57
        self::__construct();
58
    }
59
 
60
    function prevent_local_passwords() {
61
        return true;
62
    }
63
 
64
    /**
65
     * Authenticates user against CAS
66
     * Returns true if the username and password work and false if they are
67
     * wrong or don't exist.
68
     *
69
     * @param string $username The username (with system magic quotes)
70
     * @param string $password The password (with system magic quotes)
71
     * @return bool Authentication success or failure.
72
     */
73
    function user_login($username, $password) {
74
        $this->connectCAS();
75
        return phpCAS::isAuthenticated() && (trim(core_text::strtolower(phpCAS::getUser())) == $username);
76
    }
77
 
78
    /**
79
     * Returns true if this authentication plugin is 'internal'.
80
     *
81
     * @return bool
82
     */
83
    function is_internal() {
84
        return false;
85
    }
86
 
87
    /**
88
     * Returns true if this authentication plugin can change the user's
89
     * password.
90
     *
91
     * @return bool
92
     */
93
    function can_change_password() {
94
        return false;
95
    }
96
 
97
    /**
98
     * Authentication choice (CAS or other)
99
     * Redirection to the CAS form or to login/index.php
100
     * for other authentication
101
     */
102
    function loginpage_hook() {
103
        global $frm;
104
        global $CFG;
105
        global $SESSION, $OUTPUT, $PAGE;
106
 
107
        $site = get_site();
108
        $CASform = get_string('CASform', 'auth_cas');
109
        $username = optional_param('username', '', PARAM_RAW);
110
        $courseid = optional_param('courseid', 0, PARAM_INT);
111
 
112
        if (!empty($username)) {
113
            if (isset($SESSION->wantsurl) && (strstr($SESSION->wantsurl, 'ticket') ||
114
                                              strstr($SESSION->wantsurl, 'NOCAS'))) {
115
                unset($SESSION->wantsurl);
116
            }
117
            return;
118
        }
119
 
120
        // Return if CAS enabled and settings not specified yet
121
        if (empty($this->config->hostname)) {
122
            return;
123
        }
124
 
125
        // If the multi-authentication setting is used, check for the param before connecting to CAS.
126
        if ($this->config->multiauth) {
127
 
128
            // If there is an authentication error, stay on the default authentication page.
129
            if (!empty($SESSION->loginerrormsg)) {
130
                return;
131
            }
132
 
133
            $authCAS = optional_param('authCAS', '', PARAM_RAW);
134
            if ($authCAS != 'CAS') {
135
                return;
136
            }
137
 
138
        }
139
 
140
        // Connection to CAS server
141
        $this->connectCAS();
142
 
143
        if (phpCAS::checkAuthentication()) {
144
            $frm = new stdClass();
145
            $frm->username = phpCAS::getUser();
146
            $frm->password = 'passwdCas';
147
            $frm->logintoken = \core\session\manager::get_login_token();
148
 
149
            // Redirect to a course if multi-auth is activated, authCAS is set to CAS and the courseid is specified.
150
            if ($this->config->multiauth && !empty($courseid)) {
151
                redirect(new moodle_url('/course/view.php', array('id'=>$courseid)));
152
            }
153
 
154
            return;
155
        }
156
 
157
        if (isset($_GET['loginguest']) && ($_GET['loginguest'] == true)) {
158
            $frm = new stdClass();
159
            $frm->username = 'guest';
160
            $frm->password = 'guest';
161
            $frm->logintoken = \core\session\manager::get_login_token();
162
            return;
163
        }
164
 
165
        // Force CAS authentication (if needed).
166
        if (!phpCAS::isAuthenticated()) {
167
            phpCAS::setLang($this->config->language);
168
            phpCAS::forceAuthentication();
169
        }
170
    }
171
 
172
 
173
    /**
174
     * Connect to the CAS (clientcas connection or proxycas connection)
175
     *
176
     */
177
    function connectCAS() {
178
        global $CFG;
179
        static $connected = false;
180
 
181
        if (!$connected) {
182
            // Form the base URL of the server with just the protocol and hostname.
183
            $serverurl = new moodle_url("/");
184
            $servicebaseurl = $serverurl->get_scheme() ? $serverurl->get_scheme() . "://" : '';
185
            $servicebaseurl .= $serverurl->get_host();
186
            // Add the port if set.
187
            $servicebaseurl .= $serverurl->get_port() ? ':' . $serverurl->get_port() : '';
188
 
189
            // Make sure phpCAS doesn't try to start a new PHP session when connecting to the CAS server.
190
            if ($this->config->proxycas) {
191
                phpCAS::proxy($this->config->casversion, $this->config->hostname, (int) $this->config->port, $this->config->baseuri,
192
                    $servicebaseurl, false);
193
            } else {
194
                phpCAS::client($this->config->casversion, $this->config->hostname, (int) $this->config->port,
195
                    $this->config->baseuri, $servicebaseurl, false);
196
            }
197
            // Some CAS installs require SSLv3 that should be explicitly set.
198
            if (!empty($this->config->curl_ssl_version)) {
199
                phpCAS::setExtraCurlOption(CURLOPT_SSLVERSION, $this->config->curl_ssl_version);
200
            }
201
 
202
            $connected = true;
203
        }
204
 
205
        // If Moodle is configured to use a proxy, phpCAS needs some curl options set.
206
        if (!empty($CFG->proxyhost) && !is_proxybypass(phpCAS::getServerLoginURL())) {
207
            phpCAS::setExtraCurlOption(CURLOPT_PROXY, $CFG->proxyhost);
208
            if (!empty($CFG->proxyport)) {
209
                phpCAS::setExtraCurlOption(CURLOPT_PROXYPORT, $CFG->proxyport);
210
            }
211
            if (!empty($CFG->proxytype)) {
212
                // Only set CURLOPT_PROXYTYPE if it's something other than the curl-default http
213
                if ($CFG->proxytype == 'SOCKS5') {
214
                    phpCAS::setExtraCurlOption(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
215
                }
216
            }
217
            if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
218
                phpCAS::setExtraCurlOption(CURLOPT_PROXYUSERPWD, $CFG->proxyuser.':'.$CFG->proxypassword);
219
                if (defined('CURLOPT_PROXYAUTH')) {
220
                    // any proxy authentication if PHP 5.1
221
                    phpCAS::setExtraCurlOption(CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
222
                }
223
            }
224
        }
225
 
226
        if ($this->config->certificate_check && $this->config->certificate_path){
227
            phpCAS::setCasServerCACert($this->config->certificate_path);
228
        } else {
229
            // Don't try to validate the server SSL credentials
230
            phpCAS::setNoCasServerValidation();
231
        }
232
    }
233
 
234
    /**
235
     * Returns the URL for changing the user's pw, or empty if the default can
236
     * be used.
237
     *
238
     * @return moodle_url
239
     */
240
    function change_password_url() {
241
        return null;
242
    }
243
 
244
    /**
245
     * Returns true if user should be coursecreator.
246
     *
247
     * @param mixed $username    username (without system magic quotes)
248
     * @return boolean result
249
     */
250
    function iscreator($username) {
251
        if (empty($this->config->host_url) or (empty($this->config->attrcreators) && empty($this->config->groupecreators)) or empty($this->config->memberattribute)) {
252
            return false;
253
        }
254
 
255
        $extusername = core_text::convert($username, 'utf-8', $this->config->ldapencoding);
256
 
257
        // Test for group creator
258
        if (!empty($this->config->groupecreators)) {
259
            $ldapconnection = $this->ldap_connect();
260
            if ($this->config->memberattribute_isdn) {
261
                if(!($userid = $this->ldap_find_userdn($ldapconnection, $extusername))) {
262
                    return false;
263
                }
264
            } else {
265
                $userid = $extusername;
266
            }
267
 
268
            $group_dns = explode(';', $this->config->groupecreators);
269
            if (ldap_isgroupmember($ldapconnection, $userid, $group_dns, $this->config->memberattribute)) {
270
                return true;
271
            }
272
        }
273
 
274
        // Build filter for attrcreator
275
        if (!empty($this->config->attrcreators)) {
276
            $attrs = explode(';', $this->config->attrcreators);
277
            $filter = '(& ('.$this->config->user_attribute."=$username)(|";
278
            foreach ($attrs as $attr){
279
                if(strpos($attr, '=')) {
280
                    $filter .= "($attr)";
281
                } else {
282
                    $filter .= '('.$this->config->memberattribute."=$attr)";
283
                }
284
            }
285
            $filter .= '))';
286
 
287
            // Search
288
            $result = $this->ldap_get_userlist($filter);
289
            if (count($result) != 0) {
290
                return true;
291
            }
292
        }
293
 
294
        return false;
295
    }
296
 
297
    /**
298
     * Reads user information from LDAP and returns it as array()
299
     *
300
     * If no LDAP servers are configured, user information has to be
301
     * provided via other methods (CSV file, manually, etc.). Return
302
     * an empty array so existing user info is not lost. Otherwise,
303
     * calls parent class method to get user info.
304
     *
305
     * @param string $username username
306
     * @return mixed array with no magic quotes or false on error
307
     */
308
    function get_userinfo($username) {
309
        if (empty($this->config->host_url)) {
310
            return array();
311
        }
312
        return parent::get_userinfo($username);
313
    }
314
 
315
    /**
316
     * Syncronizes users from LDAP server to moodle user table.
317
     *
318
     * If no LDAP servers are configured, simply return. Otherwise,
319
     * call parent class method to do the work.
320
     *
321
     * @param bool $do_updates will do pull in data updates from LDAP if relevant
322
     * @return nothing
323
     */
324
    function sync_users($do_updates=true) {
325
        if (empty($this->config->host_url)) {
326
            error_log('[AUTH CAS] '.get_string('noldapserver', 'auth_cas'));
327
            return;
328
        }
329
        parent::sync_users($do_updates);
330
    }
331
 
332
    /**
333
    * Hook for logout page
334
    */
335
    function logoutpage_hook() {
336
        global $USER, $redirect;
337
 
338
        // Only do this if the user is actually logged in via CAS
339
        if ($USER->auth === $this->authtype) {
340
            // Check if there is an alternative logout return url defined
341
            if (isset($this->config->logout_return_url) && !empty($this->config->logout_return_url)) {
342
                // Set redirect to alternative return url
343
                $redirect = $this->config->logout_return_url;
344
            }
345
        }
346
    }
347
 
348
    /**
349
     * Post logout hook.
350
     *
351
     * Note: this method replace the prelogout_hook method to avoid redirect to CAS logout
352
     * before the event userlogout being triggered.
353
     *
354
     * @param stdClass $user clone of USER object object before the user session was terminated
355
     */
356
    public function postlogout_hook($user) {
357
        global $CFG;
358
        // Only redirect to CAS logout if the user is logged as a CAS user.
359
        if (!empty($this->config->logoutcas) && $user->auth == $this->authtype) {
360
            $backurl = !empty($this->config->logout_return_url) ? $this->config->logout_return_url : $CFG->wwwroot;
361
            $this->connectCAS();
362
            phpCAS::logoutWithRedirectService($backurl);
363
        }
364
    }
365
 
366
    /**
367
     * Return a list of identity providers to display on the login page.
368
     *
369
     * @param string|moodle_url $wantsurl The requested URL.
370
     * @return array List of arrays with keys url, iconurl and name.
371
     */
372
    public function loginpage_idp_list($wantsurl) {
373
        if (empty($this->config->hostname)) {
374
            // CAS is not configured.
375
            return [];
376
        }
377
 
378
        if ($this->config->auth_logo) {
379
            $iconurl = moodle_url::make_pluginfile_url(
380
                context_system::instance()->id,
381
                'auth_cas',
382
                'logo',
383
                null,
384
                null,
385
                $this->config->auth_logo);
386
        } else {
387
            $iconurl = null;
388
        }
389
 
390
        return [
391
            [
392
                'url' => new moodle_url(get_login_url(), [
393
                        'authCAS' => 'CAS',
394
                    ]),
395
                'iconurl' => $iconurl,
396
                'name' => format_string($this->config->auth_name),
397
            ],
398
        ];
399
    }
400
}