Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

UsersTable::generateInviteCode()   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

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 3
cp 0
crap 2
1
<?php
2
3
namespace Elgg\Database;
4
5
use Elgg\Config as Conf;
6
use Elgg\Database;
7
use ElggUser;
8
use RegistrationException;
9
10
/**
11
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
12
 *
13
 * @access private
14
 *
15
 * @package    Elgg.Core
16
 * @subpackage Database
17
 * @since      1.10.0
18
 */
19
class UsersTable {
20
21
	use \Elgg\TimeUsing;
22
23
	/**
24
	 * @var Conf
25
	 */
26
	protected $config;
27
28
	/**
29
	 * @var Database
30
	 */
31
	protected $db;
32
33
	/**
34
	 * @var MetadataTable
35
	 */
36
	protected $metadata;
37
38
	/**
39
	 * Constructor
40
	 *
41
	 * @param Conf          $config   Config
42
	 * @param Database      $db       Database
43
	 * @param MetadataTable $metadata Metadata table
44
	 */
45 4419
	public function __construct(Conf $config, Database $db, MetadataTable $metadata) {
46 4419
		$this->config = $config;
47 4419
		$this->db = $db;
48 4419
		$this->metadata = $metadata;
49 4419
	}
50
51
	/**
52
	 * Get user by username
53
	 *
54
	 * @param string $username The user's username
55
	 *
56
	 * @return ElggUser|false Depending on success
57
	 */
58 459
	public function getByUsername($username) {
59
60
		// Fixes #6052. Username is frequently sniffed from the path info, which,
61
		// unlike $_GET, is not URL decoded. If the username was not URL encoded,
62
		// this is harmless.
63 459
		$username = rawurldecode($username);
64
65 459
		if (!$username) {
66
			return false;
67
		}
68
69 459
		$entity =_elgg_services()->dataCache->usernames->load($username);
70 459
		if ($entity instanceof ElggUser) {
71 8
			return $entity;
72
		}
73
74 459
		$users = elgg_get_entities([
75 459
			'types' => 'user',
76
			'metadata_name_value_pairs' => [
77
				[
78 459
					'name' => 'username',
79 459
					'value' => $username,
80
				],
81
			],
82 459
			'limit' => 1,
83
		]);
84
		
85 459
		return $users ? $users[0] : false;
86
	}
87
88
	/**
89
	 * Get an array of users from an email address
90
	 *
91
	 * @param string $email Email address
92
	 * @return array
93
	 */
94 162
	public function getByEmail($email) {
95 162
		if (!$email) {
96
			return [];
97
		}
98
		
99 162
		$users = elgg_get_entities([
100 162
			'types' => 'user',
101
			'metadata_name_value_pairs' => [
102
				[
103 162
					'name' => 'email',
104 162
					'value' => $email,
105
				],
106
			],
107 162
			'limit' => 1,
108
		]);
109
110 162
		return $users ? : [];
0 ignored issues
show
Bug Best Practice introduced by Ismayil Khayredinov
The expression return $users ?: array() also could return the type integer which is incompatible with the documented return type array.
Loading history...
111
	}
112
113
	/**
114
	 * Return users (or the number of them) who have been active within a recent period.
115
	 *
116
	 * @param array $options Array of options with keys:
117
	 *
118
	 *   seconds (int)  => Length of period (default 600 = 10min)
119
	 *   limit   (int)  => Limit (default 10)
120
	 *   offset  (int)  => Offset (default 0)
121
	 *   count   (bool) => Return a count instead of users? (default false)
122
	 *
123
	 * @return \ElggUser[]|int
124
	 */
125 1
	public function findActive(array $options = []) {
126
	
127 1
		$options = array_merge([
128 1
			'seconds' => 600,
129 1
			'limit' => $this->config->default_limit,
130 1
		], $options);
131
132
		// cast options we're sending to hook
133 1
		foreach (['seconds', 'limit', 'offset'] as $key) {
134 1
			$options[$key] = (int) $options[$key];
135
		}
136 1
		$options['count'] = (bool) $options['count'];
137
138
		// allow plugins to override
139
		$params = [
140 1
			'seconds' => $options['seconds'],
141 1
			'limit' => $options['limit'],
142 1
			'offset' => $options['offset'],
143 1
			'count' => $options['count'],
144 1
			'options' => $options,
145
		];
146 1
		$data = _elgg_services()->hooks->trigger('find_active_users', 'system', $params, null);
0 ignored issues
show
Bug introduced by Evan Winslow
Are you sure the assignment to $data is correct as _elgg_services()->hooks-...system', $params, null) targeting Elgg\PluginHooksService::trigger() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
147
		// check null because the handler could legitimately return falsey values.
148 1
		if ($data !== null) {
149
			return $data;
150
		}
151
152 1
		$time = $this->getCurrentTime()->getTimestamp() - $options['seconds'];
153 1
		return elgg_get_entities([
0 ignored issues
show
Bug Best Practice introduced by Ismayil Khayredinov
The expression return elgg_get_entities... 'e.last_action desc')) could also return false which is incompatible with the documented return type ElggUser[]|integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
154 1
			'type' => 'user',
155 1
			'limit' => $options['limit'],
156 1
			'offset' => $options['offset'],
157 1
			'count' => $options['count'],
158 1
			'wheres' => ["e.last_action >= {$time}"],
159 1
			'order_by' => "e.last_action desc",
160
		]);
