PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.2
WP 2FA – Two-factor authentication for WordPress v2.9.2
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-setup-wizard.php
wp-2fa / includes / classes / Admin Last commit date
Controllers 10 months ago Fly-Out 10 months ago Helpers 10 months ago Methods 10 months ago SettingsPages 10 months ago Views 10 months ago class-help-contact-us.php 10 months ago class-plugin-updated-notice.php 10 months ago class-premium-features.php 10 months ago class-settings-page.php 10 months ago class-setup-wizard.php 10 months ago class-user-listing.php 10 months ago class-user-notices.php 10 months ago class-user-profile.php 10 months ago class-user-registered.php 10 months ago index.php 10 months ago
class-setup-wizard.php
705 lines
1 <?php
2 /**
3 * Setup wizard rendering class.
4 *
5 * @package wp2fa
6 * @subpackage setup
7 * @copyright 2025 Melapress
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\Core;
15 use WP2FA\WP2FA;
16 use WP2FA\Methods\TOTP;
17 use WP2FA\Methods\Email;
18 use WP2FA\Utils\User_Utils;
19 use WP2FA\Admin\Settings_Page;
20 use WP2FA\Methods\Backup_Codes;
21 use WP2FA\Utils\Generate_Modal;
22 use WP2FA\Utils\Settings_Utils;
23 use WP2FA\Admin\Helpers\WP_Helper;
24 use WP2FA\Admin\Views\Re_Login_2FA;
25 use WP2FA\Admin\Views\Wizard_Steps;
26 use WP2FA\Admin\Helpers\User_Helper;
27 use WP2FA\Admin\Controllers\Settings;
28 use WP2FA\Authenticator\Authentication;
29 use WP2FA\Admin\Views\First_Time_Wizard_Steps;
30 use WP2FA\Admin\SettingsPages\Settings_Page_Policies;
31
32 /**
33 * Setup_Wizard class for the wizard steps setup
34 *
35 * @since 2.4.0
36 */
37 if ( ! class_exists( '\WP2FA\Admin\Setup_Wizard' ) ) {
38 /**
39 * Our class for creating a step by step wizard for easy configuration.
40 */
41 class Setup_Wizard {
42
43 /**
44 * Wizard Steps
45 *
46 * @var array
47 *
48 * @since 2.8.0
49 */
50 private static $wizard_steps;
51
52 /**
53 * Current Step
54 *
55 * @var string
56 *
57 * @since 2.8.0
58 */
59 private static $current_step;
60
61 /**
62 * Add setup admin page. This is empty on purpose.
63 *
64 * @since 2.8.0
65 */
66 public static function admin_menus() {
67 \add_dashboard_page( '', '', 'read', 'wp-2fa-setup', '' );
68 }
69
70 /**
71 * Adding menus for multisite install
72 *
73 * @return void
74 *
75 * @since 2.2.0
76 */
77 public static function network_admin_menus() {
78 \add_dashboard_page( 'index.php', '', 'read', 'wp-2fa-setup', '' );
79 }
80
81 /**
82 * Setup Page Start.
83 *
84 * @since 2.8.0
85 */
86 public static function setup_page() {
87
88 // Get page argument from $_GET array.
89 $page = ( isset( $_GET['page'] ) ) ? \sanitize_text_field( \wp_unslash( $_GET['page'] ) ) : '';
90 if ( empty( $page ) || 'wp-2fa-setup' !== $page ) {
91 return;
92 }
93
94 // Clear out any old notices.
95 $user = \wp_get_current_user();
96
97 // First lets check if any options have been saved.
98 $settings_saved = true;
99 $settings = WP2FA::get_wp2fa_setting();
100 if ( empty( $settings ) || ! isset( $settings ) ) {
101 $settings_saved = false;
102 }
103
104 if ( Settings_Utils::get_option( 'wizard_not_finished' ) ) {
105 $settings_saved = false;
106 }
107
108 /**
109 * Wizard Steps.
110 */
111 $wizard_type = isset( $_GET['wizard_type'] ) ? \sanitize_text_field( \wp_unslash( $_GET['wizard_type'] ) ) : 'default';
112
113 $is_user_forced_to_setup = User_Helper::get_user_enforced_instantly( $user );
114 if ( ! empty( $is_user_forced_to_setup ) ) {
115 \add_filter( 'wp_2fa_wizard_default_steps', array( __CLASS__, 'wp_2fa_add_intro_step' ) );
116 }
117
118 $user_type = User_Utils::determine_user_2fa_status( $user );
119
120 $wizard_steps = array(
121 'welcome' => array(
122 'name' => \esc_html__( 'Welcome', 'wp-2fa' ),
123 'content' => array( __CLASS__, 'wp_2fa_step_welcome' ),
124 'wizard_type' => 'welcome_wizard',
125 ),
126 'settings_configuration' => array(
127 'name' => \esc_html__( 'Configure 2FA methods & Policies', 'wp-2fa' ),
128 'content' => array( __CLASS__, 'wp_2fa_step_global_2fa_methods' ),
129 'save' => array( __CLASS__, 'wp_2fa_step_global_2fa_methods_save' ),
130 'wizard_type' => 'welcome_wizard',
131 ),
132 'finish' => array(
133 'name' => \esc_html__( 'Setup Finish', 'wp-2fa' ),
134 'content' => array( __CLASS__, 'wp_2fa_step_finish' ),
135 'save' => array( __CLASS__, 'wp_2fa_step_finish_save' ),
136 'wizard_type' => 'welcome_wizard',
137 ),
138 );
139
140 // Admin user setting up fresh install of 2FA plugin.
141 if ( in_array( 'can_manage_options', $user_type, true ) && ! $settings_saved ) {
142 unset( $wizard_steps['user_choose_2fa_method'] );
143 unset( $wizard_steps['reconfigure_method'] );
144 }
145
146 // We will use this setting to determine if defaults have already been saved to the DB.
147 $have_defaults_been_applied = Settings_Utils::get_option( 'default_settings_applied', false );
148 // If we have settings, but they are the defaults, then we want to consider the settings to be unsaved at this point.
149 if ( in_array( 'can_manage_options', $user_type, true ) && $settings_saved && $have_defaults_been_applied ) {
150 $settings_saved = false;
151 }
152
153 // Ensure user has minimum capabilities needed to be here.
154 if ( in_array( 'can_read', $user_type, true ) && $settings_saved ) {
155
156 switch ( $wizard_type ) {
157 case 'user_2fa_config':
158 $wizard_steps = array_intersect_key( $wizard_steps, array_flip( array( 'user_choose_2fa_method', 'setup_method', 'finish', 'backup_codes' ) ) );
159 break;
160
161 case 'backup_codes_config':
162 $wizard_steps = array_intersect_key( $wizard_steps, array_flip( array( 'backup_codes' ) ) );
163 break;
164
165 case 'user_reconfigure_config':
166 $wizard_steps = array_intersect_key( $wizard_steps, array_flip( array( 'reconfigure_method' ) ) );
167 break;
168
169 default:
170 $wizard_steps = array_intersect_key( $wizard_steps, array_flip( array( 'choose_2fa_method', 'setup_method', 'finish', 'backup_codes', 'reconfigure_method' ) ) );
171 }
172
173 // Remove 1st step if only one method is available.
174 if ( empty( WP2FA::get_wp2fa_setting( TOTP::POLICY_SETTINGS_NAME ) ) || empty( WP2FA::get_wp2fa_setting( Email::POLICY_SETTINGS_NAME ) ) ) {
175 unset( $wizard_steps['choose_2fa_method'] );
176 }
177
178 // If the user has codes setup already, no need to add the slide.
179 if ( ! in_array( 'user_needs_to_setup_backup_codes', $user_type, true ) && 'backup_codes_config' !== $wizard_type ) {
180 unset( $wizard_steps['backup_codes'] );
181 }
182 }
183
184 /**
185 * Filter: `Wizard Default Steps`
186 *
187 * Filter to filter wizard steps before they are displayed.
188 *
189 * @param array $wizard_steps – Wizard Steps.
190 *
191 * @since 2.8.0
192 */
193 self::$wizard_steps = apply_filters( WP_2FA_PREFIX . 'wizard_default_steps', $wizard_steps );
194
195 // Set current step.
196 $current_step = ( isset( $_GET['current-step'] ) ) ? \sanitize_text_field( \wp_unslash( $_GET['current-step'] ) ) : '';
197 self::$current_step = ! empty( $current_step ) ? $current_step : current( array_keys( self::$wizard_steps ) );
198
199 if ( Backup_Codes::METHOD_NAME === self::$current_step && ! Backup_Codes::are_backup_codes_enabled_for_role( User_Helper::get_user_role( $user ) ) ) {
200
201 $redirect_to_finish = add_query_arg(
202 array(
203 'current-step' => 'finish',
204 'all-set' => 1,
205 )
206 );
207 \wp_safe_redirect( \esc_url_raw( $redirect_to_finish ) );
208 }
209
210 /**
211 * Enqueue Scripts.
212 */
213 \wp_enqueue_style(
214 'wp_2fa_setup_wizard',
215 Core\style_url( 'setup-wizard', 'admin' ),
216 array( 'select2' ),
217 WP_2FA_VERSION
218 );
219
220 \wp_enqueue_style(
221 'wp_2fa_admin-style',
222 Core\style_url( 'admin-style', 'admin' ),
223 array(),
224 WP_2FA_VERSION
225 );
226
227 \WP2FA\Core\enqueue_select2_scripts();
228
229 \wp_enqueue_script(
230 'wp_2fa_admin',
231 Core\script_url( 'admin', 'admin' ),
232 array( 'jquery-ui-widget', 'jquery-ui-core', 'jquery-ui-autocomplete', 'select2' ),
233 WP_2FA_VERSION,
234 true
235 );
236
237 \wp_enqueue_script(
238 'wp_2fa_micromodal',
239 Core\script_url( 'micromodal', 'admin', 'select2' ),
240 array(),
241 WP_2FA_VERSION,
242 true
243 );
244
245 // Data array.
246 $data_array = array(
247 'ajaxURL' => \admin_url( 'admin-ajax.php' ),
248 'roles' => WP_Helper::get_roles_wp(),
249 'nonce' => \wp_create_nonce( 'wp-2fa-settings-nonce' ),
250 'invalidEmail' => \esc_html__( 'Please use a valid email address', 'wp-2fa' ),
251 'backupCodesSent' => \esc_html__( 'Backup codes sent', 'wp-2fa' ),
252 );
253 \wp_localize_script( 'wp_2fa_admin', 'wp2faData', $data_array );
254
255 $re_login = Settings_Utils::get_setting_role( User_Helper::get_user_role(), Re_Login_2FA::RE_LOGIN_SETTINGS_NAME );
256
257 // Data array.
258 $data_array = array(
259 'ajaxURL' => \admin_url( 'admin-ajax.php' ),
260 'nonce' => \wp_create_nonce( 'wp2fa-verify-wizard-page' ),
261 'codesPreamble' => \esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
262 'readyText' => \esc_html__( 'I\'m ready', 'wp-2fa' ),
263 'codeReSentText' => \esc_html__( 'New code sent', 'wp-2fa' ),
264 'reLogin' => $re_login,
265 'reLoginEnabled' => Re_Login_2FA::ENABLED_SETTING_VALUE,
266 );
267
268 /**
269 * Gives the ability to change the default JS wizard settings.
270 *
271 * @param int $data_array - The array with all the JS wizard settings.
272 *
273 * @since 2.2.0
274 */
275 $data_array = apply_filters( WP_2FA_PREFIX . 'js_wizard_settings', $data_array );
276 \wp_localize_script( 'wp_2fa_admin', 'wp2faWizardData', $data_array );
277
278 /**
279 * Save Wizard Settings.
280 */
281 $save_step = ( isset( $_POST['save_step'] ) ) ? \sanitize_text_field( \wp_unslash( $_POST['save_step'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
282 if ( ! empty( $save_step ) && ! empty( self::$wizard_steps[ self::$current_step ]['save'] ) ) {
283 call_user_func( self::$wizard_steps[ self::$current_step ]['save'] );
284 }
285
286 self::setup_page_header();
287 self::setup_page_steps();
288 self::setup_page_content();
289 self::setup_page_footer();
290
291 exit();
292 }
293
294 /**
295 * Setup Page Header.
296 *
297 * @since 2.8.0
298 */
299 private static function setup_page_header() {
300 ?>
301 <!DOCTYPE html>
302 <html <?php language_attributes(); ?>>
303 <head>
304 <meta name="viewport" content="width=device-width, initial-scale=1" />
305 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
306 <title><?php \esc_html_e( 'WP 2FA &rsaquo; Setup Wizard', 'wp-2fa' ); ?></title>
307 <?php
308 \wp_print_scripts( 'jquery' );
309 \wp_print_scripts( 'jquery-ui-core' );
310 \wp_print_scripts( 'wp_2fa_setup_wizard' );
311 \wp_print_scripts( 'wp_2fa_micromodal' );
312 \wp_print_scripts( 'wp_2fa_admin' );
313
314 \wp_enqueue_script( 'jquery' );
315 \wp_enqueue_script( 'jquery-ui-core' );
316 \wp_enqueue_script( 'wp_2fa_setup_wizard' );
317 \wp_enqueue_script( 'wp_2fa_micromodal' );
318 \wp_enqueue_script( 'wp_2fa_admin' );
319 \wp_enqueue_style( 'common' );
320 \wp_enqueue_style( 'forms' );
321 \wp_enqueue_style( 'buttons' );
322 \wp_enqueue_style( 'wp-jquery-ui-dialog' );
323 \wp_enqueue_style( 'wp_2fa_admin' );
324 \remove_action( 'admin_print_styles', 'print_emoji_styles' );
325 /**
326 * Gives the ability for 3rd party scripts to add their own JS to the plugin setup page.
327 *
328 * @since 2.2.0
329 */
330 \do_action( WP_2FA_PREFIX . 'setup_page_scripts' );
331 \do_action( 'admin_print_styles' );
332 ?>
333 </head>
334 <body class="wp2fa-setup wp-core-ui">
335 <div class="setup-wizard-wrapper wp-2fa-settings-wrapper wp2fa-form-styles">
336 <h1 id="wp2fa-logo"><a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=wp_2fa_logo" target="_blank" ><img style="max-width: 80px;" src="<?php echo \esc_url( WP_2FA_URL . 'dist/images/wp-2fa-color_opt.png' ); ?>" alt="WP 2FA Logo"></a></h1>
337 <?php
338 }
339
340 /**
341 * Setup Page Footer.
342 *
343 * @since 2.8.0
344 */
345 private static function setup_page_footer() {
346 $user = \wp_get_current_user();
347
348 $redirect = Settings::get_settings_page_link();
349 ?>
350 <div class="wp2fa-setup-footer">
351 <?php if ( 'welcome' !== self::$current_step && 'finish' !== self::$current_step ) { // Don't show the link on the first & last step. ?>
352 <?php if ( ! User_Helper::get_user_enforced_instantly( $user ) ) { ?>
353 <a class="close-wizard-link" href="<?php echo \esc_url( $redirect ); ?>"><?php \esc_html_e( 'Close Wizard', 'wp-2fa' ); ?></a>
354 <?php
355 }
356 }
357 ?>
358 </div>
359 </div>
360 </body>
361 </html>
362 <?php
363 // phpcs:ignore
364 echo Generate_Modal::generate_modal(
365 'notify-admin-settings-page',
366 '',
367 \esc_html__( 'If you cancel this wizard, the default plugin settings will be applied. You can always configure the plugin settings and two-factor authentication policies at a later stage from the ', 'wp-2fa' ) . ' <b>' . \esc_html__( 'WP 2FA', 'wp-2fa' ) . '</b>' . \esc_html__( ' entry in your WordPress dashboard menu.', 'wp-2fa' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
368 array(
369 '<a href="#" id="close-settings" class="button button-primary wp-2fa-button-primary" data-redirect-url="' . \esc_url( $redirect ) . '">' . \esc_html__( 'OK, close wizard', 'wp-2fa' ) . '</a>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
370 '<a href="#" class="button button-secondary wp-2fa-button-secondary wp-2fa-button-secondary" data-close-2fa-modal>' . \esc_html__( 'Continue with wizard', 'wp-2fa' ) . '</a>', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
371 ),
372 '',
373 '580px'
374 );
375 }
376
377 /**
378 * Setup Page Steps.
379 *
380 * @since 2.8.0
381 */
382 private static function setup_page_steps() {
383 ?>
384 <ul class="steps">
385 <?php
386 foreach ( self::$wizard_steps as $key => $step ) {
387 if ( 'welcome_wizard' === $step['wizard_type'] || ( is_array( $step['wizard_type'] ) && in_array( 'welcome_wizard', $step['wizard_type'], true ) ) ) {
388 $step_name = isset( $step['name'] ) ? \esc_html( $step['name'] ) : '';
389 if ( $key === self::$current_step ) {
390 ?>
391 <li class="is-active"><?php echo \esc_html( $step_name ); ?></li>
392 <?php
393 } else {
394 ?>
395 <li><?php echo \esc_html( $step_name ); ?></li>
396 <?php
397 }
398 }
399 }
400 ?>
401 </ul>
402 <?php
403 }
404
405 /**
406 * Get Next Step URL.
407 *
408 * @return string
409 *
410 * @since 2.8.0
411 */
412 private static function get_next_step() {
413 // Get current step.
414 $current_step = self::$current_step;
415
416 // Array of step keys.
417 $keys = array_keys( self::$wizard_steps );
418 if ( end( $keys ) === $current_step ) { // If last step is active then return WP Admin URL.
419 return \network_admin_url();
420 }
421
422 // Search for step index in step keys.
423 $step_index = array_search( $current_step, $keys, true );
424 if ( false === $step_index ) { // If index is not found then return empty string.
425 return '';
426 }
427
428 // Return next step.
429 return \add_query_arg( 'current-step', \sanitize_text_field( $keys[ $step_index + 1 ] ) );
430 }
431
432 /**
433 * Setup Page Content.
434 *
435 * @since 2.8.0
436 */
437 private static function setup_page_content() {
438 ?>
439 <div class="wp2fa-setup-content">
440 <?php
441 if ( ! empty( self::$wizard_steps[ self::$current_step ]['content'] ) ) {
442 call_user_func( self::$wizard_steps[ self::$current_step ]['content'] );
443 }
444 ?>
445 </div>
446 <?php
447 }
448
449 /**
450 * Step View: `Welcome`
451 *
452 * @since 2.8.0
453 */
454 private static function wp_2fa_step_welcome() {
455 Wizard_Steps::welcome_step( self::get_next_step() );
456 }
457
458 /**
459 * Step View: `Finish`
460 *
461 * @since 2.8.0
462 */
463 private static function wp_2fa_step_finish() {
464 User_Helper::remove_user_needs_to_reconfigure_2fa( User_Helper::get_user_object() );
465 Wizard_Steps::congratulations_step( true );
466 }
467
468 /**
469 * Step Save: `Finish`
470 *
471 * @since 2.8.0
472 */
473 private static function wp_2fa_step_finish_save() {
474 // Verify nonce.
475 \check_admin_referer( 'wp2fa-step-finish' );
476 \wp_safe_redirect( \esc_url_raw( self::get_next_step() ) );
477
478 exit();
479 }
480
481 /**
482 * Step View: `Choose Methods`
483 *
484 * @since 2.8.0
485 */
486 private static function wp_2fa_step_global_2fa_methods() {
487 ?>
488 <form method="post" class="wp2fa-setup-form wp2fa-form-styles wp2fa-first-time-wizard" autocomplete="off">
489 <?php wp_nonce_field( 'wp2fa-step-choose-method' ); ?>
490 <div class="step-setting-wrapper active" data-step-title="<?php \esc_html_e( '2FA methods', 'wp-2fa' ); ?>">
491 <?php First_Time_Wizard_Steps::select_method( true ); ?>
492 <div class="wp2fa-setup-actions">
493 <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>
494 </div>
495 </div>
496 <div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( 'Alternative methods', 'wp-2fa' ); ?>">
497 <?php First_Time_Wizard_Steps::backup_method( true ); ?>
498 <div class="wp2fa-setup-actions">
499 <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>
500 </div>
501 </div>
502 <div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( '2FA policy', 'wp-2fa' ); ?>">
503 <?php First_Time_Wizard_Steps::enforcement_policy( true ); ?>
504 <div class="wp2fa-setup-actions">
505 <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>
506 <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>
507 </div>
508 </div>
509 <div class="step-setting-wrapper hidden" data-step-title="<?php \esc_html_e( 'Exclude users', 'wp-2fa' ); ?>">
510 <?php First_Time_Wizard_Steps::exclude_users( true ); ?>
511 <div class="wp2fa-setup-actions">
512 <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>
513 </div>
514 </div>
515
516 <?php if ( WP_Helper::is_multisite() ) : ?>
517 <div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( 'Exclude sites', 'wp-2fa' ); ?>">
518 <?php First_Time_Wizard_Steps::excluded_network_sites( true ); ?>
519 <div class="wp2fa-setup-actions">
520 <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>
521 </div>
522 </div>
523 <?php endif; ?>
524
525 <div class="step-setting-wrapper hidden" data-step-title="<?php \esc_html_e( 'Grace period', 'wp-2fa' ); ?>">
526 <h3><?php \esc_html_e( 'How long should the grace period for your users be?', 'wp-2fa' ); ?></h3>
527 <p class="description"><?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>
528 <?php First_Time_Wizard_Steps::grace_period( true ); ?>
529 <div class="wp2fa-setup-actions">
530 <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>
531 </div>
532 </div>
533
534 </form>
535 <?php
536 }
537
538 /**
539 * Step Save: `Choose Method`
540 *
541 * @since 2.8.0
542 */
543 private static function wp_2fa_step_global_2fa_methods_save() {
544 // Check nonce.
545 \check_admin_referer( 'wp2fa-step-choose-method' );
546
547 $input = ( isset( $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] ) && ! empty( $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] ) && \is_array( $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] ) ) ? $_POST[ WP_2FA_POLICY_SETTINGS_NAME ] : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
548
549 $input = \map_deep( $input, 'sanitize_text_field' );
550
551 if ( ! WP_Helper::is_multisite() ) {
552 \unregister_setting(
553 WP_2FA_POLICY_SETTINGS_NAME,
554 WP_2FA_POLICY_SETTINGS_NAME
555 );
556 }
557
558 $sanitized_settings = Settings_Page_Policies::validate_and_sanitize( $input, 'setup_wizard' );
559 WP2FA::update_plugin_settings( $sanitized_settings );
560
561 \wp_safe_redirect( \esc_url_raw( self::get_next_step() ) );
562 exit();
563 }
564
565 /**
566 * Send email with fresh code, or to setup email 2fa.
567 *
568 * @param int $user_id - User id we want to send the message to.
569 * @param string $nominated_email_address - The user custom address to use (name of the meta key to check for).
570 * @param bool $is_reset_protection - That call is for reset code.
571 *
572 * @return bool
573 *
574 * @since 2.8.0
575 */
576 public static function send_authentication_setup_email( $user_id, $nominated_email_address = 'nominated_email_address', $is_reset_protection = false ) {
577
578 // If we have a nonce posted, check it.
579 if ( \wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
580 $nonce_check = \wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_POST['nonce'] ) ), 'wp-2fa-send-setup-email' );
581 if ( ! $nonce_check ) {
582 \wp_send_json_error( new \WP_Error( 400, \esc_html__( 'Nonce checking failed', 'wp-2fa' ) ), 400 );
583 return false;
584 }
585 }
586
587 if ( empty( $user_id ) ) {
588 $user = \wp_get_current_user();
589 } else {
590 $user = \get_userdata( $user_id );
591 }
592
593 if ( ! $user ) {
594 return false;
595 }
596
597 // Grab email address if it's provided.
598 $email = sanitize_email( $user->user_email );
599 if ( isset( $_POST['email_address'] ) ) {
600 $email = sanitize_email( \wp_unslash( $_POST['email_address'] ) );
601 }
602
603 if ( wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
604 User_Helper::set_nominated_email_for_user( $email, $user );
605 }
606
607 $email_address = '';
608 if ( ! empty( $nominated_email_address ) ) {
609 if ( 'nominated_email_address' === $nominated_email_address ) {
610 $email_address = User_Helper::get_nominated_email_for_user( $user );
611 } elseif ( 'backup_email_address' === $nominated_email_address ) {
612 $email_address = User_Helper::get_backup_email_for_user( $user );
613 }
614 } else {
615 $email_address = $user->user_email;
616 }
617
618 // Generate a token and setup email.
619 $token = Authentication::generate_token( $user->ID );
620
621
622 if ( $is_reset_protection ) {
623 } elseif ( wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
624 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_setup_email_subject' ), $user->ID ) );
625 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_setup_email_body' ), $user->ID, $token ) );
626 } else {
627 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_email_subject' ), $user->ID ) );
628 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_email_body' ), $user->ID, $token ) );
629 }
630
631 // phpcs:disable
632 /* @free:start */
633 // phpcs:enable
634 $message .= '<p>' . \esc_html__( 'Email sent by', 'wp-2fa' );
635 $message .= ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=melapress_wp_2fa_plugin_link" target="_blank">' . \esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
636 $message .= '</p>';
637 // phpcs:disable
638 /* @free:end */
639 // phpcs:enable
640
641 // If we have a nonce posted, check it.
642 if ( \wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
643 $mail_sent = Settings_Page::send_email( $email_address, $subject, $message );
644 if ( ! $mail_sent ) {
645 \wp_send_json_error( new \WP_Error( 500, \esc_html__( 'Email sending failed', 'wp-2fa' ) ), 400 );
646 return false;
647 }
648
649 return $mail_sent;
650 }
651
652 return Settings_Page::send_email( $email_address, $subject, $message );
653 }
654
655 /**
656 * 3rd Party plugins
657 *
658 * @param array $wizard_steps - Array with the current wizard steps.
659 *
660 * @return array
661 *
662 * @since 2.8.0
663 */
664 public static function wp_2fa_add_intro_step( $wizard_steps ) {
665 $new_wizard_steps = array(
666 'test' => array(
667 'name' => __( 'Welcome to WP 2FA', 'wp-2fa' ),
668 'content' => array( __CLASS__, 'introduction_step' ),
669 'save' => array( __CLASS__, 'introduction_step_save' ),
670 'wizard_type' => 'welcome_wizard',
671 ),
672 );
673
674 // combine the two arrays.
675 $wizard_steps = $new_wizard_steps + $wizard_steps;
676
677 return $wizard_steps;
678 }
679
680 /**
681 * Shows introduction step of the wizard
682 *
683 * @return void
684 *
685 * @since 2.8.0
686 */
687 private static function introduction_step() {
688 Wizard_Steps::introduction_step();
689 }
690
691 /**
692 * Step Save: `Addons`
693 *
694 * @since 2.8.0
695 */
696 private static function introduction_step_save() {
697 // Check nonce.
698 check_admin_referer( 'wp2fa-step-addon' );
699
700 wp_safe_redirect( \esc_url_raw( self::get_next_step() ) );
701 exit();
702 }
703 }
704 }
705