Passed
Branch php-cs-fixer (b9836a)
by Fabio
15:02
created

TUserManager::loadUserDataFromPhp()   D

Complexity

Conditions 17
Paths 28

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 17
nc 28
nop 1
dl 0
loc 30
rs 4.9807
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * TUserManager class
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @copyright Copyright &copy; 2005-2016 The PRADO Group
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 * @package Prado\Security
10
 */
11
12
namespace Prado\Security;
13
14
use Prado\Exceptions\TConfigurationException;
15
use Prado\Exceptions\TInvalidOperationException;
16
use Prado\Prado;
17
use Prado\TApplication;
18
use Prado\TPropertyValue;
19
use Prado\Xml\TXmlDocument;
20
21
/**
22
 * TUserManager class
23
 *
24
 * TUserManager manages a static list of users {@link TUser}.
25
 * The user information is specified via module configuration using the following XML syntax,
26
 * <code>
27
 * <module id="users" class="System.Security.TUserManager" PasswordMode="Clear">
28
 *   <user name="Joe" password="demo" />
29
 *   <user name="John" password="demo" />
30
 *   <role name="Administrator" users="John" />
31
 *   <role name="Writer" users="Joe,John" />
32
 * </module>
33
 * </code>
34
 *
35
 * PHP configuration style:
36
 * <code>
37
 * array(
38
 *   'users' => array(
39
 *      'class' => 'System.Security.TUserManager',
40
 *      'properties' => array(
41
 *         'PasswordMode' => 'Clear',
42
 *       ),
43
 *       'users' => array(
44
 *          array('name'=>'Joe','password'=>'demo'),
45
 *          array('name'=>'John','password'=>'demo'),
46
 *       ),
47
 *       'roles' => array(
48
 *          array('name'=>'Administrator','users'=>'John'),
49
 *          array('name'=>'Writer','users'=>'Joe,John'),
50
 *       ),
51
 *    ),
52
 * )
53
 * </code>
54
 *
55
 * In addition, user information can also be loaded from an external file
56
 * specified by {@link setUserFile UserFile} property. Note, the property
57
 * only accepts a file path in namespace format. The user file format is
58
 * similar to the above sample.
59
 *
60
 * The user passwords may be specified as clear text, SH1 or MD5 hashed by setting
61
 * {@link setPasswordMode PasswordMode} as <b>Clear</b>, <b>SHA1</b> or <b>MD5</b>.
62
 * The default name for a guest user is <b>Guest</b>. It may be changed
63
 * by setting {@link setGuestName GuestName} property.
64
 *
65
 * TUserManager may be used together with {@link TAuthManager} which manages
66
 * how users are authenticated and authorized in a Prado application.
67
 *
68
 * @author Qiang Xue <[email protected]>
69
 * @author Carl Mathisen <[email protected]>
70
 * @package Prado\Security
71
 * @since 3.0
72
 */
