PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 1.5.2
WP 2FA – Two-factor authentication for WordPress v1.5.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 / WP2FA.php
wp-2fa / includes / classes Last commit date
Admin 5 years ago Authenticator 5 years ago BackgroundProcessing 5 years ago Cron 5 years ago Shortcodes 5 years ago Utils 5 years ago .gitkeep 6 years ago EmailTemplate.php 5 years ago WP2FA.php 5 years ago
WP2FA.php
679 lines
1 <?php // phpcs:ignore
2
3 namespace WP2FA;
4
5 use WP2FA\Admin\SettingsPage;
6 use WP2FA\Utils\DateTimeUtils;
7
8 /**
9 * Main WP2FA Class.
10 */
11 class WP2FA {
12
13 /**
14 * Plugin version.
15 *
16 * @var string
17 */
18 public $version = WP_2FA_VERSION;
19
20 /**
21 * Options variables.
22 *
23 * @var array
24 */
25 protected static $wp_2fa_options;
26 protected static $wp_2fa_email_templates;
27
28 /**
29 * Instance wrapper.
30 *
31 * @var object
32 */
33 private static $instance = null;
34
35 /**
36 * Return plugin instance.
37 */
38 public static function get_instance() {
39
40 if ( null === self::$instance ) {
41 self::$instance = new self();
42 }
43
44 return self::$instance;
45 }
46
47 /**
48 * Contructor.
49 */
50 private function __construct() {
51 if ( self::is_this_multisite() ) {
52 self::$wp_2fa_options = get_network_option( null, 'wp_2fa_settings' );
53 self::$wp_2fa_email_templates = get_network_option( null, 'wp_2fa_email_settings' );
54 } else {
55 self::$wp_2fa_options = get_option( 'wp_2fa_settings' );
56 self::$wp_2fa_email_templates = get_option( 'wp_2fa_email_settings' );
57 }
58
59 // Activation/Deactivation.
60 register_activation_hook( WP_2FA_FILE, '\WP2FA\Core\activate' );
61 register_deactivation_hook( WP_2FA_FILE, '\WP2FA\Core\deactivate' );
62 // Register our uninstallation hook.
63 register_uninstall_hook( WP_2FA_FILE, '\WP2FA\Core\uninstall' );
64 }
65
66 /**
67 * Fire up classes.
68 */
69 public function init() {
70 // Bootstrap.
71 Core\setup();
72
73 $this->settings = new Admin\SettingsPage();
74 $this->wizard = new Admin\SetupWizard();
75 $this->authentication = new Authenticator\Authentication();
76 $this->backupcodes = new Authenticator\BackupCodes();
77 $this->login = new Authenticator\Login();
78 $this->user_notices = new Admin\UserNotices();
79 $this->crontasks = new Cron\CronTasks();
80 $this->user_registered = new Admin\UserRegistered();
81 $this->shortcodes = new Shortcodes\Shortcodes();
82 // BG Processors.
83 $this->bg_enforce_2fa = new BackgroundProcessing\Enforce2FA();
84 $this->bg_delete_grace = new BackgroundProcessing\DeleteGracePeriod();
85 $this->bg_remove_methods = new BackgroundProcessing\RemoveEnabledMethods();
86 $this->bg_wipe_2fa = new BackgroundProcessing\RemoveAllUserData();
87
88 global $pagenow;
89 if ( 'profile.php' !== $pagenow || 'user-edit.php' !== $pagenow ) {
90 $this->user_profiles = new Admin\UserProfile();
91 }
92
93 $this->add_actions();
94 }
95
96 /**
97 * Add our plugins actions.
98 */
99 public function add_actions() {
100 // Plugin redirect on activation, only if we have no settings currently saved.
101 if ( empty( self::$wp_2fa_options ) || ! isset( self::$wp_2fa_options ) ) {
102 add_action( 'admin_init', array( $this, 'setup_redirect' ), 10 );
103 }
104
105 // SettingsPage.
106 if ( self::is_this_multisite() ) {
107 add_action( 'network_admin_menu', array( $this->settings, 'create_settings_admin_menu_multisite' ) );
108 add_action( 'network_admin_edit_update_wp2fa_network_options', array( $this->settings, 'update_wp2fa_network_options' ) );
109 add_action( 'network_admin_edit_update_wp2fa_network_email_options', array( $this->settings, 'update_wp2fa_network_email_options' ) );
110 add_action( 'network_admin_notices', array( $this->settings, 'settings_saved_network_admin_notice' ) );
111 } else {
112 add_action( 'admin_menu', array( $this->settings, 'create_settings_admin_menu' ) );
113 }
114 add_action( 'wp_ajax_get_all_users', array( $this->settings, 'get_all_users' ) );
115 add_action( 'wp_ajax_get_all_network_sites', array( $this->settings, 'get_all_network_sites' ) );
116 add_action( 'wp_ajax_unlock_account', array( $this->settings, 'unlock_account' ), 10, 1 );
117 add_action( 'admin_action_unlock_account', array( $this->settings, 'unlock_account' ), 10, 1 );
118 add_action( 'admin_action_remove_user_2fa', array( $this->settings, 'remove_user_2fa' ), 10, 1 );
119 add_action( 'wp_ajax_remove_user_2fa', array( $this->settings, 'remove_user_2fa' ), 10, 1 );
120 add_action( 'admin_menu', array( $this->settings, 'hide_settings' ), 999 );
121 add_action( 'plugin_action_links_' . WP_2FA_BASE, array( $this->settings, 'add_plugin_action_links' ) );
122 add_filter( 'display_post_states', array( $this->settings, 'add_display_post_states' ), 10, 2 );
123 add_action( 'wp_ajax_wp_2fa_cancel_bg_processes', array( $this->settings, 'cancel_bg_processes' ) );
124
125 // SetupWizard.
126 if ( self::is_this_multisite() ) {
127 add_action( 'network_admin_menu', array( $this->wizard, 'network_admin_menus' ), 10 );
128 add_action( 'admin_menu', array( $this->wizard, 'admin_menus' ), 10 );
129 } else {
130 add_action( 'admin_menu', array( $this->wizard, 'admin_menus' ), 10 );
131 }
132 add_action( 'plugins_loaded', array( $this, 'add_wizard_actions' ), 10 );
133 add_action( 'wp_ajax_send_authentication_setup_email', array( $this->wizard, 'send_authentication_setup_email' ) );
134 add_action( 'wp_ajax_regenerate_authentication_key', array( $this->wizard, 'regenerate_authentication_key' ) );
135
136 // UserNotices.
137 add_action( 'wp_ajax_dismiss_nag', array( $this->user_notices, 'dismiss_nag' ) );
138 add_action( 'wp_ajax_dismiss_nag', array( $this->user_notices, 'dismiss_reconfigure_nag' ) );
139 add_action( 'clear_auth_cookie', array( $this->user_notices, 'reset_nag' ) );
140
141 // UserProfile.
142 global $pagenow;
143 if ( 'profile.php' !== $pagenow || 'user-edit.php' !== $pagenow ) {
144 add_action( 'show_user_profile', array( $this->user_profiles, 'user_2fa_options' ) );
145 add_action( 'edit_user_profile', array( $this->user_profiles, 'user_2fa_options' ) );
146 if ( self::is_this_multisite() ) {
147 add_action( 'personal_options_update', array( $this->user_profiles, 'save_user_2fa_options' ) );
148 }
149 }
150 add_filter( 'user_row_actions', array( $this->user_profiles, 'user_2fa_row_actions' ), 10, 2 );
151 if ( self::is_this_multisite() ) {
152 add_filter( 'ms_user_row_actions', array( $this->user_profiles, 'user_2fa_row_actions' ), 10, 2 );
153 }
154 add_action( 'wp_ajax_validate_authcode_via_ajax', array( $this->user_profiles, 'validate_authcode_via_ajax' ) );
155 add_action( 'wp_ajax_wp2fa_test_email', array( $this, 'handle_send_test_email_ajax' ) );
156
157 // Login.
158 add_action( 'init', array( $this->login, 'get_providers' ) );
159 add_action( 'wp_login', array( $this->login, 'wp_login' ), 20, 2 );
160 add_action( 'login_form_validate_2fa', array( $this->login, 'login_form_validate_2fa' ) );
161 add_action( 'login_form_backup_2fa', array( $this->login, 'backup_2fa' ) );
162
163 /**
164 * Keep track of all the user sessions for which we need to invalidate the
165 * authentication cookies set during the initial password check.
166 */
167 add_action( 'set_auth_cookie', array( $this->login, 'collect_auth_cookie_tokens' ) );
168 add_action( 'set_logged_in_cookie', array( $this->login, 'collect_auth_cookie_tokens' ) );
169
170 // Run only after the core wp_authenticate_username_password() check.
171 add_filter( 'authenticate', array( $this->login, 'filter_authenticate' ), 50 );
172 add_filter( 'wp_authenticate_user', array( $this->login, 'is_user_locked' ), 10, 2 );
173
174 // User Register.
175 add_action( 'set_user_role', array( $this->user_registered, 'check_user_upon_role_change' ), 10, 3 );
176
177 // Block users from admin if needed.
178 $user_block_hook = is_admin() || is_network_admin() ? 'init' : 'wp';
179 add_action( $user_block_hook, array( $this, 'block_unconfigured_users_from_admin' ), 10 );
180 }
181
182 /**
183 * Add actions specific to the wizard.
184 */
185 public function add_wizard_actions() {
186 new BackgroundProcessing\Enforce2FA();
187 if ( function_exists( 'wp_get_current_user' ) && current_user_can( 'read' ) ) {
188 add_action( 'admin_init', array( $this->wizard, 'setup_page' ), 10 );
189 }
190 }
191
192 /**
193 * Redirect user to 1st time setup.
194 */
195 public function setup_redirect() {
196
197 // Bail early before the redirect if the user can't manage options.
198 if ( ! current_user_can( 'manage_options' ) ) {
199 return;
200 }
201
202 if ( get_option( 'wp_2fa_redirect_on_activate', false ) ) {
203 // Delete redirect option.
204 delete_option( 'wp_2fa_redirect_on_activate' );
205
206 $page = ( self::is_this_multisite() && is_super_admin() ) ? network_admin_url( 'index.php' ) : admin_url( 'options-general.php' );
207 $redirect = add_query_arg(
208 array(
209 'page' => 'wp-2fa-setup',
210 'is_initial_setup' => 'true',
211 ),
212 admin_url( 'user-edit.php' )
213 );
214
215 wp_safe_redirect( $redirect );
216 exit();
217 }
218 }
219
220 /**
221 * Check is this is a multisite setup.
222 */
223 public static function is_this_multisite() {
224 return function_exists( 'is_multisite' ) && is_multisite();
225 }
226
227 /**
228 * Return user roles.
229 *
230 * @return array User roles.
231 */
232 public static function wp_2fa_get_roles() {
233 global $wp_roles;
234 $roles = $wp_roles->role_names;
235 return $roles;
236 }
237
238 /**
239 * Check to see if the user or user role is excluded.
240 *
241 * @param int $user_id User id.
242 * @return boolean Is user excluded or not.
243 */
244 public static function is_user_excluded( $user_id, $excluded_users = '', $excluded_roles = '', $excluded_sites = '' ) {
245 $user = false;
246 $user_roles = false;
247
248 // If we have been passed an object, handle accordingly.
249 if ( is_a( $user_id, '\WP_User' ) ) {
250 $user = $user_id;
251 $user_roles = $user->roles;
252 }
253
254 // If we have an int instead, lets get the user data for that ID.
255 if ( is_numeric( $user_id ) || isset( $_GET['user_id'] ) && is_numeric( $user_id ) ) {
256 $user = get_user_by( 'id', $user_id );
257 $user_roles = $user->roles;
258 }
259
260 // Finally, we could have an array consisting of ID or user_login.
261 if ( is_array( $user_id ) && isset( $user_id['ID'] ) ) {
262 $user = get_user_by( 'id', $user_id['ID'] );
263 $user_roles = $user->roles;
264 }
265
266 // Finally, if we reach this point with no $user or $user_roles lets get the current user.
267 if ( ! $user || ! $user_roles ) {
268 $user = wp_get_current_user();
269 $user_roles = $user->roles;
270 }
271
272 $user_excluded = false;
273
274 if ( isset( $excluded_users ) && ! empty( $excluded_users ) ) {
275 $excluded_users = $excluded_users;
276 } else {
277 $excluded_users = WP2FA::get_wp2fa_setting( 'excluded_users' );
278 }
279
280 if ( ! empty( $excluded_users ) ) {
281 // Turn it into an array.
282 $excluded_users_array = explode( ',', $excluded_users );
283 // Compare our roles with the users and see if we get a match.
284 $result = in_array( $user->user_login, $excluded_users_array, true );
285 if ( $result ) {
286 $user_excluded = true;
287 return true;
288 }
289 }
290
291 if ( isset( $excluded_roles ) && ! empty( $excluded_roles ) ) {
292 $excluded_roles = $excluded_roles;
293 } else {
294 $excluded_roles = WP2FA::get_wp2fa_setting( 'excluded_roles' );
295 }
296
297 if ( ! empty( $excluded_roles ) ) {
298 // Turn it into an array.
299 $excluded_roles_array = explode( ',', strtolower( $excluded_roles ) );
300 // Compare our roles with the users and see if we get a match.
301 $result = array_intersect( $excluded_roles_array, $user->roles );
302 if ( $result ) {
303 $user_excluded = true;
304 return true;
305 }
306 }
307
308 if ( self::is_this_multisite() ) {
309 if ( isset( $excluded_sites ) && ! empty( $excluded_sites ) ) {
310 $excluded_sites = $excluded_sites;
311 } else {
312 $excluded_sites = WP2FA::get_wp2fa_setting( 'excluded_sites' );
313 }
314
315 if ( ! empty( $excluded_sites ) ) {
316 // Turn it into an array.
317 $excluded_site_array = explode( ',', strtolower( $excluded_sites ) );
318 $site_ids_only = array();
319 // Remove everything but the excluded sites ID, which we will use to check if user is a member.
320 foreach ( $excluded_site_array as $excluded_site ) {
321 if ( isset( explode( ':', $excluded_site )[1] ) ) {
322 $id = trim( explode( ':', $excluded_site )[1] );
323 $site_ids_only[] = $id;
324 }
325 }
326 foreach ( $site_ids_only as $site_id ) {
327 if ( is_user_member_of_blog( $user->ID, $site_id ) ) {
328 // User is a member of the a blog we are excluding from 2FA.
329 $user_excluded = true;
330 return true;
331 } else {
332 // User is NOT a member of the a blog we are excluding.
333 $user_excluded = false;
334 }
335 }
336 }
337 }
338
339 if ( true === $user_excluded ) {
340 return true;
341 }
342
343 return false;
344 }
345
346 /**
347 * Util function to grab settings or apply defaults if no settings are saved into the db.
348 *
349 * @param string $setting_name Settings to grab value of.
350 * @return string Settings value or default value.
351 */
352 public static function get_wp2fa_setting( $setting_name = '' ) {
353 $default_settings = array(
354 'enable_totp' => 'enable_totp',
355 'enable_email' => 'enable_email',
356 'enforcement-policy' => 'do-not-enforce',
357 'excluded_users' => '',
358 'excluded_roles' => '',
359 'enforced_users' => '',
360 'enforced_roles' => '',
361 'grace-period' => 3,
362 'grace-period-denominator' => 'days',
363 'enable_grace_cron' => '',
364 'enable_destroy_session' => '',
365 'limit_access' => '',
366 '2fa_settings_last_updated_by' => '',
367 '2fa_main_user' => '',
368 'grace-period-expiry-time' => '',
369 'plugin_version' => WP_2FA_VERSION,
370 'delete_data_upon_uninstall' => '',
371 'excluded_sites' => '',
372 'create-custom-user-page' => 'no',
373 'custom-user-page-url' => '',
374 'custom-user-page-id' => '',
375 'hide_remove_button' => '',
376 'grace-policy' => 'use-grace-period',
377 );
378
379 $apply_defaults = false;
380
381 // If we have no setting name, return them all.
382 if ( empty( $setting_name ) ) {
383 return self::$wp_2fa_options;
384 }
385
386 // First lets check if any options have been saved.
387 if ( empty( self::$wp_2fa_options ) || ! isset( self::$wp_2fa_options ) ) {
388 $apply_defaults = true;
389 }
390
391 if ( $apply_defaults ) {
392 return $default_settings[ $setting_name ];
393 } elseif ( ! isset( self::$wp_2fa_options[ $setting_name ] ) ) {
394 return false;
395 } else {
396 return self::$wp_2fa_options[ $setting_name ];
397 }
398 }
399
400 /**
401 * Util function to grab EMAIL settings or apply defaults if no settings are saved into the db.
402 *
403 * @param string $setting_name Settings to grab value of.
404 * @return string Settings value or default value.
405 */
406 public static function get_wp2fa_email_templates( $setting_name = '', $grab_default = '' ) {
407
408 $apply_defaults = false;
409
410 // First lets check if any options have been saved.
411 if ( empty( self::$wp_2fa_email_templates ) || ! isset( self::$wp_2fa_email_templates ) ) {
412 $apply_defaults = true;
413 }
414
415 // If we have no setting name, return what ever is saved.
416 if ( empty( $setting_name ) ) {
417 return self::$wp_2fa_email_templates;
418 }
419
420 // If we have a saved setting, return it.
421 if ( $setting_name && isset( self::$wp_2fa_email_templates[ $setting_name ] ) ) {
422 return self::$wp_2fa_email_templates[ $setting_name ];
423 }
424
425 // If we reach this point, we have nothing saved and we are looking for defaults.
426 // Create Enable 2FA Message.
427 $enable_2fa_subject = __( 'Please enable 2FA on {site_url}', 'wp-2fa' );
428 $enable_2fa_body = '';
429
430 $enable_2fa_body .= '<p>'. __( 'The administrator of the website', 'wp-2fa' ) .' {site_url} '. esc_html__( 'enforced two-factor authentication. You have', 'wp-2fa' ) .' <strong>{grace_period}</strong> '. esc_html__( 'to enable and configure 2FA on your WordPress user', 'wp-2fa' ) .' <strong>{user_login_name}</strong></p>';
431
432 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
433 $enable_2fa_body .= '<p>'. esc_html__( 'You can configure 2FA from this page:', 'wp-2fa' ) .' <a href="{2fa_settings_page_url}" target="_blank">{2fa_settings_page_url}.</a></p>';
434 }
435
436 $enable_2fa_body .= '<p>'. __( 'Failing to enable 2FA within the grace period will result in a locked account. In case that happens contact the website’s administrator.', 'wp-2fa' ) .'</p><p>'. esc_html__( 'Thank you.', 'wp-2fa' ) .'</p><p>'. esc_html__( 'Email sent by', 'wp-2fa' ).' <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">'. esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) .'</a></p>';
437
438 // Create Login Code Message.
439 $login_code_subject = __( 'Your login confirmation code for {site_name}', 'wp-2fa' );
440 $login_code_body = sprintf(
441 /* translators: %s: the token for the user to use */
442 '<p>%1$s <strong>%2$s</strong> %3$s</p><p>%4$s</p><p>%5$s <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">%6$s</a></p>',
443 esc_html__( 'Enter', 'wp-2fa' ),
444 '{login_code}',
445 esc_html__( 'to log in.', 'wp-2fa' ),
446 esc_html__( 'Thank you.', 'wp-2fa' ),
447 esc_html__( 'Email sent by', 'wp-2fa' ),
448 esc_html__( 'WP 2FA plugin.', 'wp-2fa' )
449 );
450
451 // Create User Locked Message.
452 $user_locked_subject = __( 'Your user on {site_name} has been locked', 'wp-2fa' );
453 $user_locked_body = sprintf(
454 /* translators: %1$s: is the user name, %2$s is the website name */
455 '<p>%1$s</p><p>%2$s %3$s %4$s %5$s %6$s</p><p>%7$s</p><p>%8$s</p><p>%9$s <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">%10$s</a></p>',
456 esc_html__( 'Hello.', 'wp-2fa' ),
457 esc_html__( 'Since you have not enabled two-factor authentication for the user', 'wp-2fa' ),
458 '{user_login_name}',
459 esc_html__( 'on the website', 'wp-2fa' ),
460 '{site_name}',
461 esc_html__( 'within the grace period, your account has been locked.', 'wp-2fa' ),
462 esc_html__( 'Contact your website administrator to unlock your account.', 'wp-2fa' ),
463 esc_html__( 'Thank you.', 'wp-2fa' ),
464 esc_html__( 'Email sent by', 'wp-2fa' ),
465 esc_html__( 'WP 2FA plugin.', 'wp-2fa' )
466 );
467
468 // Create User unlocked Message.
469 $user_unlocked_subject = __( 'Your user on {site_name} has been unlocked', 'wp-2fa' );
470 $user_unlocked_body = '';
471
472 $user_unlocked_body .= '<p>'. __( 'Hello,', 'wp-2fa' ) .'</p><p>'. esc_html__( 'Your user', 'wp-2fa' ) .' <strong>{user_login_name}</strong> '. esc_html__( 'on the website', 'wp-2fa' ) .' {site_url} '. __( 'has been unlocked. Please configure two-factor authentication within the grace period, otherwise your account will be locked again.', 'wp-2fa' ) .'</p>';
473
474 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
475 $user_unlocked_body .= '<p>'. __( 'You can configure 2FA from this page:', 'wp-2fa' ) .' <a href="{2fa_settings_page_url}" target="_blank">{2fa_settings_page_url}.</a></p>';
476 }
477
478 $user_unlocked_body .='<p>'. __( 'Thank you.', 'wp-2fa' ) .'</p><p>'. __( 'Email sent by', 'wp-2fa' ) .' <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">'. __( 'WP 2FA plugin', 'wp-2fa' ) .'</a></p>';
479
480 // Array of defaults, now we have things setup above.
481 $default_settings = array(
482 'email_from_setting' => 'use-defaults',
483 'custom_from_email_address' => '',
484 'custom_from_display_name' => '',
485 'enforced_email_subject' => $enable_2fa_subject,
486 'enforced_email_body' => $enable_2fa_body,
487 'login_code_email_subject' => $login_code_subject,
488 'login_code_email_body' => $login_code_body,
489 'user_account_locked_email_subject' => $user_locked_subject,
490 'user_account_locked_email_body' => $user_locked_body,
491 'user_account_unlocked_email_subject' => $user_unlocked_subject,
492 'user_account_unlocked_email_body' => $user_unlocked_body,
493 'send_enforced_email' => 'enable_enforced_email',
494 'send_account_locked_email' => 'enable_account_locked_email',
495 'send_account_unlocked_email' => 'enable_account_unlocked_email',
496 );
497
498 if ( $apply_defaults || ! empty( $grab_default ) ) {
499 return $default_settings[ $setting_name ];
500 }
501 }
502
503 /**
504 * Util which we use to replace our {strings} with actual, useful stuff.
505 *
506 * @param string $input Text we are working on.
507 * @param int|string $user_id User id, if its needed.
508 * @param string $token Login code, if its needed..
509 * @return string The output, with all the {strings} swapped out.
510 */
511 public static function replace_email_strings( $input = '', $user_id = '', $token = '', $override_grace_period = '' ) {
512
513 // Gather grace period.
514 $grace_period_string = '';
515 if ( isset( $override_grace_period ) && ! empty( $override_grace_period ) ) {
516 $grace_period_string = $override_grace_period;
517 } else {
518 $grace_policy = self::get_wp2fa_setting( 'grace-policy' );
519 $grace_period_string = DateTimeUtils::format_grace_period_expiration_string( $grace_policy );
520 }
521
522 // Setup user data.
523 if ( isset( $user_id ) && ! empty( $user_id ) ) {
524 $user = get_userdata( $user_id );
525 } else {
526 $user = wp_get_current_user();
527 }
528
529 // Setup token.
530 if ( isset( $token ) && ! empty( $token ) ) {
531 $login_code = $token;
532 } else {
533 $login_code = '';
534 }
535
536 $new_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
537 if ( ! empty( $new_page_id ) ) {
538 $new_page_permalink = get_permalink( $new_page_id );
539 } else {
540 $new_page_permalink = '';
541 }
542
543 // These are the strings we are going to search for, as well as there respective replacements.
544 $replacements = array(
545 '{site_url}' => esc_url( get_bloginfo( 'url' ) ),
546 '{site_name}' => sanitize_text_field( get_bloginfo( 'name' ) ),
547 '{grace_period}' => sanitize_text_field( $grace_period_string ),
548 '{user_login_name}' => sanitize_text_field( $user->user_login ),
549 '{login_code}' => sanitize_text_field( $login_code ),
550 '{2fa_settings_page_url}' => esc_url( $new_page_permalink ),
551 );
552
553 $replacements = apply_filters(
554 'wp_2fa_replacement_email_strings',
555 $replacements
556 );
557
558 $final_output = str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
559 return $final_output;
560 }
561
562 /**
563 * If a user is trying to access anywhere other than the 2FA config area, this blocks them.
564 */
565 public function block_unconfigured_users_from_admin() {
566 global $pagenow;
567
568 if ( 'use-grace-period' !== WP2FA::get_wp2fa_setting( 'grace-policy' ) ) {
569 $user = wp_get_current_user();
570 $is_user_instantly_enforced = get_user_meta( $user->ID, 'wp_2fa_user_enforced_instantly', true );
571 $grace_period_expiry_time = get_user_meta( $user->ID, 'wp_2fa_grace_period_expiry', true );
572 $time_now = time();
573 if ( $is_user_instantly_enforced && ! empty( $grace_period_expiry_time ) && $grace_period_expiry_time < $time_now ) {
574
575 /*
576 * We should only allow:
577 * - 2FA setup wizard in the administration
578 * - custom 2FA page if enabled and created
579 * - AJAX requests originating from these 2FA setup UIs
580 */
581 if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], [ 'send_authentication_setup_email', 'validate_authcode_via_ajax' ] )) {
582 return;
583 }
584
585 if ( is_admin() || is_network_admin() ) {
586 $allowed_admin_page = self::is_this_multisite() ? 'index.php' : 'options-general.php';
587 if ( $pagenow === $allowed_admin_page && ( isset( $_GET['page'] ) && $_GET['page'] === 'wp-2fa-setup' ) ) {
588 return;
589 }
590 }
591
592 if ( is_page() ) {
593 $custom_user_page_id = \WP2FA\WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
594 if ( !empty( $custom_user_page_id ) && get_the_ID() == (int) $custom_user_page_id ) {
595 return;
596 }
597 }
598
599 // force a redirect to the 2FA set-up page if it exists
600 $custom_user_page_id = \WP2FA\WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
601 if ( !empty( $custom_user_page_id ) ) {
602 wp_redirect( get_permalink($custom_user_page_id) );
603 exit;
604 }
605
606 // custom 2FA page is not set-up, force redirect to the wizard in administration
607 $url = self::is_this_multisite() ? 'index.php' : 'options-general.php';
608 wp_redirect( add_query_arg( [
609 'page' => 'wp-2fa-setup'
610 ], admin_url( $url ) ) );
611 exit;
612 }
613 }
614 }
615
616 /**
617 * Handles AJAX calls for sending test emails.
618 */
619 public function handle_send_test_email_ajax() {
620
621 // check user permissions
622 if ( ! current_user_can( 'manage_options' ) ) {
623 wp_send_json_error();
624 }
625
626 // check email id
627 $email_id = filter_input(INPUT_POST, 'email_id', FILTER_SANITIZE_STRING);
628 if ($email_id === null || $email_id === false) {
629 wp_send_json_error();
630 }
631
632 // check nonce
633 $nonce = filter_input(INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING);
634 if ($nonce === null || $nonce === false || ! wp_verify_nonce($nonce, 'wp-2fa-email-test-' . $email_id)) {
635 wp_send_json_error();
636 }
637
638 $user_id = get_current_user_id();
639 // Grab user data.
640 $user = get_userdata( $user_id );
641 // Grab user email.
642 $email = $user->user_email;
643
644 if ('config_test' === $email_id) {
645 $email_sent = SettingsPage::send_email(
646 $email,
647 esc_html__('Test email from WP 2FA', 'wp-2fa'),
648 esc_html__('This email was sent by the WP 2FA plugin to test the email delivery.', 'wp-2fa')
649 );
650 if ( $email_sent ) {
651 wp_send_json_success('Test email was successfully sent to <strong>' . $email . '</strong>' );
652 }
653
654 wp_send_json_error();
655 }
656
657 /** @var EmailTemplate[] $email_templates */
658 $email_templates = $this->settings->get_email_notification_definitions();
659 foreach ($email_templates as $email_template) {
660 if ($email_id === $email_template->getEmailContentId()) {
661 // send the test email
662
663
664
665 // Setup the email contents.
666 $subject = wp_strip_all_tags( WP2FA::get_wp2fa_email_templates( $email_id . '_email_subject' ) );
667 $message = wpautop( WP2FA::get_wp2fa_email_templates( $email_id . '_email_body' ), $user_id );
668
669 $email_sent = SettingsPage::send_email( $email, $subject, $message );
670 if ( $email_sent ) {
671 wp_send_json_success('Test email <strong>' . $email_template->getTitle() . '</strong> was successfully sent to <strong>' . $email . '</strong>' );
672 }
673
674 wp_send_json_error();
675 }
676 }
677 }
678 }
679