Passed
Push — scrutinizer-code-quality ( 09f5a1...c4c5fb )
by Adam
56:05 queued 14:08
created

SugarAuthenticate::canChangeUserName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3
/*********************************************************************************
4
 * SugarCRM Community Edition is a customer relationship management program developed by
5
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6
7
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 *
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 *
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 *
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
42
43
44
/**
45
 * This file is used to control the authentication process.
46
 * It will call on the user authenticate and controll redirection
47
 * based on the users validation
48
 *
49
 */
50
class SugarAuthenticate{
51
	var $userAuthenticateClass = 'SugarAuthenticateUser';
52
	var $authenticationDir = 'SugarAuthenticate';
53
	/**
54
	 * Constructs SugarAuthenticate
55
	 * This will load the user authentication class
56
	 *
57
	 * @return SugarAuthenticate
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
58
	 */
59
	public function __construct()
60
	{
61
	    // check in custom dir first, in case someone want's to override an auth controller
62
		if (file_exists('custom/modules/Users/authentication/'.$this->authenticationDir.'/' . $this->userAuthenticateClass . '.php')) {
63
            require_once('custom/modules/Users/authentication/'.$this->authenticationDir.'/' . $this->userAuthenticateClass . '.php');
64
        }
65
        elseif (file_exists('modules/Users/authentication/'.$this->authenticationDir.'/' . $this->userAuthenticateClass . '.php')) {
66
            require_once('modules/Users/authentication/'.$this->authenticationDir.'/' . $this->userAuthenticateClass . '.php');
67
        }
68
69
        $this->userAuthenticate = new $this->userAuthenticateClass();
70
	}
71
72
	/**
73
	 * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead
74
	 */
75
	public function SugarAuthenticate(){
76
		$deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
77
		if(isset($GLOBALS['log'])) {
78
			$GLOBALS['log']->deprecated($deprecatedMessage);
79
		}
80
		else {
81
			trigger_error($deprecatedMessage, E_USER_DEPRECATED);
82
		}
83
		self::__construct();
84
	}
85
86
	/**
87
	 * Authenticates a user based on the username and password
88
	 * returns true if the user was authenticated false otherwise
89
	 * it also will load the user into current user if he was authenticated
90
	 *
91
	 * @param string $username
92
	 * @param string $password
93
	 * @return boolean
94
	 */
95
	function loginAuthenticate($username, $password, $fallback=false, $PARAMS = array ()){
96
		global $mod_strings;
97
		unset($_SESSION['login_error']);
98
		$usr= new user();
99
		$usr_id=$usr->retrieve_user_id($username);
100
		$usr->retrieve($usr_id);
101
		$_SESSION['login_error']='';
102
		$_SESSION['waiting_error']='';
103
		$_SESSION['hasExpiredPassword']='0';
104
		if ($this->userAuthenticate->loadUserOnLogin($username, $password, $fallback, $PARAMS)) {
105
			require_once('modules/Users/password_utils.php');
106
			if(hasPasswordExpired($username)) {
107
				$_SESSION['hasExpiredPassword'] = '1';
108
			}
109
			// now that user is authenticated, reset loginfailed
110
			if ($usr->getPreference('loginfailed') != '' && $usr->getPreference('loginfailed') != 0) {
111
				$usr->setPreference('loginfailed','0');
112
				$usr->savePreferencesToDB();
113
			}
114
			return $this->postLoginAuthenticate();
115
116
		}
117
		else
118
		{
119
			//if(!empty($usr_id) && $res['lockoutexpiration'] > 0){
120
            if(!empty($usr_id)){
121
				if (($logout=$usr->getPreference('loginfailed'))=='')
122
	        		$usr->setPreference('loginfailed','1');
123
	    		else
124
	        		$usr->setPreference('loginfailed',$logout+1);
125
	    		$usr->savePreferencesToDB();
126
    		}
127
		}
128
		if(strtolower(get_class($this)) != 'sugarauthenticate'){
129
			$sa = new SugarAuthenticate();
130
			$error = (!empty($_SESSION['login_error']))?$_SESSION['login_error']:'';
131
			if($sa->loginAuthenticate($username, $password, true, $PARAMS)){
132
				return true;
133
			}
134
			$_SESSION['login_error'] = $error;
135
		}
136
137
138
		$_SESSION['login_user_name'] = $username;
139
		$_SESSION['login_password'] = $password;
140
		if(empty($_SESSION['login_error'])){
141
			$_SESSION['login_error'] = translate('ERR_INVALID_PASSWORD', 'Users');
142
		}
143
144
		return false;
145
146
	}
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
154
		global $reset_language_on_default_user, $sugar_config;
155
		
156
		//just do a little house cleaning here
157
		unset($_SESSION['login_password']);
158
		unset($_SESSION['login_error']);
159
		unset($_SESSION['login_user_name']);
160
		unset($_SESSION['ACL']);
161
162
		//set the server unique key
163
		if (isset ($sugar_config['unique_key']))$_SESSION['unique_key'] = $sugar_config['unique_key'];
164
165
		//set user language
166
		if (isset ($reset_language_on_default_user) && $reset_language_on_default_user && $GLOBALS['current_user']->user_name == $sugar_config['default_user_name']) {
167
			$authenticated_user_language = $sugar_config['default_language'];
168
		} else {
169
			$authenticated_user_language = isset($_REQUEST['login_language']) ? $_REQUEST['login_language'] : (isset ($_REQUEST['ck_login_language_20']) ? $_REQUEST['ck_login_language_20'] : $sugar_config['default_language']);
170
		}
171
172
		$_SESSION['authenticated_user_language'] = $authenticated_user_language;
173
174
		$GLOBALS['log']->debug("authenticated_user_language is $authenticated_user_language");
175
176
		// Clear all uploaded import files for this user if it exists
177
        require_once('modules/Import/ImportCacheFiles.php');
178
        $tmp_file_name = ImportCacheFiles::getImportDir()."/IMPORT_" . $GLOBALS['current_user']->id;
179
180
		if (file_exists($tmp_file_name)) {
181
			unlink($tmp_file_name);
182
		}
183
184
		return true;
185
	}
