PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 1.4.1
WP 2FA – Two-factor authentication for WordPress v1.4.1
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 / SetupWizard.php
wp-2fa / includes / classes / Admin Last commit date
SettingsPage.php 5 years ago SetupWizard.php 5 years ago UserNotices.php 5 years ago UserProfile.php 5 years ago UserRegistered.php 5 years ago
SetupWizard.php
1810 lines
1 <?php // phpcs:ignore
2
3 namespace WP2FA\Admin;
4
5 use \WP2FA\Authenticator\Authentication as Authentication;
6 use \WP2FA\WP2FA as WP2FA;
7 use \WP2FA\Core as Core;
8 use \WP2FA\Admin\SettingsPage as SettingsPage;
9 use \WP2FA\BackgroundProcessing\ProcessUserMetaUpdate as ProcessUserMetaUpdate;
10
11 /**
12 * Our class for creating a step by step wizard for easy configuration.
13 */
14 class SetupWizard {
15
16 /**
17 * Wizard Steps
18 *
19 * @var array
20 */
21 private $wizard_steps;
22
23 /**
24 * Current Step
25 *
26 * @var string
27 */
28 private $current_step;
29
30 /**
31 * Notices Meta key
32 *
33 * @var array
34 */
35 const NOTICES_META_KEY = 'wp_2fa_totp_notices';
36
37 /**
38 * Method: Constructor.
39 */
40 public function __construct() { }
41
42 /**
43 * Add setup admin page. This is empty on purpose.
44 */
45 public function admin_menus() {
46 add_dashboard_page( '', '', 'read', 'wp-2fa-setup', '' );
47 }
48
49 public function network_admin_menus() {
50 add_dashboard_page( 'index.php', '', 'read', 'wp-2fa-setup', '' );
51 }
52
53 /**
54 * Setup Page Start.
55 */
56 public function setup_page() {
57 // Get page argument from $_GET array.
58 $page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
59 if ( empty( $page ) || 'wp-2fa-setup' !== $page ) {
60 return;
61 }
62
63 // Clear out any old notices.
64 $user = wp_get_current_user();
65 delete_user_meta( $user->ID, self::NOTICES_META_KEY );
66
67 // First lets check if any options have been saved.
68 $settings_saved = true;
69 $settings = WP2FA::get_wp2fa_setting();
70 if ( empty( $settings ) || ! isset( $settings ) ) {
71 $settings_saved = false;
72 }
73
74 /**
75 * Wizard Steps.
76 */
77 $get_array = filter_input_array( INPUT_GET );
78 if ( isset( $get_array['wizard_type'] ) ) {
79 $wizard_type = sanitize_text_field( $get_array['wizard_type'] );
80 } else {
81 $wizard_type = '';
82 }
83
84 $is_user_forced_to_setup = get_user_meta( $user->ID, 'wp_2fa_user_enforced_instantly', true );
85 if ( ! empty( $is_user_forced_to_setup ) ) {
86 add_filter( 'wp_2fa_wizard_default_steps', array( $this, 'wp_2fa_add_intro_step' ) );
87 }
88
89 if ( ! empty( $wizard_type ) && 'user_2fa_config' === $get_array['wizard_type'] && current_user_can( 'read' ) ) {
90
91 $wizard_steps = array(
92 'user_choose_2fa_method' => array(
93 'name' => esc_html__( 'Choose Method', 'wp-2fa' ),
94 'content' => array( $this, 'wp_2fa_step_user_choose_method' ),
95 'save' => array( $this, 'wp_2fa_step_user_choose_method_save' ),
96 'wizard_type' => 'welcome_wizard',
97 ),
98 'setup_method' => array(
99 'name' => esc_html__( 'Setup the 2FA method', 'wp-2fa' ),
100 'content' => array( $this, 'wp_2fa_step_setup_authenticator' ),
101 'save' => array( $this, 'wp_2fa_step_setup_authenticator_save' ),
102 'wizard_type' => 'welcome_wizard',
103 ),
104 'backup_codes' => array(
105 'name' => esc_html__( 'Backup Codes', 'wp-2fa' ),
106 'content' => array( $this, 'wp_2fa_step_backup_codes' ),
107 'save' => array( $this, 'wp_2fa_step_backup_codes_save' ),
108 'wizard_type' => array( 'welcome_wizard', 'backup_codes_wizard' ),
109 ),
110 );
111
112 } elseif ( ! empty( $wizard_type ) && 'backup_codes_config' === $wizard_type && current_user_can( 'read' ) ) {
113
114 $wizard_steps = array(
115 'backup_codes' => array(
116 'name' => esc_html__( 'Backup Codes', 'wp-2fa' ),
117 'content' => array( $this, 'wp_2fa_step_backup_codes' ),
118 'save' => array( $this, 'wp_2fa_step_backup_codes_save' ),
119 'wizard_type' => array( 'welcome_wizard', 'backup_codes_wizard' ),
120 ),
121 );
122
123 } elseif ( ! empty( $wizard_type ) && 'user_reconfigure_config' === $wizard_type ) {
124
125 $wizard_steps = array(
126 'reconfigure_method' => array(
127 'name' => esc_html__( 'Setup the Authenticator 2FA', 'wp-2fa' ),
128 'content' => array( $this, 'wp_2fa_step_reconfigure_authenticator' ),
129 'save' => array( $this, 'wp_2fa_step_reconfigure_authenticator_save' ),
130 'wizard_type' => 'reconfigure_wizard',
131 ),
132 );
133
134 } elseif ( current_user_can( 'manage_options' ) && ! $settings_saved ) {
135 $wizard_steps = array(
136 'welcome' => array(
137 'name' => esc_html__( 'Welcome', 'wp-2fa' ),
138 'content' => array( $this, 'wp_2fa_step_welcome' ),
139 'wizard_type' => 'welcome_wizard',
140 ),
141 'choose_2fa_method' => array(
142 'name' => esc_html__( 'Choose Method', 'wp-2fa' ),
143 'content' => array( $this, 'wp_2fa_step_choose_method' ),
144 'save' => array( $this, 'wp_2fa_step_choose_method_save' ),
145 'wizard_type' => 'welcome_wizard',
146 ),
147 'setup_method' => array(
148 'name' => esc_html__( 'Setup the 2FA method', 'wp-2fa' ),
149 'content' => array( $this, 'wp_2fa_step_setup_authenticator' ),
150 'save' => array( $this, 'wp_2fa_step_setup_authenticator_save' ),
151 'wizard_type' => 'welcome_wizard',
152 ),
153 'finish' => array(
154 'name' => esc_html__( 'Setup Finish', 'wp-2fa' ),
155 'content' => array( $this, 'wp_2fa_step_finish' ),
156 'save' => array( $this, 'wp_2fa_step_finish_save' ),
157 'wizard_type' => 'welcome_wizard',
158 ),
159 'settings_configuation' => array(
160 'name' => esc_html__( 'Select 2FA Methods', 'wp-2fa' ),
161 'content' => array( $this, 'wp_2fa_step_global_2fa_methods' ),
162 'save' => array( $this, 'wp_2fa_step_global_2fa_methods_save' ),
163 'wizard_type' => 'welcome_wizard',
164 ),
165 'backup_codes' => array(
166 'name' => esc_html__( 'Backup Codes', 'wp-2fa' ),
167 'content' => array( $this, 'wp_2fa_step_backup_codes' ),
168 'save' => array( $this, 'wp_2fa_step_backup_codes_save' ),
169 'wizard_type' => array( 'welcome_wizard', 'backup_codes_wizard' ),
170 ),
171 'reconfigure_method' => array(
172 'name' => esc_html__( 'Setup the Authenticator 2FA', 'wp-2fa' ),
173 'content' => array( $this, 'wp_2fa_step_reconfigure_authenticator' ),
174 'save' => array( $this, 'wp_2fa_step_reconfigure_authenticator_save' ),
175 'wizard_type' => 'reconfigure_wizard',
176 ),
177 );
178
179 } elseif ( current_user_can( 'read' ) ) {
180
181 $wizard_steps = array(
182 'choose_2fa_method' => array(
183 'name' => esc_html__( 'Choose Method', 'wp-2fa' ),
184 'content' => array( $this, 'wp_2fa_step_choose_method' ),
185 'save' => array( $this, 'wp_2fa_step_choose_method_save' ),
186 'wizard_type' => 'welcome_wizard',
187 ),
188 'setup_method' => array(
189 'name' => esc_html__( 'Setup the 2FA method', 'wp-2fa' ),
190 'content' => array( $this, 'wp_2fa_step_setup_authenticator' ),
191 'save' => array( $this, 'wp_2fa_step_setup_authenticator_save' ),
192 'wizard_type' => 'welcome_wizard',
193 ),
194 'backup_codes' => array(
195 'name' => esc_html__( 'Backup Codes', 'wp-2fa' ),
196 'content' => array( $this, 'wp_2fa_step_backup_codes' ),
197 'save' => array( $this, 'wp_2fa_step_backup_codes_save' ),
198 'wizard_type' => array( 'welcome_wizard', 'backup_codes_wizard' ),
199 ),
200 'reconfigure_method' => array(
201 'name' => esc_html__( 'Setup the Authenticator 2FA', 'wp-2fa' ),
202 'content' => array( $this, 'wp_2fa_step_reconfigure_authenticator' ),
203 'save' => array( $this, 'wp_2fa_step_reconfigure_authenticator_save' ),
204 'wizard_type' => 'reconfigure_wizard',
205 ),
206 );
207
208 }
209
210 /**
211 * Filter: `Wizard Default Steps`
212 *
213 * WSAL filter to filter wizard steps before they are displayed.
214 *
215 * @param array $wizard_steps – Wizard Steps.
216 */
217 $this->wizard_steps = apply_filters( 'wp_2fa_wizard_default_steps', $wizard_steps );
218
219 // Set current step.
220 $current_step = filter_input( INPUT_GET, 'current-step', FILTER_SANITIZE_STRING );
221 $this->current_step = ! empty( $current_step ) ? $current_step : current( array_keys( $this->wizard_steps ) );
222
223 /**
224 * Enqueue Scripts.
225 */
226 wp_enqueue_style(
227 'wp_2fa_setup_wizard',
228 Core\style_url( 'setup-wizard', 'admin' ),
229 array(),
230 WP_2FA_VERSION
231 );
232
233 wp_enqueue_style(
234 'wp_2fa_admin',
235 Core\style_url( 'admin-style', 'admin' ),
236 array(),
237 WP_2FA_VERSION
238 );
239
240 wp_enqueue_script(
241 'wp_2fa_admin',
242 Core\script_url( 'admin', 'admin' ),
243 array( 'jquery-ui-widget', 'jquery-ui-core', 'jquery-ui-autocomplete' ),
244 WP_2FA_VERSION,
245 true
246 );
247
248 wp_enqueue_script(
249 'wp_2fa_micromodal',
250 Core\script_url( 'micro-modal', 'admin' ),
251 WP_2FA_VERSION,
252 true
253 );
254
255 // Data array.
256 $data_array = array(
257 'ajaxURL' => admin_url( 'admin-ajax.php' ),
258 'roles' => WP2FA::wp_2fa_get_roles(),
259 'nonce' => wp_create_nonce( 'wp-2fa-settings-nonce' ),
260 );
261 wp_localize_script( 'wp_2fa_admin', 'wp2faData', $data_array );
262
263 // Data array.
264 $data_array = array(
265 'ajaxURL' => admin_url( 'admin-ajax.php' ),
266 'nonce' => wp_create_nonce( 'wp2fa-verify-wizard-page' ),
267 'codesPreamble' => esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
268 'readyText' => esc_html__( 'I\'m ready', 'wp-2fa' ),
269 'codeReSentText' => esc_html__( 'New code sent', 'wp-2fa' ),
270 );
271 wp_localize_script( 'wp_2fa_admin', 'wp2faWizardData', $data_array );
272
273 /**
274 * Save Wizard Settings.
275 */
276 $save_step = filter_input( INPUT_POST, 'save_step', FILTER_SANITIZE_STRING );
277 if ( ! empty( $save_step ) && ! empty( $this->wizard_steps[ $this->current_step ]['save'] ) ) {
278 call_user_func( $this->wizard_steps[ $this->current_step ]['save'] );
279 }
280
281 $this->setup_page_header();
282 $this->setup_page_steps();
283 $this->setup_page_content();
284 $this->setup_page_footer();
285
286 exit;
287 }
288
289 /**
290 * Setup Page Header.
291 */
292 private function setup_page_header() {
293 ?>
294 <!DOCTYPE html>
295 <html <?php language_attributes(); ?>>
296 <head>
297 <meta name="viewport" content="width=device-width" />
298 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
299 <title><?php esc_html_e( 'WP 2FA &rsaquo; Setup Wizard', 'wp-2fa' ); ?></title>
300 <?php wp_print_scripts( 'jquery' ); ?>
301 <?php wp_print_scripts( 'jquery-ui-core' ); ?>
302 <?php wp_print_scripts( 'wp_2fa_setup_wizard' ); ?>
303 <?php wp_print_scripts( 'wp_2fa_micromodal' ); ?>
304 <?php wp_print_scripts( 'wp_2fa_admin' ); ?>
305 <?php wp_print_styles( 'common' ); ?>
306 <?php wp_print_styles( 'forms' ); ?>
307 <?php wp_print_styles( 'buttons' ); ?>
308 <?php wp_print_styles( 'wp-jquery-ui-dialog' ); ?>
309 <?php wp_print_styles( 'wp_2fa_admin' ); ?>
310 <?php do_action( 'admin_print_styles' ); ?>
311 <?php do_action( 'admin_head' ); ?>
312 </head>
313 <body class="wp2fa-setup wp-core-ui">
314 <div class="setup-wizard-wrapper">
315 <h1 id="wp2fa-logo"><a href="https://wpsecurityauditlog.com" target="_blank"><img src="<?php echo esc_url( WP_2FA_URL . '/dist/images/wizard-logo.png' ); ?>"></a></h1>
316 <?php
317 }
318
319 /**
320 * Setup Page Footer.
321 */
322 private function setup_page_footer() {
323 $user = wp_get_current_user();
324 $roles = (array) $user->roles;
325
326 $redirect = get_edit_profile_url( $user->ID );
327 ?>
328 <div class="wp2fa-setup-footer">
329 <?php if ( 'welcome' !== $this->current_step && 'finish' !== $this->current_step ) : // Don't show the link on the first & last step. ?>
330 <?php if ( ! get_user_meta( $user->ID, 'wp_2fa_user_enforced_instantly', true ) ) : ?>
331 <a class="close-wizard-link" href="<?php echo esc_url( $redirect ); ?>"><?php esc_html_e( 'Close Wizard', 'wp-2fa' ); ?></a>
332 <?php endif; ?>
333 <?php endif; ?>
334 </div>
335 </div>
336 <div class="wp2fa-modal micromodal-slide" id="confirm-change-2fa" aria-hidden="true">
337 <div class="modal__overlay" tabindex="-1" data-micromodal-close>
338 <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
339 <header class="modal__header">
340 <h2 class="modal__title" id="modal-1-title">
341 <?php esc_html_e( 'Change 2FA Method?', 'wp-2fa' ); ?>
342 </h2>
343 <button class="modal__close" aria-label="Close modal" data-micromodal-close></button>
344 </header>
345 <main class="modal__content" id="modal-1-content">
346 <p>
347 <?php esc_html_e( 'By switching to a new method the previously used method will be disabled.', 'wp-2fa' ); ?>
348 </p>
349 </main>
350 <footer class="modal__footer">
351 <a href="#" class="modal__btn modal__btn-primary" data-trigger-submit-form><?php esc_html_e( 'OK', 'wp-2fa' ); ?></a>
352 <button class="modal__btn" data-micromodal-close aria-label="Close this dialog window"><?php esc_html_e( 'No thanks', 'wp-2fa' ); ?></button>
353 </footer>
354 </div>
355 </div>
356 </div>
357 </body>
358 </html>
359 <?php
360 }
361
362 /**
363 * Setup Page Steps.
364 */
365 private function setup_page_steps() {
366 ?>
367 <ul class="steps">
368 <?php
369 foreach ( $this->wizard_steps as $key => $step ) :
370 if ( 'welcome_wizard' === $step['wizard_type'] || is_array( $step['wizard_type'] ) && in_array( 'welcome_wizard', $step['wizard_type'], true ) ) :
371 if ( $key === $this->current_step ) :
372 ?>
373 <li class="is-active"><?php echo esc_html( $step['name'] ); ?></li>
374 <?php
375 else :
376 ?>
377 <li><?php echo esc_html( $step['name'] ); ?></li>
378 <?php
379 endif;
380 endif;
381 endforeach;
382 ?>
383 </ul>
384 <?php
385 }
386
387 /**
388 * Get Next Step URL.
389 *
390 * @return string
391 */
392 private function get_next_step() {
393 // Get current step.
394 $current_step = $this->current_step;
395
396 // Array of step keys.
397 $keys = array_keys( $this->wizard_steps );
398 if ( end( $keys ) === $current_step ) { // If last step is active then return WP Admin URL.
399 return admin_url();
400 }
401
402 // Search for step index in step keys.
403 $step_index = array_search( $current_step, $keys, true );
404 if ( false === $step_index ) { // If index is not found then return empty string.
405 return '';
406 }
407
408 // Return next step.
409 return add_query_arg( 'current-step', $keys[ $step_index + 1 ] );
410 }
411
412 /**
413 * Setup Page Content.
414 */
415 private function setup_page_content() {
416 ?>
417 <div class="wp2fa-setup-content">
418 <?php
419 if ( ! empty( $this->wizard_steps[ $this->current_step ]['content'] ) ) {
420 call_user_func( $this->wizard_steps[ $this->current_step ]['content'] );
421 }
422 ?>
423 </div>
424 <?php
425 }
426
427 /**
428 * Step View: `Welcome`
429 */
430 private function wp_2fa_step_welcome() {
431 // Grab current user.
432 $user = wp_get_current_user();
433
434 if ( WP2FA::is_this_multisite() ) {
435 $redirect = add_query_arg( 'page', 'wp-2fa-settings', network_admin_url( 'settings.php' ) );
436 } else {
437 // Otherwise redirect to main audit log view.
438 $redirect = add_query_arg( 'page', 'wp-2fa-settings', admin_url( 'options-general.php' ) );
439 }
440
441 ?>
442 <h3><?php esc_html_e( 'Let us help you get started', 'wp-2fa' ); ?></h3>
443 <p><?php esc_html_e( 'Thank you for installing WP 2FA. This wizard will assist you setup two-factor authentication (2FA) for your WordPress user and configure the plugin’s generic settings.', 'wp-2fa' ); ?></p>
444
445 <div class="wp2fa-setup-actions">
446 <a class="button button-primary"
447 href="<?php echo esc_url( $this->get_next_step() ); ?>">
448 <?php esc_html_e( 'Let’s get started!', 'wp-2fa' ); ?>
449 </a>
450 <a class="button button-secondary"
451 href="<?php echo esc_url( $redirect ); ?>">
452 <?php esc_html_e( 'Skip Wizard - I know how to do this', 'wp-2fa' ); ?>
453 </a>
454 </div>
455 <?php
456 }
457
458 /**
459 * Step View: `Choose Methods`
460 */
461 private function wp_2fa_step_choose_method() {
462 ?>
463 <form method="post" class="wp2fa-setup-form" autocomplete="off">
464 <?php wp_nonce_field( 'wp2fa-step-choose-method' ); ?>
465
466 <?php
467 // Change text if this is the user configuring there 2fa settings
468 // Filter $_GET array for security.
469 $get_array = filter_input_array( INPUT_GET );
470 // If this is the user setting things up, lets show nice message.
471 if ( isset( $get_array['first_time_setup'] ) ) {
472 if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) && ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) {
473 $intro_text = esc_html__( 'Let’s get started; Choose the two-factor authentication method', 'wp-2fa' );
474 $sub_text = esc_html__( 'There are two methods available from which you can choose for 2FA:', 'wp-2fa' );
475 } else {
476 $intro_text = esc_html__( 'Let’s get started', 'wp-2fa' );
477 $sub_text = esc_html__( 'Only the below 2FA method is allowed on this website:', 'wp-2fa' );
478 }
479 ?>
480 <h3><?php echo sanitize_text_field( $intro_text ); ?></h3>
481 <p><?php echo sanitize_text_field( $sub_text ); ?></p>
482 <?php } else { ?>
483 <h3><?php esc_html_e( 'Choose the 2FA authentication method', 'wp-2fa' ); ?></h3>
484 <p><?php esc_html_e( 'There are two methods available from which you can choose for 2FA:', 'wp-2fa' ); ?></p>
485 <?php } ?>
486
487 <fieldset>
488 <?php if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) ) { ?>
489 <div class="option-pill">
490 <label for="basic">
491 <input id="basic" name="wp_2fa_enabled_methods" type="radio" value="totp" checked>
492 <?php esc_html_e( 'One-time code generated with your app of choice (most reliable and secure)', 'wp-2fa' ); ?>
493 </label>
494 <?php
495 printf( '<p class="description">%1$s <a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/" target="_blank">%2$s</a> %3$s</p>', esc_html__( 'Note: This method requires you to install one of the following 2FA apps: Google Authenticator, FreeOTP, Microsoft Authenticator, Duo Security, Authy and LastPass. All of these apps are free and can be downloaded from the Google Play and Apple Appstore. Read our guides on', 'wp-2fa' ), esc_html__( 'our knowledge base', 'wp-2fa' ), esc_html__( 'for more information on how to setup these apps.', 'wp-2fa' ) );
496 ?>
497 </div>
498 <?php } ?>
499 <?php if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) { ?>
500 <div class="option-pill">
501 <label for="geek">
502 <input id="geek" name="wp_2fa_enabled_methods" type="radio" value="email">
503 <?php esc_html_e( 'One-time code sent to you over email', 'wp-2fa' ); ?>
504 </label>
505 </div>
506 <?php } ?>
507 </fieldset>
508 <div class="wp2fa-setup-actions">
509 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Next', 'wp-2fa' ); ?>"><?php esc_html_e( 'Next', 'wp-2fa' ); ?></button>
510 </div>
511 </form>
512 <?php
513 }
514
515 /**
516 * Step Save: `Choose Method`
517 */
518 private function wp_2fa_step_choose_method_save() {
519 // Check nonce.
520 check_admin_referer( 'wp2fa-step-choose-method' );
521
522 // Grab current user.
523 $user = wp_get_current_user();
524
525 // Add our enabled methods to the user metadata.
526 if ( isset( $_POST['wp_2fa_enabled_methods'] ) ) {
527 $next = add_query_arg( 'enabled_methods', sanitize_text_field( wp_unslash( $_POST['wp_2fa_enabled_methods'] ) ), $this->get_next_step() );
528 wp_safe_redirect( esc_url_raw( $next ) );
529 }
530
531 exit();
532 }
533
534 /**
535 * Choose user 2FA method
536 */
537 private function wp_2fa_step_user_choose_method() {
538 $user = wp_get_current_user();
539 $enabled_method = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
540
541 ?>
542 <form id="wp2fa-setup-form" method="post" class="wp2fa-setup-form" autocomplete="off">
543 <?php wp_nonce_field( 'wp2fa-step-choose-method' ); ?>
544
545 <?php
546 $nonce = wp_create_nonce( 'wp-2fa-backup-codes-generate-json-' . $user->ID );
547 // Change text if this is the user configuring there 2fa settings
548 // Filter $_GET array for security.
549 $get_array = filter_input_array( INPUT_GET );
550 // If this is the user setting things up, lets show nice message.
551 if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) && ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) {
552 $intro_text = esc_html__( 'Choose the 2FA authentication method', 'wp-2fa' );
553 $sub_text = esc_html__( 'There are two methods available from which you can choose for 2FA:', 'wp-2fa' );
554 } else {
555 $intro_text = esc_html__( 'Choose the 2FA authentication method', 'wp-2fa' );
556 $sub_text = esc_html__( 'Only the below 2FA method is allowed on this website:', 'wp-2fa' );
557 }
558 ?>
559 <h3><?php echo sanitize_text_field( $intro_text ); ?></h3>
560 <p><?php echo sanitize_text_field( $sub_text ); ?></p>
561
562 <fieldset>
563 <?php
564 if ( 'totp' === $enabled_method ) {
565 ?>
566 <div class="option-pill">
567 <label for="basic">
568 <input id="basic" name="wp_2fa_enabled_methods" type="radio" value="totp" checked>
569 <?php esc_html_e( 'Reconfigure the 2FA App', 'wp-2fa' ); ?>
570 </label>
571 <p class="description"><?php esc_html_e( 'Click the below button to reconfigure the current 2FA method. Note that once reset reset you will have to re-scan the QR code on all devices you want this to work on because the previous codes will stop working.', 'wp-2fa' ); ?>
572 </p>
573 <button class="button button-primary" data-trigger-reset-key data-nonce="<?php echo esc_attr( $nonce ); ?>" data-user-id="<?php echo esc_attr( $user->ID ); ?>" type="submit" name="save_step" value="<?php esc_attr_e( 'Reset', 'wp-2fa' ); ?>"><?php esc_html_e( 'Reset', 'wp-2fa' ); ?></button>
574 </div>
575 <div class="option-pill">
576 <p class="description">
577 <label for="geek">
578 <input id="email" name="wp_2fa_enabled_methods" type="radio" value="email">
579 <?php esc_html_e( 'One-time code sent to you over email', 'wp-2fa' ); ?>
580 </label>
581 </p>
582 <a href="#" class="button button-primary change-2fa-confirm" onclick="MicroModal.show('confirm-change-2fa');" data-check-on-click="#email"><?php esc_html_e( 'Configure and use this method instead', 'wp-2fa' ); ?></a>
583 <button class="button button-primary change-2fa-confirm hidden" type="submit" name="save_step" value="<?php esc_attr_e( 'Configure and use this method instead', 'wp-2fa' ); ?>"><?php esc_html_e( 'Submit', 'wp-2fa' ); ?></button>
584 </div>
585 <?php
586 }
587 if ( 'email' === $enabled_method ) {
588 ?>
589 <div class="option-pill">
590 <p class="description">
591 <label for="geek">
592 <input id="geek" name="wp_2fa_enabled_methods" type="radio" value="email" checked>
593 <?php esc_html_e( 'Reconfigure the email address for email 2FA', 'wp-2fa' ); ?>
594 </label>
595 </p>
596 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Reconfigure email 2FA', 'wp-2fa' ); ?>"><?php esc_html_e( 'Reconfigure email 2FA', 'wp-2fa' ); ?></button>
597 </div>
598 <div class="option-pill">
599 <p class="description">
600 <label for="wp_2fa_enabled_methods">
601 <input id="totp" name="wp_2fa_enabled_methods" type="radio" value="totp">
602 <?php esc_html_e( 'One-time code generated with your app of choice (most reliable and secure)', 'wp-2fa' ); ?>
603 </label>
604 </p>
605 <a href="#" class="button button-primary change-2fa-confirm" onclick="MicroModal.show('confirm-change-2fa');" data-check-on-click="#totp"><?php esc_html_e( 'Configure and use this method instead', 'wp-2fa' ); ?></a>
606 <button class="button button-primary change-2fa-confirm hidden" type="submit" name="save_step" value="<?php esc_attr_e( 'Configure and use this method instead', 'wp-2fa' ); ?>"><?php esc_html_e( 'Submit', 'wp-2fa' ); ?></button>
607 </div>
608 <?php
609 }
610 ?>
611 </fieldset>
612 <div class="wp2fa-setup-actions"></div>
613 </form>
614 <?php
615 }
616
617 /**
618 * Step Save: `Choose Method`
619 */
620 private function wp_2fa_step_user_choose_method_save() {
621 // Check nonce.
622 check_admin_referer( 'wp2fa-step-choose-method' );
623
624 // Grab current user.
625 $user = wp_get_current_user();
626
627 // Add our enabled methods to the user metadata.
628 if ( isset( $_POST['wp_2fa_enabled_methods'] ) ) {
629 $next = add_query_arg( 'enabled_methods', sanitize_text_field( wp_unslash( $_POST['wp_2fa_enabled_methods'] ) ), $this->get_next_step() );
630 wp_safe_redirect( esc_url_raw( $next ) );
631 }
632
633 exit();
634 }
635
636 /**
637 * Step View: `Setup Authenticator`
638 */
639 private function wp_2fa_step_setup_authenticator() {
640 // Grab current user.
641 $user = wp_get_current_user();
642
643 // Grab key from user meta.
644 $key = Authentication::get_user_totp_key( $user->ID );
645 $enabled_method = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
646
647 // If no key is present, lets make one.
648 if ( empty( $key ) ) {
649 $key = Authentication::generate_key();
650 $update = update_user_meta( $user->ID, 'wp_2fa_totp_key', $key );
651 }
652
653 // Setup site information, used when generating our QR code.
654 $site_name = get_bloginfo( 'name', 'display' );
655 $totp_title = apply_filters( 'wp_2fa_totp_title', $site_name . ':' . $user->user_login, $user );
656
657 // Now lets grab the users enabled 2fa methods.
658 $get_array = filter_input_array( INPUT_GET );
659 if ( isset( $_REQUEST['enabled_method'] ) ) {
660 $selected_method = $_REQUEST['enabled_method'];
661 } else {
662 $selected_method = $get_array['enabled_methods'];
663 }
664
665
666 // Create a nonce incase we want to reset the key.
667 $nonce = wp_create_nonce( 'wp-2fa-backup-codes-generate-json-' . $user->ID );
668
669 // Grab notices.
670 $notices = get_user_meta( $user->ID, self::NOTICES_META_KEY, true );
671
672 if ( ! isset( $notices['error'] ) && empty( $notices['error'] ) ) {
673 $is_active = 'active';
674 $is_active2 = '';
675 } else {
676 $is_active = '';
677 $is_active2 = 'active';
678 }
679
680 // TOTP is enabled for the user, so lets display the relevant steps.
681 // Here we wrap each "sub step" (a step within a step) in .tep-setting-wrapper, and nudge to next "sub step" with next_step_setting button.
682 if ( 'totp' === $selected_method ) {
683 ?>
684 <div class="step-setting-wrapper <?php echo esc_attr( $is_active ); ?>" data-step-title="<?php esc_html_e( 'Download authenticator', 'wp-2fa' ); ?>">
685 <h3><?php esc_html_e( 'Setup the 2FA method', 'wp-2fa' ); ?></h3>
686 <div class="option-pill">
687 <img src="<?php echo esc_url( Authentication::get_google_qr_code( $totp_title, $key, $site_name ) ); ?>" id="wp-2fa-totp-qrcode" />
688 <ol>
689 <li><?php esc_html_e( 'Download the app of your choice', 'wp-2fa' ); ?></li>
690 <li><?php esc_html_e( 'Scan the QR code to the right.', 'wp-2fa' ); ?></li>
691 </ol>
692 <p><?php esc_html_e( 'Otherwise, select Enter a provided key and type in the key below:', 'wp-2fa' ); ?></p>
693 <code><?php echo esc_html( $key ); ?></code>
694 </div>
695 <br>
696 <br>
697 <br>
698 <br>
699 <h4><?php esc_html_e( 'For detailed guides for your desired app, click below.', 'wp-2fa' ); ?></h4>
700 <a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/#authy" target="_blank"><img style="max-width: 110px;" src="<?php echo esc_url( WP_2FA_URL . '/dist/images/authy-logo.png' ); ?>"></a>
701 <a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/#google" target="_blank"><img style="max-width: 110px;" src="<?php echo esc_url( WP_2FA_URL . '/dist/images/google-logo.png' ); ?>"></a>
702 <a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/#microsoft" target="_blank"><img style="max-width: 110px;" src="<?php echo esc_url( WP_2FA_URL . '/dist/images/microsoft-logo.png' ); ?>"></a>
703 <a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/#duo" target="_blank"><img style="max-width: 110px;" src="<?php echo esc_url( WP_2FA_URL . '/dist/images/duo-logo.png' ); ?>"></a>
704 <a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/#lastpass" target="_blank"><img style="max-width: 110px" src="<?php echo esc_url( WP_2FA_URL . '/dist/images/lastpass-logo.png' ); ?>"></a>
705 <a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/#freeota" target="_blank"><img style="max-width: 110px;" src="<?php echo esc_url( WP_2FA_URL . '/dist/images/free-otp-logo.png' ); ?>"></a>
706 <div class="wp2fa-setup-actions">
707 <button class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>"><?php esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
708 </div>
709 </div>
710
711 <div class="step-setting-wrapper <?php echo esc_attr( $is_active2 ); ?>" data-step-title="<?php esc_html_e( 'Verify configuration', 'wp-2fa' ); ?>">
712 <form method="post" class="wp2fa-setup-form" autocomplete="off">
713 <?php wp_nonce_field( 'wp2fa-step-login' ); ?>
714 <h3><?php esc_html_e( 'Almost there…', 'wp-2fa' ); ?></h3>
715 <p><?php esc_html_e( 'Please type in the one-time code from your Google Authenticator app to finalize the setup.', 'wp-2fa' ); ?></p>
716 <fieldset>
717 <label for="2fa-totp-authcode">
718 <?php esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?>
719 <input type="tel" name="wp-2fa-totp-authcode" id="wp-2fa-totp-authcode" class="input" value="" size="20" pattern="[0-9]*" />
720 </label>
721 </fieldset>
722 <input type="hidden" name="wp-2fa-totp-key" value="<?php echo esc_attr( $key ); ?>" />
723 <div class="wp2fa-setup-actions">
724 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Finish', 'wp-2fa' ); ?>"><?php esc_html_e( 'Finish', 'wp-2fa' ); ?></button>
725 </div>
726 </form>
727 </div>
728
729 <?php
730 // Display any error notices if they are available.
731 if ( isset( $notices['error'] ) && ! empty( $notices['error'] ) ) {
732 foreach ( $notices['error'] as $notice ) {
733 echo '<p class="description error">' . wp_kses_post( $notice ) . '</p>';
734 }
735 }
736 } elseif ( 'email' === $selected_method ) {
737 $setupnonce = wp_create_nonce( 'wp-2fa-send-setup-email' );
738 ?>
739 <div class="step-setting-wrapper <?php echo esc_attr( $is_active ); ?>" data-step-title="<?php esc_html_e( 'Configure email', 'wp-2fa' ); ?>">
740 <h3><?php esc_html_e( 'Setup the 2FA method', 'wp-2fa' ); ?></h3>
741 <p>
742 <?php esc_html_e( 'Please select the email address where the one-time code should be sent:', 'wp-2fa' ); ?>
743 </p>
744 <fieldset>
745 <label for="use_wp_email">
746 <input type="radio" name="wp_2fa_email_address" id="use_wp_email" value="<?php echo esc_attr( $user->user_email ); ?>" checked>
747 <span><?php esc_html_e( 'Use my WordPress user email (', 'wp-2fa' ); ?><small><?php echo esc_attr( $user->user_email ); ?></small><?php esc_html_e( ')', 'wp-2fa' ); ?></span>
748 </label>
749 <br/>
750 <label for="use_custom_email">
751 <input type="radio" name="wp_2fa_email_address" id="use_custom_email" value="use_custom_email">
752 <span><?php esc_html_e( 'Use a different email address:', 'wp-2fa' ); ?></span>
753 <input type="email" name="custom-email-address" id="custom-email-address" class="input wide" value=""/>
754 </label>
755 </fieldset>
756 <p class="description"><?php esc_html_e( 'Note: you should be able to access the mailbox of the email address to complete the following step.', 'wp-2fa' ); ?></p>
757 <div class="wp2fa-setup-actions">
758 <button class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" data-trigger-setup-email data-user-id="<?php echo esc_attr( $user->ID ); ?>" data-nonce="<?php echo esc_attr( $setupnonce ); ?>"><?php esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
759 </div>
760 </div>
761
762 <div class="step-setting-wrapper <?php echo esc_attr( $is_active2 ); ?>" data-step-title="<?php esc_html_e( 'Verify configuration', 'wp-2fa' ); ?>">
763 <form method="post" class="wp2fa-setup-form" autocomplete="off">
764 <?php wp_nonce_field( 'wp2fa-step-login' ); ?>
765 <h4><?php esc_html_e( 'Almost there…', 'wp-2fa' ); ?></h4>
766 <p><?php esc_html_e( 'Please type in the one-time code sent to your email address to finalize the setup.', 'wp-2fa' ); ?></p>
767 <fieldset>
768 <label for="2fa-email-authcode">
769 <?php esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?>
770 <input type="tel" name="wp-2fa-email-authcode" id="wp-2fa-email-authcode" class="input" value="" size="20" pattern="[0-9]*" />
771 </label>
772 </fieldset>
773
774 <input type="hidden" name="wp-2fa-totp-key" value="<?php echo esc_attr( $key ); ?>" />
775 <div class="wp2fa-setup-actions">
776 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Finish', 'wp-2fa' ); ?>"><?php esc_html_e( 'Finish', 'wp-2fa' ); ?></button>
777 <a href="#" class="button button-secondary resend-email-code" data-trigger-setup-email data-user-id="<?php echo esc_attr( $user->ID ); ?>" data-nonce="<?php echo esc_attr( $setupnonce ); ?>">
778 <span class="resend-inner"><?php esc_html_e( 'Send me another code', 'wp-2fa' ); ?></span>
779 </a>
780 </div>
781 </form>
782 </div>
783 <?php
784 // Display any error notices if they are available.
785 if ( isset( $notices['error'] ) && ! empty( $notices['error'] ) ) {
786 foreach ( $notices['error'] as $notice ) {
787 echo '<p class="description error">' . wp_kses_post( $notice ) . '</p>';
788 }
789 }
790 }
791 }
792
793 /**
794 * Step Save: `Setup Authenticator`
795 */
796 private function wp_2fa_step_setup_authenticator_save() {
797 // Check nonce.
798 check_admin_referer( 'wp2fa-step-login' );
799
800 // Grab current user.
801 $user = wp_get_current_user();
802
803 // Setup some empty arrays which will may fill later, should an error arise along the way.
804 $notices = array();
805 $errors = array();
806
807 // Grab key from the $_POST.
808 if ( isset( $_POST['wp-2fa-totp-key'] ) ) {
809 $current_key = sanitize_text_field( wp_unslash( $_POST['wp-2fa-totp-key'] ) );
810 }
811
812 // Grab authcode and ensure its a number.
813 if ( isset( $_POST['wp-2fa-totp-authcode'] ) ) {
814 $_POST['wp-2fa-totp-authcode'] = (int) $_POST['wp-2fa-totp-authcode'];
815 }
816
817 // Check if we are dealing with totp or email, if totp validate and store a new secret key.
818 if ( ! empty( $_POST['wp-2fa-totp-authcode'] ) && ! empty( $current_key ) ) {
819 if ( Authentication::is_valid_key( $current_key ) || ! is_numeric( $_POST['wp-2fa-totp-authcode'] ) ) {
820 if ( ! Authentication::is_valid_authcode( $current_key, sanitize_text_field( wp_unslash( $_POST['wp-2fa-totp-authcode'] ) ) ) ) {
821 $errors[] = esc_html__( 'Invalid Two Factor Authentication code.', 'wp-2fa' );
822 }
823 } else {
824 $errors[] = esc_html__( 'Invalid Two Factor Authentication secret key.', 'wp-2fa' );
825 }
826
827 // If its not totp, is it email.
828 } elseif ( ! empty( $_POST['wp-2fa-email-authcode'] ) ) {
829 if ( ! Authentication::validate_token( $user->ID, sanitize_text_field( wp_unslash( $_POST['wp-2fa-email-authcode'] ) ) ) ) {
830 $errors[] = __( 'Invalid Email Authentication code.', 'wp-2fa' );
831 }
832 } else {
833 $errors[] = __( 'Please enter the code to finalize the 2FA setup.', 'wp-2fa' );
834 }
835
836 if ( ! empty( $errors ) ) {
837 $notices['error'] = $errors;
838 }
839
840 if ( ! empty( $notices ) ) {
841 update_user_meta( $user->ID, self::NOTICES_META_KEY, $notices );
842 }
843
844 // If no errors found, lets continue to next step and clear the notices, should any be present from previous attempts.
845 if ( empty( $notices ) ) {
846 if ( isset( $_POST['use_wp_email'] ) ) {
847 update_user_meta( $user->ID, 'wp_2fa_nominated_email_address', $user->user_email );
848 } elseif ( isset( $_POST['use_custom_email'] ) && isset( $_POST['custom-email-address'] ) ) {
849 update_user_meta( $user->ID, 'wp_2fa_nominated_email_address', sanitize_email( wp_unslash( $_POST['custom-email-address'] ) ) );
850 }
851
852 // Now lets grab the users enabled 2fa methods.
853 $get_array = filter_input_array( INPUT_GET );
854 $selected_method = sanitize_text_field( $get_array['enabled_methods'] );
855 // Check its one of our options.
856 if ( 'totp' === $selected_method || 'email' === $selected_method ) {
857 update_user_meta( $user->ID, 'wp_2fa_enabled_methods', sanitize_text_field( wp_unslash( $selected_method ) ) );
858 delete_user_meta( $user->ID, 'wp_2fa_grace_period_expiry' );
859 delete_user_meta( $user->ID, 'wp_2fa_user_enforced_instantly' );
860 }
861
862 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
863 exit();
864 }
865 }
866
867 /**
868 * Step View: `Finish`
869 */
870 private function wp_2fa_step_finish() {
871 ?>
872
873 <?php
874 $get_array = filter_input_array( INPUT_GET );
875 if ( isset( $get_array['first_time_setup'] ) ) {
876 $first_time_setup = sanitize_text_field( $get_array['first_time_setup'] );
877 } else {
878 $first_time_setup = '';
879 }
880 $user = wp_get_current_user();
881
882 $redirect = get_edit_profile_url( $user->ID );
883 if ( ! empty( $first_time_setup ) ) :
884 ?>
885 <h3><?php esc_html_e( 'Your login just got more secure', 'wp-2fa' ); ?></h3>
886 <p><?php esc_html_e( 'Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!', 'wp-2fa' ); ?></p>
887
888 <p><?php esc_html_e( 'You can exit this wizard now or continue to create backup codes.', 'wp-2fa' ); ?></p>
889
890 <form method="post" class="wp2fa-setup-form" autocomplete="off">
891 <?php wp_nonce_field( 'wp2fa-step-finish' ); ?>
892 <div class="wp2fa-setup-actions">
893 <a class="button button-primary" href="<?php echo esc_url( admin_url( 'options-general.php?page=wp-2fa-setup&current-step=backup_codes' ) ); ?>">
894 <?php esc_html_e( 'Continue & configure backup codes', 'wp-2fa' ); ?>
895 </a>
896 <a href="<?php echo esc_url( $redirect ); ?>" class="button button-secondary">
897 <?php esc_html_e( 'Close wizard, I’ll configure them later', 'wp-2fa' ); ?>
898 </a>
899 </div>
900 </form>
901
902 <?php else : ?>
903
904 <h3><?php esc_html_e( 'Your website just got more secure!', 'wp-2fa' ); ?></h3>
905 <p><?php esc_html_e( 'Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!', 'wp-2fa' ); ?></p>
906
907 <p><?php esc_html_e( 'You can exit this wizard now or continue to configure the plugin’s general settings. ', 'wp-2fa' ); ?></p>
908
909 <form method="post" class="wp2fa-setup-form" autocomplete="off">
910 <?php wp_nonce_field( 'wp2fa-step-finish' ); ?>
911 <div class="wp2fa-setup-actions">
912 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Continue & configure the settings', 'wp-2fa' ); ?>">
913 <?php esc_html_e( 'Continue & configure the settings', 'wp-2fa' ); ?>
914 </button>
915 <a href="<?php echo esc_url( $redirect ); ?>" class="button button-secondary">
916 <?php esc_html_e( 'Close wizard, I’ll configure them later', 'wp-2fa' ); ?>
917 </a>
918 </div>
919 </form>
920
921 <p class="description"><?php esc_html_e( 'Note: all the settings can be configured from the Settings > Two-factor Authentication entry of your WordPress menu.', 'wp-2fa' ); ?></p>
922
923 <?php
924 endif;
925 }
926
927 /**
928 * Step Save: `Finish`
929 */
930 private function wp_2fa_step_finish_save() {
931 // Verify nonce.
932 check_admin_referer( 'wp2fa-step-finish' );
933 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
934 exit();
935 }
936
937 /**
938 * Step View: `Finish`
939 */
940 private function wp_2fa_step_backup_codes() {
941 // Grab current user.
942 $user = wp_get_current_user();
943 $roles = (array) $user->roles;
944 // Create a nonce for use in ajax call to generate codes.
945 $nonce = wp_create_nonce( 'wp-2fa-backup-codes-generate-json-' . $user->ID );
946
947 $redirect = get_edit_user_link( $user->ID );
948 ?>
949 <div class="step-setting-wrapper active" data-step-title="<?php esc_html_e( 'Generate codes', 'wp-2fa' ); ?>">
950 <h3><?php esc_html_e( 'Generate backup codes', 'wp-2fa' ); ?></h3>
951 <p><?php esc_html_e( 'It is recommended to generate and print some backup codes in case you lose access to your primary 2FA method. ', 'wp-2fa' ); ?></p>
952
953 <form method="post" class="wp2fa-setup-form" autocomplete="off">
954 <?php wp_nonce_field( 'wp2fa-step-finish' ); ?>
955 <div class="wp2fa-setup-actions">
956 <button class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Generate backup codes', 'wp-2fa' ); ?>" data-trigger-generate-backup-codes data-nonce="<?php echo esc_attr( $nonce ); ?>" data-user-id="<?php echo esc_attr( $user->ID ); ?>">
957 <?php esc_html_e( 'Generate backup codes', 'wp-2fa' ); ?>
958 </button>
959 <a href="<?php echo esc_url( $redirect ); ?>" class="button button-secondary" type="submit" name="save_step" value="<?php esc_attr_e( 'I’ll generate them later', 'wp-2fa' ); ?>">
960 <?php esc_html_e( 'I’ll generate them later', 'wp-2fa' ); ?>
961 </a>
962 </div>
963 </form>
964 </div>
965
966 <div class="step-setting-wrapper align-center" data-step-title="<?php esc_html_e( 'Your backup codes', 'wp-2fa' ); ?>">
967 <h3><?php esc_html_e( 'Backup codes generated', 'wp-2fa' ); ?></h3>
968 <p><?php esc_html_e( 'Here are your backup codes:', 'wp-2fa' ); ?></p>
969 <code id="backup-codes-wrapper"></code>
970 <div class="wp2fa-setup-actions">
971 <button class="button button-primary" type="submit" value="<?php esc_attr_e( 'Download', 'wp-2fa' ); ?>" data-trigger-backup-code-download data-user="<?php echo esc_attr( $user->display_name ); ?>" data-website-url="<?php echo esc_attr( get_home_url() ); ?>">
972 <?php esc_html_e( 'Download', 'wp-2fa' ); ?>
973 </button>
974 <button class="button button-secondary" type="submit" value="<?php esc_attr_e( 'Print', 'wp-2fa' ); ?>" data-trigger-print data-nonce="<?php echo esc_attr( $nonce ); ?>" data-user-id="<?php echo esc_attr( $user->display_name ); ?>" data-website-url="<?php echo esc_attr( get_home_url() ); ?>">
975 <?php esc_html_e( 'Print', 'wp-2fa' ); ?>
976 </button>
977 </div>
978 </div>
979 <style>
980 .close-wizard-link { display: none; }
981 </style>
982 <?php
983 }
984
985 /**
986 * Step Save: `Finish`
987 */
988 private function wp_2fa_step_backup_codes_save() {
989 // Verify nonce.
990 check_admin_referer( 'wp2fa-step-finish' );
991 }
992
993 /**
994 * Step View: `Choose Methods`
995 */
996 private function wp_2fa_step_global_2fa_methods() {
997 $enforced_roles = trim( WP2FA::get_wp2fa_setting( 'enforced_roles' ) );
998 $enforced_users = trim( WP2FA::get_wp2fa_setting( 'enforced_users' ) );
999 $excluded_users = trim( WP2FA::get_wp2fa_setting( 'excluded_users' ) );
1000 $excluded_roles = trim( WP2FA::get_wp2fa_setting( 'excluded_roles' ) );
1001 ?>
1002 <form method="post" class="wp2fa-setup-form" autocomplete="off">
1003 <?php wp_nonce_field( 'wp2fa-step-choose-method' ); ?>
1004 <div class="step-setting-wrapper active" data-step-title="<?php esc_html_e( 'Choose 2FA methods', 'wp-2fa' ); ?>">
1005 <h3><?php esc_html_e( 'Which two-factor authentication methods can your users use on this website?', 'wp-2fa' ); ?></h3>
1006 <p><?php esc_html_e( 'When you disable one of the below 2FA methods none of your users can use it.', 'wp-2fa' ); ?></p>
1007 <fieldset>
1008 <div class="option-pill">
1009 <label for="basic">
1010 <input id="basic" name="wp_2fa_settings[enable_totp]" type="checkbox" value="enable_totp"
1011 <?php checked( 'enable_totp', WP2FA::get_wp2fa_setting( 'enable_totp' ), true ); ?>
1012 >
1013 <?php esc_html_e( 'One-time code generated with your app of choice (most reliable and secure)', 'wp-2fa' ); ?>
1014 </label>
1015 <?php
1016 printf( '<p class="description">%1$s <a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/" target="_blank">%2$s</a> %3$s</p>', esc_html__( 'Note: This method requires you to install one of the following 2FA apps: Google Authenticator, FreeOTP, Microsoft Authenticator, Duo Security, Authy and LastPass. All of these apps are free and can be downloaded from the Google Play and Apple Appstore. Read our guides on', 'wp-2fa' ), esc_html__( 'our knowledge base', 'wp-2fa' ), esc_html__( 'for more information on how to setup these apps.', 'wp-2fa' ) );
1017 ?>
1018 </p>
1019 </div>
1020 <div class="option-pill">
1021 <label for="geek">
1022 <input id="geek" name="wp_2fa_settings[enable_email]" type="checkbox" value="enable_email"
1023 <?php checked( WP2FA::get_wp2fa_setting( 'enable_email' ), 'enable_email' ); ?>
1024 >
1025 <?php esc_html_e( 'One-time code sent to user over email', 'wp-2fa' ); ?>
1026 </label>
1027 </div>
1028 </fieldset>
1029 <div class="wp2fa-setup-actions">
1030 <a class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
1031 </div>
1032 </div>
1033 <div class="step-setting-wrapper" data-step-title="<?php esc_html_e( '2FA policy', 'wp-2fa' ); ?>">
1034 <h3><?php esc_html_e( 'Do you want to enforce 2FA for some, or all the users?', 'wp-2fa' ); ?></h3>
1035 <p><?php esc_html_e( 'When you enforce 2FA the users will be prompted to configure 2FA the next time they login. Users have a grace period for configuring 2FA. You can configure the grace period and also exclude user(s) or role(s) in this settings page.', 'wp-2fa' ); ?></p>
1036 <fieldset class="contains-hidden-inputs">
1037 <label for="all-users">
1038 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="all-users" value="all-users"
1039 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'all-users' ); ?>
1040 >
1041 <span><?php esc_html_e( 'All users', 'wp-2fa' ); ?></span>
1042 </label>
1043
1044 <br/>
1045 <label for="certain-roles-only">
1046 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="certain-roles-only" value="certain-roles-only"
1047 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'certain-roles-only' ); ?>
1048 data-unhide-when-checked=".certain-roles-only-inputs, .certain-users-only-inputs">
1049 <span><?php esc_html_e( 'Only for specific users and roles', 'wp-2fa' ); ?></span>
1050 </label>
1051 <fieldset class="hidden certain-roles-only-inputs">
1052 <br/>
1053 <input type="text" id="enforced_roles_search" placeholder="Search roles">
1054 <input type="hidden" id="enforced_roles" name="wp_2fa_settings[enforced_roles]" value="<?php echo esc_attr( $enforced_roles ); ?>">
1055 <div id="enforced_roles_buttons"></div>
1056 </fieldset>
1057 <fieldset class="hidden certain-users-only-inputs">
1058 <br/>
1059 <input type="text" id="enforced_users_search" placeholder="Search users">
1060 <input type="hidden" id="enforced_users" name="wp_2fa_settings[enforced_users]" value="<?php echo esc_attr( $enforced_users ); ?>">
1061 <div id="enforced_users_buttons"></div>
1062 </fieldset>
1063 </label>
1064
1065 <br/>
1066 <label for="do-not-enforce">
1067 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="do-not-enforce" value="do-not-enforce"
1068 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'do-not-enforce' ); ?>
1069 >
1070 <span><?php esc_html_e( 'Do not enforce 2FA on any users', 'wp-2fa' ); ?></span>
1071 </label>
1072 <br/>
1073 </fieldset>
1074 <div class="wp2fa-setup-actions">
1075 <a class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
1076 </div>
1077 </div>
1078 <div class="step-setting-wrapper" data-step-title="<?php esc_html_e( 'Exclude users', 'wp-2fa' ); ?>">
1079 <h3><?php esc_html_e( 'Do you want to exclude any users or roles from 2FA?', 'wp-2fa' ); ?></h3>
1080 <p><?php esc_html_e( 'If you are enforcing 2FA on all users but for some reason you would like to exclude individual user(s) or users with a specific role, you can exclude them below', 'wp-2fa' ); ?></p>
1081 <fieldset>
1082 <div class="option-pill">
1083 <label for="basic"><?php esc_html_e( 'Exclude the following users', 'wp-2fa' ); ?>
1084 <input type="text" class="input wide" id="excluded_users_search" placeholder="Search user name">
1085 <input type="hidden" id="excluded_users" name="wp_2fa_settings[excluded_users]" value="<?php echo esc_attr( $excluded_users ); ?>">
1086 <div id="excluded_users_buttons"></div>
1087 </label>
1088 <label for="geek"><?php esc_html_e( 'Exclude the following roles', 'wp-2fa' ); ?>
1089 <input type="text" class="input wide" id="excluded_roles_search" placeholder="Search roles">
1090 <input type="hidden" id="excluded_roles" name="wp_2fa_settings[excluded_roles]" value="<?php echo esc_attr( $excluded_roles ); ?>">
1091 <div id="excluded_roles_buttons"></div>
1092 </label>
1093 </div>
1094 </fieldset>
1095 <div class="wp2fa-setup-actions">
1096 <a class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
1097 </div>
1098 </div>
1099
1100 <?php if ( WP2FA::is_this_multisite() ) : ?>
1101 <div class="step-setting-wrapper" data-step-title="<?php esc_html_e( 'Exclude sites', 'wp-2fa' ); ?>">
1102 <h3><?php esc_html_e( 'Do you want to exclude all the users of a site from 2FA?', 'wp-2fa' ); ?></h3>
1103 <p><?php esc_html_e( 'If you are enforcing 2FA on all users but for some reason you do not want to enforce it on a specific sub site, specify the sub site name below:', 'wp-2fa' ); ?></p>
1104 <fieldset>
1105 <div class="option-pill">
1106 <label for="excluded_sites_search"><?php esc_html_e( 'Exclude the following sites', 'wp-2fa' ); ?>
1107 <input type="text" id="excluded_sites_search" placeholder="Search network">
1108 <input type="hidden" id="excluded_sites" name="wp_2fa_settings[excluded_sites]"
1109 value="<?php echo trim( sanitize_text_field( WP2FA::get_wp2fa_setting( 'excluded_sites' ) ) ); ?>">
1110 <div id="excluded_sites_buttons"></div>
1111 </label>
1112 </div>
1113 </fieldset>
1114 <div class="wp2fa-setup-actions">
1115 <a class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
1116 </div>
1117 </div>
1118 <?php endif; ?>
1119
1120 <?php
1121 $grace_period = (int) WP2FA::get_wp2fa_setting( 'grace-period' );
1122 $testing = get_option( 'wp_2fa_test_grace' );
1123 if ( '1' === $testing ) {
1124 $grace_max = 600;
1125 } else {
1126 $grace_max = 10;
1127 }
1128 ?>
1129
1130 <div class="step-setting-wrapper" data-step-title="<?php esc_html_e( 'Grace period', 'wp-2fa' ); ?>">
1131 <h3><?php esc_html_e( 'How long should the grace period for your users be?', 'wp-2fa' ); ?></h3>
1132 <p><?php esc_html_e( 'When you configure the 2FA policies and require users to configure 2FA, they can either have a grace period to configure 2FA, or can be required to configure 2FA before the next time they login. Choose which method you\'d like to use:', 'wp-2fa' ); ?></p>
1133 <fieldset class="contains-hidden-inputs">
1134 <label for="no-grace-period">
1135 <input type="radio" name="wp_2fa_settings[grace-policy]" id="no-grace-period" value="no-grace-period"
1136 <?php checked( WP2FA::get_wp2fa_setting( 'grace-policy' ), 'no-grace-period' ); ?>
1137 >
1138 <span><?php esc_html_e( 'Users have to configure 2FA straight away.', 'wp-2fa' ); ?></span>
1139 </label>
1140
1141 <br/>
1142 <label for="use-grace-period">
1143 <input type="radio" name="wp_2fa_settings[grace-policy]" id="use-grace-period" value="use-grace-period"
1144 <?php checked( WP2FA::get_wp2fa_setting( 'grace-policy' ), 'use-grace-period' ); ?>
1145 data-unhide-when-checked=".grace-period-inputs">
1146 <span><?php esc_html_e( 'Give users a grace period to configure 2FA', 'wp-2fa' ); ?></span>
1147 </label>
1148 <br>
1149 <fieldset class="grace-period-inputs">
1150 <br/>
1151 <input type="number" id="grace-period" name="wp_2fa_settings[grace-period]" value="<?php echo esc_attr( $grace_period ); ?>" min="1" max="<?php echo esc_attr( $grace_max ); ?>">
1152 <label class="radio-inline">
1153 <input type="radio" name="wp_2fa_settings[grace-period-denominator]" value="hours"
1154 <?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'hours' ); ?>
1155 >
1156 <?php esc_html_e( 'Hours', 'wp-2fa' ); ?>
1157 </label>
1158 <label class="radio-inline">
1159 <input type="radio" name="wp_2fa_settings[grace-period-denominator]" value="days"
1160 <?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'days' ); ?>
1161 >
1162 <?php esc_html_e( 'Days', 'wp-2fa' ); ?>
1163 </label>
1164 <?php
1165 $testing = get_option( 'wp_2fa_test_grace' );
1166 if ( '1' === $testing ) {
1167 ?>
1168 <label class="radio-inline">
1169 <input type="radio" name="wp_2fa_settings[grace-period-denominator]" value="seconds"
1170 <?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'seconds' ); ?>
1171 >
1172 <?php esc_html_e( 'Seconds', 'wp-2fa' ); ?>
1173 </label>
1174 <?php
1175 }
1176 ?>
1177 <p><?php esc_html_e( 'Note: If users do not configure it within the configured stipulated time, their account will be locked and have to be unlocked manually.', 'wp-2fa' ); ?></p>
1178 </fieldset>
1179 </fieldset>
1180 <div class="wp2fa-setup-actions">
1181 <a class="button button-primary continue-wizard hidden" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
1182 <button class="button button-primary save-wizard" type="submit" name="save_step" value="<?php esc_attr_e( 'All done', 'wp-2fa' ); ?>"><?php esc_html_e( 'All done', 'wp-2fa' ); ?></button>
1183 </div>
1184 </div>
1185
1186 <div class="step-setting-wrapper hidden" data-step-title="<?php esc_html_e( 'Notify users', 'wp-2fa' ); ?>">
1187 <h3><?php esc_html_e( 'Do you want to notify users now?', 'wp-2fa' ); ?></h3>
1188 <p><?php esc_html_e( 'When you require users to configure 2FA via policies, the plugin notifies the user with an email and a message in the WordPress dashboard. Do you want to send the emails now?', 'wp-2fa' ); ?></p>
1189 <fieldset>
1190 <div class="option-pill">
1191 <label for="notify_users">
1192 <input type="checkbox" id="notify_users" name="wp_2fa_settings[notify_users]" value="notify_users" checked>
1193 <span><?php esc_html_e( 'Notify users now.', 'wp-2fa' ); ?></span>
1194 </label>
1195 </div>
1196
1197 </fieldset>
1198 <div class="wp2fa-setup-actions">
1199 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'All done', 'wp-2fa' ); ?>"><?php esc_html_e( 'All done', 'wp-2fa' ); ?></button>
1200 </div>
1201 </div>
1202
1203 </form>
1204 <?php
1205 }
1206
1207 /**
1208 * Step Save: `Choose Method`
1209 */
1210 private function wp_2fa_step_global_2fa_methods_save() {
1211 // Check nonce.
1212 check_admin_referer( 'wp2fa-step-choose-method' );
1213
1214 // Grab current user.
1215 $user = wp_get_current_user();
1216
1217 // Setup args we may need, depending if this is a MS setup or not.
1218 $users = array();
1219 $users_args = array();
1220 if ( WP2FA::is_this_multisite() ) {
1221 $users_args['blog_id'] = 0;
1222 }
1223 $total_users = count_users();
1224 $batch_size = 2000;
1225 $slices = ceil( $total_users['total_users'] / $batch_size );
1226
1227 // Ensure user has capacity to be making these kinds of changes.
1228 if ( isset( $_POST['wp_2fa_settings'] ) && current_user_can( 'manage_options' ) ) {
1229 $settings = array_map( 'esc_attr', wp_unslash( $_POST['wp_2fa_settings'] ) );
1230
1231 // Remove grace period from users via BG process.
1232 for ( $count = 0; $count < $slices; $count++ ) {
1233 $users_args = array(
1234 'number' => $batch_size,
1235 'offset' => $count * $batch_size,
1236 'fields' => array( 'ID' ),
1237 );
1238 if ( WP2FA::is_this_multisite() ) {
1239 $users_args['blog_id'] = 0;
1240 }
1241 $users = get_users( $users_args );
1242 if ( ! empty( $users ) ) {
1243 $background_process = new ProcessUserMetaUpdate();
1244 $item_to_process = array();
1245 $item_to_process['users'] = $users;
1246 $item_to_process['task'] = 'delete_grace_period';
1247 $background_process->push_to_queue( $item_to_process );
1248 }
1249 $background_process->save()->dispatch();
1250 }
1251
1252 if ( ! isset( $settings['enable_totp'] ) && ! isset( $settings['enable_email'] ) ) {
1253 add_settings_error(
1254 'wp_2fa_settings',
1255 esc_attr( 'settings_error' ),
1256 esc_html__( 'At least one 2FA method should be enabled.', 'wp-2fa' ),
1257 'error'
1258 );
1259 }
1260
1261 // Compare current to old value to see if a method which was once enabled, has now been disabled.
1262 if ( ! isset( $settings['enable_totp'] ) && 'enable_totp' === WP2FA::get_wp2fa_setting( 'enable_totp' ) ) {
1263 for ( $count = 0; $count < $slices; $count++ ) {
1264 $users_args = array(
1265 'number' => $batch_size,
1266 'offset' => $count * $batch_size,
1267 'fields' => array( 'ID' ),
1268 );
1269 $users = get_users( $users_args );
1270 if ( ! empty( $users ) ) {
1271 $background_process = new ProcessUserMetaUpdate();
1272 $item_to_process = array();
1273 $item_to_process['users'] = $users;
1274 $item_to_process['task'] = 'remove_enabled_methods';
1275 $item_to_process['method_to_remove'] = 'totp';
1276 $background_process->push_to_queue( $item_to_process );
1277 }
1278
1279 $background_process->save()->dispatch();
1280 }
1281 }
1282 if ( ! isset( $settings['enable_email'] ) && 'enable_email' === WP2FA::get_wp2fa_setting( 'enable_email' ) ) {
1283 for ( $count = 0; $count < $slices; $count++ ) {
1284 $users_args = array(
1285 'number' => $batch_size,
1286 'offset' => $count * $batch_size,
1287 'fields' => array( 'ID' ),
1288 );
1289 $users = get_users( $users_args );
1290 if ( ! empty( $users ) ) {
1291 $background_process = new ProcessUserMetaUpdate();
1292 $item_to_process = array();
1293 $item_to_process['users'] = $users;
1294 $item_to_process['task'] = 'remove_enabled_methods';
1295 $item_to_process['method_to_remove'] = 'email';
1296 $background_process->push_to_queue( $item_to_process );
1297 }
1298
1299 $background_process->save()->dispatch();
1300 }
1301 }
1302
1303 if ( isset( $settings['enable_totp'] ) && 'enable_totp' === $settings['enable_totp'] ) {
1304 $output['enable_totp'] = sanitize_text_field( $settings['enable_totp'] );
1305 }
1306
1307 if ( isset( $settings['enable_email'] ) && 'enable_email' === $settings['enable_email'] ) {
1308 $output['enable_email'] = sanitize_text_field( $settings['enable_email'] );
1309 }
1310
1311 if ( isset( $settings['enforcment-policy'] ) && 'all-users' === $settings['enforcment-policy'] || isset( $settings['enforcment-policy'] ) && 'certain-users-only' === $settings['enforcment-policy'] || isset( $settings['enforcment-policy'] ) && 'certain-roles-only' === $settings['enforcment-policy'] || isset( $settings['enforcment-policy'] ) && 'do-not-enforce' === $settings['enforcment-policy'] ) {
1312 $output['enforcment-policy'] = $settings['enforcment-policy'];
1313 }
1314
1315 if ( isset( $settings['enforced_roles'] ) ) {
1316 $output['enforced_roles'] = trim( sanitize_text_field( $settings['enforced_roles'] ) );
1317 }
1318
1319 if ( isset( $settings['enforced_users'] ) ) {
1320 $output['enforced_users'] = trim( sanitize_text_field( $settings['enforced_users'] ) );
1321 }
1322
1323 if ( isset( $settings['excluded_users'] ) ) {
1324 $output['excluded_users'] = trim( sanitize_text_field( $settings['excluded_users'] ) );
1325
1326 // Wipe user 2fa data.
1327 $excluded_users_array = explode( ',', $output['excluded_users'] );
1328 for ( $count = 0; $count < $slices; $count++ ) {
1329 $users_args = array(
1330 'number' => $batch_size,
1331 'offset' => $count * $batch_size,
1332 'fields' => array( 'ID' ),
1333 );
1334 $users = get_users( $users_args );
1335 if ( ! empty( $users ) ) {
1336 $background_process = new ProcessUserMetaUpdate();
1337 $item_to_process = array();
1338 $item_to_process['users'] = $users;
1339 $item_to_process['task'] = 'wipe_all_2fa_user_data';
1340 $item_to_process['excluded_users'] = $excluded_users_array;
1341 $background_process->push_to_queue( $item_to_process );
1342 }
1343
1344 $background_process->save()->dispatch();
1345 }
1346 }
1347
1348 if ( isset( $settings['excluded_roles'] ) ) {
1349 $output['excluded_roles'] = trim( sanitize_text_field( $settings['excluded_roles'] ) );
1350
1351 // Wipe user 2fa data.
1352 $excluded_roles_array = array_filter( explode( ',', strtolower( $output['excluded_roles'] ) ) );
1353 for ( $count = 0; $count < $slices; $count++ ) {
1354 $users_args = array(
1355 'number' => $batch_size,
1356 'offset' => $count * $batch_size,
1357 'fields' => array( 'ID' ),
1358 );
1359 $users = get_users( $users_args );
1360 if ( ! empty( $users ) ) {
1361 $background_process = new ProcessUserMetaUpdate();
1362 $item_to_process = array();
1363 $item_to_process['users'] = $users;
1364 $item_to_process['task'] = 'wipe_all_2fa_user_data';
1365 $item_to_process['excluded_roles'] = $excluded_roles_array;
1366 $background_process->push_to_queue( $item_to_process );
1367 }
1368
1369 $background_process->save()->dispatch();
1370 }
1371
1372 if ( WP2FA::is_this_multisite() ) {
1373 if ( isset( $settings['excluded_sites'] ) ) {
1374 $output['excluded_sites'] = trim( sanitize_text_field( $settings['excluded_sites'] ) );
1375 } else {
1376 $output['excluded_sites'] = '';
1377 }
1378 } else {
1379 $output['excluded_sites'] = '';
1380 }
1381
1382 if ( isset( $settings['grace-policy'] ) ) {
1383 $output['grace-policy'] = sanitize_text_field( $settings['grace-policy'] );
1384 }
1385
1386 if ( isset( $settings['grace-period'] ) ) {
1387 if ( 0 === (int) $settings['grace-period'] ) {
1388 add_settings_error(
1389 'wp_2fa_settings',
1390 esc_attr( 'settings_error' ),
1391 esc_html__( 'Grace period must be at least 1 day/hour', 'wp-2fa' ),
1392 'error'
1393 );
1394 $output['grace-period'] = 1;
1395 } else {
1396 $output['grace-period'] = (int) $settings['grace-period'];
1397 }
1398 }
1399
1400 if ( isset( $settings['grace-period-denominator'] ) && 'days' === $settings['grace-period-denominator'] || isset( $settings['grace-period-denominator'] ) && 'hours' === $settings['grace-period-denominator'] || isset( $settings['grace-period-denominator'] ) && 'seconds' === $settings['grace-period-denominator'] ) {
1401 $output['grace-period-denominator'] = sanitize_text_field( $settings['grace-period-denominator'] );
1402 }
1403
1404 if ( isset( $settings['enable_grace_cron'] ) ) {
1405 $output['enable_grace_cron'] = (bool) $settings['enable_grace_cron'];
1406 }
1407
1408 if ( isset( $settings['2fa_main_user'] ) ) {
1409 $output['2fa_settings_last_updated_by'] = (int) $settings['2fa_main_user'];
1410 }
1411
1412 if ( isset( $settings['limit_access'] ) ) {
1413 $output['limit_access'] = (bool) $settings['limit_access'];
1414 }
1415
1416 if ( isset( $settings['grace-period'] ) && isset( $settings['grace-period-denominator'] ) ) {
1417 // Turn inputs into a useable string.
1418 $create_a_string = $output['grace-period'] . ' ' . $output['grace-period-denominator'];
1419 // Turn that string into a time.
1420 $grace_expiry = strtotime( $create_a_string );
1421 $output['grace-period-expiry-time'] = sanitize_text_field( $grace_expiry );
1422 }
1423
1424 // Ensure default is not affected.
1425 $output['create-custom-user-page'] = 'no';
1426
1427 // Fetch users and apply the grace period tp their user meta.
1428 if ( isset( $settings['enforcment-policy'] ) && 'do-not-enforce' !== $settings['enforcment-policy'] && ! isset( $settings['grace-period-expiry-time'] ) ) {
1429
1430 // If we are specifying to enforce 2fa for specific users, we have no need to check if they are eligble or excluded, so we dont.
1431 if ( isset( $settings['enforcment-policy'] ) && 'certain-roles-only' === $settings['enforcment-policy'] && isset( $settings['enforced_users'] ) && WP2FA::get_wp2fa_setting( 'enforced_users' ) !== $settings['enforced_users'] || isset( $settings['enforcment-policy'] ) && 'certain-roles-only' === $settings['enforcment-policy'] && isset( $settings['enforced_roles'] ) && WP2FA::get_wp2fa_setting( 'enforced_roles' ) !== $settings['enforced_roles'] ) {
1432 $enforced_users_array = array_filter( explode( ',', $settings['enforced_users'] ) );
1433 $grace_string = $output['grace-period'] . ' ' . $output['grace-period-denominator'];
1434 // Flush the old expiry away from ALL users, we will re-apply them based on the current setup at the end of this.
1435 for ( $count = 0; $count < $slices; $count++ ) {
1436 $users_args = array(
1437 'number' => $batch_size,
1438 'offset' => $count * $batch_size,
1439 'fields' => array( 'ID', 'user_login' ),
1440 );
1441 if ( WP2FA::is_this_multisite() ) {
1442 $users_args['blog_id'] = 0;
1443 }
1444 $users = get_users( $users_args );
1445 if ( ! empty( $users ) ) {
1446 foreach ( $users as $user ) {
1447 if ( in_array( $user->user_login, $enforced_users_array ) ) {
1448 $background_process = new ProcessUserMetaUpdate();
1449 $item_to_process = array();
1450 $item_to_process['user'] = $user;
1451 $item_to_process['task'] = 'enforce_2fa_for_user';
1452 $item_to_process['grace_expiry'] = $grace_expiry;
1453 $item_to_process['grace_policy'] = sanitize_text_field( $settings['grace-policy'] );
1454 $item_to_process['notify_users'] = isset( $settings['notify_users'] ) ? $settings['notify_users'] : false;
1455 $item_to_process['grace-period-expiry-time'] = $grace_string;
1456 $background_process->push_to_queue( $item_to_process );
1457 } else {
1458 if ( isset( $output['enforced_roles'] ) && empty( $output['enforced_roles'] ) ) {
1459 $enforced_roles = 'none';
1460 } else {
1461 $enforced_roles = $output['enforced_roles'];
1462 }
1463 if ( isset( $output['enforced_users'] ) && empty( $output['enforced_users'] ) ) {
1464 $enforced_users = 'none';
1465 } else {
1466 $enforced_users = $output['enforced_users'];
1467 }
1468 $is_needed = Authentication::is_user_eligible_for_2fa( $user->ID, $settings['enforcment-policy'], $output['excluded_users'], $output['excluded_roles'], $enforced_users, $enforced_roles );
1469 $is_user_excluded = WP2FA::is_user_excluded( $user, $output['excluded_users'], $output['excluded_roles'], $output['excluded_sites'] );
1470 if ( $is_needed && ! $is_user_excluded ) {
1471 $item_to_process = array();
1472 $item_to_process['user'] = $user;
1473 $item_to_process['task'] = 'enforce_2fa_for_user';
1474 $item_to_process['grace_expiry'] = $grace_expiry;
1475 $item_to_process['grace_policy'] = sanitize_text_field( $output['grace-policy'] );
1476 $item_to_process['notify_users'] = isset( $settings['notify_users'] ) ? $settings['notify_users'] : false;
1477 $item_to_process['grace-period-expiry-time'] = $grace_string;
1478 $background_process->push_to_queue( $item_to_process );
1479 }
1480 }
1481 }
1482 }
1483
1484 $background_process->save()->dispatch();
1485 }
1486 } else {
1487 $grace_string = $output['grace-period'] . ' ' . $output['grace-period-denominator'];
1488 for ( $count = 0; $count < $slices; $count++ ) {
1489 $users_args = array(
1490 'number' => $batch_size,
1491 'offset' => $count * $batch_size,
1492 'fields' => array( 'ID' ),
1493 );
1494 if ( WP2FA::is_this_multisite() ) {
1495 $users_args['blog_id'] = 0;
1496 }
1497 $users = get_users( $users_args );
1498
1499 if ( ! empty( $users ) ) {
1500 $background_process = new ProcessUserMetaUpdate();
1501 $item_to_process = array();
1502 $item_to_process['users'] = $users;
1503 $item_to_process['task'] = 'enforce_2fa_for_user';
1504 $item_to_process['grace_expiry'] = $grace_expiry;
1505 $item_to_process['grace_policy'] = sanitize_text_field( $settings['grace-policy'] );
1506 $item_to_process['notify_users'] = isset( $settings['notify_users'] ) ? $settings['notify_users'] : false;
1507 $item_to_process['enforcment-policy'] = $settings['enforcment-policy'];
1508 $item_to_process['excluded_users'] = $output['excluded_users'];
1509 $item_to_process['excluded_roles'] = $output['excluded_roles'];
1510 $item_to_process['enforced_users'] = $output['enforced_users'];
1511 $item_to_process['enforced_roles'] = $output['enforced_roles'];
1512 $item_to_process['excluded_sites'] = $output['excluded_sites'];
1513 $item_to_process['grace-period-expiry-time'] = $grace_string;
1514 $background_process->push_to_queue( $item_to_process );
1515 }
1516
1517 $background_process->save()->dispatch();
1518 }
1519 }
1520 }
1521 }
1522
1523 if ( WP2FA::is_this_multisite() ) {
1524 update_network_option( null, 'wp_2fa_settings', $output );
1525 add_network_option( null, 'wp_2fa_setup_wizard_complete', true );
1526 } else {
1527 update_option( 'wp_2fa_settings', $output );
1528 add_option( 'wp_2fa_setup_wizard_complete', true );
1529 }
1530 }
1531
1532 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
1533 exit();
1534 }
1535
1536 /**
1537 * Send email with fresh code, or to setup email 2fa.
1538 *
1539 * @param int $user_id User id we want to send the message to.
1540 * @param string $nonce The nonce.
1541 */
1542 public static function send_authentication_setup_email( $user_id, $nonce = '' ) {
1543
1544 // If we have a nonce posted, check it.
1545 if ( wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
1546 $nonce_check = wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'wp-2fa-send-setup-email' );
1547 if ( ! $nonce_check ) {
1548 return false;
1549 exit();
1550 }
1551 }
1552
1553 if ( isset( $_POST['user_id'] ) ) {
1554 $user = get_userdata( intval( $_POST['user_id'] ) );
1555 } else {
1556 $user = get_userdata( $user_id );
1557 }
1558
1559 // Seeing as we got this far, we need to clear notices to make way for anything fresh.
1560 delete_user_meta( $user->ID, self::NOTICES_META_KEY );
1561
1562 // Grab email address is its provided.
1563 if ( isset( $_POST['email_address'] ) ) {
1564 $email = sanitize_email( $_POST['email_address'] );
1565 } else {
1566 $email = sanitize_email( $user->user_email );
1567 }
1568
1569 if ( wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
1570 update_user_meta( $user->ID, 'wp_2fa_nominated_email_address', $email );
1571 }
1572
1573 $enabled_email_address = get_user_meta( $user->ID, 'wp_2fa_nominated_email_address', true );
1574
1575 // Generate a token and setup email.
1576 $token = Authentication::generate_token( $user->ID );
1577 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_email_subject' ), $user->ID ) );
1578 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_email_body' ), $user->ID, $token ) );
1579
1580 if ( ! empty( $enabled_email_address ) ) {
1581 $email_address = $enabled_email_address;
1582 } else {
1583 $email_address = $user->user_email;
1584 }
1585 $headers = 'Content-type: text/html;charset=utf-8' . "\r\n";
1586
1587 if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
1588 $headers .= 'From: ' . WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) . ' <' . WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) . '>' . "\r\n";
1589 }
1590
1591 return wp_mail( $email_address, $subject, $message, $headers );
1592 }
1593
1594 /**
1595 * Send email to setup authentication
1596 */
1597 public function regenerate_authentication_key() {
1598 // Grab current user.
1599 $user = wp_get_current_user();
1600
1601 // Delete the key and enabled methods
1602 Authentication::delete_user_totp_key( $user->ID );
1603 $wipe_enabled_methods = delete_user_meta( $user->ID, 'wp_2fa_enabled_methods' );
1604
1605 // Return something, not sure why
1606 return true;
1607 }
1608
1609 /**
1610 * Step View: `Setup Authenticator`
1611 */
1612 private function wp_2fa_step_reconfigure_authenticator() {
1613 // Grab current user
1614 $user = wp_get_current_user();
1615
1616 // Grab key from user meta
1617 $key = Authentication::get_user_totp_key( $user->ID );
1618
1619 // If no key is present, lets make one
1620 if ( empty( $key ) ) {
1621 $key = Authentication::generate_key();
1622 $update = update_user_meta( $user->ID, 'wp_2fa_totp_key', $key );
1623 }
1624
1625 // Setup site information, used when generating our QR code
1626 $site_name = get_bloginfo( 'name', 'display' );
1627 $totp_title = apply_filters( 'wp_2fa_totp_title', $site_name . ':' . $user->user_login, $user );
1628
1629 // Now lets grab the users enabled 2fa methods.
1630 $selected_method = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
1631
1632 // Create a nonce incase we want to reset the key
1633 $nonce = wp_create_nonce( 'wp-2fa-backup-codes-generate-json-' . $user->ID );
1634
1635 if ( ! isset( $notices['error'] ) && empty( $notices['error'] ) ) {
1636 $is_active = 'active';
1637 $is_active2 = '';
1638 } else {
1639 $is_active = '';
1640 $is_active2 = 'active';
1641 }
1642
1643 // TOTP is enabled for the user, so lets display the relevant steps.
1644 // Here we wrap each "sub step" (a step within a step) in .tep-setting-wrapper, and nudge to next "sub step" with next_step_setting button.
1645 if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) ) {
1646 ?>
1647 <div class="step-setting-wrapper <?php echo esc_attr( $is_active ); ?>" data-step-title="<?php esc_html_e( 'Reconfigure 2FA', 'wp-2fa' ); ?>">
1648 <h3>
1649 <?php esc_html_e( 'Reconfigure the 2FA App', 'wp-2fa' ); ?>
1650 </h3>
1651 <p>
1652 <?php esc_html_e( 'Click the below button to reconfigure the current 2FA method. You can use this if for example, you want to change your device or 2FA app. Note that once reset reset you will have to re-scan the QR code on all devices you want this to work on because the previous codes will stop working.', 'wp-2fa' ); ?>
1653 </p>
1654 <div class="wp2fa-setup-actions">
1655 <a href="<?php echo esc_url( admin_url( 'options-general.php?page=wp-2fa-setup&current-step=setup_method&enabled_method=totp' ) ); ?>" class="button button-primary do-not-reload" data-trigger-reset-key data-nonce="<?php echo esc_attr( $nonce ); ?>" data-user-id="<?php echo esc_attr( $user->ID ); ?>"><?php esc_html_e( 'Reset Key', 'wp-2fa' ); ?></a>
1656 </div>
1657 </div>
1658
1659 <?php
1660 } elseif ( ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) {
1661 $setupnonce = wp_create_nonce( 'wp-2fa-send-setup-email' );
1662 ?>
1663 <div class="step-setting-wrapper <?php echo esc_attr( $is_active ); ?>" data-step-title="<?php esc_html_e( 'Configure email', 'wp-2fa' ); ?>">
1664 <h3><?php esc_html_e( 'Setup the 2FA method', 'wp-2fa' ); ?></h3>
1665 <p>
1666 <?php esc_html_e( 'Please select the email address where the one-time code should be sent:', 'wp-2fa' ); ?>
1667 </p>
1668 <fieldset>
1669 <label for="use_wp_email">
1670 <span><?php esc_html_e( 'Type in below the new email address where you want to receive the 2FA one-time codes.', 'wp-2fa' ); ?></span>
1671 <input type="email" name="custom-email-address" id="custom-email-address" class="input wide" value=""/>
1672 </label>
1673 </fieldset>
1674 <div class="wp2fa-setup-actions">
1675 <button class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" data-trigger-setup-email data-user-id="<?php echo esc_attr( $user->ID ); ?>" data-nonce="<?php echo esc_attr( $setupnonce ); ?>"><?php esc_html_e( 'Change email address', 'wp-2fa' ); ?></button>
1676 </div>
1677 </div>
1678
1679 <div class="step-setting-wrapper <?php echo esc_attr( $is_active2 ); ?>" data-step-title="<?php esc_html_e( 'Validate email', 'wp-2fa' ); ?>">
1680 <form method="post" class="wp2fa-setup-form" autocomplete="off">
1681 <?php wp_nonce_field( 'wp2fa-step-login' ); ?>
1682 <h4><?php esc_html_e( 'Almost there…', 'wp-2fa' ); ?></h4>
1683 <p><?php esc_html_e( 'Please type in the one-time code sent to your email address to finalize the setup.', 'wp-2fa' ); ?></p>
1684 <fieldset>
1685 <label for="2fa-email-authcode">
1686 <?php esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?>
1687 <input type="tel" name="wp-2fa-email-authcode" id="wp-2fa-email-authcode" class="input" value="" size="20" pattern="[0-9]*" />
1688 </label>
1689 </fieldset>
1690
1691 <input type="hidden" name="2fa-totp-key" value="<?php echo esc_attr( $key ); ?>" />
1692 <div class="wp2fa-setup-actions">
1693 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Finish', 'wp-2fa' ); ?>"><?php esc_html_e( 'Finish', 'wp-2fa' ); ?></button>
1694 </div>
1695 </form>
1696 </div>
1697 <?php
1698 // Display any error notices if they are available.
1699 if ( isset( $notices['error'] ) && ! empty( $notices['error'] ) ) {
1700 foreach ( $notices['error'] as $notice ) {
1701 echo '<p class="description error">' . wp_kses_post( $notice ) . '</p>';
1702 }
1703 }
1704 }
1705 }
1706
1707 /**
1708 * Step Save: `Setup Authenticator`
1709 */
1710 private function wp_2fa_step_reconfigure_authenticator_save() {
1711 // Check nonce.
1712 check_admin_referer( 'wp2fa-step-login' );
1713 // Grab current user
1714 $user = wp_get_current_user();
1715
1716 // Setup some empty arrays which will may fill later, should an error arise along the way.
1717 $notices = array();
1718 $errors = array();
1719
1720 // Grab key from the $_POST
1721 if ( isset( $_POST['wp-2fa-totp-key'] ) ) {
1722 $current_key = sanitize_text_field( wp_unslash( $_POST['wp-2fa-totp-key'] ) );
1723 }
1724
1725 // Check if we are dealing with totp or email, if totp validate and store a new secret key.
1726 if ( ! empty( $_POST['wp-2fa-totp-authcode'] ) && ! empty( $current_key ) ) {
1727 if ( Authentication::is_valid_key( $current_key ) || ! is_numeric( $_POST['wp-2fa-totp-authcode'] ) ) {
1728 if ( ! Authentication::is_valid_authcode( $current_key, $_POST['wp-2fa-totp-authcode'] ) ) {
1729 $errors[] = esc_html__( 'Invalid Two Factor Authentication code.', 'wp-2fa' );
1730 }
1731 } else {
1732 $errors[] = esc_html__( 'Invalid Two Factor Authentication secret key.', 'wp-2fa' );
1733 }
1734
1735 // If its not totp, is it email?
1736 } elseif ( ! empty( $_POST['wp-2fa-email-authcode'] ) ) {
1737 if ( ! Authentication::validate_token( $user->ID, $_POST['wp-2fa-email-authcode'] ) ) {
1738 $errors[] = __( 'Invalid Email Authentication code.', 'wp-2fa' );
1739 }
1740 } else {
1741 $errors[] = __( 'Please enter the code to finalize the 2FA setup.', 'wp-2fa' );
1742 }
1743
1744 if ( ! empty( $errors ) ) {
1745 $notices['error'] = $errors;
1746 }
1747
1748 if ( ! empty( $notices ) ) {
1749 update_user_meta( $user->ID, self::NOTICES_META_KEY, $notices );
1750 delete_user_meta( $user->ID, 'wp_2fa_nominated_email_address' );
1751 }
1752
1753 // If no errors found, lets continue to next step and clear the notices, should any be present from previous attempts.
1754 if ( empty( $notices ) ) {
1755 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
1756 exit();
1757 }
1758 }
1759
1760 /**
1761 * 3rd Party plugins
1762 */
1763 function wp_2fa_add_intro_step( $wizard_steps ) {
1764 $new_wizard_steps = array(
1765 'test' => array(
1766 'name' => __( 'Welcome to WP 2FA', 'wp-security-audit-log' ),
1767 'content' => array( $this, 'introduction_step' ),
1768 'save' => array( $this, 'introduction_step_save' ),
1769 'wizard_type' => 'welcome_wizard',
1770 ),
1771 );
1772
1773 // combine the two arrays.
1774 $wizard_steps = $new_wizard_steps + $wizard_steps;
1775
1776 return $wizard_steps;
1777 }
1778
1779 private function introduction_step() {
1780 ?>
1781 <form method="post" class="wsal-setup-form">
1782 <?php wp_nonce_field( 'wsal-step-addon' ); ?>
1783 <h3><?php esc_html_e( 'You are required to configure 2FA.', 'wp-security-audit-log' ); ?></h3>
1784 <p><?php esc_html_e( 'In order to keep this site - and your details secure, this website’s administrator requires you to enable 2FA authentication to continue.', 'wp-security-audit-log' ); ?></p>
1785 <p><?php esc_html_e( 'Two factor authentication ensures only you have access to your account by creating an added layer of security when logging in -', 'wp-security-audit-log' ); ?> <a href="https://www.wpwhitesecurity.com/two-factor-authentication-wordpress/" target="_blank"><?php esc_html_e( 'Learn more', 'wp-security-audit-log' ); ?></a></p>
1786
1787 <div class="wsal-setup-actions">
1788 <button class="button button-primary"
1789 type="submit"
1790 name="save_step"
1791 value="<?php esc_attr_e( 'Next', 'wp-security-audit-log' ); ?>">
1792 <?php esc_html_e( 'Next', 'wp-security-audit-log' ); ?>
1793 </button>
1794 </div>
1795 </form>
1796 <?php
1797 }
1798
1799 /**
1800 * Step Save: `Addons`
1801 */
1802 private function introduction_step_save() {
1803 // Check nonce.
1804 check_admin_referer( 'wsal-step-addon' );
1805
1806 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
1807 exit();
1808 }
1809 }
1810