PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.1.0
WP 2FA – Two-factor authentication for WordPress v2.1.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 4 years ago Authenticator 4 years ago Cron 4 years ago Shortcodes 4 years ago Utils 4 years ago .gitkeep 6 years ago EmailTemplate.php 5 years ago WP2FA.php 4 years ago index.php 5 years ago
WP2FA.php
855 lines
1 <?php // phpcs:ignore
2
3 namespace WP2FA;
4
5 use WP2FA\Admin\User;
6 use WP2FA\Admin\UserListing;
7 use WP2FA\Admin\SettingsPage;
8 use WP2FA\Admin\HelpContactUs;
9 use WP2FA\Utils\RequestUtils;
10 use WP2FA\Utils\DateTimeUtils;
11 use WP2FA\Authenticator\Open_SSL;
12 use WP2FA\Admin\Controllers\Settings;
13 use WP2FA\Utils\SettingsUtils as SettingsUtils;
14
15 /**
16 * Main WP2FA Class.
17 */
18 class WP2FA {
19
20 /**
21 * Plugin version.
22 *
23 * @var string
24 */
25 public $version = WP_2FA_VERSION;
26
27 /**
28 * Holds the global plugin secret key for storing the TOTP
29 *
30 * @var string
31 *
32 * @since 2.0.0
33 */
34 private static $secret_key = null;
35
36 /**
37 * Local static cache for plugins settings.
38 *
39 * @var array
40 *
41 * @since 2.0.0
42 */
43 private static $plugin_settings = array();
44
45 /**
46 * Local static cache for email template settings.
47 *
48 * @var array
49 */
50 protected static $wp_2fa_email_templates;
51
52 /**
53 * Count of the available methods
54 *
55 * @var integer
56 */
57 public static $methodsCount = 3;
58
59 /**
60 * Instance wrapper.
61 *
62 * @var WP2FA
63 */
64 private static $instance = null;
65
66 /**
67 * Holds array with all the sites in multisite WP installation
68 *
69 * @var array
70 */
71 private static $sites = [];
72
73 /**
74 * Return plugin instance.
75 */
76 public static function get_instance() {
77 if ( null === self::$instance ) {
78 self::$instance = new self();
79 }
80
81 return self::$instance;
82 }
83
84 /**
85 * Constructor.
86 */
87 private function __construct() {
88
89 self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] = SettingsUtils::get_option( WP_2FA_POLICY_SETTINGS_NAME );
90 self::$plugin_settings[ WP_2FA_SETTINGS_NAME ] = SettingsUtils::get_option( WP_2FA_SETTINGS_NAME );
91 self::$plugin_settings[ WP_2FA_WHITE_LABEL_SETTINGS_NAME ] = SettingsUtils::get_option( WP_2FA_WHITE_LABEL_SETTINGS_NAME );
92
93 self::$wp_2fa_email_templates = SettingsUtils::get_option( WP_2FA_EMAIL_SETTINGS_NAME );
94
95 /** We need to exclude all the possible ways, that logic to be executed by some WP request which could come from cron job or AJAX call, which will break the wizard (by storing the settings for the plugin) before it is completed by the user. We also have to check if the user is still processing first time wizard ($_GET parameter), and if the wizard has been finished already (wp_2fa_wizard_not_finished) */
96 if ( SettingsUtils::get_option( 'wizard_not_finished' ) && ! isset( $_GET['is_initial_setup'] ) && ! wp_doing_ajax() && ! defined( 'DOING_CRON' ) ) {
97
98 if ( ! SettingsUtils::get_option( WP_2FA_SETTINGS_NAME ) ) {
99 self::updatePluginSettings( self::getDefaultSettings() );
100 }
101
102 // Set a flag so we know we have default values present, not custom.
103 SettingsUtils::update_option( 'default_settings_applied', true );
104 SettingsUtils::delete_option( 'wizard_not_finished' );
105 }
106
107 // Activation/Deactivation.
108 register_activation_hook( WP_2FA_FILE, '\WP2FA\Core\activate' );
109 register_deactivation_hook( WP_2FA_FILE, '\WP2FA\Core\deactivate' );
110 // Register our uninstallation hook.
111 register_uninstall_hook( WP_2FA_FILE, '\WP2FA\Core\uninstall' );
112 }
113
114 public static function getDefaultSettings() {
115 $default_settings = array(
116 'enable_totp' => 'enable_totp',
117 'enable_email' => 'enable_email',
118 'backup_codes_enabled' => 'yes',
119 'enforcement-policy' => 'do-not-enforce',
120 'excluded_users' => array(),
121 'excluded_roles' => array(),
122 'enforced_users' => array(),
123 'enforced_roles' => array(),
124 'grace-period' => 3,
125 'grace-period-denominator' => 'days',
126 'enable_grace_cron' => '',
127 'enable_destroy_session' => '',
128 'limit_access' => '',
129 '2fa_settings_last_updated_by' => '',
130 '2fa_main_user' => '',
131 'grace-period-expiry-time' => '',
132 'plugin_version' => WP_2FA_VERSION,
133 'delete_data_upon_uninstall' => '',
134 'excluded_sites' => '',
135 'included_sites' => array(),
136 'create-custom-user-page' => 'no',
137 'redirect-user-custom-page' => '',
138 'redirect-user-custom-page-global' => '',
139 'custom-user-page-url' => '',
140 'custom-user-page-id' => '',
141 'hide_remove_button' => '',
142 'grace-policy' => 'use-grace-period',
143 'superadmins-role-add' => 'no',
144 'superadmins-role-exclude' => 'no',
145 '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' ),
146 'email-code-period' => 5,
147 'specify-email_hotp' => '',
148 'default-backup-code-page' => __( 'Enter a backup verification code.', 'wp-2fa' ),
149 );
150
151 $default_settings = apply_filters( 'wp_2fa_default_settings', $default_settings );
152
153 return $default_settings;
154 }
155
156 /**
157 * Fire up classes.
158 */
159 public function init() {
160 // Bootstrap.
161 Core\setup();
162
163 $this->settings = new Admin\SettingsPage();
164 $this->settings_email = new Admin\SettingsPages\Settings_Page_Email();
165 $this->wizard = new Admin\SetupWizard();
166 $this->authentication = new Authenticator\Authentication();
167 $this->backupcodes = new Authenticator\BackupCodes();
168 $this->login = new Authenticator\Login();
169 $this->user_notices = new Admin\UserNotices();
170 $this->crontasks = new Cron\CronTasks();
171 $this->user_registered = new Admin\UserRegistered();
172 $this->shortcodes = new Shortcodes\Shortcodes();
173 $this->helpcontactus = new Admin\HelpContactUs();
174 $this->premiumfeatures = new Admin\PremiumFeatures();
175
176 global $pagenow;
177 if ( 'profile.php' !== $pagenow || 'user-edit.php' !== $pagenow ) {
178 $this->user_profiles = new Admin\UserProfile();
179 }
180
181 if ( is_admin() ) {
182 UserListing::init();
183
184 }
185
186 $this->add_actions();
187
188 }
189
190 /**
191 * Add our plugins actions.
192 */
193 public function add_actions() {
194 // Plugin redirect on activation, only if we have no settings currently saved.
195 if ( ! isset( self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] ) || empty( self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] ) ) {
196 add_action( 'admin_init', array( $this, 'setup_redirect' ), 10 );
197 }
198
199 // SettingsPage.
200 if ( self::is_this_multisite() ) {
201 add_action( 'network_admin_menu', array( $this->settings, 'create_settings_admin_menu_multisite' ) );
202 add_action( 'network_admin_edit_update_wp2fa_network_options', array( $this->settings, 'update_wp2fa_network_options' ) );
203 add_action( 'network_admin_edit_update_wp2fa_network_email_options', array( $this->settings, 'update_wp2fa_network_email_options' ) );
204 add_action( 'network_admin_notices', array( $this->settings, 'settings_saved_network_admin_notice' ) );
205 } else {
206 add_action( 'admin_menu', array( $this->settings, 'create_settings_admin_menu' ) );
207 add_action( 'admin_notices', array( $this->settings, 'settings_saved_admin_notice' ) );
208 }
209 add_action( 'wp_ajax_get_all_users', array( $this->settings, 'get_all_users' ) );
210 add_action( 'wp_ajax_get_all_network_sites', array( $this->settings, 'get_all_network_sites' ) );
211 add_action( 'wp_ajax_unlock_account', array( $this->settings, 'unlock_account' ), 10, 1 );
212 add_action( 'admin_action_unlock_account', array( $this->settings, 'unlock_account' ), 10, 1 );
213 add_action( 'admin_action_remove_user_2fa', array( $this->settings, 'remove_user_2fa' ), 10, 1 );
214 add_action( 'wp_ajax_remove_user_2fa', array( $this->settings, 'remove_user_2fa' ), 10, 1 );
215 add_action( 'admin_menu', array( $this->settings, 'hide_settings' ), 999 );
216 add_action( 'plugin_action_links_' . WP_2FA_BASE, array( $this->settings, 'add_plugin_action_links' ) );
217 add_filter( 'display_post_states', array( $this->settings, 'add_display_post_states' ), 10, 2 );
218
219 // SetupWizard.
220 if ( self::is_this_multisite() ) {
221 add_action( 'network_admin_menu', array( $this->wizard, 'network_admin_menus' ), 10 );
222 add_action( 'admin_menu', array( $this->wizard, 'admin_menus' ), 10 );
223 } else {
224 add_action( 'admin_menu', array( $this->wizard, 'admin_menus' ), 10 );
225 }
226 add_action( 'plugins_loaded', array( $this, 'add_wizard_actions' ), 10 );
227 add_action( 'wp_ajax_send_authentication_setup_email', array( $this->wizard, 'send_authentication_setup_email' ) );
228 add_action( 'wp_ajax_regenerate_authentication_key', array( $this->wizard, 'regenerate_authentication_key' ) );
229
230 // UserNotices.
231 add_action( 'wp_ajax_dismiss_nag', array( $this->user_notices, 'dismiss_nag' ) );
232 add_action( 'wp_ajax_wp2fa_dismiss_reconfigure_nag', array( $this->user_notices, 'dismiss_nag' ) );
233 add_action( 'wp_logout', array( $this->user_notices, 'reset_nag' ), 10, 1 );
234
235 // UserProfile.
236 global $pagenow;
237 if ( 'profile.php' !== $pagenow || 'user-edit.php' !== $pagenow ) {
238 add_action( 'show_user_profile', array( $this->user_profiles, 'inline_2fa_profile_form' ) );
239 add_action( 'edit_user_profile', array( $this->user_profiles, 'inline_2fa_profile_form' ) );
240 if ( self::is_this_multisite() ) {
241 add_action( 'personal_options_update', array( $this->user_profiles, 'save_user_2fa_options' ) );
242 }
243 }
244 add_filter( 'user_row_actions', array( $this->user_profiles, 'user_2fa_row_actions' ), 10, 2 );
245 if ( self::is_this_multisite() ) {
246 add_filter( 'ms_user_row_actions', array( $this->user_profiles, 'user_2fa_row_actions' ), 10, 2 );
247 }
248 add_action( 'wp_ajax_validate_authcode_via_ajax', array( $this->user_profiles, 'validate_authcode_via_ajax' ) );
249 add_action( 'wp_ajax_wp2fa_test_email', array( $this, 'handle_send_test_email_ajax' ) );
250
251 // Login.
252 add_action( 'init', array( $this->login, 'get_providers' ) );
253 add_action( 'wp_login', array( $this->login, 'wp_login' ), 20, 2 );
254 add_action( 'login_form_validate_2fa', array( $this->login, 'login_form_validate_2fa' ) );
255 add_action( 'login_form_backup_2fa', array( $this->login, 'backup_2fa' ) );
256
257 /**
258 * Keep track of all the user sessions for which we need to invalidate the
259 * authentication cookies set during the initial password check.
260 */
261 add_action( 'set_auth_cookie', array( $this->login, 'collect_auth_cookie_tokens' ) );
262 add_action( 'set_logged_in_cookie', array( $this->login, 'collect_auth_cookie_tokens' ) );
263
264 // Run only after the core wp_authenticate_username_password() check.
265 add_filter( 'authenticate', array( $this->login, 'filter_authenticate' ), 50 );
266 add_filter( 'wp_authenticate_user', array( $this->login, 'run_authentication_check' ), 10, 2 );
267
268 // User Register.
269 add_action( 'set_user_role', array( $this->user_registered, 'check_user_upon_role_change' ), 10, 3 );
270
271 // Block users from admin if needed.
272 $user_block_hook = is_admin() || is_network_admin() ? 'init' : 'wp';
273 add_action( $user_block_hook, array( $this, 'block_unconfigured_users_from_admin' ), 10 );
274 // Check if usermeta is out of sync with settings.
275 add_action( $user_block_hook, array( $this, 'update_usermeta_if_required' ), 5 );
276
277 // Help & Contact Us.
278 add_action( 'wp_2fa_after_admin_menu_created', array( $this->helpcontactus, 'add_extra_menu_item' ) );
279 // Premium Features.
280 add_action( 'wp_2fa_after_admin_menu_created', array( $this->premiumfeatures, 'add_extra_menu_item' ) );
281 }
282
283 /**
284 * Add actions specific to the wizard.
285 */
286 public function add_wizard_actions() {
287 if ( function_exists( 'wp_get_current_user' ) && current_user_can( 'read' ) ) {
288 add_action( 'admin_init', array( $this->wizard, 'setup_page' ), 10 );
289 }
290 }
291
292 /**
293 * Redirect user to 1st time setup.
294 */
295 public function setup_redirect() {
296
297 // Bail early before the redirect if the user can't manage options.
298 if ( ! current_user_can( 'manage_options' ) ) {
299 return;
300 }
301
302 $registered_and_active = 'yes';
303 if ( function_exists( 'wp2fa_freemius' ) ) {
304 $registered_and_active = wp2fa_freemius()->is_registered() && wp2fa_freemius()->has_active_valid_license() ? 'yes' : 'no';
305 }
306
307 if ( SettingsUtils::get_option( 'redirect_on_activate', false ) && 'yes' === $registered_and_active ) {
308 // Delete redirect option.
309 SettingsUtils::delete_option( 'redirect_on_activate' );
310
311 SettingsUtils::update_option( 'wizard_not_finished', true );
312
313 $redirect = add_query_arg(
314 array(
315 'page' => 'wp-2fa-setup',
316 'is_initial_setup' => 'true',
317 ),
318 admin_url( 'user-edit.php' )
319 );
320
321 wp_safe_redirect( $redirect );
322 exit();
323 }
324 }
325
326 /**
327 * Check is this is a multisite setup.
328 */
329 public static function is_this_multisite() {
330 return function_exists( 'is_multisite' ) && is_multisite();
331 }
332
333 /**
334 * Return user roles.
335 *
336 * @return array User roles.
337 */
338 public static function wp_2fa_get_roles() {
339 global $wp_roles;
340 return $wp_roles->role_names;
341 }
342
343 /**
344 * Util function to grab settings or apply defaults if no settings are saved into the db.
345 *
346 * @param string $setting_name Settings to grab value of.
347 * @param boolean $getDefaultOnEmpty return default setting value if current one is empty
348 * @param boolean $getDefaultValue return default value setting (ignore the stored ones)
349 * @return mixed Settings value or default value.
350 */
351 public static function get_wp2fa_setting( $setting_name = '', $getDefaultOnEmpty = false, $getDefaultValue = false, $role = 'global' ) {
352 $role = is_null( $role ) ? 'global' : $role;
353 return self::get_wp2fa_setting_generic( WP_2FA_POLICY_SETTINGS_NAME, $setting_name, $getDefaultOnEmpty, $getDefaultValue, $role );
354 }
355
356 /**
357 * Util function to grab settings or apply defaults if no settings are saved into the db.
358 *
359 * @param string $setting_name Settings to grab value of.
360 * @param boolean $getDefaultOnEmpty return default setting value if current one is empty
361 * @param boolean $getDefaultValue return default value setting (ignore the stored ones)
362 * @return mixed Settings value or default value.
363 */
364 public static function get_wp2fa_general_setting( $setting_name = '', $getDefaultOnEmpty = false, $getDefaultValue = false ) {
365
366 return self::get_wp2fa_setting_generic( WP_2FA_SETTINGS_NAME, $setting_name, $getDefaultOnEmpty, $getDefaultValue );
367 }
368
369 /**
370 * Util function to grab white label settings or apply defaults if no settings are saved into the db.
371 *
372 * @param string $setting_name Settings to grab value of.
373 * @param boolean $getDefaultOnEmpty return default setting value if current one is empty
374 * @param boolean $getDefaultValue return default value setting (ignore the stored ones)
375 * @return string Settings value or default value.
376 */
377 public static function get_wp2fa_white_label_setting( $setting_name = '', $getDefaultOnEmpty = false, $getDefaultValue = false ) {
378
379 return self::get_wp2fa_setting_generic( WP_2FA_WHITE_LABEL_SETTINGS_NAME, $setting_name, $getDefaultOnEmpty, $getDefaultValue );
380 }
381
382 private static function get_wp2fa_setting_generic( $wp_2fa_setting = WP_2FA_POLICY_SETTINGS_NAME, $setting_name = '', $get_default_on_empty = false, $get_default_value = false, $role = 'global' ) {
383 $default_settings = self::getDefaultSettings();
384 $role = is_null( $role ) ? 'global' : $role;
385
386 if ( true === $get_default_value ) {
387 if ( isset( $default_settings[ $setting_name ] ) ) {
388 return $default_settings[ $setting_name ];
389 }
390
391 return false;
392 }
393
394 $apply_defaults = false;
395
396 $wp2fa_setting = self::$plugin_settings[ $wp_2fa_setting ];
397
398 // If we have no setting name, return them all.
399 if ( empty( $setting_name ) ) {
400 return $wp2fa_setting;
401 }
402
403 // First lets check if any options have been saved.
404 if ( empty( $wp2fa_setting ) || ! isset( $wp2fa_setting ) ) {
405 $apply_defaults = true;
406 }
407
408 if ( $apply_defaults ) {
409 return $default_settings[ $setting_name ];
410 } elseif ( ! isset( $wp2fa_setting[ $setting_name ] ) ) {
411 if ( true === $get_default_on_empty ) {
412 if ( isset( $default_settings[ $setting_name ] ) ) {
413 return $default_settings[ $setting_name ];
414 }
415 }
416 return false;
417 } else {
418
419 if ( WP_2FA_POLICY_SETTINGS_NAME === $wp_2fa_setting ) {
420 /**
421 * Extensions could change the extracted value, based on custom / different / specific for role settings.
422 *
423 * @param mixed - Value of the setting.
424 * @param string - The name of the setting.
425 * @param string - The role name.
426 *
427 * @since 2.0.0
428 */
429 return apply_filters( 'wp_2fa_setting_generic', $wp2fa_setting[ $setting_name ], $setting_name, $role );
430 } else {
431 return $wp2fa_setting[ $setting_name ];
432 }
433 }
434 }
435
436 /**
437 * Util function to grab EMAIL settings or apply defaults if no settings are saved into the db.
438 *
439 * @param string $setting_name Settings to grab value of.
440 */
441 public static function get_wp2fa_email_templates( $setting_name = '' ) {
442
443 // If we have no setting name, return what ever is saved.
444 if ( empty( $setting_name ) ) {
445 return self::$wp_2fa_email_templates;
446 }
447
448 // If we have a saved setting, return it.
449 if ( $setting_name && isset( self::$wp_2fa_email_templates[ $setting_name ] ) ) {
450 return self::$wp_2fa_email_templates[ $setting_name ];
451 }
452
453 // Create Login Code Message.
454 $login_code_subject = __( 'Your login confirmation code for {site_name}', 'wp-2fa' );
455
456
457 $login_code_body = '<p>' . sprintf(
458 esc_html__( 'Enter %1$s to log in.', 'wp-2fa' ),
459 '<strong>{login_code}</strong>'
460 );
461 $login_code_body .= '</p>';
462 $login_code_body .= '<p>' . esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
463 $login_code_body .= '<p>' . esc_html__( 'Email sent by', 'wp-2fa' );
464 $login_code_body .= ' <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">' . esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
465 $login_code_body .= '</p>';
466
467 // Create User Locked Message.
468 $user_locked_subject = __( 'Your user on {site_name} has been locked', 'wp-2fa' );
469
470 $user_locked_body = '<p>' . esc_html__( 'Hello.', 'wp-2fa' ) . '</p>';
471 $user_locked_body .= '<p>' . sprintf(
472 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' ),
473 '{user_login_name}',
474 '{site_name}'
475 );
476 $user_locked_body .= '</p>';
477 $user_locked_body .= '<p>' . esc_html__( 'Contact your website administrator to unlock your account.', 'wp-2fa' ) . '</p>';
478 $user_locked_body .= '<p>' . esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
479 $user_locked_body .= '<p>' . esc_html__( 'Email sent by', 'wp-2fa' );
480 $user_locked_body .= ' <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">' . esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
481 $user_locked_body .= '</p>';
482
483 // Create User unlocked Message.
484 $user_unlocked_subject = __( 'Your user on {site_name} has been unlocked', 'wp-2fa' );
485 $user_unlocked_body = '';
486
487 $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>';
488
489 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
490 $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>';
491 }
492
493 $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>';
494
495 // Array of defaults, now we have things setup above.
496 $default_settings = array(
497 'email_from_setting' => 'use-defaults',
498 'custom_from_email_address' => '',
499 'custom_from_display_name' => '',
500 'login_code_email_subject' => $login_code_subject,
501 'login_code_email_body' => $login_code_body,
502 'user_account_locked_email_subject' => $user_locked_subject,
503 'user_account_locked_email_body' => $user_locked_body,
504 'user_account_unlocked_email_subject' => $user_unlocked_subject,
505 'user_account_unlocked_email_body' => $user_unlocked_body,
506 'send_account_locked_email' => 'enable_account_locked_email',
507 'send_account_unlocked_email' => 'enable_account_unlocked_email',
508 );
509
510 /**
511 * Allows 3rd party providers to their own settings for the mail templates.
512 *
513 * @param array $default_settings - Array with the default settings.
514 *
515 * @since 2.0.0
516 */
517 $default_settings = apply_filters( 'wp_2fa_mail_default_settings', $default_settings );
518
519 return $default_settings[ $setting_name ];
520 }
521
522 /**
523 * Util which we use to replace our {strings} with actual, useful stuff.
524 *
525 * @param string $input Text we are working on.
526 * @param int|string $user_id User id, if its needed.
527 * @param string $token Login code, if its needed..
528 * @return string The output, with all the {strings} swapped out.
529 */
530 public static function replace_email_strings( $input = '', $user_id = '', $token = '', $override_grace_period = '' ) {
531
532 // Gather grace period.
533 $grace_period_string = '';
534 if ( isset( $override_grace_period ) && ! empty( $override_grace_period ) ) {
535 $grace_period_string = $override_grace_period;
536 } else {
537 $grace_policy = self::get_wp2fa_setting( 'grace-policy' );
538 $grace_period_string = DateTimeUtils::format_grace_period_expiration_string( $grace_policy );
539 }
540
541 // Setup user data.
542 if ( isset( $user_id ) && ! empty( $user_id ) ) {
543 $user = get_userdata( $user_id );
544 } else {
545 $user = wp_get_current_user();
546 }
547
548 // Setup token.
549 if ( isset( $token ) && ! empty( $token ) ) {
550 $login_code = $token;
551 } else {
552 $login_code = '';
553 }
554
555 $new_page_id = Settings::get_role_or_default_setting( 'custom-user-page-id', $user );
556 if ( ! empty( $new_page_id ) ) {
557 $new_page_permalink = get_permalink( $new_page_id );
558 } else {
559 $new_page_permalink = '';
560 }
561
562 // These are the strings we are going to search for, as well as there respective replacements.
563 $replacements = array(
564 '{site_url}' => esc_url( get_bloginfo( 'url' ) ),
565 '{site_name}' => sanitize_text_field( get_bloginfo( 'name' ) ),
566 '{grace_period}' => sanitize_text_field( $grace_period_string ),
567 '{user_login_name}' => sanitize_text_field( $user->user_login ),
568 '{user_first_name}' => sanitize_text_field( $user->firstname ),
569 '{user_last_name}' => sanitize_text_field( $user->lastname ),
570 '{user_display_name}' => sanitize_text_field( $user->display_name ),
571 '{login_code}' => $login_code,
572 '{2fa_settings_page_url}' => esc_url( $new_page_permalink ),
573 '{user_ip_address}' => RequestUtils::get_ip(),
574 );
575
576 $replacements = apply_filters(
577 'wp_2fa_replacement_email_strings',
578 $replacements
579 );
580
581 $final_output = str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
582 return $final_output;
583 }
584
585 /**
586 * If a user is trying to access anywhere other than the 2FA config area, this blocks them.
587 */
588 public function block_unconfigured_users_from_admin() {
589 global $pagenow;
590
591 $user = User::get_instance();
592 $is_user_instantly_enforced = $user->getEnforcedInstantly();
593 $grace_period_expiry_time = $user->getGracePeriodExpiration();
594 $time_now = time();
595 if ( $is_user_instantly_enforced && ! empty( $grace_period_expiry_time ) && $grace_period_expiry_time < $time_now && ! User::is_excluded( $user->getUser()->ID ) ) {
596
597 /**
598 * We should only allow:
599 * - 2FA setup wizard in the administration
600 * - custom 2FA page if enabled and created
601 * - AJAX requests originating from these 2FA setup UIs
602 */
603 if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && self::action_check() ) {
604 return;
605 }
606
607 if ( is_admin() || is_network_admin() ) {
608 $allowed_admin_page = 'profile.php';
609 if ( $pagenow === $allowed_admin_page && ( isset( $_GET['show'] ) && $_GET['show'] === 'wp-2fa-setup' ) ) {
610 return;
611 }
612 }
613
614 if ( is_page() ) {
615 $custom_user_page_id = Settings::get_role_or_default_setting( 'custom-user-page-id', $user->getUser() );
616 if ( ! empty( $custom_user_page_id ) && get_the_ID() == (int) $custom_user_page_id ) {
617 return;
618 }
619 }
620
621 // force a redirect to the 2FA set-up page if it exists.
622 $custom_user_page_id = Settings::get_role_or_default_setting( 'custom-user-page-id', $user->getUser() );
623 if ( ! empty( $custom_user_page_id ) ) {
624 wp_redirect( Settings::get_custom_page_link( $user->getUser() ) );
625 exit;
626 }
627
628 // custom 2FA page is not set-up, force redirect to the wizard in administration.
629 wp_redirect( Settings::get_setup_page_link() );
630 exit;
631 }
632 }
633
634 /**
635 * Checks if user's settings hash matches the current one, and if not, updates it.
636 *
637 * @return void
638 * @since 1.7.0
639 */
640 public function update_usermeta_if_required() {
641 if ( wp_doing_ajax() || ! is_user_logged_in()) {
642 return;
643 }
644
645 // doing this invokes update of necessary user metadata in the User class.
646 User::get_instance();
647 }
648
649 /**
650 * Handles AJAX calls for sending test emails.
651 */
652 public function handle_send_test_email_ajax() {
653
654 // check user permissions
655 if ( ! current_user_can( 'manage_options' ) ) {
656 wp_send_json_error();
657 }
658
659 // check email id
660 $email_id = filter_input(INPUT_POST, 'email_id', FILTER_SANITIZE_STRING);
661 if ($email_id === null || $email_id === false) {
662 wp_send_json_error();
663 }
664
665 // check nonce
666 $nonce = filter_input(INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING);
667 if ($nonce === null || $nonce === false || ! wp_verify_nonce($nonce, 'wp-2fa-email-test-' . $email_id)) {
668 wp_send_json_error();
669 }
670
671 $user_id = get_current_user_id();
672 // Grab user data.
673 $user = get_userdata( $user_id );
674 // Grab user email.
675 $email = $user->user_email;
676
677 if ('config_test' === $email_id) {
678 $email_sent = SettingsPage::send_email(
679 $email,
680 esc_html__('Test email from WP 2FA', 'wp-2fa'),
681 esc_html__('This email was sent by the WP 2FA plugin to test the email delivery.', 'wp-2fa')
682 );
683 if ( $email_sent ) {
684 wp_send_json_success('Test email was successfully sent to <strong>' . $email . '</strong>' );
685 }
686
687 wp_send_json_error();
688 }
689
690 /** @var EmailTemplate[] $email_templates */
691 $email_templates = $this->settings_email->get_email_notification_definitions();
692 foreach ($email_templates as $email_template) {
693 if ($email_id === $email_template->getEmailContentId()) {
694 // send the test email
695
696
697
698 // Setup the email contents.
699 $subject = wp_strip_all_tags( WP2FA::get_wp2fa_email_templates( $email_id . '_email_subject' ) );
700 $message = wpautop( WP2FA::get_wp2fa_email_templates( $email_id . '_email_body' ), $user_id );
701
702 $email_sent = SettingsPage::send_email( $email, $subject, $message );
703 if ( $email_sent ) {
704 wp_send_json_success('Test email <strong>' . $email_template->getTitle() . '</strong> was successfully sent to <strong>' . $email . '</strong>' );
705 }
706
707 wp_send_json_error();
708 }
709 }
710 }
711
712 /**
713 * Collects all the sites from multisite WP installation
714 *
715 * @return array
716 */
717 public static function getMultiSites() {
718 if ( self::is_this_multisite() ) {
719 if ( empty( self::$sites ) ) {
720
721 self::$sites = \get_sites();
722 }
723
724 return self::$sites;
725 }
726
727 return [];
728 }
729
730 /**
731 * Returns text with the number of plugins supported
732 *
733 * @since 1.6
734 *
735 * @return string
736 */
737 public static function getNumberOfPluginsText() {
738 $methodsCount = self::$methodsCount;
739
740 if ( \class_exists('NumberFormatter') ) {
741 $number_formatter = new \NumberFormatter( get_locale(), \NumberFormatter::SPELLOUT );
742 $methodsCount = $number_formatter->format( self::$methodsCount );
743 }
744
745 return
746 sprintf(
747 \_n(
748 'There is %s method available from which you can choose for 2FA:',
749 'There are %s methods available from which you can choose for 2FA:',
750 self::$methodsCount,
751 'wp-2fa'
752 ),
753 $methodsCount
754 );
755 }
756
757 /**
758 * Returns currently stored settings
759 *
760 * @return void
761 */
762 public static function getPolicySettings() {
763 /**
764 * Extensions could change the stored settings value, based on custom / different / specific for role settings.
765 *
766 * @param array - Value of the settings.
767 *
768 * @since 2.0.0
769 */
770 $settings = apply_filters( 'wp_2fa_policy_settings', self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] );
771
772 return $settings;
773 }
774 /**
775 * Checks the action parameter against given list of actions
776 *
777 * @return bool
778 *
779 * @since 2.0.0
780 */
781 private static function action_check() {
782 $actions_array = array(
783 'send_authentication_setup_email',
784 'validate_authcode_via_ajax',
785 'heartbeat',
786 );
787
788 /**
789 * Allows 3rd party providers to their own settings for the mail templates.
790 *
791 * @param array $actions_array - Array with the default settings.
792 *
793 * @since 2.0.0
794 */
795 $actions_array = apply_filters( 'wp_2fa_actions_check', $actions_array );
796
797 return in_array( $_REQUEST['action'], $actions_array );
798 }
799
800 /**
801 * Updates the plugin settings, the settings hash in the database as well as a local (cached) copy of the settings.
802 *
803 * @param array $settings
804 * @param bool $skip_option_save If true, the settings themselves are not saved. This is needed when saving settings from settings page as WordPress options API takes care of that.
805 *
806 * @since 2.0.0
807 */
808 public static function updatePluginSettings( $settings, $skip_option_save = false, $settings_name=WP_2FA_POLICY_SETTINGS_NAME ) {
809 // update local copy of settings.
810 self::$plugin_settings[ $settings_name ] = $settings;
811
812 if ( ! $skip_option_save ) {
813 // update the database option itself.
814 SettingsUtils::update_option( $settings_name, $settings );
815 }
816
817 if ( WP_2FA_POLICY_SETTINGS_NAME === $settings_name ) {
818 // Create a hash for comparison when we interact with a use.
819 $settings_hash = SettingsUtils::create_settings_hash( self::getPolicySettings() );
820 SettingsUtils::update_option( WP_2FA_PREFIX . 'settings_hash', $settings_hash );
821 }
822 }
823
824 /**
825 * Getter for the TOTP secret key of the plugin for the current instance
826 *
827 * @return string
828 *
829 * @since 2.0.0
830 */
831 public static function get_secret_key() {
832 if ( null === self::$secret_key ) {
833 self::$secret_key = SettingsUtils::get_option('secret_key');
834 if ( empty( self::$secret_key ) ) {
835 self::$secret_key = base64_encode( Open_SSL::secure_random() );
836 SettingsUtils::update_option( 'secret_key', self::$secret_key );
837 }
838 }
839
840 return self::$secret_key;
841 }
842
843 /**
844 * Returns message for the WP Mail SMTP plugin usage suggestion
845 *
846 * @return string
847 *
848 * @since 2.0.0
849 */
850 public static function print_email_deliverability_message() {
851
852 return sprintf( '%1$s <a href="https://wordpress.org/plugins/wp-mail-smtp/" target="_blank">%2$s</a>.', esc_html__( 'To ensure emails are delivered so users do not have problems logging in, we recommend using the free plugin', 'wp-2fa' ), esc_html__( 'WP Mail SMTP', 'wp-2fa' ) );
853 }
854 }
855