PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.2.1
WP 2FA – Two-factor authentication for WordPress v2.2.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 / Admin / class-user-profile.php
wp-2fa / includes / classes / Admin Last commit date
Controllers 4 years ago Helpers 4 years ago SettingsPages 4 years ago Views 4 years ago class-help-contact-us.php 4 years ago class-premium-features.php 4 years ago class-settings-page.php 4 years ago class-settingspage.php 4 years ago class-setup-wizard.php 4 years ago class-user-listing.php 4 years ago class-user-notices.php 4 years ago class-user-profile.php 4 years ago class-user-registered.php 4 years ago class-user.php 4 years ago index.php 5 years ago
class-user-profile.php
697 lines
1 <?php
2 /**
3 * Responsible for WP2FA user's profile settings.
4 *
5 * @package wp2fa
6 * @subpackage user-utils
7 * @copyright 2021 WP White Security
8 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
9 * @link https://wordpress.org/plugins/wp-2fa/
10 */
11
12 namespace WP2FA\Admin;
13
14 use \WP2FA\WP2FA as WP2FA;
15 use \WP2FA\Utils\User_Utils as User_Utils;
16 use WP2FA\Utils\Settings_Utils as Settings_Utils;
17 use WP2FA\Utils\Generate_Modal;
18 use WP2FA\Authenticator\Open_SSL;
19 use WP2FA\Authenticator\Backup_Codes;
20 use WP2FA\Authenticator\Authentication;
21 use WP2FA\Admin\Views\Wizard_Steps;
22 use WP2FA\Admin\Helpers\WP_Helper;
23 use WP2FA\Admin\Helpers\User_Helper;
24 use WP2FA\Admin\Controllers\Settings;
25 use WP2FA\Admin\Controllers\Methods;
26
27 /**
28 * User_Profile - Class for handling user things such as profile settings and admin list views.
29 */
30 class User_Profile {
31
32 /**
33 * Add our buttons to the user profile editing screen.
34 *
35 * @param object $user User data.
36 * @param array $additional_args - Array with extra parameters for the method.
37 */
38 public function user_2fa_options( $user, $additional_args = array() ) {
39
40 if ( isset( $_GET['user_id'] ) ) { // phpcs:ignore
41 $user_id = (int) $_GET['user_id']; // phpcs:ignore
42 $user = get_user_by( 'id', $user_id );
43 } else {
44 // Get current user, we're going to need this regardless.
45 $user = wp_get_current_user();
46 }
47
48 if ( ! is_a( $user, '\WP_User' ) ) {
49 return;
50 }
51
52 // Ensure we have something in the settings.
53 if ( empty( Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME ) ) ) {
54 return;
55 }
56
57 $show_preamble = true;
58 if ( isset( $additional_args['show_preamble'] ) ) {
59 $show_preamble = \filter_var( $additional_args['show_preamble'], FILTER_VALIDATE_BOOLEAN );
60 }
61
62 $user_type = User_Utils::determine_user_2fa_status( $user );
63
64 $form_output = '';
65 $form_content = '';
66 $description = esc_html__( 'Add two-factor authentication to strengthen the security of your user account.', 'wp-2fa' );
67 $show_form_table = true;
68 $page_url = ( WP_Helper::is_multisite() ) ? 'index.php' : 'options-general.php';
69
70 // Orphan user (a user with no role or capabilities).
71 if ( in_array( 'orphan_user', $user_type, true ) ) {
72 // We want to use the same form/buttons used in the shortcode.
73 $additional_args['is_shortcode'] = true;
74
75 // Create useful message for admin.
76 if ( User_Utils::in_array_all( array( 'user_needs_to_setup_2fa', 'can_manage_options' ), $user_type ) ) {
77 $description = esc_html__( 'This user is required to setup 2FA but has not yet done so.', 'wp-2fa' );
78 }
79
80 if ( User_Utils::in_array_all( array( 'user_is_excluded', 'can_manage_options' ), $user_type ) ) {
81 $description = esc_html__( 'This user is excluded from configuring 2FA.', 'wp-2fa' );
82 }
83 }
84
85 // Excluded user.
86 if ( in_array( 'user_is_excluded', $user_type, true ) ) {
87 return;
88 }
89
90 // A user viewing their own profile AND has a 2FA method configured.
91 if ( User_Utils::in_array_all( array( 'viewing_own_profile' ), $user_type ) ) {
92 if (
93 User_Utils::in_array_all( array( 'has_enabled_methods' ), $user_type ) ||
94 User_Utils::in_array_all( array( 'no_required_has_enabled' ), $user_type )
95 ) {
96 // Create wizard link based on which 2fa methods are allowed by admin.
97 if ( ! empty( Settings::get_role_or_default_setting( 'enable_totp', $user ) ) && ! empty( Settings::get_role_or_default_setting( 'enable_email', $user ) ) ) {
98 $setup_2fa_url = add_query_arg(
99 array(
100 'page' => 'wp-2fa-setup',
101 'current-step' => 'user_choose_2fa_method',
102 'wizard_type' => 'user_2fa_config',
103 ),
104 admin_url( $page_url )
105 );
106 } else {
107 $setup_2fa_url = add_query_arg(
108 array(
109 'page' => 'wp-2fa-setup',
110 'current-step' => 'reconfigure_method',
111 'wizard_type' => 'user_reconfigure_config',
112 ),
113 admin_url( $page_url )
114 );
115 }
116
117 // Create backup codes URL.
118 $backup_codes_url = add_query_arg(
119 array(
120 'page' => 'wp-2fa-setup',
121 'current-step' => 'backup_codes',
122 'wizard_type' => 'backup_codes_config',
123 ),
124 admin_url( $page_url )
125 );
126
127 $form_content .= '<a href="' . esc_url( $setup_2fa_url ) . '" class="button button-primary">' . esc_html__( 'Change 2FA Settings', 'wp-2fa' ) . '</a>';
128
129 if ( self::can_user_remove_2fa( $user->ID ) ) {
130 $form_content .= '<a href="#" class="button button-primary remove-2fa" onclick="MicroModal.show(\'confirm-remove-2fa\');">' . esc_html__( 'Remove 2FA', 'wp-2fa' ) . '</a>';
131 }
132
133 $form_content .= '<br /><br />';
134
135 if ( Settings_Page::are_backup_codes_enabled( User_Helper::get_user_role( $user ) ) ) {
136 $form_content .= '<a href="' . esc_url( $backup_codes_url ) . '" class="button button-primary">' . esc_html__( 'Generate list of Backup Codes', 'wp-2fa' ) . '</a>';
137
138 $codes_remaining = Backup_Codes::codes_remaining_for_user( $user );
139 if ( $codes_remaining > 0 ) {
140 $form_content .= '<span class="description mt-5px">' . esc_attr( (int) $codes_remaining ) . ' ' . esc_html__( 'unused backup codes remaining.', 'wp-2fa' ) . '</span>';
141 } elseif ( 0 === $codes_remaining ) {
142 $form_content .= '<a class="learn_more_link" href="https://www.wpwhitesecurity.com/2fa-backup-codes/?utm_source=plugin&utm_medium=referral&utm_campaign=WP2FA&utm_content=settings+pages" target="_blank">' . esc_html__( 'Learn more about backup codes', 'wp-2fa' ) . '</a>';
143 }
144 }
145
146 if ( isset( $additional_args['is_shortcode'] ) && $additional_args['is_shortcode'] ) {
147 $form_content = '<a href="#" class="button button-primary remove-2fa" data-open-configure-2fa-wizard>' . esc_html__( 'Change 2FA settings', 'wp-2fa' ) . '</a>';
148
149 if ( self::can_user_remove_2fa( $user->ID ) ) {
150 $form_content .= '<a href="#" class="button button-primary remove-2fa" onclick="MicroModal.show(\'confirm-remove-2fa\');">' . esc_html__( 'Remove 2FA', 'wp-2fa' ) . '</a>';
151 }
152 if ( Settings_Page::are_backup_codes_enabled( User_Helper::get_user_role( $user ) ) ) {
153 $form_content .= '</td><tr><th class="backup-methods-label">';
154 $codes_remaining = Backup_Codes::codes_remaining_for_user( $user );
155 if ( $codes_remaining > 0 ) {
156 $backup_codes_desc = '<span class="description mt-5px">' . esc_attr( (int) $codes_remaining ) . ' ' . esc_html__( 'unused backup codes remaining.', 'wp-2fa' ) . '</span>';
157 } elseif ( 0 === $codes_remaining ) {
158 $backup_codes_desc = '<a class="learn_more_link" href="https://www.wpwhitesecurity.com/2fa-backup-codes/?utm_source=plugin&utm_medium=referral&utm_campaign=WP2FA&utm_content=settings+pages" target="_blank">' . esc_html__( 'Learn more about backup codes', 'wp-2fa' ) . '</a>';
159 }
160
161 $form_content .= Wizard_Steps::get_generate_codes_link() . $backup_codes_desc;
162
163 /**
164 * Add an option for external providers to add their own user form buttons.
165 *
166 * @since 2.0.0
167 */
168 $form_content = apply_filters( WP_2FA_PREFIX . 'additional_form_buttons', $form_content );
169
170 $form_content .= '</th></tr>';
171 }
172 }
173 }
174
175 $show_if_user_is_not_in = array(
176 'user_is_excluded',
177 'has_enabled_methods',
178 'no_required_has_enabled',
179 );
180
181 // User viewing own profile and needs to enable 2FA.
182 if (
183 User_Utils::in_array_all( array( 'user_needs_to_setup_2fa' ), $user_type ) ||
184 User_Utils::role_is_not( $show_if_user_is_not_in, $user_type )
185 ) {
186 $first_time_setup_url = Settings::get_setup_page_link();
187
188 if ( isset( $additional_args['is_shortcode'] ) && $additional_args['is_shortcode'] ) {
189 $form_content .= '<a href="#" class="button button-primary" data-open-configure-2fa-wizard>' . esc_html__( 'Configure 2FA', 'wp-2fa' ) . '</a>';
190 }
191
192 if ( empty( $additional_args ) ) {
193 $form_content .= '<a href="' . esc_url( $first_time_setup_url ) . '" class="button button-primary">' . esc_html__( 'Configure Two-factor authentication (2FA)', 'wp-2fa' ) . '</a>';
194 }
195 }
196 }
197
198 // Admin viewing users profile AND user has a configured 2FA method.
199 if ( User_Utils::in_array_all( array( 'can_manage_options', 'has_enabled_methods' ), $user_type ) && ! in_array( 'viewing_own_profile', $user_type, true ) ) {
200 $description = esc_html__( 'The user has already configured 2FA. When you reset the user\'s current 2FA configuration, the user can log back in with just the username and password.', 'wp-2fa' );
201
202 $remove_users_2fa_url = add_query_arg(
203 array(
204 'action' => 'remove_user_2fa',
205 'user_id' => $user->ID,
206 'wp_2fa_nonce' => wp_create_nonce( 'wp-2fa-remove-user-2fa-nonce' ),
207 'admin_reset' => 'yes',
208 ),
209 admin_url( 'user-edit.php' )
210 );
211
212 $form_content .= '<a href="' . esc_url( $remove_users_2fa_url ) . '" class="button button-primary">' . esc_html__( 'Reset 2FA configuration', 'wp-2fa' ) . '</a>';
213 }
214
215 // Admin viewing users profile AND users grace period has expired.
216 if ( User_Utils::in_array_all( array( 'can_manage_options', 'grace_has_expired' ), $user_type ) ) {
217 $unlock_user_url = add_query_arg(
218 array(
219 'action' => 'unlock_account',
220 'user_id' => $user->ID,
221 'wp_2fa_nonce' => wp_create_nonce( 'wp-2fa-unlock-account-nonce' ),
222 ),
223 admin_url( 'user-edit.php' )
224 );
225 $form_content .= '<a href="' . esc_url( $unlock_user_url ) . '" class="button button-primary">' . esc_html__( 'Unlock user and reset the grace period', 'wp-2fa' ) . '</a>';
226 }
227
228 if ( $show_preamble ) {
229 $form_output .= '<h2>' . esc_html__( 'Two-factor authentication settings', 'wp-2fa' ) . '</h2>';
230
231 if ( $description ) {
232 $form_output .= '<p class="description">' . $description . '</p>';
233 }
234 }
235 /**
236 * Gives the ability to add more content to the profile page.
237 *
238 * @param string $form_content - The parsed HTML of the form.
239 */
240 $form_content = apply_filters( WP_2FA_PREFIX . 'append_to_profile_form_content', $form_content );
241
242 if ( $show_form_table && ! empty( $form_content ) ) {
243 $form_output .= '
244 <table class="form-table wp-2fa-user-profile-form" role="presentation">
245 <tbody>
246 <tr>
247 <th><label>' . esc_html__( '2FA Setup:', 'wp-2fa' ) . '</label></th>
248 <td>
249 ' . $form_content . '
250 </td>
251 </tr>
252 </tbody>
253 </table>';
254
255 if ( ( isset( $_GET['show'] ) && 'wp-2fa-setup' === $_GET['show'] ) || User_Helper::get_user_enforced_instantly( $user ) ) { // phpcs:ignore
256 $form_output .= '
257 <script>
258 window.addEventListener("load", function() {
259 wp2fa_fireWizard();
260 });
261 </script>
262 ';
263 }
264 }
265
266 echo $form_output; // phpcs:ignore
267
268 $this->generate_inline_modals( $user_type );
269 }
270
271 /**
272 * Responsible for the building of all the modals.
273 *
274 * @param array $user_type - The WP user type.
275 *
276 * @return void
277 */
278 public function generate_inline_modals( $user_type = array() ) {
279
280 ob_start();
281
282 $user = wp_get_current_user();
283
284 if ( User_Utils::in_array_all( array( 'user_needs_to_setup_2fa', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( 'has_enabled_methods', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( 'no_required_not_enabled', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( 'no_determined_yet', 'viewing_own_profile' ), $user_type ) ) { ?>
285 <div>
286 <div class="wp2fa-modal micromodal-slide" id="configure-2fa" aria-hidden="true">
287 <div class="modal__overlay" tabindex="-1" data-micromodal-close>
288 <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
289 <?php
290 echo Generate_Modal::generate_modal( // phpcs:ignore
291 'notify-users',
292 __( 'Are you sure?', 'wp-2fa' ),
293 __( 'Any unsaved changes will be lost!', 'wp-2fa' ),
294 array(
295 '<button class="modal__btn button-confirm" aria-label="Close this dialog window and the wizard">' . esc_html__( 'Yes', 'wp-2fa' ) . '</button>',
296 '<button class="modal__btn button-decline" data-micromodal-close aria-label="Close this dialog window">' . esc_html__( 'No', 'wp-2fa' ) . '</button>',
297 ),
298 '',
299 '430px'
300 );
301 ?>
302 <button class="modal__close" aria-label="Close modal"></button>
303 <main class="modal__content wp2fa-form-styles" id="modal-1-content">
304 <?php
305 $logo_section = '<p style="text-align: center; padding:0; margin: 0;"><img style="filter: invert(76.4%); width: 50px; margin: 0 auto;" src="' . WP_2FA_URL . 'dist/images/wp-2fa-white-icon20x28.svg" /></p>';
306
307 /**
308 * Filter to change the logo of the plugin login forms.
309 *
310 * @param string $logo - The parsed logo HTML.
311 *
312 * @since 2.0.0
313 */
314 $logo_section = apply_filters( WP_2FA_PREFIX . 'plugin_logo_wizard', $logo_section );
315
316 echo $logo_section; // phpcs:ignore
317
318 if ( User_Utils::in_array_all( array( 'user_needs_to_setup_2fa', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( 'no_required_not_enabled', 'viewing_own_profile' ), $user_type ) || User_Utils::in_array_all( array( 'no_determined_yet', 'viewing_own_profile' ), $user_type ) ) {
319
320 $available_methods = Methods::get_enabled_methods( User_Helper::get_user_role( $user ) );
321
322 $intro_text = esc_html__( 'Choose the 2FA authentication method', 'wp-2fa' );
323 if ( count( $available_methods[ User_Helper::get_user_role( $user ) ] ) > 1 ) {
324 $sub_text = Methods::get_number_of_methods_text( User_Helper::get_user_role( $user ) );
325 } else {
326 $sub_text = esc_html__( 'Only the below 2FA method is allowed on this website:', 'wp-2fa' );
327 }
328 ?>
329
330 <div class="wizard-step active">
331 <h3><?php echo esc_html( sanitize_text_field( $intro_text ) ); ?></h3>
332 <p><?php echo esc_html( sanitize_text_field( $sub_text ) ); ?></p>
333 <fieldset>
334 <?php Wizard_Steps::totp_option(); ?>
335 <?php Wizard_Steps::email_option(); ?>
336
337 <?php
338 /**
339 * Add an option for external providers to add their own 2fa methods options.
340 *
341 * @since 2.0.0
342 */
343 do_action( WP_2FA_PREFIX . 'methods_options' );
344 ?>
345 </fieldset>
346 <br>
347 <a href="#" class="modal__btn button button-primary 2fa-choose-method" data-name="next_step_setting_modal_wizard" data-next-step><?php esc_html_e( 'Next Step', 'wp-2fa' ); ?></a>
348 <button class="modal__btn button" data-close-2fa-modal aria-label="Close this dialog window"><?php esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
349 </div>
350 <?php } ?>
351
352 <?php if ( User_Utils::in_array_all( array( 'has_enabled_methods', 'viewing_own_profile' ), $user_type ) ) { ?>
353 <div class="wizard-step active">
354 <fieldset>
355 <?php Wizard_Steps::totp_re_configure(); ?>
356 <?php Wizard_Steps::email_re_configure(); ?>
357 <?php
358 /**
359 * Add an option for external providers to add their own reconfigure methods options.
360 *
361 * @since 2.0.0
362 */
363 do_action( WP_2FA_PREFIX . 'methods_reconfigure_options' );
364 ?>
365 </fieldset>
366 </div>
367 <?php } ?>
368
369 <?php Wizard_Steps::show_modal_methods(); ?>
370 <?php
371
372 $backup_methods = Settings::get_enabled_backup_methods_for_user_role( $user );
373
374 if ( count( $backup_methods ) > 1 ) {
375 Wizard_Steps::choose_backup_method();
376 }
377
378 /**
379 * Add an option for external providers to add their own wizard steps.
380 *
381 * @since 2.0.0
382 */
383 do_action( WP_2FA_PREFIX . 'additional_settings_steps' );
384
385 // Create a nonce for use in ajax call to generate codes.
386 if ( Settings_Page::are_backup_codes_enabled( User_Helper::get_user_role( $user ) ) ) {
387 ?>
388 <div class="wizard-step" id="2fa-wizard-config-backup-codes">
389 <?php Wizard_Steps::backup_codes_configure(); ?>
390 <?php Wizard_Steps::generated_backup_codes(); ?>
391 </div>
392 <?php } else { ?>
393 <div class="wizard-step" id="2fa-wizard-config-backup-codes">
394 <?php Wizard_Steps::congratulations_step(); ?>
395 </div>
396 <?php } ?>
397 </main>
398 </div>
399 </div>
400 </div>
401 </div>
402 <?php } ?>
403
404 <?php
405 /**
406 * Add an option for external providers to add their own 2fa methods options.
407 *
408 * @since 2.0.0
409 */
410 do_action( WP_2FA_PREFIX . 'methods_wizards' );
411 ?>
412
413 <?php if ( Settings_Page::are_backup_codes_enabled( User_Helper::get_user_role( $user ) ) ) { ?>
414 <div>
415 <div class="wp2fa-modal micromodal-slide" id="configure-2fa-backup-codes" aria-hidden="true">
416 <div class="modal__overlay" tabindex="-1" data-micromodal-close>
417 <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
418 <button class="modal__close" aria-label="Close modal" data-close-2fa-modal></button>
419 <main class="modal__content wp2fa-form-styles" id="modal-1-content">
420 <?php Wizard_Steps::generated_backup_codes( true ); ?>
421 </main>
422 </div>
423 </div>
424 </div>
425 </div>
426 <?php } ?>
427 <?php
428
429 if ( self::can_user_remove_2fa( $user->ID ) ) :
430 echo Generate_Modal::generate_modal( // phpcs:ignore
431 'confirm-remove-2fa',
432 __( 'Remove 2FA?', 'wp-2fa' ),
433 __( 'Are you sure you want to remove two-factor authentication and lower the security of your user account?', 'wp-2fa' ),
434 array(
435 '<a href="#" class="modal__btn modal__btn-primary button button-primary" data-trigger-remove-2fa data-user-id="' . esc_attr( $user->ID ) . '" data-nonce="' . wp_create_nonce( 'wp-2fa-remove-user-2fa-nonce' ) . '">' . esc_html__( 'Yes', 'wp-2fa' ) . '</a>',
436 '<button class="modal__btn button" data-close-2fa-modal aria-label="Close this dialog window">' . esc_html__( 'No', 'wp-2fa' ) . '</button>',
437 )
438 );
439 endif;
440
441 $output = ob_get_contents();
442 ob_end_clean();
443
444 echo $output; // phpcs:ignore
445 }
446
447 /**
448 * Produces the 2FA configuration form for network users, or any user with no roles.
449 *
450 * @param string $is_shortcode - Is it called from the shortcode.
451 * @param boolean $show_preamble - Show / hide preamble.
452 *
453 * @return void
454 */
455 public function inline_2fa_profile_form( $is_shortcode = '', $show_preamble = true ) {
456
457 if ( isset( $_GET['user_id'] ) ) { // phpcs:ignore
458 $user_id = (int) $_GET['user_id']; // phpcs:ignore
459 $user = get_user_by( 'id', $user_id );
460 } else {
461 $user = wp_get_current_user();
462 }
463
464 // Get current user, we going to need this regardless.
465 $current_user = wp_get_current_user();
466
467 // Bail if we still dont have an object.
468 if ( ! is_a( $user, '\WP_User' ) || ! is_a( $current_user, '\WP_User' ) ) {
469 return;
470 }
471
472 $additional_args = array(
473 'is_shortcode' => $is_shortcode,
474 'show_preamble' => $show_preamble,
475 );
476
477 $this->user_2fa_options( $user, $additional_args );
478 }
479
480 /**
481 * Add custom unlock account link to user edit admin list.
482 *
483 * @param string $actions Default actions.
484 * @param object $user_object User data.
485 * @return string Appended actions.
486 */
487 public function user_2fa_row_actions( $actions, $user_object ) {
488 $nonce = wp_create_nonce( 'wp-2fa-unlock-account-nonce' );
489 $grace_period_expired = User_Helper::get_grace_period( $user_object );
490 $url = add_query_arg(
491 array(
492 'action' => 'unlock_account',
493 'user_id' => $user_object->ID,
494 'wp_2fa_nonce' => $nonce,
495 ),
496 admin_url( 'users.php' )
497 );
498
499 if ( $grace_period_expired ) {
500 $actions['edit_badges'] = '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Unlock user', 'wp-2fa' ) . '</a>';
501 }
502 return $actions;
503 }
504
505 /**
506 * Save user profile information.
507 *
508 * @param array $input - The array with values to process.
509 *
510 * @return void
511 */
512 public function save_user_2fa_options( $input ) {
513
514 // Ensure we have the inputs we want before we process.
515 // To avoid causing issues with the rest of the user profile.
516 if ( ! is_array( $input ) ) {
517 return;
518 }
519
520 // Assign the input to post, in case we are dealing with saving the data from another page.
521 if ( isset( $input ) ) {
522 $_POST = $input;
523 }
524
525 // Grab current user.
526 $user = wp_get_current_user();
527
528 // phpcs:disable
529 // Grab authcode and ensure its a number.
530 if ( isset( $_POST['wp-2fa-totp-authcode'] ) ) {
531 $_POST['wp-2fa-totp-authcode'] = (int) $_POST['wp-2fa-totp-authcode'];
532 }
533 if ( ( ! isset( $_POST['custom-email-address'] ) || isset( $_POST['custom-email-address'] ) && empty( $_POST['custom-email-address'] ) ) &&
534 ( ! isset( $_POST['custom-oob-email-address'] ) || isset( $_POST['custom-oob-email-address'] ) && empty( $_POST['custom-oob-email-address'] ) ) ) {
535 if ( isset( $_POST['email'] ) ) {
536 update_user_meta( $user->ID, WP_2FA_PREFIX . 'nominated_email_address', $_POST['email'] );
537 } elseif ( isset( $_POST['wp_2fa_email_address'] ) && isset( $_POST['wp-2fa-totp-authcode'] ) && ! empty( $_POST['wp-2fa-totp-authcode'] ) ) {
538 update_user_meta( $user->ID, WP_2FA_PREFIX . 'nominated_email_address', $_POST['wp_2fa_email_address'] );
539 } elseif ( isset( $_POST['wp_2fa_email_oob_address'] ) && isset( $_POST['wp-2fa-oob-authcode'] ) && ! empty( $_POST['wp-2fa-oob-authcode'] ) ) {
540 if ( 'use_custom_email' !== $_POST['wp_2fa_email_oob_address'] ) {
541 update_user_meta( $user->ID, WP_2FA_PREFIX . 'nominated_email_address', $_POST['wp_2fa_email_oob_address'] );
542 } else {
543 update_user_meta( $user->ID, WP_2FA_PREFIX . 'nominated_email_address', $user->user_email );
544 }
545 }
546 } elseif ( isset( $_POST['custom-email-address'] ) && ! empty( $_POST['custom-email-address'] ) ) {
547 update_user_meta( $user->ID, WP_2FA_PREFIX . 'nominated_email_address', sanitize_email( wp_unslash( $_POST['custom-email-address'] ) ) );
548 } elseif ( isset( $_POST['custom-oob-email-address'] ) && ! empty( $_POST['custom-oob-email-address'] ) ) {
549 update_user_meta( $user->ID, WP_2FA_PREFIX . 'nominated_email_address', sanitize_email( wp_unslash( $_POST['custom-oob-email-address'] ) ) );
550 }
551
552 // Check its one of our options.
553 if ( ( isset( $_POST['wp_2fa_enabled_methods'] ) && 'totp' === $_POST['wp_2fa_enabled_methods'] ) ||
554 ( isset( $_POST['wp_2fa_enabled_methods'] ) && 'email' === $_POST['wp_2fa_enabled_methods'] ) ||
555 ( isset( $_POST['wp_2fa_enabled_methods'] ) && 'oob' === $_POST['wp_2fa_enabled_methods'] ) ) {
556 User_Helper::set_enabled_method_for_user(sanitize_text_field( wp_unslash( $_POST['wp_2fa_enabled_methods'] ) ), $user);
557 self::delete_expire_and_enforced_keys( $user->ID );
558 User::set_user_status( $user );
559 }
560
561 if ( isset( $_POST['wp-2fa-email-authcode'] ) && ! empty( $_POST['wp-2fa-email-authcode'] ) ) {
562 User_Helper::set_enabled_method_for_user( 'email', $user );
563 self::delete_expire_and_enforced_keys( $user->ID );
564 User::set_user_status( $user );
565 }
566
567 if ( isset( $_POST['wp-2fa-totp-authcode'] ) && ! empty( $_POST['wp-2fa-totp-authcode'] ) ) {
568 User_Helper::set_enabled_method_for_user( 'totp', $user );
569
570 $totp_key = $_POST['wp-2fa-totp-key'];
571 if ( Authentication::is_valid_key( $totp_key ) ) {
572 if ( Open_SSL::is_ssl_available() ) {
573 $totp_key = 'ssl_' . Open_SSL::encrypt( $totp_key );
574 }
575 User_Helper::set_user_totp_key( $totp_key, $user );
576 self::delete_expire_and_enforced_keys( $user->ID );
577 User::set_user_status( $user );
578 }
579 }
580 // phpcs:enable
581 }
582
583 /**
584 * Utility function to remove user expiry and enforced data.
585 *
586 * @param int $user_id User id to process.
587 */
588 public static function delete_expire_and_enforced_keys( $user_id ) {
589 User_Helper::remove_user_expiry_date( $user_id );
590 User_Helper::remove_user_enforced_instantly( $user_id );
591 }
592
593 /**
594 * Validate a user's code when setting up 2fa via the inline form.
595 *
596 * @return void
597 */
598 public function validate_authcode_via_ajax() {
599 check_ajax_referer( 'wp-2fa-validate-authcode' );
600
601 if ( isset( $_POST['form'] ) ) {
602 $input = wp_unslash( $_POST['form'] ); // phpcs:ignore
603 } else {
604 wp_send_json_error(
605 array(
606 'error' => esc_html__( 'No form', 'wp-2fa' ),
607 )
608 );
609 }
610
611 $user = wp_get_current_user();
612
613 $our_errors = '';
614
615 // Grab key from the $_POST.
616 if ( isset( $input['wp-2fa-totp-key'] ) ) {
617 $current_key = sanitize_text_field( wp_unslash( $input['wp-2fa-totp-key'] ) );
618 }
619
620 // Grab authcode and ensure its a number.
621 if ( isset( $input['wp-2fa-totp-authcode'] ) ) {
622 $input['wp-2fa-totp-authcode'] = (int) $input['wp-2fa-totp-authcode'];
623 }
624
625 // Check if we are dealing with totp or email, if totp validate and store a new secret key.
626 if ( ! empty( $input['wp-2fa-totp-authcode'] ) && ! empty( $current_key ) ) {
627 if ( Authentication::is_valid_key( $current_key ) || ! is_numeric( $input['wp-2fa-totp-authcode'] ) ) {
628 if ( ! Authentication::is_valid_authcode( $current_key, sanitize_text_field( wp_unslash( $input['wp-2fa-totp-authcode'] ) ) ) ) {
629 $our_errors = esc_html__( 'Invalid Two Factor Authentication code.', 'wp-2fa' );
630 }
631 } else {
632 $our_errors = esc_html__( 'Invalid Two Factor Authentication secret key.', 'wp-2fa' );
633 }
634
635 // If its not totp, is it email.
636 } elseif ( ! empty( $input['wp-2fa-email-authcode'] ) ) {
637 if ( ! Authentication::validate_token( $user, sanitize_text_field( wp_unslash( $input['wp-2fa-email-authcode'] ) ) ) ) {
638 $our_errors = esc_html__( 'Invalid Email Authentication code.', 'wp-2fa' );
639 }
640 } else {
641 $our_errors = esc_html__( 'Please enter the code to finalize the 2FA setup.', 'wp-2fa' );
642 }
643
644 if ( ! empty( $our_errors ) ) {
645 // Send the response.
646 wp_send_json_error(
647 array(
648 'error' => $our_errors,
649 )
650 );
651 } else {
652 $this->save_user_2fa_options( $input );
653 // Send the response.
654 wp_send_json_success();
655 }
656
657 wp_send_json_error(
658 array(
659 'error' => esc_html__( 'Error processing form', 'wp-2fa' ),
660 )
661 );
662 }
663
664 /**
665 * Checks the user for remove 2FA capabilities.
666 *
667 * @param int $user_id User ID.
668 *
669 * @return bool True if the user can remove 2FA from their account.
670 */
671 public static function can_user_remove_2fa( $user_id ) {
672 // check the "Hide the Remove 2FA button" setting.
673 if ( Settings::get_role_or_default_setting( 'hide_remove_button', $user_id ) ) {
674 return false;
675 }
676
677 // check grace period policy.
678 $grace_policy = Settings::get_role_or_default_setting( 'grace-policy', $user_id );
679 if ( 'no-grace-period' === $grace_policy ) {
680 // we only need to run further checks to find out if the 2FA is enforced for the user in question if there
681 // is no grace period.
682 $enforcement_policy = WP2FA::get_wp2fa_setting( 'enforcement-policy' );
683 if ( 'all-users' === $enforcement_policy ) {
684 // enforced for all users, target user is definitely included.
685 return false;
686 }
687
688 if ( 'do-not-enforce' !== $enforcement_policy ) {
689 // one of possible enforcement options is set, check the target user.
690 return User::is_enforced( $user_id );
691 }
692 }
693
694 return true;
695 }
696 }
697