PageRenderTime 55ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/auth/cas/auth.php

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