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