PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/Users/authentication/SugarAuthenticate/SugarAuthenticate.php

https://bitbucket.org/hatim_alam/sugar-8
PHP | 372 lines | 233 code | 47 blank | 92 comment | 53 complexity | efe383c770d6de175161049e4bfc980b MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause, Apache-2.0, MIT, BSD-2-Clause
  1. <?php
  2. /*
  3. * Your installation or use of this SugarCRM file is subject to the applicable
  4. * terms available at
  5. * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/.
  6. * If you do not agree to all of the applicable terms or do not have the
  7. * authority to bind the entity as an authorized representative, then do not
  8. * install or use this SugarCRM file.
  9. *
  10. * Copyright (C) SugarCRM Inc. All rights reserved.
  11. */
  12. use Sugarcrm\Sugarcrm\Security\InputValidation\InputValidation;
  13. /**
  14. * This file is used to control the authentication process.
  15. * It will call on the user authenticate and controll redirection
  16. * based on the users validation
  17. * @deprecated Will be removed in 7.11. IDM-46
  18. * @deprecated Please use new idM Mango library Glue \IdMSugarAuthenticate
  19. */
  20. class SugarAuthenticate{
  21. var $userAuthenticateClass = 'SugarAuthenticateUser';
  22. var $authenticationDir = 'SugarAuthenticate';
  23. /**
  24. * Constructs SugarAuthenticate
  25. * This will load the user authentication class
  26. *
  27. * @return SugarAuthenticate
  28. */
  29. public function __construct()
  30. {
  31. // check in custom dir first, in case someone want's to override an auth controller
  32. SugarAutoLoader::requireWithCustom('modules/Users/authentication/'.$this->authenticationDir.'/' . $this->userAuthenticateClass . '.php');
  33. $this->userAuthenticate = new $this->userAuthenticateClass();
  34. }
  35. /**
  36. * Authenticates a user based on the username and password
  37. * returns true if the user was authenticated false otherwise
  38. * it also will load the user into current user if he was authenticated
  39. *
  40. * @param string $username
  41. * @param string $password
  42. * @return boolean
  43. */
  44. function loginAuthenticate($username, $password, $fallback=false, $PARAMS = array ()){
  45. global $app_strings, $log;
  46. $log->deprecated(sprintf(
  47. 'The %s in %s was called. Please see \IdMSugarAuthenticate',
  48. __METHOD__,
  49. __CLASS__
  50. ));
  51. unset($_SESSION['login_error']);
  52. $res = $GLOBALS['sugar_config']['passwordsetting'];
  53. $usr = BeanFactory::newBean('Users');
  54. $usr->retrieve_by_string_fields(array('user_name'=>$username));
  55. $_SESSION['login_error']='';
  56. $_SESSION['waiting_error']='';
  57. $_SESSION['hasExpiredPassword']='0';
  58. $usr->reloadPreferences();
  59. // if there is too many login attempts
  60. if (!empty($usr->id) && $res['lockoutexpiration'] > 0 && $usr->getPreference('loginfailed')>=($res['lockoutexpirationlogin']) && !($usr->portal_only)){
  61. // if there is a lockout time set
  62. if ($res['lockoutexpiration'] == '2'){
  63. // lockout date is now if not set
  64. if (($logout_time=$usr->getPreference('logout_time'))==''){
  65. $usr->setPreference('logout_time',TimeDate::getInstance()->nowDb());
  66. $logout_time=$usr->getPreference('logout_time');
  67. }
  68. // Bug # 45922 - calculating the expiretime properly
  69. $stim = strtotime($logout_time);
  70. $mins = $res['lockoutexpirationtime']*$res['lockoutexpirationtype'];
  71. $expiretime = TimeDate::getInstance()->fromDb($logout_time)->modify("+$mins minutes")->asDb();
  72. // Test if the user is still locked out and return a error message
  73. if (TimeDate::getInstance()->nowDb() < $expiretime){
  74. $usr->setPreference('lockout','1');
  75. $_SESSION['login_error'] = $app_strings['LBL_LOGIN_ATTEMPTS_OVERRUN'] . ' ';
  76. $_SESSION['login_error'] .= $app_strings['LBL_LOGIN_LOGIN_TIME_ALLOWED'] . ' ';
  77. $lol= strtotime($expiretime)-strtotime(TimeDate::getInstance()->nowDb());
  78. switch (true) {
  79. case (floor($lol/86400) !=0):
  80. $_SESSION['login_error'] .= floor($lol/86400).$app_strings['LBL_LOGIN_LOGIN_TIME_DAYS'];
  81. break;
  82. case (floor($lol/3600)!=0):
  83. $_SESSION['login_error'] .= floor($lol/3600).$app_strings['LBL_LOGIN_LOGIN_TIME_HOURS'];
  84. break;
  85. case (floor($lol/60)!=0):
  86. $_SESSION['login_error'] .= floor($lol/60).$app_strings['LBL_LOGIN_LOGIN_TIME_MINUTES'];
  87. break;
  88. case (floor($lol)!=0):
  89. $_SESSION['login_error'] .= floor($lol).$app_strings['LBL_LOGIN_LOGIN_TIME_SECONDS'];
  90. break;
  91. }
  92. $usr->savePreferencesToDB();
  93. return false;
  94. }
  95. else{
  96. $usr->setPreference('lockout','');
  97. $usr->setPreference('loginfailed','0');
  98. $usr->setPreference('logout_time','');
  99. $usr->savePreferencesToDB();
  100. }
  101. }
  102. else{
  103. $usr->setPreference('lockout','1');
  104. $_SESSION['login_error']=$app_strings['LBL_LOGIN_ATTEMPTS_OVERRUN'];
  105. $_SESSION['waiting_error']=$app_strings['LBL_LOGIN_ADMIN_CALL'];
  106. $usr->savePreferencesToDB();
  107. return false;
  108. }
  109. }
  110. if ($this->userAuthenticate->loadUserOnLogin($username, $password, $fallback, $PARAMS)) {
  111. require_once('modules/Users/password_utils.php');
  112. if (hasPasswordExpired($username, true)) {
  113. $_SESSION['hasExpiredPassword'] = '1';
  114. }
  115. // now that user is authenticated, reset loginfailed
  116. if ($usr->getPreference('loginfailed') != '' && $usr->getPreference('loginfailed') != 0) {
  117. $usr->setPreference('loginfailed','0');
  118. $usr->savePreferencesToDB();
  119. }
  120. $this->updateUserLastLogin($usr);
  121. return $this->postLoginAuthenticate();
  122. }
  123. else
  124. {
  125. if(!empty($usr->id) && isset($res['lockoutexpiration']) && $res['lockoutexpiration'] > 0){
  126. if (($logout=$usr->getPreference('loginfailed'))=='')
  127. $usr->setPreference('loginfailed','1');
  128. else
  129. $usr->setPreference('loginfailed',$logout+1);
  130. $usr->savePreferencesToDB();
  131. }
  132. }
  133. if(strtolower(get_class($this)) != 'sugarauthenticate'){
  134. $sa = new SugarAuthenticate();
  135. $error = (!empty($_SESSION['login_error']))?$_SESSION['login_error']:'';
  136. if($sa->loginAuthenticate($username, $password, true, $PARAMS)){
  137. return true;
  138. }
  139. $_SESSION['login_error'] = $error;
  140. }
  141. $_SESSION['login_user_name'] = $username;
  142. $_SESSION['login_password'] = $password;
  143. if(empty($_SESSION['login_error'])){
  144. $_SESSION['login_error'] = translate('ERR_INVALID_PASSWORD', 'Users');
  145. }
  146. return false;
  147. }
  148. /**
  149. * Once a user is authenticated on login this function will be called. Populate the session with what is needed and log anything that needs to be logged
  150. *
  151. */
  152. function postLoginAuthenticate(){
  153. global $reset_theme_on_default_user, $reset_language_on_default_user, $sugar_config;
  154. //just do a little house cleaning here
  155. unset($_SESSION['login_password']);
  156. unset($_SESSION['login_error']);
  157. unset($_SESSION['login_user_name']);
  158. unset($_SESSION['ACL']);
  159. //set the server unique key
  160. if (isset ($sugar_config['unique_key']))$_SESSION['unique_key'] = $sugar_config['unique_key'];
  161. //set user language
  162. if (isset ($reset_language_on_default_user) && $reset_language_on_default_user && $GLOBALS['current_user']->user_name == $sugar_config['default_user_name']) {
  163. $authenticated_user_language = $sugar_config['default_language'];
  164. } else {
  165. $authenticated_user_language = InputValidation::getService()->getValidInputRequest('login_language', 'Assert\Language', $sugar_config['default_language']);
  166. }
  167. $_SESSION['authenticated_user_language'] = $authenticated_user_language;
  168. $GLOBALS['log']->debug("authenticated_user_language is $authenticated_user_language");
  169. // Clear all uploaded import files for this user if it exists
  170. $tmp_file_name = ImportCacheFiles::getImportDir()."/IMPORT_" . $GLOBALS['current_user']->id;
  171. if (file_exists($tmp_file_name)) {
  172. unlink($tmp_file_name);
  173. }
  174. return true;
  175. }
  176. /**
  177. * On every page hit this will be called to ensure a user is authenticated
  178. *
  179. * @return boolean
  180. */
  181. function sessionAuthenticate(){
  182. global $module, $action, $allowed_actions;
  183. $authenticated = false;
  184. $allowed_actions = array ("Authenticate", "Login"); // these are actions where the user/server keys aren't compared
  185. if (isset ($_SESSION['authenticated_user_id'])) {
  186. $GLOBALS['log']->debug("We have an authenticated user id: ".$_SESSION["authenticated_user_id"]);
  187. $authenticated = $this->postSessionAuthenticate();
  188. if (!$authenticated) {
  189. // postSessionAuthenticate failed, nuke the session
  190. if (session_id()) {
  191. session_destroy();
  192. }
  193. header("Location: index.php?action=Login&module=Users&loginErrorMessage=LBL_SESSION_EXPIRED");
  194. sugar_cleanup(true);
  195. }
  196. } else
  197. if (isset ($action) && isset ($module) && $action == "Authenticate" && $module == "Users") {
  198. $GLOBALS['log']->debug("We are authenticating user now");
  199. } else {
  200. $GLOBALS['log']->debug("The current user does not have a session. Going to the login page");
  201. $action = "Login";
  202. $module = "Users";
  203. $_REQUEST['action'] = $action;
  204. $_REQUEST['module'] = $module;
  205. }
  206. if (empty ($GLOBALS['current_user']->id) && !in_array($action, $allowed_actions)) {
  207. $GLOBALS['log']->debug("The current user is not logged in going to login page");
  208. $action = "Login";
  209. $module = "Users";
  210. $_REQUEST['action'] = $action;
  211. $_REQUEST['module'] = $module;
  212. }
  213. return $authenticated;
  214. }
  215. /**
  216. * Called after a session is authenticated - if this returns false the sessionAuthenticate will return false and destroy the session
  217. * and it will load the current user
  218. * @return boolean
  219. */
  220. function postSessionAuthenticate(){
  221. global $action, $allowed_actions, $sugar_config;
  222. $_SESSION['userTime']['last'] = time();
  223. $user_unique_key = (isset ($_SESSION['unique_key'])) ? $_SESSION['unique_key'] : '';
  224. $server_unique_key = (isset ($sugar_config['unique_key'])) ? $sugar_config['unique_key'] : '';
  225. $authenticated = true;
  226. //CHECK IF USER IS CROSSING SITES
  227. if (($user_unique_key != $server_unique_key) && (!isset($_SESSION['login_error']))) {
  228. $GLOBALS['log']->security('Destroying Session User has crossed Sites');
  229. $authenticated = false;
  230. }
  231. if (!$this->userAuthenticate->loadUserOnSession($_SESSION['authenticated_user_id'])) {
  232. $GLOBALS['log']->error('Current user session does not exist redirecting to login');
  233. $authenticated = false;
  234. }
  235. if ($authenticated) {
  236. $authenticated = $this->validateIP();
  237. }
  238. if ($authenticated) {
  239. $GLOBALS['log']->debug('Current user is: '.$GLOBALS['current_user']->user_name);
  240. }
  241. return $authenticated;
  242. }
  243. /**
  244. * Make sure a user isn't stealing sessions so check the ip to ensure that the ip address hasn't dramatically changed
  245. */
  246. public function validateIP()
  247. {
  248. $clientIp = query_client_ip();
  249. if (isset($_SESSION['ipaddress'])) {
  250. if (!validate_ip($clientIp, $_SESSION['ipaddress'])) {
  251. $GLOBALS['log']->fatal(sprintf(
  252. 'IP address mismatch: SESSION IP: %s, CLIENT IP: %s',
  253. $_SESSION['ipaddress'],
  254. $clientIp
  255. ));
  256. return false;
  257. }
  258. return true;
  259. }
  260. $_SESSION['ipaddress'] = $clientIp;
  261. return true;
  262. }
  263. /**
  264. * Called when a user requests to logout
  265. *
  266. */
  267. function logout()
  268. {
  269. if(session_id() != '') {
  270. session_destroy();
  271. }
  272. ob_clean();
  273. header('Location: index.php?module=Users&action=Login');
  274. sugar_cleanup(true);
  275. }
  276. /**
  277. * Encodes a users password. This is a static function and can be called at any time.
  278. *
  279. * @param STRING $password
  280. * @return STRING $encoded_password
  281. */
  282. public static function encodePassword($password)
  283. {
  284. return strtolower(md5($password));
  285. }
  286. /**
  287. * If a user may change there password through the Sugar UI
  288. *
  289. */
  290. function canChangePassword(){
  291. return true;
  292. }
  293. /**
  294. * If a user may change there user name through the Sugar UI
  295. *
  296. */
  297. function canChangeUserName(){
  298. return true;
  299. }
  300. /**
  301. * pre_login
  302. *
  303. * This function allows the SugarAuthenticate subclasses to perform some pre login initialization as needed
  304. */
  305. function pre_login()
  306. {
  307. if (isset($_SESSION['authenticated_user_id']))
  308. {
  309. ob_clean();
  310. // fixing bug #46837: Previosly links/URLs to records in Sugar from MSO Excel/Word were referred to the home screen and not the record
  311. // It used to appear when default browser was not MS IE
  312. header("Location: ".$GLOBALS['app']->getLoginRedirect());
  313. sugar_cleanup(true);
  314. }
  315. }
  316. /**
  317. * Updates user's last_login field with current datetime
  318. *
  319. * @param User $user
  320. */
  321. protected function updateUserLastLogin(User $user)
  322. {
  323. $user->updateLastLogin();
  324. }
  325. }