Merge branch 'MDL-54741-master' of git://github.com/mihailges/moodle
[moodle.git] / auth / cas / auth.php
blobd94c3965ba6838136f2fa0812304e989824ac0fd
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.
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/>.
17 /**
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
26 * @package auth_cas
29 defined('MOODLE_INTERNAL') || die();
31 require_once($CFG->dirroot.'/auth/ldap/auth.php');
32 require_once($CFG->dirroot.'/auth/cas/CAS/CAS.php');
34 /**
35 * CAS authentication plugin.
37 class auth_plugin_cas extends auth_plugin_ldap {
39 /**
40 * Constructor.
42 public function __construct() {
43 $this->authtype = 'cas';
44 $this->roleauth = 'auth_cas';
45 $this->errorlogtag = '[AUTH CAS] ';
46 $this->init_plugin($this->authtype);
49 /**
50 * Old syntax of class constructor. Deprecated in PHP7.
52 * @deprecated since Moodle 3.1
54 public function auth_plugin_cas() {
55 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
56 self::__construct();
59 function prevent_local_passwords() {
60 return true;
63 /**
64 * Authenticates user against CAS
65 * Returns true if the username and password work and false if they are
66 * wrong or don't exist.
68 * @param string $username The username (with system magic quotes)
69 * @param string $password The password (with system magic quotes)
70 * @return bool Authentication success or failure.
72 function user_login ($username, $password) {
73 $this->connectCAS();
74 return phpCAS::isAuthenticated() && (trim(core_text::strtolower(phpCAS::getUser())) == $username);
77 /**
78 * Returns true if this authentication plugin is 'internal'.
80 * @return bool
82 function is_internal() {
83 return false;
86 /**
87 * Returns true if this authentication plugin can change the user's
88 * password.
90 * @return bool
92 function can_change_password() {
93 return false;
96 /**
97 * Authentication choice (CAS or other)
98 * Redirection to the CAS form or to login/index.php
99 * for other authentication
101 function loginpage_hook() {
102 global $frm;
103 global $CFG;
104 global $SESSION, $OUTPUT, $PAGE;
106 $site = get_site();
107 $CASform = get_string('CASform', 'auth_cas');
108 $username = optional_param('username', '', PARAM_RAW);
109 $courseid = optional_param('courseid', 0, PARAM_INT);
111 if (!empty($username)) {
112 if (isset($SESSION->wantsurl) && (strstr($SESSION->wantsurl, 'ticket') ||
113 strstr($SESSION->wantsurl, 'NOCAS'))) {
114 unset($SESSION->wantsurl);
116 return;
119 // Return if CAS enabled and settings not specified yet
120 if (empty($this->config->hostname)) {
121 return;
124 // If the multi-authentication setting is used, check for the param before connecting to CAS.
125 if ($this->config->multiauth) {
127 // If there is an authentication error, stay on the default authentication page.
128 if (!empty($SESSION->loginerrormsg)) {
129 return;
132 $authCAS = optional_param('authCAS', '', PARAM_RAW);
133 if ($authCAS == 'NOCAS') {
134 return;
136 // Show authentication form for multi-authentication.
137 // Test pgtIou parameter for proxy mode (https connection in background from CAS server to the php server).
138 if ($authCAS != 'CAS' && !isset($_GET['pgtIou'])) {
139 $PAGE->set_url('/login/index.php');
140 $PAGE->navbar->add($CASform);
141 $PAGE->set_title("$site->fullname: $CASform");
142 $PAGE->set_heading($site->fullname);
143 echo $OUTPUT->header();
144 include($CFG->dirroot.'/auth/cas/cas_form.html');
145 echo $OUTPUT->footer();
146 exit();
150 // Connection to CAS server
151 $this->connectCAS();
153 if (phpCAS::checkAuthentication()) {
154 $frm = new stdClass();
155 $frm->username = phpCAS::getUser();
156 $frm->password = 'passwdCas';
158 // Redirect to a course if multi-auth is activated, authCAS is set to CAS and the courseid is specified.
159 if ($this->config->multiauth && !empty($courseid)) {
160 redirect(new moodle_url('/course/view.php', array('id'=>$courseid)));
163 return;
166 if (isset($_GET['loginguest']) && ($_GET['loginguest'] == true)) {
167 $frm = new stdClass();
168 $frm->username = 'guest';
169 $frm->password = 'guest';
170 return;
173 // Force CAS authentication (if needed).
174 if (!phpCAS::isAuthenticated()) {
175 phpCAS::setLang($this->config->language);
176 phpCAS::forceAuthentication();
182 * Connect to the CAS (clientcas connection or proxycas connection)
185 function connectCAS() {
186 global $CFG;
187 static $connected = false;
189 if (!$connected) {
190 // Make sure phpCAS doesn't try to start a new PHP session when connecting to the CAS server.
191 if ($this->config->proxycas) {
192 phpCAS::proxy($this->config->casversion, $this->config->hostname, (int) $this->config->port, $this->config->baseuri, false);
193 } else {
194 phpCAS::client($this->config->casversion, $this->config->hostname, (int) $this->config->port, $this->config->baseuri, false);
196 // Some CAS installs require SSLv3 that should be explicitly set.
197 if (!empty($this->config->curl_ssl_version)) {
198 phpCAS::setExtraCurlOption(CURLOPT_SSLVERSION, $this->config->curl_ssl_version);
201 $connected = true;
204 // If Moodle is configured to use a proxy, phpCAS needs some curl options set.
205 if (!empty($CFG->proxyhost) && !is_proxybypass(phpCAS::getServerLoginURL())) {
206 phpCAS::setExtraCurlOption(CURLOPT_PROXY, $CFG->proxyhost);
207 if (!empty($CFG->proxyport)) {
208 phpCAS::setExtraCurlOption(CURLOPT_PROXYPORT, $CFG->proxyport);
210 if (!empty($CFG->proxytype)) {
211 // Only set CURLOPT_PROXYTYPE if it's something other than the curl-default http
212 if ($CFG->proxytype == 'SOCKS5') {
213 phpCAS::setExtraCurlOption(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
216 if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
217 phpCAS::setExtraCurlOption(CURLOPT_PROXYUSERPWD, $CFG->proxyuser.':'.$CFG->proxypassword);
218 if (defined('CURLOPT_PROXYAUTH')) {
219 // any proxy authentication if PHP 5.1
220 phpCAS::setExtraCurlOption(CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
225 if ($this->config->certificate_check && $this->config->certificate_path){
226 phpCAS::setCasServerCACert($this->config->certificate_path);
227 } else {
228 // Don't try to validate the server SSL credentials
229 phpCAS::setNoCasServerValidation();
234 * Returns the URL for changing the user's pw, or empty if the default can
235 * be used.
237 * @return moodle_url
239 function change_password_url() {
240 return null;
244 * Returns true if user should be coursecreator.
246 * @param mixed $username username (without system magic quotes)
247 * @return boolean result
249 function iscreator($username) {
250 if (empty($this->config->host_url) or (empty($this->config->attrcreators) && empty($this->config->groupecreators)) or empty($this->config->memberattribute)) {
251 return false;
254 $extusername = core_text::convert($username, 'utf-8', $this->config->ldapencoding);
256 // Test for group creator
257 if (!empty($this->config->groupecreators)) {
258 $ldapconnection = $this->ldap_connect();
259 if ($this->config->memberattribute_isdn) {
260 if(!($userid = $this->ldap_find_userdn($ldapconnection, $extusername))) {
261 return false;
263 } else {
264 $userid = $extusername;
267 $group_dns = explode(';', $this->config->groupecreators);
268 if (ldap_isgroupmember($ldapconnection, $userid, $group_dns, $this->config->memberattribute)) {
269 return true;
273 // Build filter for attrcreator
274 if (!empty($this->config->attrcreators)) {
275 $attrs = explode(';', $this->config->attrcreators);
276 $filter = '(& ('.$this->config->user_attribute."=$username)(|";
277 foreach ($attrs as $attr){
278 if(strpos($attr, '=')) {
279 $filter .= "($attr)";
280 } else {
281 $filter .= '('.$this->config->memberattribute."=$attr)";
284 $filter .= '))';
286 // Search
287 $result = $this->ldap_get_userlist($filter);
288 if (count($result) != 0) {
289 return true;
293 return false;
297 * Reads user information from LDAP and returns it as array()
299 * If no LDAP servers are configured, user information has to be
300 * provided via other methods (CSV file, manually, etc.). Return
301 * an empty array so existing user info is not lost. Otherwise,
302 * calls parent class method to get user info.
304 * @param string $username username
305 * @return mixed array with no magic quotes or false on error
307 function get_userinfo($username) {
308 if (empty($this->config->host_url)) {
309 return array();
311 return parent::get_userinfo($username);
315 * Syncronizes users from LDAP server to moodle user table.
317 * If no LDAP servers are configured, simply return. Otherwise,
318 * call parent class method to do the work.
320 * @param bool $do_updates will do pull in data updates from LDAP if relevant
321 * @return nothing
323 function sync_users($do_updates=true) {
324 if (empty($this->config->host_url)) {
325 error_log('[AUTH CAS] '.get_string('noldapserver', 'auth_cas'));
326 return;
328 parent::sync_users($do_updates);
332 * Hook for logout page
334 function logoutpage_hook() {
335 global $USER, $redirect;
337 // Only do this if the user is actually logged in via CAS
338 if ($USER->auth === $this->authtype) {
339 // Check if there is an alternative logout return url defined
340 if (isset($this->config->logout_return_url) && !empty($this->config->logout_return_url)) {
341 // Set redirect to alternative return url
342 $redirect = $this->config->logout_return_url;
348 * Post logout hook.
350 * Note: this method replace the prelogout_hook method to avoid redirect to CAS logout
351 * before the event userlogout being triggered.
353 * @param stdClass $user clone of USER object object before the user session was terminated
355 public function postlogout_hook($user) {
356 global $CFG;
357 // Only redirect to CAS logout if the user is logged as a CAS user.
358 if (!empty($this->config->logoutcas) && $user->auth == $this->authtype) {
359 $backurl = !empty($this->config->logout_return_url) ? $this->config->logout_return_url : $CFG->wwwroot;
360 $this->connectCAS();
361 phpCAS::logoutWithRedirectService($backurl);