186
187
	/**
188
	 * On every page hit this will be called to ensure a user is authenticated
189
	 * @return boolean
190
	 */
191
	function sessionAuthenticate(){
192
193
		global $module, $action, $allowed_actions;
194
		$authenticated = false;
195
		$allowed_actions = array ("Authenticate", "Login"); // these are actions where the user/server keys aren't compared
196
		if (isset ($_SESSION['authenticated_user_id'])) {
197
198
			$GLOBALS['log']->debug("We have an authenticated user id: ".$_SESSION["authenticated_user_id"]);
199
200
			$authenticated = $this->postSessionAuthenticate();
201
202
		} else
203
		if (isset ($action) && isset ($module) && $action == "Authenticate" && $module == "Users") {
204
			$GLOBALS['log']->debug("We are authenticating user now");
205
		} else {
206
			$GLOBALS['log']->debug("The current user does not have a session.  Going to the login page");
207
			$action = "Login";
208
			$module = "Users";
209
			$_REQUEST['action'] = $action;
210
			$_REQUEST['module'] = $module;
211
		}
212
		if (empty ($GLOBALS['current_user']->id) && !in_array($action, $allowed_actions)) {
213
214
			$GLOBALS['log']->debug("The current user is not logged in going to login page");
215
			$action = "Login";
216
			$module = "Users";
217
			$_REQUEST['action'] = $action;
218
			$_REQUEST['module'] = $module;
219
220
		}
221
222
		if($authenticated && ((empty($_REQUEST['module']) || empty($_REQUEST['action'])) || ($_REQUEST['module'] != 'Users' || $_REQUEST['action'] != 'Logout'))){
223
			$this->validateIP();
224
		}
225
		return $authenticated;
226
	}
227
228
229
230
231
	/**
232
	 * Called after a session is authenticated - if this returns false the sessionAuthenticate will return false and destroy the session
233
	 * and it will load the  current user
234
	 * @return boolean
235
	 */
236
237
	function postSessionAuthenticate(){
238
239
		global $action, $allowed_actions, $sugar_config;
240
		$_SESSION['userTime']['last'] = time();
241
		$user_unique_key = (isset ($_SESSION['unique_key'])) ? $_SESSION['unique_key'] : '';
242
		$server_unique_key = (isset ($sugar_config['unique_key'])) ? $sugar_config['unique_key'] : '';
243
244
		//CHECK IF USER IS CROSSING SITES
245
		if (($user_unique_key != $server_unique_key) && (!in_array($action, $allowed_actions)) && (!isset ($_SESSION['login_error']))) {
246
247
			$GLOBALS['log']->debug('Destroying Session User has crossed Sites');
248
		    session_destroy();
249
			header("Location: index.php?action=Login&module=Users".$GLOBALS['app']->getLoginRedirect());
250
			sugar_cleanup(true);
251
		}
252
		if (!$this->userAuthenticate->loadUserOnSession($_SESSION['authenticated_user_id'])) {
253
			session_destroy();
254
			header("Location: index.php?action=Login&module=Users&loginErrorMessage=LBL_SESSION_EXPIRED");
255
			$GLOBALS['log']->debug('Current user session does not exist redirecting to login');
256
			sugar_cleanup(true);
257
		}
258
		$GLOBALS['log']->debug('Current user is: '.$GLOBALS['current_user']->user_name);
259
		return true;
260
	}
