PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.1.0
WP 2FA – Two-factor authentication for WordPress v2.1.0
1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Utils / UserUtils.php
wp-2fa / includes / classes / Utils Last commit date
AbstractMigration.php 4 years ago DateTimeUtils.php 4 years ago Debugging.php 5 years ago GenerateModal.php 4 years ago Migration.php 4 years ago RequestUtils.php 4 years ago SettingsUtils.php 4 years ago UserUtils.php 4 years ago index.php 5 years ago
UserUtils.php
405 lines
1 <?php
2 namespace WP2FA\Utils;
3
4 use WP2FA\Admin\User;
5 use WP2FA\WP2FA as WP2FA;
6 use WP2FA\Admin\Controllers\Settings;
7 use \WP2FA\Authenticator\BackupCodes as BackupCodes;
8
9 /**
10 * Utility class for creating modal popup markup.
11 *
12 * @package WP2FA\Utils
13 * @since 1.4.2
14 */
15 class UserUtils {
16
17 /**
18 * Holds map with human readable 2FA statuses
19 *
20 * @var array
21 */
22 private static $statuses;
23
24 public static function determine_user_2fa_status( $user ) {
25
26 // Get current user, we going to need this regardless.
27 $current_user = wp_get_current_user();
28
29 // Bail if we still dont have an object.
30 if ( ! is_a( $user, '\WP_User' ) || ! is_a( $current_user, '\WP_User' ) ) {
31 return [];
32 }
33
34 $roles = (array) $user->roles;
35
36 // Grab grace period UNIX time.
37 $grace_period_expired = get_user_meta( $user->ID, WP_2FA_PREFIX . 'user_grace_period_expired', true );
38 $is_user_excluded = User::is_excluded( $user->ID );
39 $isUserEnforced = User::is_enforced( $user->ID );
40 $isUserLocked = User::isUserLocked( $user->ID );
41 $user_last_login = get_user_meta( $user->ID, WP_2FA_PREFIX . 'login_date', true );
42
43 // First lets see if the user already has a token.
44 $enabled_methods = get_user_meta( $user->ID, WP_2FA_PREFIX . 'enabled_methods', true );
45
46 $noEnforcedMethods = false;
47 if ( 'do-not-enforce' === WP2FA::get_wp2fa_setting( 'enforcement-policy' ) ) {
48 $noEnforcedMethods = true;
49 }
50
51 $user_type = array();
52
53 if ( empty( $roles ) ) {
54 $user_type[] = 'orphan_user'; // User has no role.
55 }
56
57 if ( current_user_can( 'manage_options' ) ) {
58 $user_type[] = 'can_manage_options';
59 }
60
61 if ( current_user_can( 'read' ) ) {
62 $user_type[] = 'can_read';
63 }
64
65 if ( $grace_period_expired ) {
66 $user_type[] = 'grace_has_expired';
67 }
68
69 if ( $current_user->ID === $user->ID ) {
70 $user_type[] = 'viewing_own_profile';
71 }
72
73 if ( ! empty( $enabled_methods ) ) {
74 $user_type[] = 'has_enabled_methods';
75 }
76
77 if ( $noEnforcedMethods && ! empty( $enabled_methods ) ) {
78 $user_type[] = 'no_required_has_enabled';
79 }
80
81 if ( $noEnforcedMethods && empty( $enabled_methods ) && ! $is_user_excluded ) {
82 if ( empty( $user_last_login ) ) {
83 $user_type[] = 'no_determined_yet';
84 } else {
85 $user_type[] = 'no_required_not_enabled';
86 }
87 }
88
89 if ( ! $noEnforcedMethods && empty( $enabled_methods ) && ! $is_user_excluded && $isUserEnforced ) {
90 $user_type[] = 'user_needs_to_setup_2fa';
91 }
92
93 if ( ! $noEnforcedMethods && empty( $enabled_methods ) && ! $is_user_excluded && ! $isUserEnforced ) {
94 if ( empty( $user_last_login ) ) {
95 $user_type[] = 'no_determined_yet';
96 } else {
97 $user_type[] = 'no_required_not_enabled';
98 }
99 }
100
101 if ( $is_user_excluded ) {
102 $user_type[] = 'user_is_excluded';
103 }
104
105 if ( $isUserLocked ) {
106 $user_type[] = 'user_is_locked';
107 }
108
109 $codes_remaining = BackupCodes::codes_remaining_for_user( $user );
110 if ( 0 === $codes_remaining ) {
111 $user_type[] = 'user_needs_to_setup_backup_codes';
112 }
113
114 return apply_filters( 'wp_2fa_additional_user_types', $user_type, $user );
115 }
116
117 public static function in_array_all( $needles, $haystack ) {
118 return empty( array_diff( $needles, $haystack ) );
119 }
120
121 /**
122 * Check if role is not in given array of roles
123 *
124 * @param array $roles
125 * @param array $userRoles
126 *
127 * @return bool
128 */
129 public static function roleIsNot( $roles, $userRoles ) {
130 if (
131 empty(
132 array_intersect(
133 $roles,
134 $userRoles
135 )
136 )
137 ) {
138 return true;
139 }
140
141 return false;
142 }
143
144 /**
145 * Works our a list of available 2FA methods. It doesn't include the disabled ones.
146 *
147 * @return string[]
148 * @since 2.0.0
149 */
150 public static function get_available_2fa_methods(): array {
151 $available_methods = array();
152
153 if ( ! empty( Settings::get_role_or_default_setting( 'enable_email', 'current' ) ) ) {
154 $available_methods[] = 'email';
155 }
156
157 if ( ! empty( Settings::get_role_or_default_setting( 'enable_totp', 'current' ) ) ) {
158 $available_methods[] = 'totp';
159 }
160
161 /**
162 * Add an option for external providers to implement their own 2fa methods and set them as available.
163 *
164 * @param array $available_methods - The array with all the available methods.
165 *
166 * @since 2.0.0
167 */
168 return apply_filters( 'wp_2fa_available_2fa_methods', $available_methods );
169 }
170
171 /**
172 * Return all users, either by using a direct query or get_users.
173 *
174 * @param string $method Method to use.
175 * @param array $users_args Query arguments.
176 *
177 * @return mixed Array of IDs/Object of Users.
178 */
179 public static function get_all_users_data( $method, $users_args ) {
180
181 if ( 'get_users' === $method ) {
182 return get_users( $users_args );
183 }
184
185 // method is "query", let's build the SQL query ourselves
186 global $wpdb;
187
188 $batch_size = isset( $users_args['batch_size'] ) ? $users_args['batch_size'] : false;
189 $offset = isset( $users_args['count'] ) ? $users_args['count'] * $batch_size : false;
190
191 // Default.
192 $select = 'SELECT ID, user_login FROM ' . $wpdb->users . '';
193
194 // If we want to grab users with a specific role.
195 if ( isset( $users_args['role__in'] ) && ! empty( $users_args['role__in'] ) ) {
196 $roles = $users_args['role__in'];
197 $select = '
198 SELECT ID, user_login
199 FROM ' . $wpdb->users . ' u INNER JOIN ' . $wpdb->usermeta . ' um
200 ON u.ID = um.user_id
201 WHERE um.meta_key LIKE \'' . $wpdb->base_prefix . '%capabilities' . '\'
202 AND (
203 ';
204 $i = 1;
205 foreach ( $roles as $role ) {
206 $select .= ' um.meta_value LIKE \'%"' . $role . '"%\' ';
207 if ( $i < count( $roles ) ) {
208 $select .= ' OR ';
209 }
210 $i ++;
211 }
212 $select .= ' ) ';
213
214 $excluded_users = ( ! empty( $users_args['excluded_users'] ) ) ? $users_args['excluded_users'] : [];
215
216 $excluded_users = array_map( function ( $excluded_user ) {
217 return '"' . $excluded_user . '"';
218 }, $excluded_users );
219
220 if ( ! empty( $excluded_users ) ) {
221 $select .= '
222 AND user_login NOT IN ( ' . implode( ',', $excluded_users ) . ' )
223 ';
224 }
225
226 $skip_existing_2fa_users = ( ! empty( $users_args['skip_existing_2fa_users'] ) ) ? $users_args['skip_existing_2fa_users'] : false;
227
228 if ( $skip_existing_2fa_users ) {
229 $select .= '
230 AND u.ID NOT IN (
231 SELECT DISTINCT user_id FROM ' . $wpdb->usermeta . ' WHERE meta_key = \'wp_2fa_enabled_methods\'
232 )
233 ';
234 }
235
236 }
237
238 if ( $batch_size ) {
239 $select .= ' LIMIT ' . $batch_size . ' OFFSET ' . $offset . '';
240 }
241
242 return $wpdb->get_results( $select );
243 }
244
245 /**
246 * Get list of IDs only if they have a specific 2FA method enabled.
247 *
248 * @param string $removing Method to search for.
249 * @param $users_args
250 *
251 * @return array User details.
252 */
253 public static function get_all_user_ids_based_on_enabled_2fa_method( $removing, $users_args ) {
254
255 global $wpdb;
256
257 $batch_size = isset( $users_args['batch_size'] ) ? $users_args['batch_size'] : false;
258 $offset = isset( $users_args['count'] ) ? $users_args['count'] * $batch_size : false;
259
260 $select = '
261 SELECT ID FROM ' . $wpdb->users . '
262 INNER JOIN ' . $wpdb->usermeta . ' ON ' . $wpdb->users . '.ID = ' . $wpdb->usermeta . '.user_id
263 WHERE ' . $wpdb->usermeta . '.meta_key = \'wp_2fa_enabled_methods\'
264 AND ' . $wpdb->usermeta . '.meta_value = \'' . $removing . '\'
265 ';
266
267 if ( $batch_size ) {
268 $select .= '
269 LIMIT ' . $batch_size . ' OFFSET ' . $offset . '
270 ';
271 }
272
273 $users = $wpdb->get_results( $select );
274
275 $users = array_map( function ( $user ) {
276 return (int) $user->ID;
277 }, $users );
278
279 $users = implode( ',', $users );
280
281 return $users;
282 }
283
284 public static function get_all_user_ids_who_have_wp_2fa_metadata_present( $users_args ) {
285
286 global $wpdb;
287
288 $batch_size = isset( $users_args['batch_size'] ) ? $users_args['batch_size'] : false;
289 $offset = isset( $users_args['count'] ) ? $users_args['count'] * $batch_size : false;
290
291 $select = '
292 SELECT ID FROM ' . $wpdb->users . '
293 INNER JOIN ' . $wpdb->usermeta . ' ON ' . $wpdb->users . '.ID = ' . $wpdb->usermeta . '.user_id
294 WHERE ' . $wpdb->usermeta . '.meta_key LIKE \'wp_2fa_%\'
295 ';
296
297 if ( $batch_size ) {
298 $select .= '
299 LIMIT ' . $batch_size . ' OFFSET ' . $offset . '
300 ';
301 }
302
303 $users = $wpdb->get_results( $select );
304
305 $users = array_map( function ( $user ) {
306 return (int) $user->ID;
307 }, $users );
308
309 $users = implode( ',', $users );
310
311 return $users;
312 }
313
314 /**
315 * Retrieve string of comma seperated IDs.
316 *
317 * @param string $method Method to use.
318 * @param array $users_args Query arguments.
319 *
320 * @return string List of IDs.
321 */
322 public static function get_all_user_ids( $method, $users_args ) {
323 $user_data = UserUtils::get_all_users_data( $method, $users_args );
324
325 $users = array_map( function ( $user ) {
326 return (int) $user->ID;
327 }, $user_data );
328
329 return implode( ',', $users );
330 }
331
332 /**
333 * Retrieve array if user IDs and login names.
334 *
335 * @param string $method Method to use.
336 * @param array $users_args Query arguments.
337 *
338 * @return array User details.
339 */
340 public static function get_all_user_ids_and_login_names( $method, $users_args ) {
341 $user_data = UserUtils::get_all_users_data( $method, $users_args );
342 $user_item = [];
343
344 $users = array_map( function ( $user ) {
345 $user_item['ID'] = (int) $user->ID;
346 $user_item['user_login'] = $user->user_login;
347
348 return $user_item;
349 }, $user_data );
350
351 return $users;
352 }
353
354 /**
355 * Returns the array with human readable statuses of the WP 2FA
356 *
357 * @since 1.6
358 *
359 * @return array
360 */
361 public static function getHumanReadableUserStatuses() {
362 if ( null === self::$statuses ) {
363 self::$statuses =
364 [
365 'has_enabled_methods' => __( 'Configured', 'wp-2fa' ),
366 'user_needs_to_setup_2fa' => __( 'Required but not configured', 'wp-2fa' ),
367 'no_required_has_enabled' => __( 'Configured (but not required)', 'wp-2fa' ),
368 'no_required_not_enabled' => __( 'Not required & not configured', 'wp-2fa' ),
369 'user_is_excluded' => __( 'Not allowed', 'wp-2fa' ),
370 'user_is_locked' => __( 'Locked', 'wp-2fa' ),
371 'no_determined_yet' => __( 'User has not logged in yet, 2FA status is unknown', 'wp-2fa' ),
372 ];
373 }
374
375 return self::$statuses;
376 }
377
378 /**
379 * Gets the user types extracted with @see UserUtils::determine_user_2fa_status,
380 * checks values and generates human readable 2FA status text
381 *
382 * @param array $userTypes
383 *
384 * @return array An array with the id and label elements of user 2FA status. Empty in case there is not match.
385 *
386 * @since 1.7.0 Changed the function to return the id and label of the first match it finds instead of concatenated labels of all matched statuses.
387 */
388 public static function extractStatuses( $userTypes ) {
389 if ( null === self::$statuses ) {
390 self::getHumanReadableUserStatuses();
391 }
392
393 foreach ( self::$statuses as $key => $value ) {
394 if ( in_array( $key, $userTypes ) ) {
395 return [
396 'id'=> $key,
397 'label' => $value
398 ];
399 }
400 }
401
402 return [];
403 }
404 }
405