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/vendor/autoload.php');
33 require_once($CFG->dirroot
.'/auth/cas/CAS/vendor/apereo/phpcas/source/CAS.php');
36 * CAS authentication plugin.
38 class auth_plugin_cas
extends auth_plugin_ldap
{
43 public function __construct() {
44 $this->authtype
= 'cas';
45 $this->roleauth
= 'auth_cas';
46 $this->errorlogtag
= '[AUTH CAS] ';
47 $this->init_plugin($this->authtype
);
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
);
60 function prevent_local_passwords() {
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) {
75 return phpCAS
::isAuthenticated() && (trim(core_text
::strtolower(phpCAS
::getUser())) == $username);
79 * Returns true if this authentication plugin is 'internal'.
83 function is_internal() {
88 * Returns true if this authentication plugin can change the user's
93 function can_change_password() {
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() {
105 global $SESSION, $OUTPUT, $PAGE;
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
);
120 // Return if CAS enabled and settings not specified yet
121 if (empty($this->config
->hostname
)) {
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
)) {
133 $authCAS = optional_param('authCAS', '', PARAM_RAW
);
134 if ($authCAS != 'CAS') {
140 // Connection to CAS server
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)));
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();
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() {
179 static $connected = false;
182 // Make sure phpCAS doesn't try to start a new PHP session when connecting to the CAS server.
183 if ($this->config
->proxycas
) {
184 phpCAS
::proxy($this->config
->casversion
, $this->config
->hostname
, (int) $this->config
->port
, $this->config
->baseuri
, false);
186 phpCAS
::client($this->config
->casversion
, $this->config
->hostname
, (int) $this->config
->port
, $this->config
->baseuri
, false);
188 // Some CAS installs require SSLv3 that should be explicitly set.
189 if (!empty($this->config
->curl_ssl_version
)) {
190 phpCAS
::setExtraCurlOption(CURLOPT_SSLVERSION
, $this->config
->curl_ssl_version
);
196 // If Moodle is configured to use a proxy, phpCAS needs some curl options set.
197 if (!empty($CFG->proxyhost
) && !is_proxybypass(phpCAS
::getServerLoginURL())) {
198 phpCAS
::setExtraCurlOption(CURLOPT_PROXY
, $CFG->proxyhost
);
199 if (!empty($CFG->proxyport
)) {
200 phpCAS
::setExtraCurlOption(CURLOPT_PROXYPORT
, $CFG->proxyport
);
202 if (!empty($CFG->proxytype
)) {
203 // Only set CURLOPT_PROXYTYPE if it's something other than the curl-default http
204 if ($CFG->proxytype
== 'SOCKS5') {
205 phpCAS
::setExtraCurlOption(CURLOPT_PROXYTYPE
, CURLPROXY_SOCKS5
);
208 if (!empty($CFG->proxyuser
) and !empty($CFG->proxypassword
)) {
209 phpCAS
::setExtraCurlOption(CURLOPT_PROXYUSERPWD
, $CFG->proxyuser
.':'.$CFG->proxypassword
);
210 if (defined('CURLOPT_PROXYAUTH')) {
211 // any proxy authentication if PHP 5.1
212 phpCAS
::setExtraCurlOption(CURLOPT_PROXYAUTH
, CURLAUTH_BASIC | CURLAUTH_NTLM
);
217 if ($this->config
->certificate_check
&& $this->config
->certificate_path
){
218 phpCAS
::setCasServerCACert($this->config
->certificate_path
);
220 // Don't try to validate the server SSL credentials
221 phpCAS
::setNoCasServerValidation();
226 * Returns the URL for changing the user's pw, or empty if the default can
231 function change_password_url() {
236 * Returns true if user should be coursecreator.
238 * @param mixed $username username (without system magic quotes)
239 * @return boolean result
241 function iscreator($username) {
242 if (empty($this->config
->host_url
) or (empty($this->config
->attrcreators
) && empty($this->config
->groupecreators
)) or empty($this->config
->memberattribute
)) {
246 $extusername = core_text
::convert($username, 'utf-8', $this->config
->ldapencoding
);
248 // Test for group creator
249 if (!empty($this->config
->groupecreators
)) {
250 $ldapconnection = $this->ldap_connect();
251 if ($this->config
->memberattribute_isdn
) {
252 if(!($userid = $this->ldap_find_userdn($ldapconnection, $extusername))) {
256 $userid = $extusername;
259 $group_dns = explode(';', $this->config
->groupecreators
);
260 if (ldap_isgroupmember($ldapconnection, $userid, $group_dns, $this->config
->memberattribute
)) {
265 // Build filter for attrcreator
266 if (!empty($this->config
->attrcreators
)) {
267 $attrs = explode(';', $this->config
->attrcreators
);
268 $filter = '(& ('.$this->config
->user_attribute
."=$username)(|";
269 foreach ($attrs as $attr){
270 if(strpos($attr, '=')) {
271 $filter .= "($attr)";
273 $filter .= '('.$this->config
->memberattribute
."=$attr)";
279 $result = $this->ldap_get_userlist($filter);
280 if (count($result) != 0) {
289 * Reads user information from LDAP and returns it as array()
291 * If no LDAP servers are configured, user information has to be
292 * provided via other methods (CSV file, manually, etc.). Return
293 * an empty array so existing user info is not lost. Otherwise,
294 * calls parent class method to get user info.
296 * @param string $username username
297 * @return mixed array with no magic quotes or false on error
299 function get_userinfo($username) {
300 if (empty($this->config
->host_url
)) {
303 return parent
::get_userinfo($username);
307 * Syncronizes users from LDAP server to moodle user table.
309 * If no LDAP servers are configured, simply return. Otherwise,
310 * call parent class method to do the work.
312 * @param bool $do_updates will do pull in data updates from LDAP if relevant
315 function sync_users($do_updates=true) {
316 if (empty($this->config
->host_url
)) {
317 error_log('[AUTH CAS] '.get_string('noldapserver', 'auth_cas'));
320 parent
::sync_users($do_updates);
324 * Hook for logout page
326 function logoutpage_hook() {
327 global $USER, $redirect;
329 // Only do this if the user is actually logged in via CAS
330 if ($USER->auth
=== $this->authtype
) {
331 // Check if there is an alternative logout return url defined
332 if (isset($this->config
->logout_return_url
) && !empty($this->config
->logout_return_url
)) {
333 // Set redirect to alternative return url
334 $redirect = $this->config
->logout_return_url
;
342 * Note: this method replace the prelogout_hook method to avoid redirect to CAS logout
343 * before the event userlogout being triggered.
345 * @param stdClass $user clone of USER object object before the user session was terminated
347 public function postlogout_hook($user) {
349 // Only redirect to CAS logout if the user is logged as a CAS user.
350 if (!empty($this->config
->logoutcas
) && $user->auth
== $this->authtype
) {
351 $backurl = !empty($this->config
->logout_return_url
) ?
$this->config
->logout_return_url
: $CFG->wwwroot
;
353 phpCAS
::logoutWithRedirectService($backurl);
358 * Return a list of identity providers to display on the login page.
360 * @param string|moodle_url $wantsurl The requested URL.
361 * @return array List of arrays with keys url, iconurl and name.
363 public function loginpage_idp_list($wantsurl) {
364 if (empty($this->config
->hostname
)) {
365 // CAS is not configured.
369 if ($this->config
->auth_logo
) {
370 $iconurl = moodle_url
::make_pluginfile_url(
371 context_system
::instance()->id
,
376 $this->config
->auth_logo
);
383 'url' => new moodle_url(get_login_url(), [
386 'iconurl' => $iconurl,
387 'name' => format_string($this->config
->auth_name
),