161
	}
162
163
	/**
164
	 * Registers a user, returning false if the username already exists
165
	 *
166
	 * @param string $username              The username of the new user
167
	 * @param string $password              The password
168
	 * @param string $name                  The user's display name
169
	 * @param string $email                 The user's email address
170
	 * @param bool   $allow_multiple_emails Allow the same email address to be
171
	 *                                      registered multiple times?
172
	 * @param string $subtype               Subtype of the user entity
173
	 *
174
	 * @return int|false The new user's GUID; false on failure
175
	 * @throws RegistrationException
176
	 */
177 162
	public function register($username, $password, $name, $email, $allow_multiple_emails = false, $subtype = null) {
178
179
		// no need to trim password
180 162
		$username = trim($username);
181 162
		$name = trim(strip_tags($name));
182 162
		$email = trim($email);
183
184
		// A little sanity checking
185 162
		if (empty($username) || empty($password) || empty($name) || empty($email)) {
186
			return false;
187
		}
188
189
		// Make sure a user with conflicting details hasn't registered and been disabled
190 162
		$access_status = access_get_show_hidden_status();
191 162
		access_show_hidden_entities(true);
192
193 162
		if (!validate_email_address($email)) {
194
			throw new RegistrationException(_elgg_services()->translator->translate('registration:emailnotvalid'));
195
		}
196
197 162
		if (!validate_password($password)) {
198
			throw new RegistrationException(_elgg_services()->translator->translate('registration:passwordnotvalid'));
199
		}
200
201 162
		if (!validate_username($username)) {
202
			throw new RegistrationException(_elgg_services()->translator->translate('registration:usernamenotvalid'));
203
		}
204
205 162
		if (get_user_by_username($username)) {
206
			throw new RegistrationException(_elgg_services()->translator->translate('registration:userexists'));
207
		}
208
209 162
		if ((!$allow_multiple_emails) && (get_user_by_email($email))) {
210
			throw new RegistrationException(_elgg_services()->translator->translate('registration:dupeemail'));
211
		}
212
213 162
		access_show_hidden_entities($access_status);
214
215
		// Create user
216 162
		$constructor = ElggUser::class;
217 162
		if (isset($subtype)) {
218 160
			$class = elgg_get_entity_class('user', $subtype);
219 160
			if ($class && class_exists($class) && is_subclass_of($class, ElggUser::class)) {
220
				$constructor = $class;
221
			}
222
		}
223
224 162
		$user = new $constructor();
225 162
		if (isset($subtype)) {
226 160
			$user->subtype = $subtype;
227
		}
228 162
		$user->username = $username;
229 162
		$user->email = $email;
230 162
		$user->name = $name;
231 162
		$user->access_id = ACCESS_PUBLIC;
232 162
		$user->owner_guid = 0; // Users aren't owned by anyone, even if they are admin created.
233 162
		$user->container_guid = 0; // Users aren't contained by anyone, even if they are admin created.
234 162
		$user->language = _elgg_services()->translator->getCurrentLanguage();
235 162
		if ($user->save() === false) {
236
			return false;
237
		}
238
		
239
		// doing this after save to prevent metadata save notices on unwritable metadata password_hash
240 162
		$user->setPassword($password);
241
242
		// Turn on email notifications by default
243 162
		$user->setNotificationSetting('email', true);
244
	
245 162
		return $user->getGUID();
246
	}
247
248
	/**
249
	 * Generates a unique invite code for a user
250
	 *
251
	 * @param string $username The username of the user sending the invitation
252
	 *
253
	 * @return string Invite code
254
	 * @see validateInviteCode
255
	 */
256
	public function generateInviteCode($username) {
257
		$time = $this->getCurrentTime()->getTimestamp();
258
		return "$time." . _elgg_services()->hmac->getHmac([(int) $time, $username])->getToken();
259
	}
260
261
	/**
262
	 * Validate a user's invite code
263
	 *
264
	 * @param string $username The username
265
	 * @param string $code     The invite code
266
	 *
267
	 * @return bool
268
	 * @see generateInviteCode
269
	 */
270
	public function validateInviteCode($username, $code) {
271
		// validate the format of the token created by ->generateInviteCode()
272
		if (!preg_match('~^(\d+)\.([a-zA-Z0-9\-_]+)$~', $code, $m)) {
273
			return false;
274
		}
275
		$time = $m[1];
276
		$mac = $m[2];
277
278
		return _elgg_services()->hmac->getHmac([(int) $time, $username])->matchesToken($mac);
279
	}
280
}
281