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