PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 1.6.0
WP 2FA – Two-factor authentication for WordPress v1.6.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 / 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
957 lines
1 <?php // phpcs:ignore
2
3 namespace WP2FA;
4
5 use WP2FA\Admin\UserListing;
6 use WP2FA\Admin\SettingsPage;
7 use WP2FA\Utils\DateTimeUtils;
8
9 /**
10 * Main WP2FA Class.
11 */
12 class WP2FA {
13
14 /**
15 * Plugin version.
16 *
17 * @var string
18 */
19 public $version = WP_2FA_VERSION;
20
21 /**
22 * Options variables.
23 *
24 * @var array
25 */
26 protected static $wp_2fa_options;
27 protected static $wp_2fa_email_templates;
28
29 /**
30 * Count of the available methods
31 *
32 * @var integer
33 */
34 public static $methodsCount = 2;
35
36 /**
37 * Instance wrapper.
38 *
39 * @var object
40 */
41 private static $instance = null;
42
43 /**
44 * Holds array with all the sites in multisite WP installation
45 *
46 * @var array
47 */
48 private static $sites = [];
49
50 /**
51 * Return plugin instance.
52 */
53 public static function get_instance() {
54
55 if ( null === self::$instance ) {
56 self::$instance = new self();
57 }
58
59 return self::$instance;
60 }
61
62 /**
63 * Contructor.
64 */
65 private function __construct() {
66 if ( self::is_this_multisite() ) {
67 self::$wp_2fa_options = get_network_option( null, 'wp_2fa_settings' );
68 self::$wp_2fa_email_templates = get_network_option( null, 'wp_2fa_email_settings' );
69 } else {
70 self::$wp_2fa_options = get_option( 'wp_2fa_settings' );
71 self::$wp_2fa_email_templates = get_option( 'wp_2fa_email_settings' );
72 }
73
74 // Activation/Deactivation.
75 register_activation_hook( WP_2FA_FILE, '\WP2FA\Core\activate' );
76 register_deactivation_hook( WP_2FA_FILE, '\WP2FA\Core\deactivate' );
77 // Register our uninstallation hook.
78 register_uninstall_hook( WP_2FA_FILE, '\WP2FA\Core\uninstall' );
79 }
80
81 /**
82 * Fire up classes.
83 */
84 public function init() {
85 // Bootstrap.
86 Core\setup();
87
88 $this->settings = new Admin\SettingsPage();
89 $this->wizard = new Admin\SetupWizard();
90 $this->authentication = new Authenticator\Authentication();
91 $this->backupcodes = new Authenticator\BackupCodes();
92 $this->login = new Authenticator\Login();
93 $this->user_notices = new Admin\UserNotices();
94 $this->crontasks = new Cron\CronTasks();
95 $this->user_registered = new Admin\UserRegistered();
96 $this->shortcodes = new Shortcodes\Shortcodes();
97 // BG Processors.
98 $this->bg_enforce_2fa = new BackgroundProcessing\Enforce2FA();
99 $this->bg_delete_grace = new BackgroundProcessing\DeleteGracePeriod();
100 $this->bg_remove_methods = new BackgroundProcessing\RemoveEnabledMethods();
101 $this->bg_wipe_2fa = new BackgroundProcessing\RemoveAllUserData();
102
103 global $pagenow;
104 if ( 'profile.php' !== $pagenow || 'user-edit.php' !== $pagenow ) {
105 $this->user_profiles = new Admin\UserProfile();
106 }
107
108 if ( is_admin() ) {
109 UserListing::init();
110 }
111
112 $this->add_actions();
113 }
114
115 /**
116 * Add our plugins actions.
117 */
118 public function add_actions() {
119 // Plugin redirect on activation, only if we have no settings currently saved.
120 if ( empty( self::$wp_2fa_options ) || ! isset( self::$wp_2fa_options ) ) {
121 add_action( 'admin_init', array( $this, 'setup_redirect' ), 10 );
122 }
123
124 // SettingsPage.
125 if ( self::is_this_multisite() ) {
126 add_action( 'network_admin_menu', array( $this->settings, 'create_settings_admin_menu_multisite' ) );
127 add_action( 'network_admin_edit_update_wp2fa_network_options', array( $this->settings, 'update_wp2fa_network_options' ) );
128 add_action( 'network_admin_edit_update_wp2fa_network_email_options', array( $this->settings, 'update_wp2fa_network_email_options' ) );
129 add_action( 'network_admin_notices', array( $this->settings, 'settings_saved_network_admin_notice' ) );
130 } else {
131 add_action( 'admin_menu', array( $this->settings, 'create_settings_admin_menu' ) );
132 }
133 add_action( 'wp_ajax_get_all_users', array( $this->settings, 'get_all_users' ) );
134 add_action( 'wp_ajax_get_all_network_sites', array( $this->settings, 'get_all_network_sites' ) );
135 add_action( 'wp_ajax_unlock_account', array( $this->settings, 'unlock_account' ), 10, 1 );
136 add_action( 'admin_action_unlock_account', array( $this->settings, 'unlock_account' ), 10, 1 );
137 add_action( 'admin_action_remove_user_2fa', array( $this->settings, 'remove_user_2fa' ), 10, 1 );
138 add_action( 'wp_ajax_remove_user_2fa', array( $this->settings, 'remove_user_2fa' ), 10, 1 );
139 add_action( 'admin_menu', array( $this->settings, 'hide_settings' ), 999 );
140 add_action( 'plugin_action_links_' . WP_2FA_BASE, array( $this->settings, 'add_plugin_action_links' ) );
141 add_filter( 'display_post_states', array( $this->settings, 'add_display_post_states' ), 10, 2 );
142 add_action( 'wp_ajax_wp_2fa_cancel_bg_processes', array( $this->settings, 'cancel_bg_processes' ) );
143
144 // SetupWizard.
145 if ( self::is_this_multisite() ) {
146 add_action( 'network_admin_menu', array( $this->wizard, 'network_admin_menus' ), 10 );
147 add_action( 'admin_menu', array( $this->wizard, 'admin_menus' ), 10 );
148 } else {
149 add_action( 'admin_menu', array( $this->wizard, 'admin_menus' ), 10 );
150 }
151 add_action( 'plugins_loaded', array( $this, 'add_wizard_actions' ), 10 );
152 add_action( 'wp_ajax_send_authentication_setup_email', array( $this->wizard, 'send_authentication_setup_email' ) );
153 add_action( 'wp_ajax_regenerate_authentication_key', array( $this->wizard, 'regenerate_authentication_key' ) );
154
155 // UserNotices.
156 add_action( 'wp_ajax_dismiss_nag', array( $this->user_notices, 'dismiss_nag' ) );
157 add_action( 'wp_ajax_dismiss_nag', array( $this->user_notices, 'dismiss_reconfigure_nag' ) );
158 add_action( 'clear_auth_cookie', array( $this->user_notices, 'reset_nag' ) );
159
160 // UserProfile.
161 global $pagenow;
162 if ( 'profile.php' !== $pagenow || 'user-edit.php' !== $pagenow ) {
163 add_action( 'show_user_profile', array( $this->user_profiles, 'user_2fa_options' ) );
164 add_action( 'edit_user_profile', array( $this->user_profiles, 'user_2fa_options' ) );
165 if ( self::is_this_multisite() ) {
166 add_action( 'personal_options_update', array( $this->user_profiles, 'save_user_2fa_options' ) );
167 }
168 }
169 add_filter( 'user_row_actions', array( $this->user_profiles, 'user_2fa_row_actions' ), 10, 2 );
170 if ( self::is_this_multisite() ) {
171 add_filter( 'ms_user_row_actions', array( $this->user_profiles, 'user_2fa_row_actions' ), 10, 2 );
172 }
173 add_action( 'wp_ajax_validate_authcode_via_ajax', array( $this->user_profiles, 'validate_authcode_via_ajax' ) );
174 add_action( 'wp_ajax_wp2fa_test_email', array( $this, 'handle_send_test_email_ajax' ) );
175
176 // Login.
177 add_action( 'init', array( $this->login, 'get_providers' ) );
178 add_action( 'wp_login', array( $this->login, 'wp_login' ), 20, 2 );
179 add_action( 'login_form_validate_2fa', array( $this->login, 'login_form_validate_2fa' ) );
180 add_action( 'login_form_backup_2fa', array( $this->login, 'backup_2fa' ) );
181
182 /**
183 * Keep track of all the user sessions for which we need to invalidate the
184 * authentication cookies set during the initial password check.
185 */
186 add_action( 'set_auth_cookie', array( $this->login, 'collect_auth_cookie_tokens' ) );
187 add_action( 'set_logged_in_cookie', array( $this->login, 'collect_auth_cookie_tokens' ) );
188
189 // Run only after the core wp_authenticate_username_password() check.
190 add_filter( 'authenticate', array( $this->login, 'filter_authenticate' ), 50 );
191 add_filter( 'wp_authenticate_user', array( $this->login, 'run_authentication_check' ), 10, 2 );
192
193 // User Register.
194 add_action( 'set_user_role', array( $this->user_registered, 'check_user_upon_role_change' ), 10, 3 );
195
196 // Block users from admin if needed.
197 $user_block_hook = is_admin() || is_network_admin() ? 'init' : 'wp';
198 add_action( $user_block_hook, array( $this, 'block_unconfigured_users_from_admin' ), 10 );
199 }
200
201 /**
202 * Add actions specific to the wizard.
203 */
204 public function add_wizard_actions() {
205 new BackgroundProcessing\Enforce2FA();
206 if ( function_exists( 'wp_get_current_user' ) && current_user_can( 'read' ) ) {
207 add_action( 'admin_init', array( $this->wizard, 'setup_page' ), 10 );
208 }
209 }
210
211 /**
212 * Redirect user to 1st time setup.
213 */
214 public function setup_redirect() {
215
216 // Bail early before the redirect if the user can't manage options.
217 if ( ! current_user_can( 'manage_options' ) ) {
218 return;
219 }
220
221 if ( get_option( 'wp_2fa_redirect_on_activate', false ) ) {
222 // Delete redirect option.
223 delete_option( 'wp_2fa_redirect_on_activate' );
224
225 $page = ( self::is_this_multisite() && is_super_admin() ) ? network_admin_url( 'index.php' ) : admin_url( 'options-general.php' );
226 $redirect = add_query_arg(
227 array(
228 'page' => 'wp-2fa-setup',
229 'is_initial_setup' => 'true',
230 ),
231 admin_url( 'user-edit.php' )
232 );
233
234 wp_safe_redirect( $redirect );
235 exit();
236 }
237 }
238
239 /**
240 * Check is this is a multisite setup.
241 */
242 public static function is_this_multisite() {
243 return function_exists( 'is_multisite' ) && is_multisite();
244 }
245
246 /**
247 * Return user roles.
248 *
249 * @return array User roles.
250 */
251 public static function wp_2fa_get_roles() {
252 global $wp_roles;
253 $roles = $wp_roles->role_names;
254 return $roles;
255 }
256
257 /**
258 * Check to see if the user or user role is excluded.
259 *
260 * @param int $user_id User id.
261 * @return boolean Is user excluded or not.
262 */
263 public static function is_user_excluded( $user_id, $excluded_users = '', $excluded_roles = '', $excluded_sites = '', $included_sites = [] ) {
264 $user = false;
265 $user_roles = false;
266
267 // If we have been passed an object, handle accordingly.
268 if ( is_a( $user_id, '\WP_User' ) ) {
269 $user = $user_id;
270 $user_roles = $user->roles;
271 }
272
273 // If we have an int instead, lets get the user data for that ID.
274 if ( is_numeric( $user_id ) || isset( $_GET['user_id'] ) && is_numeric( $user_id ) ) {
275 $user = get_user_by( 'id', $user_id );
276 $user_roles = $user->roles;
277 }
278
279 // Finally, we could have an array consisting of ID or user_login.
280 if ( is_array( $user_id ) && isset( $user_id['ID'] ) ) {
281 $user = get_user_by( 'id', $user_id['ID'] );
282 $user_roles = $user->roles;
283 }
284
285 // Finally, if we reach this point with no $user or $user_roles lets get the current user.
286 if ( ! $user || ! $user_roles ) {
287 $user = wp_get_current_user();
288 $user_roles = $user->roles;
289 }
290
291 $user_excluded = false;
292
293 if ( isset( $excluded_users ) && ! empty( $excluded_users ) ) {
294 $excluded_users = $excluded_users;
295 } else {
296 $excluded_users = WP2FA::get_wp2fa_setting( 'excluded_users' );
297 }
298
299 if ( ! empty( $excluded_users ) ) {
300 // Turn it into an array.
301 $excluded_users_array = $excluded_users;
302 // Compare our roles with the users and see if we get a match.
303 $result = in_array( $user->user_login, $excluded_users_array, true );
304 if ( $result ) {
305 $user_excluded = true;
306 return true;
307 }
308 }
309
310 if ( isset( $excluded_roles ) && ! empty( $excluded_roles ) ) {
311 $excluded_roles = $excluded_roles;
312 } else {
313 $excluded_roles = WP2FA::get_wp2fa_setting( 'excluded_roles' );
314 }
315
316 if ( ! empty( $excluded_roles ) ) {
317 // Turn it into an array.
318 $excluded_roles_array = array_map('strtolower', $excluded_roles );
319 // Compare our roles with the users and see if we get a match.
320 $result = array_intersect( $excluded_roles_array, $user->roles );
321 if ( $result ) {
322 $user_excluded = true;
323 return true;
324 }
325 }
326
327 if ( self::is_this_multisite() ) {
328 if ( isset( $excluded_sites ) && ! empty( $excluded_sites ) ) {
329 $excluded_sites = $excluded_sites;
330 } else {
331 $excluded_sites = WP2FA::get_wp2fa_setting( 'excluded_sites' );
332 }
333
334 if ( ! empty( $excluded_sites ) ) {
335 // Turn it into an array.
336 $excluded_site_array = explode( ',', strtolower( $excluded_sites ) );
337 $site_ids_only = array();
338 // Remove everything but the excluded sites ID, which we will use to check if user is a member.
339 foreach ( $excluded_site_array as $excluded_site ) {
340 if ( isset( explode( ':', $excluded_site )[1] ) ) {
341 $id = trim( explode( ':', $excluded_site )[1] );
342 $site_ids_only[] = $id;
343 }
344 }
345 foreach ( $site_ids_only as $site_id ) {
346 if ( is_user_member_of_blog( $user->ID, $site_id ) ) {
347 // User is a member of the a blog we are excluding from 2FA.
348 $user_excluded = true;
349 return true;
350 } else {
351 // User is NOT a member of the a blog we are excluding.
352 $user_excluded = false;
353 }
354 }
355 }
356
357 if ( ! isset( $included_sites ) || empty( $included_sites ) ) {
358 $included_sites = WP2FA::get_wp2fa_setting( 'included_sites' );
359 }
360
361 foreach ( $included_sites as $siteId ) {
362 if ( is_user_member_of_blog( $user->ID, $siteId ) ) {
363 $user_excluded = false;
364 }
365 }
366 }
367
368 if ( true === $user_excluded ) {
369 return true;
370 }
371
372 return false;
373 }
374
375 /**
376 * Checks if user is enforced
377 *
378 * @since 1.6
379 *
380 * @param [type] $user_id
381 * @param string $current_policy
382 * @param string $excluded_users
383 * @param string $excluded_roles
384 * @param string $enforced_users
385 * @param string $enforced_roles
386 *
387 * @return boolean
388 */
389 public static function isUserEnforced( $user_id, $current_policy = '', $excluded_users = '', $excluded_roles = '', $enforced_users = '', $enforced_roles = '' ) {
390 if ( isset( $user_id ) ) {
391 $user = get_user_by( 'id', $user_id );
392 $user_roles = $user->roles;
393 } else {
394 $user = wp_get_current_user();
395 $user_roles = $user->roles;
396 }
397
398 if ( $current_policy ) {
399 $current_policy = $current_policy;
400 } else {
401 $current_policy = self::get_wp2fa_setting( 'enforcement-policy' );
402 }
403
404 $enabled_method = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
405 $user_eligable = false;
406
407 // Lets check the policy settings and if the user has setup totp/email by checking for the usermeta.
408 if ( empty( $enabled_method ) && self::is_this_multisite() && 'superadmins-only' === $current_policy ) {
409 return is_super_admin( $user->ID );
410 } else if ( 'all-users' === $current_policy && empty( $enabled_method ) ) {
411
412 if ( isset( $excluded_users ) ) {
413 $excluded_users = $excluded_users;
414 } else {
415 $excluded_users = self::get_wp2fa_setting( 'excluded_users' );
416 }
417
418 if ( ! empty( $excluded_users ) ) {
419 // Turn it into an array.
420 $excluded_users_array = explode( ',', $excluded_users );
421 // Compare our roles with the users and see if we get a match.
422 $result = in_array( $user->user_login, $excluded_users_array, true );
423 if ( ! $result ) {
424 $user_eligable = true;
425 }
426 }
427
428 if ( isset( $excluded_roles ) ) {
429 $excluded_roles = $excluded_roles;
430 } else {
431 $excluded_roles = self::get_wp2fa_setting( 'excluded_roles' );
432 }
433
434 if ( ! empty( $excluded_roles ) ) {
435 // Turn it into an array.
436 $excluded_roles_array = explode( ',', strtolower( $excluded_roles ) );
437 // Compare our roles with the users and see if we get a match.
438 $result = array_intersect( $excluded_roles_array, $user->roles );
439
440 if ( ! empty( $result ) ) {
441 $user_eligable = true;
442 }
443
444 if ( self::is_this_multisite() ) {
445 $users_caps = array();
446 $subsites = get_sites();
447 // Check each site and add to our array so we know each users actual roles.
448 foreach ( $subsites as $subsite ) {
449 $subsite_id = get_object_vars( $subsite )['blog_id'];
450 $users_caps[] = get_user_meta( $user->ID, 'wp_' .$subsite_id .'_capabilities', true );
451 }
452 // Strip the top layer ready.
453 $users_caps = $users_caps;
454 foreach ( $users_caps as $key => $value ) {
455 if ( ! empty( $value ) ) {
456 foreach ( $value as $key => $value ) {
457 $result = in_array( $key, $excluded_roles_array, true );
458 }
459 }
460 }
461 if ( ! empty( $result ) ) {
462 return false;
463 }
464 }
465 }
466
467 if ( true === $user_eligable || empty( $enabled_method ) ) {
468 return true;
469 }
470 } elseif ( 'certain-roles-only' === $current_policy && empty( $enabled_method ) ) {
471
472 if ( isset( $enforced_users ) && ! empty( $enforced_users ) ) {
473 $enforced_users = $enforced_users;
474 } else {
475 $enforced_users = self::get_wp2fa_setting( 'enforced_users' );
476 }
477
478 if ( ! empty( $enforced_users )) {
479 // Turn it into an array.
480 $enforced_users_array = $enforced_users;
481 // Compare our roles with the users and see if we get a match.
482 $result = in_array( $user->user_login, $enforced_users_array, true );
483 // The user is one of the chosen roles we are forcing 2FA onto, so lets show the nag.
484 if ( ! empty( $result ) ) {
485 return true;
486 }
487 }
488
489 if ( isset( $enforced_roles ) && ! empty( $enforced_roles ) ) {
490 $enforced_roles = $enforced_roles;
491 } else {
492 $enforced_roles = self::get_wp2fa_setting( 'enforced_roles' );
493 }
494
495 if ( ! empty( $enforced_roles ) ) {
496 // Turn it into an array.
497 $enforced_roles_array = SettingsPage::extract_roles_from_input( $enforced_roles );
498 // Compare our roles with the users and see if we get a match.
499 $result = array_intersect( $enforced_roles_array, $user->roles );
500 // The user is one of the chosen roles we are forcing 2FA onto, so lets show the nag.
501 if ( ! empty( $result ) ) {
502 return true;
503 }
504
505 if ( self::is_this_multisite() ) {
506 $users_caps = array();
507 $subsites = get_sites();
508 // Check each site and add to our array so we know each users actual roles.
509 foreach ( $subsites as $subsite ) {
510 $subsite_id = get_object_vars( $subsite )['blog_id'];
511 $users_caps[] = get_user_meta( $user->ID, 'wp_' .$subsite_id .'_capabilities', true );
512 }
513 // Strip the top layer ready.
514 $users_caps = $users_caps;
515 foreach ( $users_caps as $key => $value ) {
516 if ( ! empty( $value ) ) {
517 foreach ( $value as $key => $value ) {
518 $result = in_array( $key, $enforced_roles_array, true );
519 }
520 }
521 }
522 if ( ! empty( $result ) ) {
523 return true;
524 }
525 }
526 }
527
528 } elseif ( 'certain-users-only' === $current_policy && empty( $enabled_method ) ) {
529
530 if ( isset( $enforced_users ) && ! empty( $enforced_users ) ) {
531 $enforced_users = $enforced_users;
532 } else {
533 $enforced_users = self::get_wp2fa_setting( 'enforced_users' );
534 }
535
536 if ( ! empty( $enforced_users ) ) {
537 // Turn it into an array.
538 $enforced_users_array = explode( ',', $enforced_users );
539 // Compare our roles with the users and see if we get a match.
540 $result = in_array( $user->user_login, $enforced_users_array, true );
541 // The user is one of the chosen roles we are forcing 2FA onto, so lets show the nag.
542 if ( ! empty( $result ) ) {
543 return true;
544 }
545 }
546 } elseif ( 'enforce-on-multisite' === $current_policy ) {
547 $includedSites = self::get_wp2fa_setting( 'included_sites' );
548
549 foreach ( $includedSites as $site_id ) {
550 if ( is_user_member_of_blog( $user_id, $site_id ) ) {
551 return true;
552 }
553 }
554 }
555
556 return false;
557 }
558
559 /**
560 * Util function to grab settings or apply defaults if no settings are saved into the db.
561 *
562 * @param string $setting_name Settings to grab value of.
563 * @param boolean $getDefaultOnEmpty return default setting value if current one is empty
564 * @param boolean $getDefaultValue return default value setting (ignore the stored ones)
565 * @return string Settings value or default value.
566 */
567 public static function get_wp2fa_setting( $setting_name = '', $getDefaultOnEmpty = false, $getDefaultValue = false ) {
568 $default_settings = array(
569 'enable_totp' => 'enable_totp',
570 'enable_email' => 'enable_email',
571 'backup_codes_enabled' => 'yes',
572 'enforcement-policy' => 'do-not-enforce',
573 'excluded_users' => [],
574 'excluded_roles' => [],
575 'enforced_users' => [],
576 'enforced_roles' => [],
577 'grace-period' => 3,
578 'grace-period-denominator' => 'days',
579 'enable_grace_cron' => '',
580 'enable_destroy_session' => '',
581 'limit_access' => '',
582 '2fa_settings_last_updated_by' => '',
583 '2fa_main_user' => '',
584 'grace-period-expiry-time' => '',
585 'plugin_version' => WP_2FA_VERSION,
586 'delete_data_upon_uninstall' => '',
587 'excluded_sites' => '',
588 'included_sites' => [],
589 'create-custom-user-page' => 'no',
590 'redirect-user-custom-page' => '',
591 'redirect-user-custom-page-global' => '',
592 'custom-user-page-url' => '',
593 'custom-user-page-id' => '',
594 'hide_remove_button' => '',
595 'grace-policy' => 'use-grace-period',
596 'default-text-code-page' => __( 'Please enter the two-factor authentication (2FA) verification code below to login. Depending on your 2FA setup, you can get the code from the 2FA app or it was sent to you by email.', 'wp-2fa' ),
597 );
598
599 if ( true === $getDefaultValue ) {
600 if ( isset( $default_settings[ $setting_name ] ) ) {
601 return $default_settings[ $setting_name ];
602 }
603
604 return false;
605 }
606
607 $apply_defaults = false;
608
609 // If we have no setting name, return them all.
610 if ( empty( $setting_name ) ) {
611 return self::$wp_2fa_options;
612 }
613
614 // First lets check if any options have been saved.
615 if ( empty( self::$wp_2fa_options ) || ! isset( self::$wp_2fa_options ) ) {
616 $apply_defaults = true;
617 }
618
619 if ( $apply_defaults ) {
620 return $default_settings[ $setting_name ];
621 } elseif ( ! isset( self::$wp_2fa_options[ $setting_name ] ) ) {
622 if ( true === $getDefaultOnEmpty ) {
623 if ( isset( $default_settings[ $setting_name ] ) ) {
624 return $default_settings[ $setting_name ];
625 }
626 }
627 return false;
628 } else {
629 return self::$wp_2fa_options[ $setting_name ];
630 }
631 }
632
633 /**
634 * Util function to grab EMAIL settings or apply defaults if no settings are saved into the db.
635 *
636 * @param string $setting_name Settings to grab value of.
637 * @return string Settings value or default value.
638 */
639 public static function get_wp2fa_email_templates( $setting_name = '', $grab_default = '' ) {
640
641 $apply_defaults = false;
642
643 // First lets check if any options have been saved.
644 if ( empty( self::$wp_2fa_email_templates ) || ! isset( self::$wp_2fa_email_templates ) ) {
645 $apply_defaults = true;
646 }
647
648 // If we have no setting name, return what ever is saved.
649 if ( empty( $setting_name ) ) {
650 return self::$wp_2fa_email_templates;
651 }
652
653 // If we have a saved setting, return it.
654 if ( $setting_name && isset( self::$wp_2fa_email_templates[ $setting_name ] ) ) {
655 return self::$wp_2fa_email_templates[ $setting_name ];
656 }
657
658 // If we reach this point, we have nothing saved and we are looking for defaults.
659 // Create Enable 2FA Message.
660 $enable_2fa_subject = __( 'Please enable 2FA on {site_url}', 'wp-2fa' );
661 $enable_2fa_body = '';
662
663 $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>';
664
665 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
666 $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>';
667 }
668
669 $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>';
670
671 // Create Login Code Message.
672 $login_code_subject = __( 'Your login confirmation code for {site_name}', 'wp-2fa' );
673
674
675 $login_code_body = '<p>' . sprintf(
676 esc_html__( 'Enter %1$s to log in.', 'wp-2fa' ),
677 '<strong>{login_code}</strong>'
678 );
679 $login_code_body .= '</p>';
680 $login_code_body .= '<p>' . esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
681 $login_code_body .= '<p>' . esc_html__( 'Email sent by', 'wp-2fa' );
682 $login_code_body .= ' <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">' . esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
683 $login_code_body .= '</p>';
684
685 // Create User Locked Message.
686 $user_locked_subject = __( 'Your user on {site_name} has been locked', 'wp-2fa' );
687
688 $user_locked_body = '<p>' . esc_html__( 'Hello.', 'wp-2fa' ) . '</p>';
689 $user_locked_body .= '<p>' . sprintf(
690 esc_html__( 'Since you have not enabled two-factor authentication for the user %1$s on the website %2$s within the grace period, your account has been locked.', 'wp-2fa' ),
691 '{user_login_name}',
692 '{site_name}'
693 );
694 $user_locked_body .= '</p>';
695 $user_locked_body .= '<p>' . esc_html__( 'Contact your website administrator to unlock your account.', 'wp-2fa' ) . '</p>';
696 $user_locked_body .= '<p>' . esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
697 $user_locked_body .= '<p>' . esc_html__( 'Email sent by', 'wp-2fa' );
698 $user_locked_body .= ' <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">' . esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
699 $user_locked_body .= '</p>';
700
701 // Create User unlocked Message.
702 $user_unlocked_subject = __( 'Your user on {site_name} has been unlocked', 'wp-2fa' );
703 $user_unlocked_body = '';
704
705 $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>';
706
707 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
708 $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>';
709 }
710
711 $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>';
712
713 // Array of defaults, now we have things setup above.
714 $default_settings = array(
715 'email_from_setting' => 'use-defaults',
716 'custom_from_email_address' => '',
717 'custom_from_display_name' => '',
718 'enforced_email_subject' => $enable_2fa_subject,
719 'enforced_email_body' => $enable_2fa_body,
720 'login_code_email_subject' => $login_code_subject,
721 'login_code_email_body' => $login_code_body,
722 'user_account_locked_email_subject' => $user_locked_subject,
723 'user_account_locked_email_body' => $user_locked_body,
724 'user_account_unlocked_email_subject' => $user_unlocked_subject,
725 'user_account_unlocked_email_body' => $user_unlocked_body,
726 'send_enforced_email' => 'enable_enforced_email',
727 'send_account_locked_email' => 'enable_account_locked_email',
728 'send_account_unlocked_email' => 'enable_account_unlocked_email',
729 );
730
731 if ( $apply_defaults || ! empty( $grab_default ) ) {
732 return $default_settings[ $setting_name ];
733 }
734 }
735
736 /**
737 * Util which we use to replace our {strings} with actual, useful stuff.
738 *
739 * @param string $input Text we are working on.
740 * @param int|string $user_id User id, if its needed.
741 * @param string $token Login code, if its needed..
742 * @return string The output, with all the {strings} swapped out.
743 */
744 public static function replace_email_strings( $input = '', $user_id = '', $token = '', $override_grace_period = '' ) {
745
746 // Gather grace period.
747 $grace_period_string = '';
748 if ( isset( $override_grace_period ) && ! empty( $override_grace_period ) ) {
749 $grace_period_string = $override_grace_period;
750 } else {
751 $grace_policy = self::get_wp2fa_setting( 'grace-policy' );
752 $grace_period_string = DateTimeUtils::format_grace_period_expiration_string( $grace_policy );
753 }
754
755 // Setup user data.
756 if ( isset( $user_id ) && ! empty( $user_id ) ) {
757 $user = get_userdata( $user_id );
758 } else {
759 $user = wp_get_current_user();
760 }
761
762 // Setup token.
763 if ( isset( $token ) && ! empty( $token ) ) {
764 $login_code = $token;
765 } else {
766 $login_code = '';
767 }
768
769 $new_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
770 if ( ! empty( $new_page_id ) ) {
771 $new_page_permalink = get_permalink( $new_page_id );
772 } else {
773 $new_page_permalink = '';
774 }
775
776 // These are the strings we are going to search for, as well as there respective replacements.
777 $replacements = array(
778 '{site_url}' => esc_url( get_bloginfo( 'url' ) ),
779 '{site_name}' => sanitize_text_field( get_bloginfo( 'name' ) ),
780 '{grace_period}' => sanitize_text_field( $grace_period_string ),
781 '{user_login_name}' => sanitize_text_field( $user->user_login ),
782 '{login_code}' => sanitize_text_field( $login_code ),
783 '{2fa_settings_page_url}' => esc_url( $new_page_permalink ),
784 );
785
786 $replacements = apply_filters(
787 'wp_2fa_replacement_email_strings',
788 $replacements
789 );
790
791 $final_output = str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
792 return $final_output;
793 }
794
795 /**
796 * If a user is trying to access anywhere other than the 2FA config area, this blocks them.
797 */
798 public function block_unconfigured_users_from_admin() {
799 global $pagenow;
800
801 if ( 'use-grace-period' !== WP2FA::get_wp2fa_setting( 'grace-policy' ) ) {
802 $user = wp_get_current_user();
803 $is_user_instantly_enforced = get_user_meta( $user->ID, 'wp_2fa_user_enforced_instantly', true );
804 $grace_period_expiry_time = get_user_meta( $user->ID, 'wp_2fa_grace_period_expiry', true );
805 $time_now = time();
806 if ( $is_user_instantly_enforced && ! empty( $grace_period_expiry_time ) && $grace_period_expiry_time < $time_now ) {
807
808 /*
809 * We should only allow:
810 * - 2FA setup wizard in the administration
811 * - custom 2FA page if enabled and created
812 * - AJAX requests originating from these 2FA setup UIs
813 */
814 if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], [ 'send_authentication_setup_email', 'validate_authcode_via_ajax' ] )) {
815 return;
816 }
817
818 if ( is_admin() || is_network_admin() ) {
819 $allowed_admin_page = self::is_this_multisite() ? 'index.php' : 'options-general.php';
820 if ( $pagenow === $allowed_admin_page && ( isset( $_GET['page'] ) && $_GET['page'] === 'wp-2fa-setup' ) ) {
821 return;
822 }
823 }
824
825 if ( is_page() ) {
826 $custom_user_page_id = \WP2FA\WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
827 if ( !empty( $custom_user_page_id ) && get_the_ID() == (int) $custom_user_page_id ) {
828 return;
829 }
830 }
831
832 // force a redirect to the 2FA set-up page if it exists
833 $custom_user_page_id = \WP2FA\WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
834 if ( !empty( $custom_user_page_id ) ) {
835 wp_redirect( get_permalink($custom_user_page_id) );
836 exit;
837 }
838
839 // custom 2FA page is not set-up, force redirect to the wizard in administration
840 $url = self::is_this_multisite() ? 'index.php' : 'options-general.php';
841 wp_redirect( add_query_arg( [
842 'page' => 'wp-2fa-setup'
843 ], admin_url( $url ) ) );
844 exit;
845 }
846 }
847 }
848
849 /**
850 * Handles AJAX calls for sending test emails.
851 */
852 public function handle_send_test_email_ajax() {
853
854 // check user permissions
855 if ( ! current_user_can( 'manage_options' ) ) {
856 wp_send_json_error();
857 }
858
859 // check email id
860 $email_id = filter_input(INPUT_POST, 'email_id', FILTER_SANITIZE_STRING);
861 if ($email_id === null || $email_id === false) {
862 wp_send_json_error();
863 }
864
865 // check nonce
866 $nonce = filter_input(INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING);
867 if ($nonce === null || $nonce === false || ! wp_verify_nonce($nonce, 'wp-2fa-email-test-' . $email_id)) {
868 wp_send_json_error();
869 }
870
871 $user_id = get_current_user_id();
872 // Grab user data.
873 $user = get_userdata( $user_id );
874 // Grab user email.
875 $email = $user->user_email;
876
877 if ('config_test' === $email_id) {
878 $email_sent = SettingsPage::send_email(
879 $email,
880 esc_html__('Test email from WP 2FA', 'wp-2fa'),
881 esc_html__('This email was sent by the WP 2FA plugin to test the email delivery.', 'wp-2fa')
882 );
883 if ( $email_sent ) {
884 wp_send_json_success('Test email was successfully sent to <strong>' . $email . '</strong>' );
885 }
886
887 wp_send_json_error();
888 }
889
890 /** @var EmailTemplate[] $email_templates */
891 $email_templates = $this->settings->get_email_notification_definitions();
892 foreach ($email_templates as $email_template) {
893 if ($email_id === $email_template->getEmailContentId()) {
894 // send the test email
895
896
897
898 // Setup the email contents.
899 $subject = wp_strip_all_tags( WP2FA::get_wp2fa_email_templates( $email_id . '_email_subject' ) );
900 $message = wpautop( WP2FA::get_wp2fa_email_templates( $email_id . '_email_body' ), $user_id );
901
902 $email_sent = SettingsPage::send_email( $email, $subject, $message );
903 if ( $email_sent ) {
904 wp_send_json_success('Test email <strong>' . $email_template->getTitle() . '</strong> was successfully sent to <strong>' . $email . '</strong>' );
905 }
906
907 wp_send_json_error();
908 }
909 }
910 }
911
912 /**
913 * Collects all the sites from multisite WP installation
914 *
915 * @return array
916 */
917 public static function getMultiSites() {
918 if ( self::is_this_multisite() ) {
919 if ( empty( self::$sites ) ) {
920
921 self::$sites = \get_sites();
922 }
923
924 return self::$sites;
925 }
926
927 return [];
928 }
929
930 /**
931 * Returns text with the number of plugins supported
932 *
933 * @since 1.6
934 *
935 * @return string
936 */
937 public static function getNumberOfPluginsText() {
938 $methodsCount = self::$methodsCount;
939
940 if ( \class_exists('NumberFormatter') ) {
941 $number_formatter = new \NumberFormatter( get_locale(), \NumberFormatter::SPELLOUT );
942 $methodsCount = $number_formatter->format( self::$methodsCount );
943 }
944
945 return
946 sprintf(
947 \_n(
948 'There is %s method available from which you can choose for 2FA:',
949 'There are %s methods available from which you can choose for 2FA:',
950 self::$methodsCount,
951 'wp-2fa'
952 ),
953 $methodsCount
954 );
955 }
956 }
957