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