sso.Drupal.class.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. <?php
  2. use \ChamiloSession as Session;
  3. /* For licensing terms, see /license.txt */
  4. /**
  5. * This file contains the necessary elements to implement a Single Sign On
  6. * mechanism with an external Drupal application (on which the Chamilo module
  7. * 7.x-1.0-alpha3 or above must be implemented)
  8. *
  9. * To use this class, set variable "sso_authentication_subclass" to "Drupal"
  10. * in Chamilo settings. If not yet available in the "Security" tab, execute the
  11. * following on the Chamilo database:
  12. * INSERT INTO `settings_current` (`variable`, `type`, `category`, `selected_value`, `title`, `comment`, `access_url`)
  13. * VALUES ('sso_authentication_subclass', 'textfield', 'Security', 'Drupal', 'SSOSubclass', 'SSOSubclassComment', 1);
  14. *
  15. * @package chamilo.auth.sso
  16. */
  17. /**
  18. * The SSO class allows for management of remote Single Sign On resources
  19. */
  20. class ssoDrupal {
  21. public $protocol; // 'http://',
  22. public $domain; // 'localhost/project/drupal',
  23. public $auth_uri; // '/?q=user',
  24. public $deauth_uri; // '/?q=logout',
  25. public $referer; // http://my.chamilo.com/main/auth/profile.php
  26. /**
  27. * Instanciates the object, initializing all relevant URL strings
  28. */
  29. public function __construct() {
  30. $this->protocol = api_get_setting('sso_authentication_protocol');
  31. // There can be multiple domains, so make sure to take only the first
  32. // This might be later extended with a decision process
  33. $domains = split(',',api_get_setting('sso_authentication_domain'));
  34. $this->domain = trim($domains[0]);
  35. $this->auth_uri = api_get_setting('sso_authentication_auth_uri');
  36. $this->deauth_uri = api_get_setting('sso_authentication_unauth_uri');
  37. //cut the string to avoid recursive URL construction in case of failure
  38. $this->referer = $this->protocol.$_SERVER['HTTP_HOST'].substr($_SERVER['REQUEST_URI'],0,strpos($_SERVER['REQUEST_URI'],'sso'));
  39. $this->deauth_url = $this->protocol.$this->domain.$this->deauth_uri;
  40. $this->master_url = $this->protocol.$this->domain.$this->auth_uri;
  41. $this->target = api_get_path(WEB_PATH);
  42. }
  43. /**
  44. * Unlogs the user from the remote server
  45. */
  46. public function logout() {
  47. header('Location: '.$this->deauth_url);
  48. exit;
  49. }
  50. /**
  51. * Sends the user to the master URL for a check of active connection
  52. */
  53. public function ask_master() {
  54. // Generate a single usage token that must be encoded by the master
  55. $_SESSION['sso_challenge'] = api_generate_password(48);
  56. // Redirect browser to the master URL
  57. $params = 'sso_referer='.urlencode($this->referer).'&sso_target='.urlencode($this->target).'&sso_challenge='.urlencode($_SESSION['sso_challenge']);
  58. if (strpos($this->master_url, "?") === false) {
  59. $params = "?{$params}";
  60. } else {
  61. $params = "&{$params}";
  62. }
  63. header('Location: '.$this->master_url.$params);
  64. exit;
  65. }
  66. /**
  67. * Validates the received active connection data with the database
  68. * @return bool Return the loginFailed variable value to local.inc.php
  69. */
  70. public function check_user() {
  71. global $_user;
  72. $loginFailed = false;
  73. //change the way we recover the cookie depending on how it is formed
  74. $sso = $this->decode_cookie($_GET['sso_cookie']);
  75. //get token that should have been used and delete it
  76. //from session since it can only be used once
  77. $sso_challenge = '';
  78. if (isset($_SESSION['sso_challenge'])) {
  79. $sso_challenge = $_SESSION['sso_challenge'];
  80. unset($_SESSION['sso_challenge']);
  81. }
  82. //lookup the user in the main database
  83. $user_table = Database::get_main_table(TABLE_MAIN_USER);
  84. $sql = "SELECT user_id, username, password, auth_source, active, expiration_date, status
  85. FROM $user_table
  86. WHERE username = '".trim(Database::escape_string($sso['username']))."'";
  87. $result = Database::query($sql);
  88. if (Database::num_rows($result) > 0) {
  89. $uData = Database::fetch_array($result);
  90. //Check the user's password
  91. if ($uData['auth_source'] == PLATFORM_AUTH_SOURCE) {
  92. if ($sso['secret'] === sha1($uData['username'].$sso_challenge.api_get_security_key())
  93. && ($sso['username'] == $uData['username'])) {
  94. //Check if the account is active (not locked)
  95. if ($uData['active']=='1') {
  96. // check if the expiration date has not been reached
  97. if ($uData['expiration_date'] > date('Y-m-d H:i:s') OR $uData['expiration_date']=='0000-00-00 00:00:00') {
  98. //If Multiple URL is enabled
  99. if (api_get_multiple_access_url()) {
  100. //Check the access_url configuration setting if the user is registered in the access_url_rel_user table
  101. //Getting the current access_url_id of the platform
  102. $current_access_url_id = api_get_current_access_url_id();
  103. // my user is subscribed in these
  104. //sites: $my_url_list
  105. $my_url_list = api_get_access_url_from_user($uData['user_id']);
  106. } else {
  107. $current_access_url_id = 1;
  108. $my_url_list = array(1);
  109. }
  110. $my_user_is_admin = UserManager::is_admin($uData['user_id']);
  111. if ($my_user_is_admin === false) {
  112. if (is_array($my_url_list) && count($my_url_list) > 0 ) {
  113. if (in_array($current_access_url_id, $my_url_list)) {
  114. // the user has permission to enter at this site
  115. $_user['user_id'] = $uData['user_id'];
  116. $_user = api_get_user_info($_user['user_id']);
  117. Session::write('_user', $_user);
  118. event_login();
  119. // Redirect to homepage
  120. $sso_target = isset($sso['target']) ? $sso['target'] : api_get_path(WEB_PATH) .'.index.php';
  121. header('Location: '. $sso_target);
  122. exit;
  123. } else {
  124. // user does not have permission for this site
  125. $loginFailed = true;
  126. Session::erase('_uid');
  127. header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
  128. exit;
  129. }
  130. } else {
  131. // there is no URL in the multiple
  132. // urls list for this user
  133. $loginFailed = true;
  134. Session::erase('_uid');
  135. header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
  136. exit;
  137. }
  138. } else {
  139. //Only admins of the "main" (first) Chamilo
  140. // portal can login wherever they want
  141. if (in_array(1, $my_url_list)) {
  142. //Check if this admin is admin on the
  143. // principal portal
  144. $_user['user_id'] = $uData['user_id'];
  145. $_user = api_get_user_info($_user['user_id']);
  146. $is_platformAdmin = $uData['status'] == COURSEMANAGER;
  147. Session::write('is_platformAdmin', $is_platformAdmin);
  148. Session::write('_user', $_user);
  149. event_login();
  150. } else {
  151. //Secondary URL admin wants to login
  152. // so we check as a normal user
  153. if (in_array($current_access_url_id, $my_url_list)) {
  154. $_user['user_id'] = $uData['user_id'];
  155. $_user = api_get_user_info($_user['user_id']);
  156. Session::write('_user',$_user);
  157. event_login();
  158. } else {
  159. $loginFailed = true;
  160. Session::erase('_uid');
  161. header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
  162. exit;
  163. }
  164. }
  165. }
  166. } else {
  167. // user account expired
  168. $loginFailed = true;
  169. Session::erase('_uid');
  170. header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_expired');
  171. exit;
  172. }
  173. } else {
  174. //User not active
  175. $loginFailed = true;
  176. Session::erase('_uid');
  177. header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_inactive');
  178. exit;
  179. }
  180. } else {
  181. //SHA1 of password is wrong
  182. $loginFailed = true;
  183. Session::erase('_uid');
  184. header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_password');
  185. exit;
  186. }
  187. } else {
  188. //Auth_source is wrong
  189. $loginFailed = true;
  190. Session::erase('_uid');
  191. header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_authentication_source');
  192. exit;
  193. }
  194. } else {
  195. //No user by that login
  196. $loginFailed = true;
  197. Session::erase('_uid');
  198. header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=user_not_found');
  199. exit;
  200. }
  201. return $loginFailed;
  202. }
  203. /**
  204. * Decode the cookie (this function may vary depending on the
  205. * Single Sign On implementation
  206. * @param string Encoded cookie
  207. * @return array Parsed and unencoded cookie
  208. */
  209. private function decode_cookie($cookie) {
  210. return unserialize(base64_decode($cookie));
  211. }
  212. /**
  213. * Generate the URL for profile editing
  214. * @return string If the URL is obtained return the drupal_user_id. Otherwise return false
  215. */
  216. public function generateProfileEditingURL()
  217. {
  218. $userId = api_get_user_id();
  219. $userExtraFieldValue = new ExtraFieldValue('user');
  220. $drupalUserIdData = $userExtraFieldValue->get_values_by_handler_and_field_variable($userId, 'drupal_user_id');
  221. if ($drupalUserIdData === false) {
  222. return false;
  223. }
  224. $drupalUserId = $drupalUserIdData['field_value'];
  225. $url = "{$this->protocol}{$this->domain}/user/{$drupalUserId}/edit";
  226. return $url;
  227. }
  228. }