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