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