PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 1.7.0
WP 2FA – Two-factor authentication for WordPress v1.7.0
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 SettingsUtils.php 5 years ago UserUtils.php 5 years ago index.php 5 years ago
UserUtils.php
382 lines
1 <?php
2 namespace WP2FA\Utils;
3
4 use WP2FA\Admin\User;
5 use WP2FA\WP2FA as WP2FA;
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_PREFIX . 'user_grace_period_expired', true );
37 $is_user_excluded = WP2FA::is_user_excluded( $user->ID );
38 $isUserEnforced = WP2FA::isUserEnforced( $user->ID );
39 $isUserLocked = User::isUserLocked( $user->ID );
40
41 // First lets see if the user already has a token.
42 $enabled_methods = get_user_meta( $user->ID, WP_2FA_PREFIX . '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 return apply_filters( 'wp_2fa_additional_user_types', $user_type, $user );
105 }
106
107 public static function in_array_all( $needles, $haystack ) {
108 return empty( array_diff( $needles, $haystack ) );
109 }
110
111 /**
112 * Check if role is not in given array of roles
113 *
114 * @param array $roles
115 * @param array $userRoles
116 *
117 * @return bool
118 */
119 public static function roleIsNot( $roles, $userRoles ) {
120 if (
121 empty(
122 array_intersect(
123 $roles,
124 $userRoles
125 )
126 )
127 ) {
128 return true;
129 }
130
131 return false;
132 }
133
134 public static function get_2fa_methods_available_to_user( $user ) {
135
136 $available_methods = array();
137
138 if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) {
139 $available_methods[] = 'email';
140 }
141
142 if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) ) {
143 $available_methods[] = 'totp';
144 }
145
146 return apply_filters( 'wp_2fa_available_2fa_methods', $available_methods );
147 }
148
149 /**
150 * Return all users, either by using a direct query or get_users.
151 *
152 * @param string $method Method to use.
153 * @param array $users_args Query arguments.
154 *
155 * @return mixed Array of IDs/Object of Users.
156 */
157 public static function get_all_users_data( $method, $users_args ) {
158
159 if ( 'get_users' === $method ) {
160 return get_users( $users_args );
161 }
162
163 // method is "query", let's build the SQL query ourselves
164 global $wpdb;
165
166 $batch_size = isset( $users_args['batch_size'] ) ? $users_args['batch_size'] : false;
167 $offset = isset( $users_args['count'] ) ? $users_args['count'] * $batch_size : false;
168
169 // Default.
170 $select = 'SELECT ID, user_login FROM ' . $wpdb->users . '';
171
172 // If we want to grab users with a specific role.
173 if ( isset( $users_args['role__in'] ) && ! empty( $users_args['role__in'] ) ) {
174 $roles = $users_args['role__in'];
175 $select = '
176 SELECT ID, user_login
177 FROM ' . $wpdb->users . ' u INNER JOIN ' . $wpdb->usermeta . ' um
178 ON u.ID = um.user_id
179 WHERE um.meta_key LIKE \'' . $wpdb->base_prefix . '%capabilities' . '\'
180 AND (
181 ';
182 $i = 1;
183 foreach ( $roles as $role ) {
184 $select .= ' um.meta_value LIKE \'%"' . $role . '"%\' ';
185 if ( $i < count( $roles ) ) {
186 $select .= ' OR ';
187 }
188 $i ++;
189 }
190 $select .= ' ) ';
191
192 $excluded_users = ( ! empty( $users_args['excluded_users'] ) ) ? $users_args['excluded_users'] : [];
193
194 $excluded_users = array_map( function ( $excluded_user ) {
195 return '"' . $excluded_user . '"';
196 }, $excluded_users );
197
198 if ( ! empty( $excluded_users ) ) {
199 $select .= '
200 AND user_login NOT IN ( ' . implode( ',', $excluded_users ) . ' )
201 ';
202 }
203
204 $skip_existing_2fa_users = ( ! empty( $users_args['skip_existing_2fa_users'] ) ) ? $users_args['skip_existing_2fa_users'] : false;
205
206 if ( $skip_existing_2fa_users ) {
207 $select .= '
208 AND u.ID NOT IN (
209 SELECT DISTINCT user_id FROM ' . $wpdb->usermeta . ' WHERE meta_key = \'wp_2fa_enabled_methods\'
210 )
211 ';
212 }
213
214 }
215
216 if ( $batch_size ) {
217 $select .= ' LIMIT ' . $batch_size . ' OFFSET ' . $offset . '';
218 }
219
220 return $wpdb->get_results( $select );
221 }
222
223 /**
224 * Get list of IDs only if they have a specific 2FA method enabled.
225 *
226 * @param string $removing Method to search for.
227 * @param $users_args
228 *
229 * @return array User details.
230 */
231 public static function get_all_user_ids_based_on_enabled_2fa_method( $removing, $users_args ) {
232
233 global $wpdb;
234
235 $batch_size = isset( $users_args['batch_size'] ) ? $users_args['batch_size'] : false;
236 $offset = isset( $users_args['count'] ) ? $users_args['count'] * $batch_size : false;
237
238 $select = '
239 SELECT ID FROM ' . $wpdb->users . '
240 INNER JOIN ' . $wpdb->usermeta . ' ON ' . $wpdb->users . '.ID = ' . $wpdb->usermeta . '.user_id
241 WHERE ' . $wpdb->usermeta . '.meta_key = \'wp_2fa_enabled_methods\'
242 AND ' . $wpdb->usermeta . '.meta_value = \'' . $removing . '\'
243 ';
244
245 if ( $batch_size ) {
246 $select .= '
247 LIMIT ' . $batch_size . ' OFFSET ' . $offset . '
248 ';
249 }
250
251 $users = $wpdb->get_results( $select );
252
253 $users = array_map( function ( $user ) {
254 return (int) $user->ID;
255 }, $users );
256
257 $users = implode( ',', $users );
258
259 return $users;
260 }
261
262 public static function get_all_user_ids_who_have_wp_2fa_metadata_present( $users_args ) {
263
264 global $wpdb;
265
266 $batch_size = isset( $users_args['batch_size'] ) ? $users_args['batch_size'] : false;
267 $offset = isset( $users_args['count'] ) ? $users_args['count'] * $batch_size : false;
268
269 $select = '
270 SELECT ID FROM ' . $wpdb->users . '
271 INNER JOIN ' . $wpdb->usermeta . ' ON ' . $wpdb->users . '.ID = ' . $wpdb->usermeta . '.user_id
272 WHERE ' . $wpdb->usermeta . '.meta_key LIKE \'wp_2fa_%\'
273 ';
274
275 if ( $batch_size ) {
276 $select .= '
277 LIMIT ' . $batch_size . ' OFFSET ' . $offset . '
278 ';
279 }
280
281 $users = $wpdb->get_results( $select );
282
283 $users = array_map( function ( $user ) {
284 return (int) $user->ID;
285 }, $users );
286
287 $users = implode( ',', $users );
288
289 return $users;
290 }
291
292 /**
293 * Retrieve string of comma seperated IDs.
294 *
295 * @param string $method Method to use.
296 * @param array $users_args Query arguments.
297 *
298 * @return string List of IDs.
299 */
300 public static function get_all_user_ids( $method, $users_args ) {
301 $user_data = UserUtils::get_all_users_data( $method, $users_args );
302
303 $users = array_map( function ( $user ) {
304 return (int) $user->ID;
305 }, $user_data );
306
307 return implode( ',', $users );
308 }
309
310 /**
311 * Retrieve array if user IDs and login names.
312 *
313 * @param string $method Method to use.
314 * @param array $users_args Query arguments.
315 *
316 * @return array User details.
317 */
318 public static function get_all_user_ids_and_login_names( $method, $users_args ) {
319 $user_data = UserUtils::get_all_users_data( $method, $users_args );
320 $user_item = [];
321
322 $users = array_map( function ( $user ) {
323 $user_item['ID'] = (int) $user->ID;
324 $user_item['user_login'] = $user->user_login;
325
326 return $user_item;
327 }, $user_data );
328
329 return $users;
330 }
331
332 /**
333 * Returns the array with human readable statuses of the WP 2FA
334 *
335 * @since 1.6
336 *
337 * @return array
338 */
339 public static function getHumanReadableUserStatuses() {
340 if ( null === self::$statuses ) {
341 self::$statuses =
342 [
343 'has_enabled_methods' => __( 'Configured', 'wp-2fa' ),
344 'user_needs_to_setup_2fa' => __( 'Required but not configured', 'wp-2fa' ),
345 'no_required_has_enabled' => __( 'Configured (but not required)', 'wp-2fa' ),
346 'no_required_not_enabled' => __( 'Not required & not configured', 'wp-2fa' ),
347 'user_is_excluded' => __( 'Not allowed', 'wp-2fa' ),
348 'user_is_locked' => __( 'Locked', 'wp-2fa' ),
349 ];
350 }
351
352 return self::$statuses;
353 }
354
355 /**
356 * Gets the user types extracted with @see UserUtils::determine_user_2fa_status,
357 * checks values and generates human readable 2FA status text
358 *
359 * @param array $userTypes
360 *
361 * @return array An array with the id and label elements of user 2FA status. Empty in case there is not match.
362 *
363 * @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.
364 */
365 public static function extractStatuses( $userTypes ) {
366 if ( null === self::$statuses ) {
367 self::getHumanReadableUserStatuses();
368 }
369
370 foreach ( self::$statuses as $key => $value ) {
371 if ( in_array( $key, $userTypes ) ) {
372 return [
373 'id'=> $key,
374 'label' => $value
375 ];
376 }
377 }
378
379 return [];
380 }
381 }
382