73
class TUserManager extends \Prado\TModule implements IUserManager
74
{
75
	/**
76
	 * extension name to the user file
77
	 */
78
	const USER_FILE_EXT = '.xml';
79
80
	/**
81
	 * @var array list of users managed by this module
82
	 */
83
	private $_users = [];
84
	/**
85
	 * @var array list of roles managed by this module
86
	 */
87
	private $_roles = [];
88
	/**
89
	 * @var string guest name
90
	 */
91
	private $_guestName = 'Guest';
92
	/**
93
	 * @var TUserManagerPasswordMode password mode
94
	 */
95
	private $_passwordMode = TUserManagerPasswordMode::MD5;
96
	/**
97
	 * @var boolean whether the module has been initialized
98
	 */
99
	private $_initialized = false;
100
	/**
101
	 * @var string user/role information file
102
	 */
103
	private $_userFile;
104
105
	/**
106
	 * Initializes the module.
0 ignored issues
show
Bug introduced by
The type Prado\Security\module was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
107
	 * This method is required by IModule and is invoked by application.
108
	 * It loads user/role information from the module configuration.
109
	 * @param mixed module configuration
110
	 */
111
	public function init($config)
112
	{
113
		$this->loadUserData($config);
114
		if($this->_userFile !== null)
0 ignored issues
show
introduced by
The condition $this->_userFile !== null can never be false.
Loading history...
115
		{
116
			if($this->getApplication()->getConfigurationType() == TApplication::CONFIG_TYPE_PHP)
117
			{
118
				$userFile = include $this->_userFile;
119
				$this->loadUserDataFromPhp($userFile);
120
			}
121
			else
122
			{
123
				$dom = new TXmlDocument;
124
				$dom->loadFromFile($this->_userFile);
125
				$this->loadUserDataFromXml($dom);
126
			}
127
		}
128
		$this->_initialized = true;
129
	}
130
131
	/*
132
	 * Loads user/role information
133
	 * @param mixed the variable containing the user information
0 ignored issues
show
Bug introduced by
The type Prado\Security\the was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
134
	 */
135
	private function loadUserData($config)
136
	{
137
		if($this->getApplication()->getConfigurationType() == TApplication::CONFIG_TYPE_PHP)
138
			$this->loadUserDataFromPhp($config);
139
		else
140
			$this->loadUserDataFromXml($config);
141
	}
142
143
	/**
144
	 * Loads user/role information from an php array.
145
	 * @param array the array containing the user information
146
	 */
147
	private function loadUserDataFromPhp($config)
148
	{
149
		if(isset($config['users']) && is_array($config['users']))
150
		{
151
			foreach($config['users'] as $user)
152
			{
153
				$name = trim(strtolower(isset($user['name'])?$user['name']:''));
154
				$password = isset($user['password'])?$user['password']:'';
155
				$this->_users[$name] = $password;
156
				$roles = isset($user['roles'])?$user['roles']:'';
157
				if($roles !== '')
158
				{
159
					foreach(explode(',', $roles) as $role)
160
					{
161
						if(($role = trim($role)) !== '')
162
							$this->_roles[$name][] = $role;
163
					}
164
				}
165
			}
166
		}
167
		if(isset($config['roles']) && is_array($config['roles']))
168
		{
169
			foreach($config['roles'] as $role)
170
			{
171
				$name = isset($role['name'])?$role['name']:'';
172
				$users = isset($role['users'])?$role['users']:'';
173
				foreach(explode(',', $users) as $user)
174
				{
175
					if(($user = trim($user)) !== '')
176
						$this->_roles[strtolower($user)][] = $name;
177
				}
178
			}
179
		}
180
	}
181
182
	/**
183
	 * Loads user/role information from an XML node.
184
	 * @param TXmlElement the XML node containing the user information
185
	 */
186
	private function loadUserDataFromXml($xmlNode)
187
	{
188
		foreach($xmlNode->getElementsByTagName('user') as $node)
189
		{
190
			$name = trim(strtolower($node->getAttribute('name')));
191
			$this->_users[$name] = $node->getAttribute('password');
192
			if(($roles = trim($node->getAttribute('roles'))) !== '')
193
			{
194
				foreach(explode(',', $roles) as $role)
195
				{
196
					if(($role = trim($role)) !== '')
197
						$this->_roles[$name][] = $role;
198
				}
199
			}
200
		}
201
		foreach($xmlNode->getElementsByTagName('role') as $node)
202
		{
203
			foreach(explode(',', $node->getAttribute('users')) as $user)
204
			{
205
				if(($user = trim($user)) !== '')
206
					$this->_roles[strtolower($user)][] = $node->getAttribute('name');
207
			}
208
		}
209
	}
210
211
	/**
212
	 * Returns an array of all users.
213
	 * Each array element represents a single user.
214
	 * The array key is the username in lower case, and the array value is the
215
	 * corresponding user password.
216
	 * @return array list of users
217
	 */
218
	public function getUsers()
219
	{
220
		return $this->_users;
221
	}
222
223
	/**
224
	 * Returns an array of user role information.
225
	 * Each array element represents the roles for a single user.
226
	 * The array key is the username in lower case, and the array value is
227
	 * the roles (represented as an array) that the user is in.
228
	 * @return array list of user role information
229
	 */
230
	public function getRoles()
231
	{
232
		return $this->_roles;
233
	}
234
235
	/**
236
	 * @return string the full path to the file storing user/role information
237
	 */
238
	public function getUserFile()
239
	{
240
		return $this->_userFile;
241
	}
242
243
	/**
244
	 * @param string user/role data file path (in namespace form). The file format is XML
245
	 * whose content is similar to that user/role block in application configuration.
246
	 * @throws TInvalidOperationException if the module is already initialized
247
	 * @throws TConfigurationException if the file is not in proper namespace format
248
	 */
0 ignored issues
show
Documentation Bug introduced by
The doc comment user/role at position 0 could not be parsed: Unknown type name 'user/role' at position 0 in user/role.
Loading history...
249
	public function setUserFile($value)
250
	{
251
		if($this->_initialized)
252
			throw new TInvalidOperationException('usermanager_userfile_unchangeable');
253
		elseif(($this->_userFile = Prado::getPathOfNamespace($value, self::USER_FILE_EXT)) === null || !is_file($this->_userFile))
254
			throw new TConfigurationException('usermanager_userfile_invalid', $value);
255
	}
256
257
	/**
258
	 * @return string guest name, defaults to 'Guest'
259
	 */
260
	public function getGuestName()
261
	{
262
		return $this->_guestName;
263
	}
264
265
	/**
266
	 * @param string name to be used for guest users.
0 ignored issues
show
Bug introduced by
The type Prado\Security\name was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
267
	 */
268
	public function setGuestName($value)
269
	{
270
		$this->_guestName = $value;
271
	}
272
273
	/**
274
	 * @return TUserManagerPasswordMode how password is stored, clear text, or MD5 or SHA1 hashed. Default to TUserManagerPasswordMode::MD5.
275
	 */
276
	public function getPasswordMode()
277
	{
278
		return $this->_passwordMode;
279
	}
280
281
	/**
282
	 * @param TUserManagerPasswordMode how password is stored, clear text, or MD5 or SHA1 hashed.
0 ignored issues
show
Bug introduced by
The type Prado\Security\how was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
283
	 */
284
	public function setPasswordMode($value)
285
	{
286
		$this->_passwordMode = TPropertyValue::ensureEnum($value, 'Prado\\Security\\TUserManagerPasswordMode');
0 ignored issues
show
Documentation Bug introduced by
It seems like Prado\TPropertyValue::en...erManagerPasswordMode') of type string is incompatible with the declared type Prado\Security\TUserManagerPasswordMode of property $_passwordMode.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
287
	}
288
289
	/**
290
	 * Validates if the username and password are correct.
0 ignored issues
show
Bug introduced by
The type Prado\Security\password was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
291
	 * @param string user name
292
	 * @param string password
293
	 * @return boolean true if validation is successful, false otherwise.
294
	 */
295
	public function validateUser($username, $password)
296
	{
297
		if($this->_passwordMode === TUserManagerPasswordMode::MD5)
0 ignored issues
show
introduced by
The condition $this->_passwordMode ===...anagerPasswordMode::MD5 can never be true.
Loading history...
298
			$password = md5($password);
299
		elseif($this->_passwordMode === TUserManagerPasswordMode::SHA1)
0 ignored issues
show
introduced by
The condition $this->_passwordMode ===...nagerPasswordMode::SHA1 can never be true.
Loading history...
300
			$password = sha1($password);
301
		$username = strtolower($username);
302
		return (isset($this->_users[$username]) && $this->_users[$username] === $password);
303
	}
304
305
	/**
306
	 * Returns a user instance given the user name.
0 ignored issues
show
Bug introduced by
The type Prado\Security\user was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
307
	 * @param string user name, null if it is a guest.
308
	 * @return TUser the user instance, null if the specified username is not in the user database.
309
	 */
310
	public function getUser($username = null)
311
	{
312
		if($username === null)
313
		{
314
			$user = new TUser($this);
315
			$user->setIsGuest(true);
316
			return $user;
317
		}
318
		else
319
		{
320
			$username = strtolower($username);
321
			if(isset($this->_users[$username]))
322
			{
323
				$user = new TUser($this);
324
				$user->setName($username);
325
				$user->setIsGuest(false);
326
				if(isset($this->_roles[$username]))
327
					$user->setRoles($this->_roles[$username]);
328
				return $user;
329
			}
330
			else
331
				return null;
332
		}
333
	}
334
335
	/**
336
	 * Returns a user instance according to auth data stored in a cookie.
337
	 * @param THttpCookie the cookie storing user authentication information
338
	 * @return TUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data.
339
	 * @since 3.1.1
340
	 */
341
	public function getUserFromCookie($cookie)
342
	{
343
		if(($data = $cookie->getValue()) !== '')
344
		{
345
			$data = unserialize($data);
346
			if(is_array($data) && count($data) === 2)
347
			{
348
				list($username, $token) = $data;
349
				if(isset($this->_users[$username]) && $token === md5($username . $this->_users[$username]))
350
					return $this->getUser($username);
351
			}
352
		}
353
		return null;
354
	}
355
356
	/**
357
	 * Saves user auth data into a cookie.
358
	 * @param THttpCookie the cookie to receive the user auth data.
359
	 * @since 3.1.1
360
	 */
361
	public function saveUserToCookie($cookie)
362
	{
363
		$user = $this->getApplication()->getUser();
364
		$username = strtolower($user->getName());
365
		if(isset($this->_users[$username]))
366
		{
367
			$data = [$username,md5($username . $this->_users[$username])];
368
			$cookie->setValue(serialize($data));
369
		}
370
	}
371
372
	/**
373
	 * Sets a user as a guest.
374
	 * User name is changed as guest name, and roles are emptied.
375
	 * @param TUser the user to be changed to a guest.
376
	 */
377
	public function switchToGuest($user)
378
	{
379
		$user->setIsGuest(true);
380
	}
381
}