2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Authentication Plugin: CAS Authentication
20 * Authentication using CAS (Central Authentication Server).
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
29 defined('MOODLE_INTERNAL') ||
die();
31 require_once($CFG->dirroot
.'/auth/ldap/auth.php');
32 require_once($CFG->dirroot
.'/auth/cas/CAS/CAS.php');
35 * CAS authentication plugin.
37 class auth_plugin_cas
extends auth_plugin_ldap
{
42 function auth_plugin_cas() {
43 $this->authtype
= 'cas';
44 $this->roleauth
= 'auth_cas';
45 $this->errorlogtag
= '[AUTH CAS] ';
46 $this->init_plugin($this->authtype
);
49 function prevent_local_passwords() {
54 * Authenticates user against CAS
55 * Returns true if the username and password work and false if they are
56 * wrong or don't exist.
58 * @param string $username The username (with system magic quotes)
59 * @param string $password The password (with system magic quotes)
60 * @return bool Authentication success or failure.
62 function user_login ($username, $password) {
64 return phpCAS
::isAuthenticated() && (trim(core_text
::strtolower(phpCAS
::getUser())) == $username);
68 * Returns true if this authentication plugin is 'internal'.
72 function is_internal() {
77 * Returns true if this authentication plugin can change the user's
82 function can_change_password() {
87 * Authentication choice (CAS or other)
88 * Redirection to the CAS form or to login/index.php
89 * for other authentication
91 function loginpage_hook() {
94 global $SESSION, $OUTPUT, $PAGE;
97 $CASform = get_string('CASform', 'auth_cas');
98 $username = optional_param('username', '', PARAM_RAW
);
99 $courseid = optional_param('courseid', 0, PARAM_INT
);
101 if (!empty($username)) {
102 if (isset($SESSION->wantsurl
) && (strstr($SESSION->wantsurl
, 'ticket') ||
103 strstr($SESSION->wantsurl
, 'NOCAS'))) {
104 unset($SESSION->wantsurl
);
109 // Return if CAS enabled and settings not specified yet
110 if (empty($this->config
->hostname
)) {
114 // If the multi-authentication setting is used, check for the param before connecting to CAS.
115 if ($this->config
->multiauth
) {
116 $authCAS = optional_param('authCAS', '', PARAM_RAW
);
117 if ($authCAS == 'NOCAS') {
120 // Show authentication form for multi-authentication.
121 // Test pgtIou parameter for proxy mode (https connection in background from CAS server to the php server).
122 if ($authCAS != 'CAS' && !isset($_GET['pgtIou'])) {
123 $PAGE->set_url('/login/index.php');
124 $PAGE->navbar
->add($CASform);
125 $PAGE->set_title("$site->fullname: $CASform");
126 $PAGE->set_heading($site->fullname
);
127 echo $OUTPUT->header();
128 include($CFG->dirroot
.'/auth/cas/cas_form.html');
129 echo $OUTPUT->footer();
134 // Connection to CAS server
137 if (phpCAS
::checkAuthentication()) {
138 $frm = new stdClass();
139 $frm->username
= phpCAS
::getUser();
140 $frm->password
= 'passwdCas';
142 // Redirect to a course if multi-auth is activated, authCAS is set to CAS and the courseid is specified.
143 if ($this->config
->multiauth
&& !empty($courseid)) {
144 redirect(new moodle_url('/course/view.php', array('id'=>$courseid)));
150 if (isset($_GET['loginguest']) && ($_GET['loginguest'] == true)) {
151 $frm = new stdClass();
152 $frm->username
= 'guest';
153 $frm->password
= 'guest';
157 // Force CAS authentication (if needed).
158 if (!phpCAS
::isAuthenticated()) {
159 phpCAS
::setLang($this->config
->language
);
160 phpCAS
::forceAuthentication();
166 * Connect to the CAS (clientcas connection or proxycas connection)
169 function connectCAS() {
171 static $connected = false;
174 // Make sure phpCAS doesn't try to start a new PHP session when connecting to the CAS server.
175 if ($this->config
->proxycas
) {
176 phpCAS
::proxy($this->config
->casversion
, $this->config
->hostname
, (int) $this->config
->port
, $this->config
->baseuri
, false);
178 phpCAS
::client($this->config
->casversion
, $this->config
->hostname
, (int) $this->config
->port
, $this->config
->baseuri
, false);
183 // If Moodle is configured to use a proxy, phpCAS needs some curl options set.
184 if (!empty($CFG->proxyhost
) && !is_proxybypass($this->config
->hostname
)) {
185 phpCAS
::setExtraCurlOption(CURLOPT_PROXY
, $CFG->proxyhost
);
186 if (!empty($CFG->proxyport
)) {
187 phpCAS
::setExtraCurlOption(CURLOPT_PROXYPORT
, $CFG->proxyport
);
189 if (!empty($CFG->proxytype
)) {
190 // Only set CURLOPT_PROXYTYPE if it's something other than the curl-default http
191 if ($CFG->proxytype
== 'SOCKS5') {
192 phpCAS
::setExtraCurlOption(CURLOPT_PROXYTYPE
, CURLPROXY_SOCKS5
);
195 if (!empty($CFG->proxyuser
) and !empty($CFG->proxypassword
)) {
196 phpCAS
::setExtraCurlOption(CURLOPT_PROXYUSERPWD
, $CFG->proxyuser
.':'.$CFG->proxypassword
);
197 if (defined('CURLOPT_PROXYAUTH')) {
198 // any proxy authentication if PHP 5.1
199 phpCAS
::setExtraCurlOption(CURLOPT_PROXYAUTH
, CURLAUTH_BASIC | CURLAUTH_NTLM
);
204 if ($this->config
->certificate_check
&& $this->config
->certificate_path
){
205 phpCAS
::setCasServerCACert($this->config
->certificate_path
);
207 // Don't try to validate the server SSL credentials
208 phpCAS
::setNoCasServerValidation();
213 * Prints a form for configuring this authentication plugin.
215 * This function is called from admin/auth.php, and outputs a full page with
216 * a form for configuring this plugin.
218 * @param array $page An object containing all the data for this page.
220 function config_form($config, $err, $user_fields) {
221 global $CFG, $OUTPUT;
223 if (!function_exists('ldap_connect')) { // Is php-ldap really there?
224 echo $OUTPUT->notification(get_string('auth_ldap_noextension', 'auth_ldap'));
226 // Don't return here, like we do in auth/ldap. We cas use CAS without LDAP.
227 // So just warn the user (done above) and define the LDAP constants we use
228 // in config.html, to silence the warnings.
229 if (!defined('LDAP_DEREF_NEVER')) {
230 define ('LDAP_DEREF_NEVER', 0);
232 if (!defined('LDAP_DEREF_ALWAYS')) {
233 define ('LDAP_DEREF_ALWAYS', 3);
237 include($CFG->dirroot
.'/auth/cas/config.html');
241 * A chance to validate form data, and last chance to
242 * do stuff before it is inserted in config_plugin
243 * @param object object with submitted configuration settings (without system magic quotes)
244 * @param array $err array of error messages
246 function validate_form($form, &$err) {
247 $certificate_path = trim($form->certificate_path
);
248 if ($form->certificate_check
&& empty($certificate_path)) {
249 $err['certificate_path'] = get_string('auth_cas_certificate_path_empty', 'auth_cas');
254 * Returns the URL for changing the user's pw, or empty if the default can
259 function change_password_url() {
264 * Processes and stores configuration data for this authentication plugin.
266 function process_config($config) {
269 if (!isset($config->hostname
)) {
270 $config->hostname
= '';
272 if (!isset($config->port
)) {
275 if (!isset($config->casversion
)) {
276 $config->casversion
= '';
278 if (!isset($config->baseuri
)) {
279 $config->baseuri
= '';
281 if (!isset($config->language
)) {
282 $config->language
= '';
284 if (!isset($config->proxycas
)) {
285 $config->proxycas
= '';
287 if (!isset($config->logoutcas
)) {
288 $config->logoutcas
= '';
290 if (!isset($config->multiauth
)) {
291 $config->multiauth
= '';
293 if (!isset($config->certificate_check
)) {
294 $config->certificate_check
= '';
296 if (!isset($config->certificate_path
)) {
297 $config->certificate_path
= '';
299 if (!isset($config->logout_return_url
)) {
300 $config->logout_return_url
= '';
304 if (!isset($config->host_url
)) {
305 $config->host_url
= '';
307 if (!isset($config->start_tls
)) {
308 $config->start_tls
= false;
310 if (empty($config->ldapencoding
)) {
311 $config->ldapencoding
= 'utf-8';
313 if (!isset($config->pagesize
)) {
314 $config->pagesize
= LDAP_DEFAULT_PAGESIZE
;
316 if (!isset($config->contexts
)) {
317 $config->contexts
= '';
319 if (!isset($config->user_type
)) {
320 $config->user_type
= 'default';
322 if (!isset($config->user_attribute
)) {
323 $config->user_attribute
= '';
325 if (!isset($config->search_sub
)) {
326 $config->search_sub
= '';
328 if (!isset($config->opt_deref
)) {
329 $config->opt_deref
= LDAP_DEREF_NEVER
;
331 if (!isset($config->bind_dn
)) {
332 $config->bind_dn
= '';
334 if (!isset($config->bind_pw
)) {
335 $config->bind_pw
= '';
337 if (!isset($config->ldap_version
)) {
338 $config->ldap_version
= '3';
340 if (!isset($config->objectclass
)) {
341 $config->objectclass
= '';
343 if (!isset($config->memberattribute
)) {
344 $config->memberattribute
= '';
347 if (!isset($config->memberattribute_isdn
)) {
348 $config->memberattribute_isdn
= '';
350 if (!isset($config->attrcreators
)) {
351 $config->attrcreators
= '';
353 if (!isset($config->groupecreators
)) {
354 $config->groupecreators
= '';
356 if (!isset($config->removeuser
)) {
357 $config->removeuser
= AUTH_REMOVEUSER_KEEP
;
361 set_config('hostname', trim($config->hostname
), $this->pluginconfig
);
362 set_config('port', trim($config->port
), $this->pluginconfig
);
363 set_config('casversion', $config->casversion
, $this->pluginconfig
);
364 set_config('baseuri', trim($config->baseuri
), $this->pluginconfig
);
365 set_config('language', $config->language
, $this->pluginconfig
);
366 set_config('proxycas', $config->proxycas
, $this->pluginconfig
);
367 set_config('logoutcas', $config->logoutcas
, $this->pluginconfig
);
368 set_config('multiauth', $config->multiauth
, $this->pluginconfig
);
369 set_config('certificate_check', $config->certificate_check
, $this->pluginconfig
);
370 set_config('certificate_path', $config->certificate_path
, $this->pluginconfig
);
371 set_config('logout_return_url', $config->logout_return_url
, $this->pluginconfig
);
373 // save LDAP settings
374 set_config('host_url', trim($config->host_url
), $this->pluginconfig
);
375 set_config('start_tls', $config->start_tls
, $this->pluginconfig
);
376 set_config('ldapencoding', trim($config->ldapencoding
), $this->pluginconfig
);
377 set_config('pagesize', (int)trim($config->pagesize
), $this->pluginconfig
);
378 set_config('contexts', trim($config->contexts
), $this->pluginconfig
);
379 set_config('user_type', core_text
::strtolower(trim($config->user_type
)), $this->pluginconfig
);
380 set_config('user_attribute', core_text
::strtolower(trim($config->user_attribute
)), $this->pluginconfig
);
381 set_config('search_sub', $config->search_sub
, $this->pluginconfig
);
382 set_config('opt_deref', $config->opt_deref
, $this->pluginconfig
);
383 set_config('bind_dn', trim($config->bind_dn
), $this->pluginconfig
);
384 set_config('bind_pw', $config->bind_pw
, $this->pluginconfig
);
385 set_config('ldap_version', $config->ldap_version
, $this->pluginconfig
);
386 set_config('objectclass', trim($config->objectclass
), $this->pluginconfig
);
387 set_config('memberattribute', core_text
::strtolower(trim($config->memberattribute
)), $this->pluginconfig
);
388 set_config('memberattribute_isdn', $config->memberattribute_isdn
, $this->pluginconfig
);
389 set_config('attrcreators', trim($config->attrcreators
), $this->pluginconfig
);
390 set_config('groupecreators', trim($config->groupecreators
), $this->pluginconfig
);
391 set_config('removeuser', $config->removeuser
, $this->pluginconfig
);
397 * Returns true if user should be coursecreator.
399 * @param mixed $username username (without system magic quotes)
400 * @return boolean result
402 function iscreator($username) {
403 if (empty($this->config
->host_url
) or (empty($this->config
->attrcreators
) && empty($this->config
->groupecreators
)) or empty($this->config
->memberattribute
)) {
407 $extusername = core_text
::convert($username, 'utf-8', $this->config
->ldapencoding
);
409 // Test for group creator
410 if (!empty($this->config
->groupecreators
)) {
411 $ldapconnection = $this->ldap_connect();
412 if ($this->config
->memberattribute_isdn
) {
413 if(!($userid = $this->ldap_find_userdn($ldapconnection, $extusername))) {
417 $userid = $extusername;
420 $group_dns = explode(';', $this->config
->groupecreators
);
421 if (ldap_isgroupmember($ldapconnection, $userid, $group_dns, $this->config
->memberattribute
)) {
426 // Build filter for attrcreator
427 if (!empty($this->config
->attrcreators
)) {
428 $attrs = explode(';', $this->config
->attrcreators
);
429 $filter = '(& ('.$this->config
->user_attribute
."=$username)(|";
430 foreach ($attrs as $attr){
431 if(strpos($attr, '=')) {
432 $filter .= "($attr)";
434 $filter .= '('.$this->config
->memberattribute
."=$attr)";
440 $result = $this->ldap_get_userlist($filter);
441 if (count($result) != 0) {
450 * Reads user information from LDAP and returns it as array()
452 * If no LDAP servers are configured, user information has to be
453 * provided via other methods (CSV file, manually, etc.). Return
454 * an empty array so existing user info is not lost. Otherwise,
455 * calls parent class method to get user info.
457 * @param string $username username
458 * @return mixed array with no magic quotes or false on error
460 function get_userinfo($username) {
461 if (empty($this->config
->host_url
)) {
464 return parent
::get_userinfo($username);
468 * Syncronizes users from LDAP server to moodle user table.
470 * If no LDAP servers are configured, simply return. Otherwise,
471 * call parent class method to do the work.
473 * @param bool $do_updates will do pull in data updates from LDAP if relevant
476 function sync_users($do_updates=true) {
477 if (empty($this->config
->host_url
)) {
478 error_log('[AUTH CAS] '.get_string('noldapserver', 'auth_cas'));
481 parent
::sync_users($do_updates);
485 * Hook for logout page
487 function logoutpage_hook() {
488 global $USER, $redirect;
490 // Only do this if the user is actually logged in via CAS
491 if ($USER->auth
=== $this->authtype
) {
492 // Check if there is an alternative logout return url defined
493 if (isset($this->config
->logout_return_url
) && !empty($this->config
->logout_return_url
)) {
494 // Set redirect to alternative return url
495 $redirect = $this->config
->logout_return_url
;
503 * Note: this method replace the prelogout_hook method to avoid redirect to CAS logout
504 * before the event userlogout being triggered.
506 * @param stdClass $user clone of USER object object before the user session was terminated
508 public function postlogout_hook($user) {
510 // Only redirect to CAS logout if the user is logged as a CAS user.
511 if (!empty($this->config
->logoutcas
) && $user->auth
== $this->authtype
) {
512 $backurl = !empty($this->config
->logout_return_url
) ?
$this->config
->logout_return_url
: $CFG->wwwroot
;
514 phpCAS
::logoutWithRedirectService($backurl);