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