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