261
262
	/**
263
	 * Make sure a user isn't stealing sessions so check the ip to ensure that the ip address hasn't dramatically changed
264
	 *
265
	 */
266
	function validateIP() {
267
		global $sugar_config;
268
		// grab client ip address
269
		$clientIP = query_client_ip();
270
		$classCheck = 0;
271
		// check to see if config entry is present, if not, verify client ip
272
		if (!isset ($sugar_config['verify_client_ip']) || $sugar_config['verify_client_ip'] == true) {
273
			// check to see if we've got a current ip address in $_SESSION
274
			// and check to see if the session has been hijacked by a foreign ip
275
			if (isset ($_SESSION["ipaddress"])) {
276
				$session_parts = explode(".", $_SESSION["ipaddress"]);
277
				$client_parts = explode(".", $clientIP);
278
                if(count($session_parts) < 4) {
279
                    $classCheck = 0;
280
                }
281
                else {
282
    				// match class C IP addresses
283
    				for ($i = 0; $i < 3; $i ++) {
284
    					if ($session_parts[$i] == $client_parts[$i]) {
285
    						$classCheck = 1;
286
    						continue;
287
    					} else {
288
    						$classCheck = 0;
289
    						break;
290
    					}
291
    				}
292
                }
293
				// we have a different IP address
294
				if ($_SESSION["ipaddress"] != $clientIP && empty ($classCheck)) {
295
					$GLOBALS['log']->fatal("IP Address mismatch: SESSION IP: {$_SESSION['ipaddress']} CLIENT IP: {$clientIP}");
296
					session_destroy();
297
					die("Your session was terminated due to a significant change in your IP address.  <a href=\"{$sugar_config['site_url']}\">Return to Home</a>");
298
				}
299
			} else {
300
				$_SESSION["ipaddress"] = $clientIP;
301
			}
302
		}
303
304
	}
305
306
307
308
309
	/**
310
	 * Called when a user requests to logout
311
	 *
312
	 */
313
	function logout(){
314
			session_start();
315
			session_destroy();
316
			ob_clean();
317
			header('Location: index.php?module=Users&action=Login');
318
			sugar_cleanup(true);
319
	}
320
321
322
	/**
323
	 * Encodes a users password. This is a static function and can be called at any time.
324
	 *
325
	 * @param STRING $password
326
	 * @return STRING $encoded_password
327
	 */
328
	static function encodePassword($password){
329
		return strtolower(md5($password));
330
	}
331
332
	/**
333
	 * If a user may change there password through the Sugar UI
334
	 *
335
	 */
336
	function canChangePassword(){
337
		return true;
338
	}
339
	/**
340
	 * If a user may change there user name through the Sugar UI
341
	 *
342
	 */
343
	function canChangeUserName(){
344
		return true;
345
	}
346
347
348
    /**
349
     * pre_login
350
     *
351
     * This function allows the SugarAuthenticate subclasses to perform some pre login initialization as needed
352
     */
353
    function pre_login()
354
    {
355
        if (isset($_SESSION['authenticated_user_id']))
356
        {
357
            ob_clean();
358
            // fixing bug #46837: Previosly links/URLs to records in Sugar from MSO Excel/Word were referred to the home screen and not the record
359
            // It used to appear when default browser was not MS IE
360
            header("Location: ".$GLOBALS['app']->getLoginRedirect());
361
            sugar_cleanup(true);
362
        }
363
    }
364
365
    /**
366
     * Redirect to login page
367
     *
368
     * @param SugarApplication $app
369
     */
370
    public function redirectToLogin(SugarApplication $app)
371
    {
372
        $loginVars = $app->createLoginVars();
373
		SugarApplication::redirect('index.php?action=Login&module=Users' . $loginVars);
374
    }
375
}
376