MDL-65978 blog: Fix removing associations
[moodle.git] / auth / cas / auth.php
blob1a888207d9399a85ec9717cab3e32825a167c7b0
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/vendor/autoload.php');
33 require_once($CFG->dirroot.'/auth/cas/CAS/vendor/apereo/phpcas/source/CAS.php');
35 /**
36 * CAS authentication plugin.
38 class auth_plugin_cas extends auth_plugin_ldap {
40 /**
41 * Constructor.
43 public function __construct() {
44 $this->authtype = 'cas';
45 $this->roleauth = 'auth_cas';
46 $this->errorlogtag = '[AUTH CAS] ';
47 $this->init_plugin($this->authtype);
50 /**
51 * Old syntax of class constructor. Deprecated in PHP7.
53 * @deprecated since Moodle 3.1
55 public function auth_plugin_cas() {
56 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
57 self::__construct();
60 function prevent_local_passwords() {
61 return true;
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.
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.
73 function user_login ($username, $password) {
74 $this->connectCAS();
75 return phpCAS::isAuthenticated() && (trim(core_text::strtolower(phpCAS::getUser())) == $username);
78 /**
79 * Returns true if this authentication plugin is 'internal'.
81 * @return bool
83 function is_internal() {
84 return false;
87 /**
88 * Returns true if this authentication plugin can change the user's
89 * password.
91 * @return bool
93 function can_change_password() {
94 return false;
97 /**
98 * Authentication choice (CAS or other)
99 * Redirection to the CAS form or to login/index.php
100 * for other authentication
102 function loginpage_hook() {
103 global $frm;
104 global $CFG;
105 global $SESSION, $OUTPUT, $PAGE;
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);
112 if (!empty($username)) {
113 if (isset($SESSION->wantsurl) && (strstr($SESSION->wantsurl, 'ticket') ||
114 strstr($SESSION->wantsurl, 'NOCAS'))) {
115 unset($SESSION->wantsurl);
117 return;
120 // Return if CAS enabled and settings not specified yet
121 if (empty($this->config->hostname)) {
122 return;
125 // If the multi-authentication setting is used, check for the param before connecting to CAS.
126 if ($this->config->multiauth) {
128 // If there is an authentication error, stay on the default authentication page.
129 if (!empty($SESSION->loginerrormsg)) {
130 return;
133 $authCAS = optional_param('authCAS', '', PARAM_RAW);
134 if ($authCAS != 'CAS') {
135 return;
140 // Connection to CAS server
141 $this->connectCAS();
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();
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)));
154 return;
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;
165 // Force CAS authentication (if needed).
166 if (!phpCAS::isAuthenticated()) {
167 phpCAS::setLang($this->config->language);
168 phpCAS::forceAuthentication();
174 * Connect to the CAS (clientcas connection or proxycas connection)
177 function connectCAS() {
178 global $CFG;
179 static $connected = false;
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() : '';
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);
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);
202 $connected = true;
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);
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);
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);
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();
235 * Returns the URL for changing the user's pw, or empty if the default can
236 * be used.
238 * @return moodle_url
240 function change_password_url() {
241 return null;
245 * Returns true if user should be coursecreator.
247 * @param mixed $username username (without system magic quotes)
248 * @return boolean result
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;
255 $extusername = core_text::convert($username, 'utf-8', $this->config->ldapencoding);
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;
264 } else {
265 $userid = $extusername;
268 $group_dns = explode(';', $this->config->groupecreators);
269 if (ldap_isgroupmember($ldapconnection, $userid, $group_dns, $this->config->memberattribute)) {
270 return true;
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)";
285 $filter .= '))';
287 // Search
288 $result = $this->ldap_get_userlist($filter);
289 if (count($result) != 0) {
290 return true;
294 return false;
298 * Reads user information from LDAP and returns it as array()
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.
305 * @param string $username username
306 * @return mixed array with no magic quotes or false on error
308 function get_userinfo($username) {
309 if (empty($this->config->host_url)) {
310 return array();
312 return parent::get_userinfo($username);
316 * Syncronizes users from LDAP server to moodle user table.
318 * If no LDAP servers are configured, simply return. Otherwise,
319 * call parent class method to do the work.
321 * @param bool $do_updates will do pull in data updates from LDAP if relevant
322 * @return nothing
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;
329 parent::sync_users($do_updates);
333 * Hook for logout page
335 function logoutpage_hook() {
336 global $USER, $redirect;
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;
349 * Post logout hook.
351 * Note: this method replace the prelogout_hook method to avoid redirect to CAS logout
352 * before the event userlogout being triggered.
354 * @param stdClass $user clone of USER object object before the user session was terminated
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);
367 * Return a list of identity providers to display on the login page.
369 * @param string|moodle_url $wantsurl The requested URL.
370 * @return array List of arrays with keys url, iconurl and name.
372 public function loginpage_idp_list($wantsurl) {
373 if (empty($this->config->hostname)) {
374 // CAS is not configured.
375 return [];
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;
390 return [
392 'url' => new moodle_url(get_login_url(), [
393 'authCAS' => 'CAS',
395 'iconurl' => $iconurl,
396 'name' => format_string($this->config->auth_name),