PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.1
WP 2FA – Two-factor authentication for WordPress v2.9.1
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 / class-wp2fa.php
wp-2fa / includes / classes Last commit date
Admin 11 months ago App 11 months ago Authenticator 11 months ago Shortcodes 11 months ago Utils 11 months ago bacon 11 months ago dasprid 11 months ago class-email-template.php 11 months ago class-wp2fa.php 11 months ago index.php 11 months ago
class-wp2fa.php
1316 lines
1 <?php
2 /**
3 * Main plugin class.
4 *
5 * @package wp2fa
6 * @copyright 2025 Melapress
7 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
8 * @link https://wordpress.org/plugins/wp-2fa/
9 */
10
11 namespace WP2FA;
12
13 use WP2FA\Methods\TOTP;
14 use WP2FA\Utils\White_Label;
15 use WP2FA\Admin\Setup_Wizard;
16 use WP2FA\Admin\User_Listing;
17 use WP2FA\Admin\User_Notices;
18 use WP2FA\Admin\User_Profile;
19 use WP2FA\Admin\FlyOut\FlyOut;
20 use WP2FA\Admin\Settings_Page;
21 use WP2FA\Authenticator\Login;
22 use WP2FA\Utils\Request_Utils;
23 use WP2FA\Methods\Backup_Codes;
24 use WP2FA\Utils\Settings_Utils;
25 use WP2FA\Admin\User_Registered;
26 use WP2FA\Shortcodes\Shortcodes;
27 use WP2FA\Utils\Date_Time_Utils;
28 use WP2FA\Admin\Premium_Features;
29 use WP2FA\Authenticator\Open_SSL;
30 use WP2FA\Admin\Helpers\WP_Helper;
31 use WP2FA\Freemius\User_Licensing;
32 use WP2FA\Admin\Views\Re_Login_2FA;
33 use WP2FA\Admin\Controllers\Methods;
34 use WP2FA\Admin\Helpers\Ajax_Helper;
35 use WP2FA\Admin\Helpers\File_Writer;
36 use WP2FA\Admin\Helpers\User_Helper;
37 use WP2FA\Admin\Controllers\Settings;
38 use WP2FA\Admin\Controllers\Endpoints;
39 use WP2FA\Admin\Plugin_Updated_Notice;
40 use WP2FA\Admin\Helpers\Classes_Helper;
41 use WP2FA\Admin\Helpers\Methods_Helper;
42 use WP2FA\Authenticator\Reset_Password;
43 use WP2FA\Admin\Views\Password_Reset_2FA;
44 use WP2FA\Admin\Views\Grace_Period_Notifications;
45
46 if ( ! class_exists( '\WP2FA\WP2FA' ) ) {
47 /**
48 * Main WP2FA Class.
49 */
50 class WP2FA {
51
52 /**
53 * Holds the global plugin secret key
54 *
55 * @var string
56 *
57 * @since 2.0.0
58 */
59 private static $secret_key = null;
60
61 /**
62 * Local static cache for plugins settings.
63 *
64 * @var array
65 *
66 * @since 2.0.0
67 */
68 private static $plugin_settings = array();
69
70 /**
71 * Local static cache for plugins type.
72 *
73 * @var string
74 *
75 * @since 3.0.0
76 */
77 private static $plugin_type = null;
78
79 /**
80 * Local static cache for plugins settings.
81 *
82 * @var array
83 *
84 * @since 2.8.0
85 */
86 private static $default_settings = array();
87
88 /**
89 * Local static cache for email template settings.
90 *
91 * @var array
92 */
93 protected static $wp_2fa_email_templates;
94
95 /**
96 * Array with all the plugin default settings.
97 *
98 * @return array
99 *
100 * @since 2.2.0
101 */
102 public static function get_default_settings() {
103 if ( empty( self::$default_settings ) ) {
104 self::$default_settings = array(
105 'enforcement-policy' => 'do-not-enforce',
106 'excluded_users' => array(),
107 'excluded_roles' => array(),
108 'enforced_users' => array(),
109 'enforced_roles' => array(),
110 'grace-period' => 3,
111 'grace-period-denominator' => 'days',
112 'enable_destroy_session' => '',
113 'limit_access' => '',
114 'enable_rest' => false,
115 'brute_force_disable' => '',
116 '2fa_settings_last_updated_by' => '',
117 '2fa_main_user' => '',
118 'grace-period-expiry-time' => '',
119 'plugin_version' => WP_2FA_VERSION,
120 'delete_data_upon_uninstall' => '',
121 'excluded_sites' => array(),
122 'included_sites' => array(),
123 'create-custom-user-page' => 'no',
124 'redirect-user-custom-page' => '',
125 'redirect-user-custom-page-global' => '',
126 'custom-user-page-url' => '',
127 'custom-user-page-id' => '',
128 'hide_remove_button' => '',
129 'separate-multisite-page-url' => '',
130 'grace-policy' => 'use-grace-period',
131 'superadmins-role-add' => 'no',
132 'superadmins-role-exclude' => 'no',
133 'method_invalid_setting' => 'login_block',
134 );
135 /**
136 * Gives the ability to filter the default settings array of the plugin
137 *
138 * @param array $settings - The array with all the default settings.
139 *
140 * @since 2.0.0
141 */
142 self::$default_settings = \apply_filters( WP_2FA_PREFIX . 'default_settings', self::$default_settings );
143 }
144
145 return self::$default_settings;
146 }
147
148 /**
149 * Inits the plugin related classes and settings
150 *
151 * @return void
152 *
153 * @since 2.6.0
154 */
155 public static function init() {
156
157 \add_filter(
158 'aadvana_trigger_error',
159 function ( $trigger_error, $function_name, $errstr ) {
160 if ( '_load_textdomain_just_in_time' === $function_name && strpos( $errstr, '<code>' . WP_2FA_TEXTDOMAIN ) !== false ) {
161 $trigger_error = false;
162 }
163
164 return $trigger_error;
165 },
166 10,
167 3
168 );
169
170 Methods_Helper::init();
171
172
173 self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] = Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME, array() );
174 self::$plugin_settings[ WP_2FA_SETTINGS_NAME ] = Settings_Utils::get_option( WP_2FA_SETTINGS_NAME, array() );
175 self::$plugin_settings[ WP_2FA_WHITE_LABEL_SETTINGS_NAME ] = ( ! empty( Settings_Utils::get_option( WP_2FA_WHITE_LABEL_SETTINGS_NAME, array() ) ) ) ? Settings_Utils::get_option( WP_2FA_WHITE_LABEL_SETTINGS_NAME, array() ) : White_Label::get_default_settings();
176
177 self::$wp_2fa_email_templates = Settings_Utils::get_option( WP_2FA_EMAIL_SETTINGS_NAME );
178
179 White_Label::init();
180
181 /** 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) */
182 if ( Settings_Utils::get_option( 'wizard_not_finished' ) && ! isset( $_GET['is_initial_setup'] ) && ! wp_doing_ajax() && ! defined( 'DOING_CRON' ) ) {
183
184 if ( ! Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME ) ) {
185 self::update_plugin_settings( self::get_default_settings() );
186 }
187
188 // Set a flag so we know we have default values present, not custom.
189 Settings_Utils::update_option( 'default_settings_applied', true );
190 Settings_Utils::delete_option( 'wizard_not_finished' );
191 }
192
193
194 WP_Helper::init();
195
196 // Bootstrap.
197 Core\setup();
198
199 if ( \is_admin() && ! \wp_doing_ajax() ) {
200 User_Listing::init();
201 // Hide all unrelated to the plugin notices on the plugin admin pages.
202 \add_action( 'admin_print_scripts', array( WP_Helper::class, 'hide_unrelated_notices' ) );
203 }
204
205 Grace_Period_Notifications::init();
206 Password_Reset_2FA::init();
207 Re_Login_2FA::init();
208
209 Shortcodes::init();
210 \add_action( 'after_setup_theme', array( User_Notices::class, 'init' ), 10 );
211 \add_action( 'after_setup_theme', array( FlyOut::class, 'init' ), 10 );
212 Plugin_Updated_Notice::init();
213
214 self::add_actions();
215
216 /**
217 * Enables the API endpoints for the plugin.
218 *
219 * @since 2.9.1
220 */
221 if ( Settings_Utils::string_to_bool( self::get_wp2fa_general_setting( 'enable_rest' ) ) ) {
222 Endpoints::init();
223 }
224
225 // Inits all the additional free app extensions.
226 $free_extensions = Classes_Helper::get_classes_by_namespace( 'WP2FA\\App\\' );
227
228 foreach ( $free_extensions as $extension ) {
229 if ( method_exists( $extension, 'init' ) ) {
230 call_user_func_array( array( $extension, 'init' ), array() );
231 }
232 }
233 }
234
235 /**
236 * Inits all the plugin hooks
237 *
238 * @return void
239 *
240 * @since 2.6.0
241 */
242 public static function add_actions() {
243 // Plugin redirect on activation, only if we have no settings currently saved.
244 if ( ( ! isset( self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] ) || empty( self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] ) ) && Settings_Utils::get_option( 'redirect_on_activate', false ) ) {
245 \add_action( 'admin_init', array( __CLASS__, 'setup_redirect' ), 10 );
246 } elseif ( ! \is_array( Settings_Utils::get_option( WP_2FA_POLICY_SETTINGS_NAME ) ) ) {
247 Settings_Utils::delete_option( WP_2FA_POLICY_SETTINGS_NAME );
248 self::update_plugin_settings( self::get_default_settings() );
249 }
250 if ( Settings_Utils::get_option( 'wizard_not_finished' ) && function_exists( 'wp_get_current_user' ) && \current_user_can( 'manage_options' ) ) {
251 \add_action( 'admin_init', array( Setup_Wizard::class, 'setup_page' ), 10 );
252 }
253 if ( Settings_Utils::get_option( 'wizard_not_finished' ) && function_exists( 'wp_get_current_user' ) && \current_user_can( 'manage_options' ) ) {
254 \add_action( 'admin_init', array( Setup_Wizard::class, 'setup_page' ), 10 );
255 }
256
257 // SettingsPage.
258 if ( WP_Helper::is_multisite() ) {
259 \add_action( 'network_admin_menu', array( Settings_Page::class, 'create_settings_admin_menu_multisite' ) );
260 \add_action( 'network_admin_edit_update_wp2fa_network_options', array( Settings_Page::class, 'update_wp2fa_network_options' ) );
261 \add_action( 'network_admin_edit_update_wp2fa_network_email_options', array( Settings_Page::class, 'update_wp2fa_network_email_options' ) );
262 \add_action( 'network_admin_notices', array( Settings_Page::class, 'settings_saved_network_admin_notice' ) );
263 \add_action( 'network_admin_notices', array( __CLASS__, 'wp_not_writable' ) );
264 } else {
265 \add_action( 'admin_menu', array( Settings_Page::class, 'create_settings_admin_menu' ) );
266 \add_action( 'admin_notices', array( Settings_Page::class, 'settings_saved_admin_notice' ) );
267 \add_action( 'admin_notices', array( __CLASS__, 'wp_not_writable' ) );
268 }
269 \add_action( 'wp_ajax_wp2fa_dismiss_notice_mail_domain', array( Settings_Page::class, 'dismiss_notice_mail_domain' ) );
270
271 \add_action( 'wp_ajax_set_salt_key', array( Ajax_Helper::class, 'set_salt_key' ) );
272 \add_action( 'wp_ajax_unset_salt_key', array( Ajax_Helper::class, 'unset_salt_key' ) );
273
274 \add_action( 'wp_ajax_wp_2fa_get_all_users', array( Ajax_Helper::class, 'get_all_users' ) );
275 \add_action( 'wp_ajax_wp_2fa_get_all_roles', array( Ajax_Helper::class, 'get_ajax_user_roles' ) );
276 \add_action( 'wp_ajax_wp_2fa_get_all_network_sites', array( Ajax_Helper::class, 'get_all_network_sites' ) );
277 \add_action( 'wp_ajax_unlock_account', array( Ajax_Helper::class, 'unlock_account' ), 10, 1 );
278 \add_action( 'admin_action_unlock_account', array( Ajax_Helper::class, 'unlock_account' ), 10, 1 );
279 \add_action( 'admin_action_remove_user_2fa', array( Ajax_Helper::class, 'remove_user_2fa' ), 10, 1 );
280 \add_action( 'wp_ajax_remove_user_2fa', array( Ajax_Helper::class, 'remove_user_2fa' ), 10, 1 );
281 \add_action( 'admin_menu', array( Settings_Page::class, 'hide_settings' ), 999 );
282 \add_action( 'plugin_action_links_' . WP_2FA_BASE, array( Settings_Page::class, 'add_plugin_action_links' ) );
283 \add_filter( 'display_post_states', array( Settings_Page::class, 'add_display_post_states' ), 10, 2 );
284 \add_action( 'wp_ajax_send_authentication_setup_email', array( Setup_Wizard::class, 'send_authentication_setup_email' ) );
285 \add_action( 'wp_ajax_send_backup_codes_email', array( Backup_Codes::class, 'send_backup_codes_email' ) );
286 \add_action( 'wp_ajax_regenerate_authentication_key', array( TOTP::class, 'regenerate_authentication_key' ) );
287
288 // User_Notices.
289 \add_action( 'wp_ajax_dismiss_nag', array( User_Notices::class, 'dismiss_nag' ) );
290 \add_action( 'wp_ajax_wp2fa_dismiss_reconfigure_nag', array( User_Notices::class, 'dismiss_nag' ) );
291 \add_action( 'wp_logout', array( User_Notices::class, 'reset_nag' ), 10, 1 );
292
293 // User_Profile.
294 global $pagenow;
295 if ( 'profile.php' !== $pagenow || 'user-edit.php' !== $pagenow ) {
296 \add_action( 'show_user_profile', array( User_Profile::class, 'inline_2fa_profile_form' ) );
297 \add_action( 'edit_user_profile', array( User_Profile::class, 'inline_2fa_profile_form' ) );
298 if ( WP_Helper::is_multisite() ) {
299 \add_action( 'personal_options_update', array( User_Profile::class, 'save_user_2fa_options' ) );
300 }
301 }
302 \add_filter( 'user_row_actions', array( User_Profile::class, 'user_2fa_row_actions' ), 10, 2 );
303 if ( WP_Helper::is_multisite() ) {
304 \add_filter( 'ms_user_row_actions', array( User_Profile::class, 'user_2fa_row_actions' ), 10, 2 );
305 }
306 \add_action( 'wp_ajax_validate_authcode_via_ajax', array( User_Profile::class, 'validate_authcode_via_ajax' ) );
307 \add_action( 'wp_ajax_wp2fa_test_email', array( Ajax_Helper::class, 'handle_send_test_email_ajax' ) );
308
309 // Login.
310 \add_action( 'wp_login', array( Login::class, 'wp_login' ), 20, 2 );
311 \add_action( 'wp_loaded', array( Login::class, 'login_form_validate_2fa' ) );
312 \add_action( 'login_form_validate_2fa', array( Login::class, 'login_form_validate_2fa' ) );
313 \add_action( 'login_form_backup_2fa', array( Login::class, 'backup_2fa' ) );
314 \add_action( 'login_enqueue_scripts', array( Login::class, 'dequeue_style' ), PHP_INT_MAX );
315
316 // Reset password.
317 \add_action( 'lostpassword_post', array( Reset_Password::class, 'lostpassword_post' ), 20, 2 );
318 \add_action( 'login_form_lostpassword', array( Reset_Password::class, 'login_form_validate_2fa' ), 20 );
319 // \add_action( 'wp_loaded', array( Reset_Password::class, 'login_form_validate_2fa' ) );.
320
321 /**
322 * Keep track of all the user sessions for which we need to invalidate the
323 * authentication cookies set during the initial password check.
324 */
325 \add_action( 'set_auth_cookie', array( Login::class, 'collect_auth_cookie_tokens' ) );
326 \add_action( 'set_logged_in_cookie', array( Login::class, 'collect_auth_cookie_tokens' ) );
327
328 // Run only after the core wp_authenticate_username_password() check.
329 \add_filter( 'authenticate', array( Login::class, 'filter_authenticate' ), 50 );
330 \add_filter( 'wp_authenticate_user', array( Login::class, 'run_authentication_check' ), 10, 2 );
331
332 // User Register.
333 \add_action( 'set_user_role', array( User_Registered::class, 'check_user_upon_role_change' ), 10, 3 );
334
335 // Block users from admin if needed.
336 $user_block_hook = is_admin() || is_network_admin() ? 'init' : 'wp';
337 \add_action( $user_block_hook, array( __CLASS__, 'block_unconfigured_users_from_admin' ), 10 );
338
339 // Help & Contact Us.
340 \add_action( WP_2FA_PREFIX . 'after_admin_menu_created', array( '\WP2FA\Admin\Help_Contact_Us', 'add_extra_menu_item' ) );
341
342 // phpcs:disable
343 /* @free:start */
344 // phpcs:enable
345 // Premium Features.
346 \add_action( WP_2FA_PREFIX . 'after_admin_menu_created', array( Premium_Features::class, 'add_extra_menu_item' ) );
347 \add_action( WP_2FA_PREFIX . 'before_plugin_settings', array( Premium_Features::class, 'add_settings_banner' ) );
348 \add_action( 'admin_footer', array( Premium_Features::class, 'pricing_new_tab_js' ) );
349 // phpcs:disable
350 /* @free:end */
351 // phpcs:enable
352
353 \add_action( 'admin_footer', array( User_Profile::class, 'dismiss_nag_notice' ) );
354
355 \add_action( WP_2FA_PREFIX . 'user_authenticated', array( __CLASS__, 'clear_user_after_login' ), 10, 1 );
356
357 \add_filter( 'mepr-auto-login', array( Login::class, 'mepr_login' ) );
358 }
359
360 /**
361 * Add actions specific to the wizard.
362 *
363 * @since 2.0.0
364 */
365 public static function add_wizard_actions() {
366 if ( function_exists( 'wp_get_current_user' ) && \current_user_can( 'read' ) ) {
367 \add_action( 'admin_init', array( '\WP2FA\Admin\Setup_Wizard', 'setup_page' ), 10 );
368 }
369 }
370
371 /**
372 * Redirect user to 1st time setup.
373 *
374 * @since 2.0.0
375 */
376 public static function setup_redirect() {
377
378 // Bail early before the redirect if the user can't manage options.
379 if ( wp_doing_ajax() && ! \current_user_can( 'manage_options' ) ) {
380 return;
381 }
382
383 $registered_and_active = 'yes';
384 if ( function_exists( 'wp2fa_freemius' ) ) {
385 $registered_and_active = wp2fa_freemius()->is_registered() && wp2fa_freemius()->has_active_valid_license() ? 'yes' : 'no';
386 }
387
388 if ( Settings_Utils::get_option( 'redirect_on_activate', false ) && 'yes' === $registered_and_active ) {
389 // Delete redirect option.
390 Settings_Utils::delete_option( 'redirect_on_activate' );
391
392 Settings_Utils::update_option( 'wizard_not_finished', true );
393
394 $redirect = \add_query_arg(
395 array(
396 'page' => 'wp-2fa-setup',
397 'is_initial_setup' => 'true',
398 ),
399 \network_admin_url( 'user-edit.php' )
400 );
401
402 \wp_safe_redirect( $redirect );
403 exit();
404 }
405 }
406
407 /**
408 * Util function to grab settings or apply defaults if no settings are saved into the db.
409 *
410 * @param string $setting_name Settings to grab value of.
411 * @param boolean $get_default_on_empty return default setting value if current one is empty.
412 * @param boolean $get_default_value return default value setting (ignore the stored ones).
413 * @param string $role - The name of the user role.
414 *
415 * @return mixed Settings value or default value.
416 *
417 * @since 2.0.0
418 */
419 public static function get_wp2fa_setting( $setting_name = '', $get_default_on_empty = false, $get_default_value = false, $role = 'global' ) {
420 $role = ( is_null( $role ) || empty( $role ) ) ? 'global' : $role;
421 return self::get_wp2fa_setting_generic( WP_2FA_POLICY_SETTINGS_NAME, $setting_name, $get_default_on_empty, $get_default_value, $role );
422 }
423
424 /**
425 * Util function to grab settings or apply defaults if no settings are saved into the db.
426 *
427 * @param string $setting_name Settings to grab value of.
428 * @param boolean $get_default_on_empty return default setting value if current one is empty.
429 * @param boolean $get_default_value return default value setting (ignore the stored ones).
430 *
431 * @return mixed Settings value or default value.
432 *
433 * @since 3.0.0
434 */
435 public static function get_wp2fa_general_setting( $setting_name = '', $get_default_on_empty = false, $get_default_value = false ) {
436
437 return self::get_wp2fa_setting_generic( WP_2FA_SETTINGS_NAME, $setting_name, $get_default_on_empty, $get_default_value );
438 }
439
440 /**
441 * Util function to grab white label settings or apply defaults if no settings are saved into the db.
442 *
443 * @param string $setting_name Settings to grab value of.
444 * @param boolean $get_default_on_empty return default setting value if current one is empty.
445 * @param boolean $get_default_value return default value setting (ignore the stored ones).
446 *
447 * @return string Settings value or default value.
448 *
449 * @since 2.0.0
450 */
451 public static function get_wp2fa_white_label_setting( $setting_name = '', $get_default_on_empty = false, $get_default_value = false ) {
452
453 return (string) self::get_wp2fa_setting_generic( WP_2FA_WHITE_LABEL_SETTINGS_NAME, $setting_name, $get_default_on_empty, $get_default_value );
454 }
455
456 /**
457 * Generic method for extracting settings from the plugin
458 *
459 * @param string $wp_2fa_setting - The name of the settings type.
460 * @param string $setting_name - The name of the setting to extract.
461 * @param boolean $get_default_on_empty - Should we use default value on empty.
462 * @param boolean $get_default_value - Extract default value.
463 * @param string $role - The name of the user role.
464 *
465 * @return mixed
466 *
467 * @since 2.0.0
468 */
469 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' ) {
470
471 $default_settings = self::get_default_settings();
472 $role = ( is_null( $role ) || empty( $role ) ) ? 'global' : $role;
473
474 if ( true === $get_default_value ) {
475 if ( isset( $default_settings[ $setting_name ] ) ) {
476 return $default_settings[ $setting_name ];
477 }
478
479 return false;
480 }
481
482 $apply_defaults = false;
483
484 $wp2fa_setting = self::$plugin_settings[ $wp_2fa_setting ];
485
486 // If we have no setting name, return them all.
487 if ( empty( $setting_name ) ) {
488 return $wp2fa_setting;
489 }
490
491 // First lets check if any options have been saved.
492 if ( empty( $wp2fa_setting ) || ! isset( $wp2fa_setting ) ) {
493 $apply_defaults = true;
494 }
495
496 if ( $apply_defaults ) {
497 return isset( $default_settings[ $setting_name ] ) ? $default_settings[ $setting_name ] : false;
498 } elseif ( ! isset( $wp2fa_setting[ $setting_name ] ) ) {
499 if ( true === $get_default_on_empty ) {
500 if ( isset( $default_settings[ $setting_name ] ) ) {
501 return $default_settings[ $setting_name ];
502 } elseif ( isset( $default_settings[ $wp_2fa_setting ][ $setting_name ] ) ) {
503 return $default_settings[ $wp_2fa_setting ][ $setting_name ];
504 }
505 }
506 return false;
507 } elseif ( WP_2FA_POLICY_SETTINGS_NAME === $wp_2fa_setting ) {
508
509 /**
510 * Extensions could change the extracted value, based on custom / different / specific for role settings.
511 *
512 * @param mixed - Value of the setting.
513 * @param string - The name of the setting.
514 * @param string - The role name.
515 *
516 * @since 2.0.0
517 */
518 return \apply_filters( WP_2FA_PREFIX . 'setting_generic', $wp2fa_setting[ $setting_name ], $setting_name, $role );
519 } else {
520 return $wp2fa_setting[ $setting_name ];
521 }
522 }
523
524 /**
525 * Util function to grab EMAIL settings or apply defaults if no settings are saved into the db.
526 *
527 * @param string $setting_name Settings to grab value of.
528 *
529 * @since 2.0.0
530 */
531 public static function get_wp2fa_email_templates( $setting_name = '' ) {
532
533 // If we have no setting name, return what ever is saved.
534 if ( empty( $setting_name ) ) {
535 return self::$wp_2fa_email_templates;
536 }
537
538 // If we have a saved setting, return it.
539 if ( $setting_name && isset( self::$wp_2fa_email_templates[ $setting_name ] ) ) {
540 return self::$wp_2fa_email_templates[ $setting_name ];
541 }
542
543 // Create Login Code Message.
544 $login_code_subject = __( 'Your login confirmation code for {site_name}', 'wp-2fa' );
545
546 $login_code_body = '<p>' . \esc_html__( 'Hello {user_display_name},', 'wp-2fa' ) . '</p>';
547 $login_code_body .= '<p>' . \esc_html__( 'You are trying to log in to {site_name} using the username {user_login_name}. To complete your login, please enter the following one-time 2FA code:', 'wp-2fa' ) . '</p>';
548 $login_code_body .= '<p>' . \esc_html__( '{login_code}', 'wp-2fa' ) . '</p>';
549 $login_code_body .= '<p>' . \esc_html__( 'Enter this code on the login page to finish the authentication process and access your account.', 'wp-2fa' ) . '</p>';
550 $login_code_body .= '<p>' . \esc_html__( 'If you encounter any issues logging in, feel free to contact us at {admin_email}.', 'wp-2fa' ) . '</p>';
551 $login_code_body .= '<p>' . \esc_html__(
552 'Kind regards,
553 The {site_name} Team',
554 'wp-2fa'
555 ) . '</p>';
556
557 // $login_code_body .= '<p>' . sprintf(
558 // // translators: The login code provided from the plugin.
559 // \esc_html__( 'Enter %1$1s to log in.', 'wp-2fa' ),
560 // '<strong>{login_code}</strong>'
561 // );
562 // $login_code_body .= '</p>';
563 // $login_code_body .= '<p>' . \esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
564 // $login_code_body .= '<p>' . \esc_html__( 'Email sent by', 'wp-2fa' );
565 // $login_code_body .= ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
566 // $login_code_body .= '</p>';
567
568 // Create Reset PW Code Message.
569 $reset_password_code_subject = __( '2FA code for password reset', 'wp-2fa' );
570
571 $reset_password_code_body = '<p>' . \esc_html__( 'Hello,', 'wp-2fa' ) . '</p>';
572
573 $reset_password_code_body = '<p>' . sprintf(
574 // translators: The login code provided from the plugin.
575 \esc_html__( 'Someone from the IP address %1$1s has requested a password reset for the user %2$2s on the website %3$3s. If this was you please use the below code to proceed with the password reset:', 'wp-2fa' ),
576 '{user_ip_address}',
577 '{user_login_name}',
578 '{site_url}'
579 );
580
581 $reset_password_code_body .= '<p><strong>{login_code}</strong></p>';
582
583 $reset_password_code_body .= '</p>';
584 $reset_password_code_body .= '<p>' . \esc_html__( 'If this was not you, ignore this email and contact your website administrator.', 'wp-2fa' ) . '</p>';
585
586 $login_code_setup_body = '<p>' . sprintf(
587 // translators: The login code provided from the plugin.
588 \esc_html__( 'Please enter this code to confirm 2FA setup: %1$1s', 'wp-2fa' ),
589 '<strong>{login_code}</strong>'
590 );
591 $login_code_setup_body .= '</p>';
592 $login_code_setup_body .= '<p>' . \esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
593 $login_code_setup_body .= '<p>' . \esc_html__( 'Email sent by', 'wp-2fa' );
594 $login_code_setup_body .= ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=melapress_wp_2fa_plugin_link_2" target="_blank">' . \esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
595 $login_code_setup_body .= '</p>';
596
597 // Create User Locked Message.
598 $user_locked_subject = __( 'Your user on {site_name} has been locked', 'wp-2fa' );
599
600 $user_locked_body = '<p>' . \esc_html__( 'Hello.', 'wp-2fa' ) . '</p>';
601 $user_locked_body .= '<p>' . sprintf(
602 // translators: %1s - the name of the user
603 // translators: %2s - the name of the site.
604 \esc_html__( 'Since you have not enabled two-factor authentication for the user %1$1s on the website %2$2s within the grace period, your account has been locked.', 'wp-2fa' ),
605 '{user_login_name}',
606 '{site_name}'
607 );
608 $user_locked_body .= '</p>';
609 $user_locked_body .= '<p>' . \esc_html__( 'Contact your website administrator to unlock your account.', 'wp-2fa' ) . '</p>';
610 $user_locked_body .= '<p>' . \esc_html__( 'Thank you.', 'wp-2fa' ) . '</p>';
611 $user_locked_body .= '<p>' . \esc_html__( 'Email sent by', 'wp-2fa' );
612 $user_locked_body .= ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=melapress_wp_2fa_plugin_link_3" target="_blank">' . \esc_html__( 'WP 2FA plugin.', 'wp-2fa' ) . '</a>';
613 $user_locked_body .= '</p>';
614
615 // Create User unlocked Message.
616 $user_unlocked_subject = __( 'Your user on {site_name} has been unlocked', 'wp-2fa' );
617 $user_unlocked_body = '';
618
619 $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>';
620
621 if ( ! empty( self::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
622 $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>';
623 }
624
625 $user_unlocked_body .= '<p>' . __( 'Thank you.', 'wp-2fa' ) . '</p><p>' . __( 'Email sent by', 'wp-2fa' ) . ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=melapress_wp_2fa_plugin_link_4" target="_blank">' . __( 'WP 2FA plugin', 'wp-2fa' ) . '</a></p>';
626
627 // Create User backup codes Message.
628 $user_backup_codes_subject = __( '2FA backup codes for user {user_login_name} on {site_name}', 'wp-2fa' );
629 $user_backup_codes_body = '';
630
631 $user_backup_codes_body .= '<p>' . __( 'Hello,', 'wp-2fa' ) . '</p><p>' . \esc_html__( 'Below please find the 2FA backup codes for your user', 'wp-2fa' ) . ' <strong>{user_login_name}</strong> ' . \esc_html__( 'on the website', 'wp-2fa' ) . ' <strong>{site_name}</strong>. ' . __( 'The website\'s URL is', 'wp-2fa' ) . ' {site_url} </p>';
632
633 $user_backup_codes_body .= '{backup_codes}';
634
635 $user_backup_codes_body .= '<p>' . __( 'Thank you for enabling 2FA on your account and helping us keeping the website secure.', 'wp-2fa' ) . '</p><p>' . __( 'Email sent by', 'wp-2fa' ) . ' <a href="https://melapress.com/wordpress-2fa/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=melapress_wp_2fa_plugin_link_5" target="_blank">' . __( 'WP 2FA plugin', 'wp-2fa' ) . '</a></p>';
636
637 // Array of defaults, now we have things setup above.
638 $default_settings = array(
639 'email_from_setting' => 'use-defaults',
640 'custom_from_email_address' => '',
641 'custom_from_display_name' => '',
642 'login_code_email_subject' => $login_code_subject,
643 'login_code_email_body' => $login_code_body,
644 'reset_password_code_email_subject' => $reset_password_code_subject,
645 'reset_password_code_email_body' => $reset_password_code_body,
646 'login_code_setup_email_subject' => $login_code_subject,
647 'login_code_setup_email_body' => $login_code_setup_body,
648 'user_account_locked_email_subject' => $user_locked_subject,
649 'user_account_locked_email_body' => $user_locked_body,
650 'user_account_unlocked_email_subject' => $user_unlocked_subject,
651 'user_account_unlocked_email_body' => $user_unlocked_body,
652 'user_backup_codes_email_subject' => $user_backup_codes_subject,
653 'user_backup_codes_email_body' => $user_backup_codes_body,
654 'send_account_locked_email' => 'enable_account_locked_email',
655 'send_account_unlocked_email' => 'enable_account_unlocked_email',
656 'send_login_code_email' => 'enable_send_login_code_email',
657 'send_reset_password_code_email' => 'enable_send_reset_password_code_email',
658 );
659
660 /**
661 * Allows 3rd party providers to their own settings for the mail templates.
662 *
663 * @param array $default_settings - Array with the default settings.
664 *
665 * @since 2.0.0
666 */
667 $default_settings = \apply_filters( WP_2FA_PREFIX . 'mail_default_settings', $default_settings );
668
669 return $default_settings[ $setting_name ];
670 }
671
672 /**
673 * Util which we use to replace our {strings} with actual, useful stuff.
674 *
675 * @param string $input Text we are working on.
676 * @param int|string $user_id User id, if its needed.
677 * @param string $token Login code, if its needed..
678 * @param string $override_grace_period - Value to override grace period with.
679 *
680 * @return string The output, with all the {strings} swapped out.
681 *
682 * @since 2.0.0
683 */
684 public static function replace_email_strings( $input = '', $user_id = '', $token = '', $override_grace_period = '' ) {
685
686 if ( empty( $GLOBALS['wp_rewrite'] ) ) {
687 $GLOBALS['wp_rewrite'] = new \WP_Rewrite(); // phpcs:ignore -- WordPress.WP.GlobalVariablesOverride.Prohibited
688 }
689
690 $token = trim( (string) $token );
691
692 // Gather grace period.
693 $grace_period_string = '';
694 if ( isset( $override_grace_period ) && ! empty( $override_grace_period ) ) {
695 $grace_period_string = sanitize_text_field( $override_grace_period );
696 } else {
697 $grace_policy = self::get_wp2fa_setting( 'grace-policy' );
698 $grace_period_string = Date_Time_Utils::format_grace_period_expiration_string( $grace_policy );
699 }
700
701 // Setup user data.
702 if ( isset( $user_id ) && ! empty( $user_id ) ) {
703 $user = get_userdata( intval( $user_id ) );
704 } else {
705 $user = wp_get_current_user();
706 }
707
708 // Setup token.
709 if ( isset( $token ) && ! empty( $token ) ) {
710 $login_code = $token;
711 } else {
712 $login_code = '';
713 }
714
715 $new_page_id = Settings_Utils::get_setting_role( User_Helper::get_user_role( $user ), 'custom-user-page-id' );
716 if ( ! empty( $new_page_id ) ) {
717 $new_page_permalink = esc_url( \get_permalink( intval( $new_page_id ) ) );
718 } else {
719 $new_page_id = Settings::get_custom_settings_page_id( '', $user );
720 if ( ! empty( $new_page_id ) ) {
721 $new_page_permalink = esc_url( \get_permalink( intval( $new_page_id ) ) );
722 } else {
723 $new_page_permalink = '';
724 }
725 }
726
727 $admin_email = null;
728 if ( 'use-custom-email' === self::get_wp2fa_email_templates( 'email_from_setting' ) ) {
729 $admin_email = sanitize_email( self::get_wp2fa_email_templates( 'custom_from_email_address' ) );
730 } else {
731 $admin_email = Settings_Page::get_default_email_address();
732 }
733
734 // These are the strings we are going to search for, as well as their respective replacements.
735 $replacements = array(
736 '{site_url}' => \esc_url( \get_bloginfo( 'url' ) ),
737 '{site_name}' => \sanitize_text_field( \get_bloginfo( 'name' ) ),
738 '{grace_period}' => \sanitize_text_field( $grace_period_string ),
739 '{user_login_name}' => \sanitize_text_field( $user->user_login ),
740 '{user_first_name}' => \sanitize_text_field( $user->user_firstname ),
741 '{user_last_name}' => \sanitize_text_field( $user->user_lastname ),
742 '{user_display_name}' => \sanitize_text_field( $user->display_name ),
743 '{login_code}' => $login_code,
744 '{2fa_settings_page_url}' => $new_page_permalink,
745 '{user_ip_address}' => esc_attr( Request_Utils::get_ip() ),
746 '{admin_email}' => $admin_email,
747 );
748
749 /**
750 * 3rd party plugins could change the mail strings, or provide their own.
751 *
752 * @param array $replacements - The array with all the currently supported strings.
753 */
754 $replacements = \apply_filters(
755 WP_2FA_PREFIX . 'replacement_email_strings',
756 $replacements
757 );
758
759 $final_output = str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
760 return $final_output;
761 }
762
763 /**
764 * Util which contextualizes the wording 'reconfigure'/'configure' as needed.
765 *
766 * @param string $input - Text we are working on.
767 * @param int|string $user_id - User id, if its needed.
768 * @param string $method_to_check - Name of the method to check for.
769 *
770 * @return string The output, with all the {strings} swapped out.
771 *
772 * @since 2.5.0
773 */
774 public static function contextual_reconfigure_text( $input = '', $user_id = '', $method_to_check = '' ) {
775
776 if ( empty( trim( (string) $input ) ) ) {
777 return $input;
778 }
779
780 $enabled_method = User_Helper::get_enabled_method_for_user( intval( $user_id ) );
781
782 $text = ( $enabled_method === $method_to_check ) ? \esc_html__( 'Reconfigure', 'wp-2fa' ) : \esc_html__( 'Configure', 'wp-2fa' );
783
784 $replacements = array(
785 '{reconfigure_or_configure_capitalized}' => $text,
786 '{reconfigure_or_configure}' => strtolower( $text ),
787 );
788
789 /**
790 * 3rd party plugins could change this to their own.
791 *
792 * @param array $replacements - The array with all the currently supported strings.
793 *
794 * @since 2.5.0
795 */
796 $replacements = \apply_filters(
797 WP_2FA_PREFIX . 'replacement_reconfigure_strings',
798 $replacements
799 );
800
801 return str_replace( array_keys( $replacements ), array_values( $replacements ), \wp_kses_post( $input ) );
802 }
803
804 /**
805 * Util replace replace a placeholder with the actual remaining grace period for a user..
806 *
807 * @param string $input - Text we are working on.
808 * @param int $grace_expiry - Expiration time.
809 *
810 * @return string The output, with all the {strings} swapped out.
811 *
812 * @since 2.5.0
813 */
814 public static function replace_remaining_grace_period( $input = '', $grace_expiry = -1 ) {
815 if ( empty( trim( (string) $input ) ) || empty( trim( (string) $grace_expiry ) ) ) {
816 return sanitize_text_field( $input );
817 }
818
819 $replacements = array(
820 '{grace_period_remaining}' => esc_attr( Date_Time_Utils::format_grace_period_expiration_string( null, intval( $grace_expiry ) ) ),
821 );
822
823 return str_replace( array_keys( $replacements ), array_values( $replacements ), ( $input ) );
824 }
825
826 /**
827 * Util which we use to replace our {strings} with actual, useful stuff.
828 *
829 * @param string $input Text we are working on.
830 * @param WP_User $user The WP User.
831 *
832 * @return string The output, with all the {strings} swapped out.
833 *
834 * @since 2.0.0
835 */
836 public static function replace_wizard_strings( $input = '', $user = false ) {
837
838 if ( ! $user ) {
839 return sanitize_text_field( $input );
840 }
841
842 $available_methods = Methods::get_enabled_methods( User_Helper::get_user_role( $user ) );
843
844 // These are the strings we are going to search for, as well as their respective replacements.
845 $replacements = array(
846 '{available_methods_count}' => intval( count( $available_methods[ User_Helper::get_user_role( $user ) ] ) ),
847 );
848
849 /**
850 * 3rd party plugins could change the mail strings, or provide their own.
851 *
852 * @param array $replacements - The array with all the currently supported strings.
853 */
854 $replacements = \apply_filters(
855 WP_2FA_PREFIX . 'replacement_wizard_strings',
856 $replacements
857 );
858
859 $final_output = str_replace( array_keys( $replacements ), array_values( $replacements ), ( $input ) );
860 return $final_output;
861 }
862
863 /**
864 * If a user is trying to access anywhere other than the 2FA config area, this blocks them.
865 *
866 * @return void
867 *
868 * @since 2.0.0
869 */
870 public static function block_unconfigured_users_from_admin() {
871 global $pagenow;
872
873 $user = User_Helper::get_user();
874 if ( 0 === $user->ID ) {
875 return;
876 }
877
878 $redirect = true;
879
880 if ( class_exists( '\WP2FA\Freemius\User_Licensing' ) ) {
881 if ( Extensions_Loader::use_proxytron() ) {
882 $redirect = User_Licensing::enable_2fa_user_setting( true );
883 }
884 }
885
886
887 if ( $redirect ) {
888 $is_user_instantly_enforced = User_Helper::get_user_enforced_instantly();
889 $grace_period_expiry_time = (int) User_Helper::get_user_expiry_date();
890 $time_now = time();
891 if ( $is_user_instantly_enforced && ! empty( $grace_period_expiry_time ) && $grace_period_expiry_time < $time_now && ! User_Helper::is_excluded( $user->ID ) ) {
892
893 $has_cap = true;
894 if ( class_exists( 'WooCommerce', false ) ) {
895
896 // Lets check if the user has the required capabilities to view the 2FA settings page (or profile page in the Admin section - dashboard).
897 $has_cap = false;
898
899 $access_caps = array( 'edit_posts', 'manage_woocommerce', 'view_admin_dashboard' );
900
901 foreach ( $access_caps as $access_cap ) {
902 if ( \current_user_can( $access_cap ) ) {
903 $has_cap = true;
904 break;
905 }
906 }
907 }
908
909 /**
910 * We should only allow:
911 * - 2FA setup wizard in the administration
912 * - custom 2FA page if enabled and created
913 * - AJAX requests originating from these 2FA setup UIs
914 */
915 if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && self::action_check() ) { // phpcs:ignore
916 return;
917 }
918
919 if ( \is_admin() || \is_network_admin() ) {
920 $allowed_admin_page = 'profile.php';
921 if ( $pagenow === $allowed_admin_page && ( isset( $_GET['show'] ) && 'wp-2fa-setup' === $_GET['show'] ) ) { // phpcs:ignore
922 return;
923 }
924 }
925
926 if ( is_page() ) {
927 $custom_user_page_id = Settings_Utils::get_setting_role( User_Helper::get_user_role( $user ), 'custom-user-page-id' );
928 if ( ! empty( $custom_user_page_id ) && \get_the_ID() === (int) $custom_user_page_id ) {
929 return;
930 } else {
931 $custom_user_page_id = Settings::get_custom_settings_page_id( '', $user );
932 if ( ! empty( $custom_user_page_id ) && \get_the_ID() === (int) $custom_user_page_id ) {
933 return;
934 }
935 }
936 }
937
938 // force a redirect to the 2FA set-up page if it exists.
939 $custom_user_page_id = Settings_Utils::get_setting_role( User_Helper::get_user_role( $user ), 'custom-user-page-id' );
940 if ( ! empty( $custom_user_page_id ) ) {
941 \wp_redirect( Settings::get_custom_page_link( $user ) );
942 exit;
943 } else {
944 $custom_user_page_id = Settings::get_custom_settings_page_id( '', $user );
945 if ( ! empty( $custom_user_page_id ) && \get_the_ID() === (int) $custom_user_page_id ) {
946 \wp_redirect( \get_permalink( $custom_user_page_id ) );
947 exit;
948 }
949 }
950
951 // There is nowhere to redirect, so we have to fall back to the default which is the dashboard. If the user does not have the required capabilities to view the dashboard - lets stop the redirection.
952 if ( ! $has_cap ) {
953
954 // Is there WOO installed? If so, then lets try to extract the redirection rules from there.
955 if ( class_exists( 'WooCommerce', false ) ) {
956
957 // Lets check if there is a 2FA implemented within the WOOCommerce myaccount page.
958 $items = \wc_get_account_menu_items();
959
960 if ( isset( $items['wp-2fa'] ) ) {
961
962 if ( ! isset( $_GET['wp-2fa'] ) ) {
963 $url = \add_query_arg(
964 array(
965 'wp-2fa' => '',
966 ),
967 \get_permalink( \get_option( 'woocommerce_myaccount_page_id' ) )
968 );
969
970 \wp_redirect( $url );
971
972 exit;
973 }
974 }
975 }
976
977 // Nothing suitable found - notify the admin and bail.
978 $transient_name = WP_2FA_PREFIX . '_notified_admin_mail_nowhere_to_redirect_' . $user->ID;
979 if ( false === \get_transient( $transient_name ) ) {
980 $subject = sprintf(
981 // translators: The username.
982 \esc_html__(
983 'User %1$s logged in without 2FA',
984 'wp-2fa'
985 ),
986 $user->user_login,
987 );
988
989 $text = sprintf(
990 // translators: The username.
991 // translators: the site name.
992 \esc_html__(
993 '2FA is enforced on the user %1$s on the website %2$s. However, since the WP 2FA plugin has not been configured properly it cannot enforce the user to configure 2FA, so the user logged in without 2FA.',
994 'wp-2fa'
995 ),
996 $user->user_login,
997 \get_bloginfo( 'name' )
998 );
999 $text .= '<p>' . sprintf(
1000 // translators: the settings page.
1001 // translators: the support e-mail.
1002 \esc_html__(
1003 'To enforce 2FA on users logging in from non default WordPress login pages please configure the %1$s. If you need assistance, please contact us at %2$s.',
1004 'wp-2fa'
1005 ),
1006 '<a href="' . \esc_url(
1007 \add_query_arg(
1008 array(
1009 'page' => 'wp-2fa-settings',
1010 'tab' => 'integrations',
1011 ),
1012 \network_admin_url( 'admin.php' )
1013 )
1014 ) . '">front-end 2FA page</a>',
1015 '<a href="mailto:support@melapress.com">support@melapress.com</a>'
1016 ) . '</p>';
1017
1018 Settings_Page::send_email(
1019 \get_option( 'admin_email' ),
1020 $subject,
1021 $text
1022 );
1023
1024 \set_transient( $transient_name, 'sent', DAY_IN_SECONDS * 2 );
1025 }
1026
1027 return;
1028 }
1029
1030 // custom 2FA page is not set-up, force redirect to the wizard in administration.
1031 \wp_redirect( Settings::get_setup_page_link() );
1032 exit;
1033 }
1034 }
1035 }
1036
1037 /**
1038 * Returns currently stored settings
1039 *
1040 * @return array
1041 *
1042 * @since 2.0.0
1043 */
1044 public static function get_policy_settings() {
1045 /**
1046 * Extensions could change the stored settings value, based on custom / different / specific for role settings.
1047 *
1048 * @param array - Value of the settings.
1049 *
1050 * @since 2.0.0
1051 */
1052 $settings = \apply_filters( WP_2FA_PREFIX . 'policy_settings', self::$plugin_settings[ WP_2FA_POLICY_SETTINGS_NAME ] );
1053
1054 return $settings;
1055 }
1056
1057 /**
1058 * Returns currently stored White Labeling settings
1059 *
1060 * @return array
1061 *
1062 * @since 3.0.0
1063 */
1064 public static function get_white_labeling_settings() {
1065 /**
1066 * Extensions could change the stored settings value, based on custom / different / specific for role settings.
1067 *
1068 * @param array - Value of the settings.
1069 *
1070 * @since 3.0.0
1071 */
1072 $settings = \apply_filters( WP_2FA_PREFIX . 'whitelabel_settings', self::$plugin_settings[ \WP_2FA_WHITE_LABEL_SETTINGS_NAME ] );
1073
1074 return $settings;
1075 }
1076
1077 /**
1078 * Checks the action parameter against given list of actions
1079 *
1080 * @return bool
1081 *
1082 * @since 2.0.0
1083 */
1084 private static function action_check() {
1085 if ( ! isset( $_REQUEST['action'] ) ) {
1086 return false;
1087 }
1088 $actions_array = array(
1089 'send_authentication_setup_email',
1090 'validate_authcode_via_ajax',
1091 'heartbeat',
1092 'regenerate_authentication_key',
1093 'send_backup_codes_email',
1094 'register_user_twilio',
1095 'register_user_clickatell',
1096 );
1097
1098 /**
1099 * Allows 3rd party providers to add their own actions to the check.
1100 *
1101 * @param array $actions_array - Array with the default actions.
1102 *
1103 * @since 2.0.0
1104 */
1105 $actions_array = \apply_filters( WP_2FA_PREFIX . 'actions_check', $actions_array );
1106
1107 $action = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) );
1108
1109 return in_array( $action, $actions_array, true );
1110 }
1111
1112 /**
1113 * Updates the plugin settings, the settings hash in the database as well as a local (cached) copy of the settings.
1114 *
1115 * @param array $settings - The settings values.
1116 * @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.
1117 * @param string $settings_name - The name of the settings to extract.
1118 *
1119 * @since 2.0.0
1120 */
1121 public static function update_plugin_settings( $settings, $skip_option_save = false, $settings_name = WP_2FA_POLICY_SETTINGS_NAME ) {
1122 // update local copy of settings.
1123 self::$plugin_settings[ $settings_name ] = $settings;
1124
1125 if ( ! $skip_option_save ) {
1126 // update the database option itself.
1127 Settings_Utils::update_option( $settings_name, $settings );
1128 }
1129
1130 if ( WP_2FA_POLICY_SETTINGS_NAME === $settings_name ) {
1131 // Create a hash for comparison when we interact with a user.
1132 $settings_hash = Settings_Utils::create_settings_hash( self::get_policy_settings() );
1133 Settings_Utils::update_option( WP_2FA_PREFIX . 'settings_hash', $settings_hash );
1134 }
1135 }
1136
1137 /**
1138 * Getter for the secret key of the plugin for the current instance
1139 *
1140 * Note: that is legacy code and will be removed.
1141 *
1142 * @return string
1143 *
1144 * @since 2.0.0
1145 */
1146 public static function get_secret_key() {
1147 if ( null === self::$secret_key ) {
1148 if ( ! defined( File_Writer::SECRET_NAME ) ) {
1149 self::check_for_key();
1150 } else {
1151 self::$secret_key = constant( File_Writer::SECRET_NAME );
1152 }
1153 }
1154
1155 return self::$secret_key;
1156 }
1157
1158 /**
1159 * Checks if the wp-config.php file is writable, show notice to the admin if it is not
1160 *
1161 * @return void
1162 *
1163 * @since 2.4.0
1164 */
1165 public static function wp_not_writable() {
1166
1167 if ( ! \defined( 'WP2FA_SECRET_IS_IN_DB' ) || true !== WP2FA_SECRET_IS_IN_DB ) {
1168 return;
1169 }
1170
1171 $dismissed = (bool) Settings_Utils::get_option( 'remove_store_salt_in_wp_config_message', false );
1172
1173 if ( ! File_Writer::can_write_to_file( File_Writer::get_wp_config_file_path() ) && ! $dismissed ) {
1174 $whitelist_admin_pages = array(
1175 'wp-2fa_page_wp-2fa-settings',
1176 'wp-2fa_page_wp-2fa-settings-network',
1177 'toplevel_page_wp-2fa-policies',
1178 'toplevel_page_wp-2fa-policies-network',
1179 'wp-2fa_page_wp-2fa-help-contact-us',
1180 'wp-2fa_page_wp-2fa-help-contact-us-network',
1181 'wp-2fa_page_wp-2fa-policies-account',
1182 'wp-2fa_page_wp-2fa-policies-account-network',
1183 'wp-2fa_page_wp-2fa-reports',
1184 'wp-2fa_page_wp-2fa-reports-network',
1185 );
1186 $admin_page = \get_current_screen();
1187 if ( in_array( $admin_page->base, $whitelist_admin_pages, true ) ) {
1188 ?>
1189 <div class="notice notice-warning" id="config-update-notice">
1190 <?php
1191 $message = sprintf(
1192 '<p>%1$s <a href="https://melapress.com/support/kb/wp-2fa-add-2fa-plugin-encryption-key-wp-config/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=encryption_key_in_wp_config" noopener target="_blank">%2$s</a><br>%3$s</p>',
1193 \esc_html__( 'For security reasons WP 2FA needs to store the private key in the wp-config.php file. However, it is unable to. This can happen because of restrictive permissions, or the file is not in the default location. To fix this you can:', 'wp-2fa' ) . '<br><br>' .
1194 \esc_html__( 'Option A) allow the plugin to write to the wp-config.php file temporarily by changing the wp-config.php permissions to 755. Once ready, click the button to proceed.', 'wp-2fa' ) . '<br>' .
1195 \esc_html__( 'Option B) Add the encryption key to the wp-config.php file yourself by ', 'wp-2fa' ),
1196 \esc_html__( 'following these instructions.', 'wp-2fa' ) . '<br>',
1197 \esc_html__( 'Once you complete any of the above, please click the button below.', 'wp-2fa' )
1198 );
1199 echo $message; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1200 ?>
1201 <p><button id="salt-update" type="button">
1202 <span><?php \esc_html_e( 'Write key to file now / Check for the key in file', 'wp-2fa' ); ?></span>
1203 </button><button style="margin-left: 15px;" id="dismiss-salt-update" type="button">
1204 <span><?php \esc_html_e( 'I am aware of the risks. Please do not alert me again about this.', 'wp-2fa' ); ?></span>
1205 </button></p>
1206 </div>
1207 <script>
1208 jQuery(document).ready(function($) {
1209 jQuery(document).on('click', '#salt-update', function(event) {
1210 event.preventDefault();
1211 const ajaxURL = (typeof wp2faWizardData !== "undefined") ? wp2faWizardData.ajaxURL : ajaxurl;
1212 const nonceValue = '<?php echo \esc_attr( \wp_create_nonce( 'wp-2fa-set-salt-nonce' ) ); ?>';
1213 jQuery.ajax({
1214 url: ajaxURL,
1215 method: 'POST',
1216 data: {
1217 action: 'set_salt_key',
1218 _wpnonce: nonceValue
1219 },
1220 success: function(data) {
1221 if (data.success) {
1222 jQuery('#config-update-notice').remove();
1223 } else {
1224 alert(data.data);
1225 }
1226 },
1227 error: function(data) {
1228 alert(data.responseJSON.data[0].message);
1229 }
1230 });
1231 });
1232
1233 jQuery(document).on('click', '#dismiss-salt-update', function(event) {
1234 event.preventDefault();
1235 const ajaxURL = (typeof wp2faWizardData !== "undefined") ? wp2faWizardData.ajaxURL : ajaxurl;
1236 const nonceValue = '<?php echo \esc_attr( \wp_create_nonce( 'wp-2fa-unset-salt-nonce' ) ); ?>';
1237 jQuery.ajax({
1238 url: ajaxURL,
1239 method: 'POST',
1240 data: {
1241 action: 'unset_salt_key',
1242 _wpnonce: nonceValue
1243 },
1244 success: function(data) {
1245 if (data.success) {
1246 jQuery('#config-update-notice').remove();
1247 } else {
1248 alert(data.data);
1249 }
1250 },
1251 error: function(data) {
1252 alert(data.responseJSON.data[0].message);
1253 }
1254 });
1255 });
1256 });
1257 </script>
1258 <?php
1259 }
1260 }
1261 }
1262
1263 /**
1264 * Remove the user meta related with the code has been sent to the user.
1265 * That is so we can lower the security by giving the option not to resend codes, so eventual brute force could succeed.
1266 * The setting name - brute_force_disable
1267 *
1268 * @return void
1269 *
1270 * @since 2.5.0
1271 */
1272 public static function clear_user_after_login() {
1273 User_Helper::remove_meta( WP_2FA_PREFIX . 'code_sent' );
1274 }
1275
1276 /**
1277 * Determine the plugin type.
1278 *
1279 * @return string
1280 *
1281 * @since 3.0.0
1282 */
1283 public static function determine_plugin_type(): string {
1284
1285 if ( null === self::$plugin_type ) {
1286
1287 self::$plugin_type = 'free';
1288 // Check if the plugin is a premium version.
1289 if ( class_exists( '\WP2FA\Extensions_Loader', false ) ) {
1290 self::$plugin_type = 'premium';
1291 }
1292 }
1293
1294 return self::$plugin_type;
1295 }
1296
1297
1298 /**
1299 * Checks and sets the global wp2fa salt
1300 *
1301 * @return void
1302 *
1303 * @since 2.4.0
1304 */
1305 private static function check_for_key() {
1306 self::$secret_key = Settings_Utils::get_option( 'secret_key' );
1307 if ( empty( self::$secret_key ) ) {
1308 self::$secret_key = base64_encode( Open_SSL::secure_random() ); // phpcs:ignore
1309 if ( ! File_Writer::save_secret_key( self::$secret_key ) ) {
1310 Settings_Utils::update_option( 'secret_key', self::$secret_key );
1311 }
1312 }
1313 }
1314 }
1315 }
1316