Controllers
5 years ago
Views
5 years ago
SettingsPage.php
5 years ago
SetupWizard.php
5 years ago
User.php
5 years ago
UserListing.php
5 years ago
UserNotices.php
5 years ago
UserProfile.php
5 years ago
UserRegistered.php
5 years ago
index.php
5 years ago
SettingsPage.php
1701 lines
| 1 | <?php // phpcs:ignore |
| 2 | |
| 3 | namespace WP2FA\Admin; |
| 4 | |
| 5 | use WP2FA\EmailTemplate; |
| 6 | use \WP2FA\WP2FA as WP2FA; |
| 7 | use WP2FA\Utils\UserUtils; |
| 8 | use WP2FA\Admin\Controllers\Settings; |
| 9 | use WP2FA\Authenticator\Authentication; |
| 10 | use \WP2FA\Utils\Debugging as Debugging; |
| 11 | use WP2FA\Admin\Views\FirstTimeWizardSteps; |
| 12 | use WP2FA\Utils\SettingsUtils as SettingsUtils; |
| 13 | use \WP2FA\Utils\GenerateModal as GenerateModal; |
| 14 | |
| 15 | /** |
| 16 | * SettingsPage - Class for handling settings |
| 17 | */ |
| 18 | class SettingsPage { |
| 19 | |
| 20 | /** |
| 21 | * Holds the status of the backupcodes functionality |
| 22 | * |
| 23 | * @var bool |
| 24 | */ |
| 25 | private static $backupCodesEnabled = null; |
| 26 | |
| 27 | /** |
| 28 | * Create admin menu entru and settings page |
| 29 | */ |
| 30 | public function create_settings_admin_menu() { |
| 31 | // Create admin menu item. |
| 32 | add_menu_page( |
| 33 | esc_html__( 'WP 2FA Settings', 'wp-2fa' ), |
| 34 | esc_html__( 'WP 2FA', 'wp-2fa' ), |
| 35 | 'manage_options', |
| 36 | 'wp-2fa-settings', |
| 37 | [ |
| 38 | $this, |
| 39 | 'settings_page_render', |
| 40 | ], |
| 41 | "data:image/svg+xml;base64,".base64_encode(file_get_contents(WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg')), |
| 42 | 81 |
| 43 | ); |
| 44 | // Register our settings page. |
| 45 | register_setting( |
| 46 | WP_2FA_SETTINGS_NAME, |
| 47 | WP_2FA_SETTINGS_NAME, |
| 48 | array( $this, 'validate_and_sanitize' ) |
| 49 | ); |
| 50 | |
| 51 | register_setting( |
| 52 | WP_2FA_EMAIL_SETTINGS_NAME, |
| 53 | WP_2FA_EMAIL_SETTINGS_NAME, |
| 54 | array( $this, 'validate_and_sanitize_email' ) |
| 55 | ); |
| 56 | } |
| 57 | |
| 58 | /** |
| 59 | * Create admin menu entru and settings page |
| 60 | */ |
| 61 | public function create_settings_admin_menu_multisite() { |
| 62 | // Create admin menu item. |
| 63 | add_menu_page( |
| 64 | esc_html__( 'WP 2FA Settings', 'wp-2fa' ), |
| 65 | esc_html__( 'WP 2FA', 'wp-2fa' ), |
| 66 | 'manage_options', |
| 67 | 'wp-2fa-settings', |
| 68 | [ |
| 69 | $this, |
| 70 | 'settings_page_render', |
| 71 | ], |
| 72 | "data:image/svg+xml;base64,".base64_encode(file_get_contents(WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg')), |
| 73 | 81 |
| 74 | ); |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * Render the settings |
| 79 | */ |
| 80 | public function settings_page_render() { |
| 81 | |
| 82 | $user = wp_get_current_user(); |
| 83 | if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) { |
| 84 | $main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ); |
| 85 | } else { |
| 86 | $main_user = ''; |
| 87 | } |
| 88 | |
| 89 | // Check if new user page has been published. |
| 90 | if ( ! empty( get_transient( WP_2FA_PREFIX . 'new_custom_page_created' ) ) ) { |
| 91 | delete_transient( WP_2FA_PREFIX . 'new_custom_page_created' ); |
| 92 | $new_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' ); |
| 93 | $new_page_permalink = get_permalink( $new_page_id ); |
| 94 | |
| 95 | $new_page_modal_content = '<h3>'.esc_html__( 'The plugin created the 2FA settings page with the URL:', 'wp-2fa' ).'</h3>'; |
| 96 | $new_page_modal_content .= '<h4><a target="_blank" href="'.esc_url( $new_page_permalink ).'">'.esc_url( $new_page_permalink ).'</a></h4>'; |
| 97 | $new_page_modal_content .= '<p>'.esc_html__( 'You can edit this page using the page editor, like you do with all other pages.', 'wp-2fa' ); |
| 98 | $new_page_modal_content .= '</p>'; |
| 99 | $new_page_modal_content .= sprintf( |
| 100 | esc_html__( 'Use the %s html tag in the email templates to include the URL of the 2FA configuration page when notifying the users to configure two-factor authentication.', 'wp-2fa' ), |
| 101 | '<strong>{2fa_settings_page_url}</strong>' |
| 102 | ); |
| 103 | $new_page_modal_content .= '</p>'; |
| 104 | |
| 105 | echo GenerateModal::generate_modal( |
| 106 | 'new-page-created', |
| 107 | false, |
| 108 | $new_page_modal_content, |
| 109 | [ |
| 110 | '<a href="#" class="modal__btn modal__btn-primary button-primary" data-close-2fa-modal>'. __( 'OK', 'wp-2fa' ) .'</a>', |
| 111 | ], |
| 112 | true, |
| 113 | '560px' |
| 114 | ); |
| 115 | } |
| 116 | |
| 117 | $wp2faUser = new User( $user ); |
| 118 | $enabled_methods = $wp2faUser->getEnabledMethods(); |
| 119 | |
| 120 | if ( empty( $enabled_methods ) ) { |
| 121 | $new_page_modal_content = '<h3>'.esc_html__( 'Exclude yourself?', 'wp-2fa' ).'</h3>'; |
| 122 | $new_page_modal_content .= '</p>'. esc_html__( 'You are about to enforce 2FA instantly on all users, including yourself, however you have not yet configured your own 2FA method. What would you like to do?', 'wp-2fa' ) .'</p>'; |
| 123 | |
| 124 | echo GenerateModal::generate_modal( |
| 125 | 'exclude-self-from-instant-2fa', |
| 126 | false, |
| 127 | $new_page_modal_content, |
| 128 | [ |
| 129 | '<a href="#" class="modal__btn modal__btn-primary button-secondary" data-close-2fa-modal>'. __( 'Continue anyway', 'wp-2fa' ) .'</a>', |
| 130 | '<a href="#" class="modal__btn modal__btn-primary button-primary" data-close-2fa-modal data-user-login-name="'. esc_attr( $user->user_login ) .'">'. __( 'Exclude myself from 2FA policies', 'wp-2fa' ) .'</a>', |
| 131 | ], |
| 132 | false, |
| 133 | '560px' |
| 134 | ); |
| 135 | } |
| 136 | |
| 137 | $exclusions_modal_content = '<h3>'.esc_html__( 'Excluded Roles/Users still present', 'wp-2fa' ).'</h3>'; |
| 138 | $exclusions_modal_content .= '</p>'. esc_html__( 'Changing this setting whislt you still have roles/users excluded will result in these being removed from the settngs.', 'wp-2fa' ) .'</p>'; |
| 139 | |
| 140 | |
| 141 | echo GenerateModal::generate_modal( |
| 142 | 'warn-exclusions-will-be-removed', |
| 143 | false, |
| 144 | $exclusions_modal_content, |
| 145 | [ |
| 146 | '<a href="#" class="modal__btn modal__btn-primary button-secondary" data-close-2fa-modal data-clear-exclusions>'. __( 'Continue anyway', 'wp-2fa' ) .'</a>', |
| 147 | '<a href="#" class="modal__btn modal__btn-primary button-primary" data-close-2fa-modal data-cancel-action>'. __( 'Cancel', 'wp-2fa' ) .'</a>', |
| 148 | ], |
| 149 | false, |
| 150 | '560px' |
| 151 | ); |
| 152 | ?> |
| 153 | |
| 154 | <div class="wrap wp-2fa-settings-wrapper"> |
| 155 | <h2><?php esc_html_e( 'WP 2FA Settings', 'wp-2fa' ); ?></h2> |
| 156 | <hr> |
| 157 | <?php if ( ! empty( WP2FA::get_wp2fa_setting( 'limit_access' ) ) && $main_user !== $user->ID ) { ?> |
| 158 | |
| 159 | <?php |
| 160 | echo esc_html__( 'These settings have been disabled by your site administrator, please contact them for further assistance.', 'wp-2fa' ); |
| 161 | ?> |
| 162 | |
| 163 | <?php } else { ?> |
| 164 | |
| 165 | <div class="nav-tab-wrapper"> |
| 166 | <a href="<?php echo esc_url( add_query_arg( array( 'page' => 'wp-2fa-settings' ), network_admin_url( 'admin.php' ) ) ); ?>" class="nav-tab <?php echo ! isset( $_REQUEST['tab'] ) ? 'nav-tab-active' : ''; ?>"><?php _e( '2FA Settings', 'wp-2fa' ); ?></a> |
| 167 | <a href=" |
| 168 | <?php |
| 169 | echo esc_url( |
| 170 | add_query_arg( |
| 171 | array( |
| 172 | 'page' => 'wp-2fa-settings', |
| 173 | 'tab' => 'email-settings', |
| 174 | ), |
| 175 | network_admin_url( 'admin.php' ) |
| 176 | ) |
| 177 | ); |
| 178 | ?> |
| 179 | " class="nav-tab <?php echo isset( $_REQUEST['tab'] ) && 'email-settings' === $_REQUEST['tab'] ? 'nav-tab-active' : ''; ?>"><?php _e( 'Email Settings & Templates', 'wp-2fa' ); ?></a> |
| 180 | </div> |
| 181 | <?php |
| 182 | if ( ! current_user_can( 'manage_options' ) ) { |
| 183 | return; |
| 184 | } |
| 185 | if ( WP2FA::is_this_multisite() ) { |
| 186 | $action = 'edit.php?action=update_wp2fa_network_options'; |
| 187 | } else { |
| 188 | $action = 'options.php'; |
| 189 | } |
| 190 | if ( ! isset( $_REQUEST['tab'] ) || isset( $_REQUEST['tab'] ) && '2fa-settings' === $_REQUEST['tab'] ) : |
| 191 | ?> |
| 192 | <br/> |
| 193 | <?php |
| 194 | printf( '<p class="description">%1$s <a href="mailto:support@wpwhitesecurity.com">%2$s</a></p>', |
| 195 | esc_html__( 'Use the settings below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at', 'wp-2fa' ), |
| 196 | esc_html__( 'support@wpwhitesecurity.com', 'wp-2fa' ) |
| 197 | ); |
| 198 | ?> |
| 199 | <br/> |
| 200 | <?php $total_users = count_users(); ?> |
| 201 | <form id="wp-2fa-admin-settings" action='<?php echo esc_attr( $action ); ?>' method='post' autocomplete="off" data-2fa-total-users="<?php echo $total_users['total_users']; ?>"> |
| 202 | <?php |
| 203 | if ( ! current_user_can( 'manage_options' ) ) { |
| 204 | return; |
| 205 | } |
| 206 | |
| 207 | settings_fields( WP_2FA_SETTINGS_NAME ); |
| 208 | $this->select_method_setting(); |
| 209 | $this->select_enforcement_policy_setting(); |
| 210 | $this->excluded_roles_or_users_setting(); |
| 211 | if ( WP2FA::is_this_multisite() ) { |
| 212 | $this->excluded_network_sites(); |
| 213 | } |
| 214 | $this->grace_period_setting(); |
| 215 | $this->user_redirect_after_wizard(); |
| 216 | $this->user_profile_settings(); |
| 217 | $this->disable_2fa_removal_setting(); |
| 218 | $this->changeDefaultTextArea(); |
| 219 | $this->gracePeriodFrequency(); |
| 220 | $this->limit_settings_access(); |
| 221 | $this->remove_data_upon_uninstall(); |
| 222 | submit_button(); |
| 223 | ?> |
| 224 | </form> |
| 225 | <?php endif; ?> |
| 226 | |
| 227 | <?php |
| 228 | if ( WP2FA::is_this_multisite() ) { |
| 229 | $action = 'edit.php?action=update_wp2fa_network_email_options'; |
| 230 | } else { |
| 231 | $action = 'options.php'; |
| 232 | } |
| 233 | ?> |
| 234 | |
| 235 | <?php if ( isset( $_REQUEST['tab'] ) && 'email-settings' === $_REQUEST['tab'] ) : ?> |
| 236 | <br/> |
| 237 | <?php |
| 238 | printf( '<p class="description">%1$s <a href="mailto:support@wpwhitesecurity.com">%2$s</a></p>', |
| 239 | esc_html__( 'Use the settings below to configure the emails which are sent to users as part of the 2FA plugin. If you have any questions send us an email at', 'wp-2fa' ), |
| 240 | esc_html__( 'support@wpwhitesecurity.com', 'wp-2fa' ) |
| 241 | ); |
| 242 | ?> |
| 243 | <br/> |
| 244 | <form action='<?php echo esc_attr( $action ); ?>' method='post' autocomplete="off"> |
| 245 | <?php |
| 246 | if ( ! current_user_can( 'manage_options' ) ) { |
| 247 | return; |
| 248 | } |
| 249 | |
| 250 | settings_fields( WP_2FA_EMAIL_SETTINGS_NAME ); |
| 251 | $this->email_from_settings(); |
| 252 | $this->email_settings(); |
| 253 | submit_button( 'Save email settings and templates' ); |
| 254 | ?> |
| 255 | </form> |
| 256 | <?php endif; ?> |
| 257 | |
| 258 | <?php } ?> |
| 259 | </div> |
| 260 | <?php |
| 261 | } |
| 262 | |
| 263 | private function changeDefaultTextArea() { |
| 264 | ?> |
| 265 | <h3><?php esc_html_e( 'Change the default text used in the 2FA code page?', 'wp-2fa' ); ?></h3> |
| 266 | <p class="description"> |
| 267 | <?php esc_html_e( 'This is the text shown to the users on the page when they are asked to enter the 2FA code. To change the default text, simply type it in the below placeholder.', 'wp-2fa' ); ?> |
| 268 | </p> |
| 269 | <table class="form-table"> |
| 270 | <tbody> |
| 271 | <tr> |
| 272 | <th><label for="2fa-method"><?php esc_html_e( '2FA code page text', 'wp-2fa' ); ?></label></th> |
| 273 | <td> |
| 274 | <fieldset> |
| 275 | <label for="default-text-code-page"> |
| 276 | <textarea cols="70" rows="10" name="wp_2fa_settings[default-text-code-page]" id="default-text-code-page"><?php echo WP2FA::get_wp2fa_setting( 'default-text-code-page', true ); ?></textarea> |
| 277 | <div><span><strong><i><?php esc_html_e( 'Note:', 'wp-2fa' ); ?></i></strong> <?php esc_html_e( 'Only plain text is allowed.', 'wp-2fa' ); ?></span></div> |
| 278 | </label> |
| 279 | </fieldset> |
| 280 | </td> |
| 281 | </tr> |
| 282 | </tbody> |
| 283 | </table> |
| 284 | <?php |
| 285 | |
| 286 | } |
| 287 | |
| 288 | /** |
| 289 | * General settings |
| 290 | */ |
| 291 | private function select_method_setting() { |
| 292 | FirstTimeWizardSteps::selectMethod( false ); |
| 293 | } |
| 294 | |
| 295 | /** |
| 296 | * Policy settings |
| 297 | */ |
| 298 | private function select_enforcement_policy_setting() { |
| 299 | FirstTimeWizardSteps::enforcementPolicy( false ); |
| 300 | } |
| 301 | |
| 302 | /** |
| 303 | * User profile settings |
| 304 | */ |
| 305 | private function user_profile_settings() { |
| 306 | ?> |
| 307 | <h3><?php esc_html_e( 'Can users access the WordPress dashboard or you have custom profile pages? ', 'wp-2fa' ); ?></h3> |
| 308 | <p class="description"> |
| 309 | <?php esc_html_e( 'If your users do not have access to the WordPress dashboard (because you use custom user profile pages) enable this option. Once enabled, the plugin creates a page which ONLY authenticated users can access to configure their user 2FA settings. A link to this page is sent in the 2FA welcome email.', 'wp-2fa' ); ?></a> |
| 310 | </p> |
| 311 | <table class="form-table"> |
| 312 | <tbody> |
| 313 | <tr> |
| 314 | <th><label for="enforcement-policy"><?php esc_html_e( 'Frontend 2FA settings page', 'wp-2fa' ); ?></label></th> |
| 315 | <td> |
| 316 | <fieldset> |
| 317 | <label class="radio-inline"> |
| 318 | <input id="use_custom_page" type="radio" name="wp_2fa_settings[create-custom-user-page]" value="yes" |
| 319 | <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), 'yes' ); ?> |
| 320 | > |
| 321 | <?php esc_html_e( 'Yes', 'wp-2fa' ); ?> |
| 322 | </label> |
| 323 | <label class="radio-inline"> |
| 324 | <input id="dont_use_custom_page" type="radio" name="wp_2fa_settings[create-custom-user-page]" value="no" |
| 325 | <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), 'no' ); ?> |
| 326 | <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), '' ); ?> |
| 327 | > |
| 328 | <?php esc_html_e( 'No', 'wp-2fa' ); ?> |
| 329 | </label> |
| 330 | </fieldset> |
| 331 | </td> |
| 332 | </tr> |
| 333 | <tr class="custom-user-page-setting disabled"> |
| 334 | <th><label for="enforcement-policy"><?php esc_html_e( 'Frontend 2FA settings page URL', 'wp-2fa' ); ?></label></th> |
| 335 | <td> |
| 336 | <fieldset> |
| 337 | <?php |
| 338 | if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) { |
| 339 | $custom_slug = get_post_field( 'post_name', get_post( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ); |
| 340 | } else { |
| 341 | $custom_slug = WP2FA::get_wp2fa_setting( 'custom-user-page-url' ); |
| 342 | } |
| 343 | |
| 344 | $has_error = false; |
| 345 | $settings_errors = get_settings_errors( WP_2FA_SETTINGS_NAME ); |
| 346 | if (!empty($settings_errors)) { |
| 347 | foreach ( $settings_errors as $error ) { |
| 348 | if ($error['code'] == 'no_page_slug_provided') { |
| 349 | $has_error = true; |
| 350 | break; |
| 351 | } |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | ?> |
| 356 | <?php echo trailingslashit( get_site_url() ); ?> |
| 357 | <input type="text" id="custom-user-page-url" name="wp_2fa_settings[custom-user-page-url]" value="<?php echo sanitize_text_field( $custom_slug ); ?>"<?php if ($has_error): ?> class="error"<?php endif; ?>> |
| 358 | </fieldset> |
| 359 | <?php |
| 360 | if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) { |
| 361 | $edit_post_link = get_edit_post_link( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ); |
| 362 | $view_post_link = get_permalink( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ); |
| 363 | ?> |
| 364 | <br> |
| 365 | <a href="<?php echo esc_url( $edit_post_link ); ?>" target="_blank" class="button button-secondary" style="margin-right: 5px;"><?php esc_html_e( 'Edit Page', 'wp-2fa' ); ?></a> <a href="<?php echo esc_url( $view_post_link ); ?>" target="_blank" class="button button-primary"><?php esc_html_e( 'View Page', 'wp-2fa' ); ?></a> |
| 366 | <?php |
| 367 | } |
| 368 | ?> |
| 369 | </td> |
| 370 | </tr> |
| 371 | <tr class="custom-user-page-setting"> |
| 372 | <th colspan="2"><p class="description"><?php esc_html_e( 'Specify the page where you want to redirect your users to after they complete the 2FA setup. This will override the global redirect setting.', 'wp-2fa' ); ?></p></th> |
| 373 | </tr> |
| 374 | <tr class="custom-user-page-setting"> |
| 375 | <th><label for="enforcement-policy"><?php esc_html_e( 'Redirect users after 2FA setup', 'wp-2fa' ); ?></label></th> |
| 376 | <td> |
| 377 | <fieldset> |
| 378 | <?php |
| 379 | $custom_slug = WP2FA::get_wp2fa_setting( 'redirect-user-custom-page' ); |
| 380 | ?> |
| 381 | <?php echo trailingslashit( get_site_url() ); ?> |
| 382 | <input type="text" id="redirect-user-custom-page" name="wp_2fa_settings[redirect-user-custom-page]" value="<?php echo sanitize_text_field( $custom_slug ); ?>"> |
| 383 | </fieldset> |
| 384 | </td> |
| 385 | </tr> |
| 386 | </tbody> |
| 387 | </table> |
| 388 | <?php |
| 389 | } |
| 390 | |
| 391 | /** |
| 392 | * User profile settings |
| 393 | */ |
| 394 | private function user_redirect_after_wizard() { |
| 395 | ?> |
| 396 | <h3><?php esc_html_e( 'Do you want to redirect the user to a specific page after completing the 2FA setup wizard?', 'wp-2fa' ); ?></h3> |
| 397 | <p class="description"> |
| 398 | <?php esc_html_e( 'Specify a URL of a page where you want to redirect the users once they complete the 2FA setup wizard. Leave empty for default behaviour, in which users are redirected back to the page from where they launched the wizard.', 'wp-2fa' ); ?></a> |
| 399 | </p> |
| 400 | <table class="form-table"> |
| 401 | <tbody> |
| 402 | <tr> |
| 403 | <th><label for="redirect-user-custom-page-global"><?php esc_html_e( 'Redirect users after 2FA setup to', 'wp-2fa' ); ?></label></th> |
| 404 | <td> |
| 405 | <fieldset> |
| 406 | <?php echo trailingslashit( get_site_url() ); ?> |
| 407 | <input type="text" id="redirect-user-custom-page-global" name="wp_2fa_settings[redirect-user-custom-page-global]" value="<?php echo sanitize_text_field( WP2FA::get_wp2fa_setting( 'redirect-user-custom-page-global' ) ); ?>"> |
| 408 | </fieldset> |
| 409 | </td> |
| 410 | </tr> |
| 411 | </tbody> |
| 412 | </table> |
| 413 | <?php |
| 414 | } |
| 415 | |
| 416 | /** |
| 417 | * Role and users exclusion settings |
| 418 | */ |
| 419 | private function excluded_roles_or_users_setting() { |
| 420 | $enforcement = WP2FA::get_wp2fa_setting( 'enforcement-policy' ); |
| 421 | $disabled_class = ( 'all-users' == $enforcement ) ? 'enabled' : 'disabled'; |
| 422 | ?> |
| 423 | <div id="exclusion_settings_wrapper" class="<?php echo esc_attr( $disabled_class ); ?>"> |
| 424 | <?php FirstTimeWizardSteps::excludeUsers(); ?> |
| 425 | </div> |
| 426 | <?php |
| 427 | } |
| 428 | |
| 429 | /** |
| 430 | * Role and users exclusion settings |
| 431 | */ |
| 432 | private function excluded_network_sites() { |
| 433 | FirstTimeWizardSteps::excludedNetworkSites(); |
| 434 | } |
| 435 | |
| 436 | /** |
| 437 | * Grace period settings |
| 438 | */ |
| 439 | private function grace_period_setting() { |
| 440 | ?> |
| 441 | <br> |
| 442 | <h3><?php esc_html_e( 'Should users be asked to setup 2FA instantly or should they have a grace period?', 'wp-2fa' ); ?></h3> |
| 443 | <p class="description"> |
| 444 | <?php esc_html_e( 'When you enforce 2FA on users they have a grace period to configure 2FA. If they fail to configure it within the configured stipulated time, their account will be locked and have to be unlocked manually. Note that user accounts cannot be unlocked automatically, even if you change the settings. As a security precaution they always have to be unlocked them manually. Maximum grace period is 10 days.', 'wp-2fa' ); ?> <a href="https://www.wpwhitesecurity.com/support/kb/configure-grace-period-2fa/?utm_source=plugin&utm_medium=referral&utm_campaign=WP2FA&utm_content=settings+pages" target="_blank"><?php esc_html_e( 'Learn more.', 'wp-2fa' ); ?></a> |
| 445 | </p> |
| 446 | |
| 447 | <table class="form-table"> |
| 448 | <tbody> |
| 449 | <tr> |
| 450 | <th><label for="grace-policy"><?php esc_html_e( 'Grace period', 'wp-2fa' ); ?></label></th> |
| 451 | <td> |
| 452 | <?php FirstTimeWizardSteps::gracePeriod( true ); ?> |
| 453 | </td> |
| 454 | </tr> |
| 455 | </tbody> |
| 456 | </table> |
| 457 | <?php |
| 458 | } |
| 459 | |
| 460 | /** |
| 461 | * Grace period frequency |
| 462 | */ |
| 463 | private function gracePeriodFrequency() { |
| 464 | ?> |
| 465 | <h3><?php esc_html_e( 'How often should the plugin check if a user\'s grace period is over?', 'wp-2fa' ); ?></h3> |
| 466 | <p class="description"> |
| 467 | <?php esc_html_e( 'By default the plugin checks if a users grace periods to setup 2FA has passed when the user tries to login. If you would like the plugin to advise the user within an hour, enable the below option to add a cron job that runs every hour.', 'wp-2fa' ); ?> |
| 468 | </p> |
| 469 | <table class="form-table"> |
| 470 | <tbody> |
| 471 | <tr> |
| 472 | <th><label for="grace-period"><?php esc_html_e( 'Enable cron', 'wp-2fa' ); ?></label></th> |
| 473 | <td> |
| 474 | <fieldset> |
| 475 | <input type="checkbox" id="grace-cron" name="wp_2fa_settings[enable_grace_cron]" value="enable_grace_cron" |
| 476 | <?php checked( 1, WP2FA::get_wp2fa_setting( 'enable_grace_cron' ), true ); ?> |
| 477 | > |
| 478 | <?php esc_html_e( 'Use cron job to check grace periods', 'wp-2fa' ); ?> |
| 479 | </fieldset> |
| 480 | </td> |
| 481 | </tr> |
| 482 | <tr class="disabled destory-session-setting"> |
| 483 | <th><label for="destory-session"><?php esc_html_e( 'Destroy session', 'wp-2fa' ); ?></label></th> |
| 484 | <td> |
| 485 | <fieldset> |
| 486 | <input type="checkbox" id="destory-session" name="wp_2fa_settings[enable_destroy_session]" value="enable_destroy_session" |
| 487 | <?php checked( 1, WP2FA::get_wp2fa_setting( 'enable_destroy_session' ), true ); ?> |
| 488 | > |
| 489 | <?php esc_html_e( 'Destroy user session when grace period expires?', 'wp-2fa' ); ?> |
| 490 | </fieldset> |
| 491 | </td> |
| 492 | </tr> |
| 493 | </tbody> |
| 494 | </table> |
| 495 | <?php |
| 496 | } |
| 497 | |
| 498 | /** |
| 499 | * Disable removal of 2FA settings |
| 500 | */ |
| 501 | private function disable_2fa_removal_setting() { |
| 502 | $user = wp_get_current_user(); |
| 503 | ?> |
| 504 | <br> |
| 505 | <h3><?php esc_html_e( 'Should users be able to disable 2FA on their user profile?', 'wp-2fa' ); ?></h3> |
| 506 | <p class="description"> |
| 507 | <?php esc_html_e( 'Users can configure and also disable 2FA on their profile by clicking the "Remove 2FA" button. Enable this setting to disable the Remove 2FA button so users cannot disable 2FA from their user profile.', 'wp-2fa' ); ?> |
| 508 | </p> |
| 509 | <table class="form-table"> |
| 510 | <tbody> |
| 511 | <tr> |
| 512 | <th><label for="hide-remove-2fa"><?php esc_html_e( 'Hide the Remove 2FA button', 'wp-2fa' ); ?></label></th> |
| 513 | <td> |
| 514 | <fieldset> |
| 515 | <input type="checkbox" id="hide-remove-2fa" name="wp_2fa_settings[hide_remove_button]" value="hide_remove_button" |
| 516 | <?php checked( 1, WP2FA::get_wp2fa_setting( 'hide_remove_button' ), true ); ?> |
| 517 | > |
| 518 | <?php esc_html_e( 'Hide the Remove 2FA button on user profile pages', 'wp-2fa' ); ?> |
| 519 | </fieldset> |
| 520 | </td> |
| 521 | </tr> |
| 522 | </tbody> |
| 523 | </table> |
| 524 | <?php |
| 525 | } |
| 526 | |
| 527 | /** |
| 528 | * Limit settings setting |
| 529 | */ |
| 530 | private function limit_settings_access() { |
| 531 | ?> |
| 532 | <br> |
| 533 | <h3><?php esc_html_e( 'Limit 2FA settings access?', 'wp-2fa' ); ?></h3> |
| 534 | <p class="description"> |
| 535 | <?php esc_html_e( 'Use this setting to hide this plugin configuration area from all other admins.', 'wp-2fa' ); ?> |
| 536 | </p> |
| 537 | <table class="form-table"> |
| 538 | <tbody> |
| 539 | <tr> |
| 540 | <th><label for="grace-period"><?php esc_html_e( 'Limit access to 2FA settings', 'wp-2fa' ); ?></label></th> |
| 541 | <td> |
| 542 | <fieldset> |
| 543 | <input type="checkbox" id="limit_access" name="wp_2fa_settings[limit_access]" value="limit_access" |
| 544 | <?php checked( 1, WP2FA::get_wp2fa_setting( 'limit_access' ), true ); ?> |
| 545 | > |
| 546 | <?php esc_html_e( 'Hide settings from other administrators', 'wp-2fa' ); ?> |
| 547 | </fieldset> |
| 548 | </td> |
| 549 | </tr> |
| 550 | </tbody> |
| 551 | </table> |
| 552 | <?php |
| 553 | } |
| 554 | |
| 555 | /** |
| 556 | * Limit settings setting |
| 557 | */ |
| 558 | private function remove_data_upon_uninstall() { |
| 559 | ?> |
| 560 | <div class="danger-zone-wrapper"> |
| 561 | <h3><?php esc_html_e( 'Do you want to delete the plugin data from the database upon uninstall?', 'wp-2fa' ); ?></h3> |
| 562 | <p class="description"> |
| 563 | <?php esc_html_e( 'The plugin saves its settings in the WordPress database. By default the plugin settings are kept in the database so if it is installed again, you do not have to reconfigure the plugin. Enable this setting to delete the plugin settings from the database upon uninstall.', 'wp-2fa' ); ?> |
| 564 | </p> |
| 565 | <table class="form-table"> |
| 566 | <tbody> |
| 567 | <tr> |
| 568 | <th><label for="delete_data"><?php esc_html_e( 'Delete data', 'wp-2fa' ); ?></label></th> |
| 569 | <td> |
| 570 | <fieldset> |
| 571 | <input type="checkbox" id="elete_data" name="wp_2fa_settings[delete_data_upon_uninstall]" value="delete_data_upon_uninstall" |
| 572 | <?php checked( 1, WP2FA::get_wp2fa_setting( 'delete_data_upon_uninstall' ), true ); ?> |
| 573 | > |
| 574 | <?php esc_html_e( 'Delete data upon uninstall', 'wp-2fa' ); ?> |
| 575 | </fieldset> |
| 576 | </td> |
| 577 | </tr> |
| 578 | </tbody> |
| 579 | </table> |
| 580 | </div> |
| 581 | <?php |
| 582 | } |
| 583 | |
| 584 | /** |
| 585 | * Get all users |
| 586 | */ |
| 587 | public function get_all_users() { |
| 588 | // Die if user does not have permission to view. |
| 589 | if ( ! current_user_can( 'manage_options' ) ) { |
| 590 | die( 'Access Denied.' ); |
| 591 | } |
| 592 | // Filter $_GET array for security. |
| 593 | $get_array = filter_input_array( INPUT_GET ); |
| 594 | |
| 595 | // Die if nonce verification failed. |
| 596 | if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) { |
| 597 | die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 598 | } |
| 599 | |
| 600 | $users_args = array( |
| 601 | 'fields' => array( 'ID', 'user_login' ), |
| 602 | ); |
| 603 | if ( WP2FA::is_this_multisite() ) { |
| 604 | $users_args['blog_id'] = 0; |
| 605 | } |
| 606 | $users_data = UserUtils::get_all_user_ids_and_login_names( 'query', $users_args ); |
| 607 | |
| 608 | // Create final array which we will fill in below. |
| 609 | $users = []; |
| 610 | |
| 611 | foreach ( $users_data as $user ) { |
| 612 | if ( strpos( $user['user_login'], $get_array['term'] ) !== false ) { |
| 613 | array_push( $users, [ |
| 614 | 'value' => $user['user_login'], |
| 615 | 'label' => $user['user_login'] |
| 616 | ]); |
| 617 | } |
| 618 | } |
| 619 | |
| 620 | echo wp_json_encode( $users ); |
| 621 | exit; |
| 622 | } |
| 623 | |
| 624 | /** |
| 625 | * Get all network sites |
| 626 | */ |
| 627 | public function get_all_network_sites() { |
| 628 | // Die if user does not have permission to view. |
| 629 | if ( ! current_user_can( 'manage_options' ) ) { |
| 630 | die( 'Access Denied.' ); |
| 631 | } |
| 632 | // Filter $_GET array for security. |
| 633 | $get_array = filter_input_array( INPUT_GET ); |
| 634 | // Die if nonce verification failed. |
| 635 | if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) { |
| 636 | die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 637 | } |
| 638 | // Fetch sites. |
| 639 | $sites_found = array(); |
| 640 | |
| 641 | foreach ( get_sites() as $site ) { |
| 642 | $subsite_id = get_object_vars( $site )['blog_id']; |
| 643 | $subsite_name = get_blog_details( $subsite_id )->blogname; |
| 644 | $site_details = ''; |
| 645 | $site_details[ $subsite_id ] = $subsite_name; |
| 646 | if ( false !== stripos( $subsite_name, $get_array['term'] ) ) { |
| 647 | array_push( $sites_found, [ |
| 648 | 'label' => $subsite_id, |
| 649 | 'value' => $subsite_name |
| 650 | ]); |
| 651 | } |
| 652 | } |
| 653 | echo wp_json_encode( $sites_found ); |
| 654 | exit; |
| 655 | } |
| 656 | |
| 657 | /** |
| 658 | * Unlock users accounts if they have overrun grace period |
| 659 | * |
| 660 | * @param int $user_id User ID. |
| 661 | */ |
| 662 | public function unlock_account( $user_id ) { |
| 663 | // Die if user does not have permission to view. |
| 664 | if ( ! current_user_can( 'manage_options' ) ) { |
| 665 | die( 'Access Denied.' ); |
| 666 | } |
| 667 | |
| 668 | $grace_period = WP2FA::get_wp2fa_setting( 'grace-period' ); |
| 669 | $grace_period_denominator = WP2FA::get_wp2fa_setting( 'grace-period-denominator' ); |
| 670 | $create_a_string = $grace_period . ' ' . $grace_period_denominator; |
| 671 | // Turn that string into a time. |
| 672 | $grace_expiry = strtotime( $create_a_string ); |
| 673 | |
| 674 | // Filter $_GET array for security. |
| 675 | $get_array = filter_input_array( INPUT_GET ); |
| 676 | $nonce = sanitize_text_field( $get_array['wp_2fa_nonce'] ); |
| 677 | |
| 678 | // Die if nonce verification failed. |
| 679 | if ( ! wp_verify_nonce( $nonce, 'wp-2fa-unlock-account-nonce' ) ) { |
| 680 | die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 681 | } |
| 682 | |
| 683 | if ( isset( $get_array['user_id'] ) ) { |
| 684 | global $wpdb; |
| 685 | $wpdb->query( |
| 686 | $wpdb->prepare( |
| 687 | " |
| 688 | DELETE FROM $wpdb->usermeta |
| 689 | WHERE user_id = %d |
| 690 | AND meta_key IN ( %s, %s ) |
| 691 | ", |
| 692 | [ |
| 693 | intval( $get_array['user_id'] ), |
| 694 | WP_2FA_PREFIX . 'user_grace_period_expired', |
| 695 | WP_2FA_PREFIX . 'locked_account_notification', |
| 696 | ] |
| 697 | ) |
| 698 | ); |
| 699 | $update = update_user_meta( intval( $get_array['user_id'] ), WP_2FA_PREFIX . 'grace_period_expiry', $grace_expiry ); |
| 700 | $this->send_account_unlocked_email( intval( $get_array['user_id'] ) ); |
| 701 | add_action( 'admin_notices', array( $this, 'user_unlocked_notice' ) ); |
| 702 | } |
| 703 | } |
| 704 | |
| 705 | /** |
| 706 | * Remove user 2fa config |
| 707 | * |
| 708 | * @param int $user_id User ID. |
| 709 | */ |
| 710 | public function remove_user_2fa( $user_id ) { |
| 711 | // Filter $_GET array for security. |
| 712 | $get_array = filter_input_array( INPUT_GET ); |
| 713 | $nonce = sanitize_text_field( $get_array['wp_2fa_nonce'] ); |
| 714 | |
| 715 | if ( ! wp_verify_nonce( $nonce, 'wp-2fa-remove-user-2fa-nonce' ) ) { |
| 716 | die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 717 | } |
| 718 | |
| 719 | if ( isset( $get_array['user_id'] ) ) { |
| 720 | $user_id = intval( $get_array['user_id'] ); |
| 721 | global $wpdb; |
| 722 | $wpdb->query( |
| 723 | $wpdb->prepare( |
| 724 | "DELETE FROM $wpdb->usermeta |
| 725 | WHERE user_id = %d |
| 726 | AND meta_key LIKE %s", |
| 727 | [ |
| 728 | $user_id, |
| 729 | 'wp_2fa_%' |
| 730 | ] |
| 731 | ) |
| 732 | ); |
| 733 | |
| 734 | $is_needed = WP2FA::isUserEnforced( $user_id ); |
| 735 | |
| 736 | if ( $is_needed ) { |
| 737 | if ( 'do-not-enforce' !== WP2FA::get_wp2fa_setting( 'enforcement-policy' ) ) { |
| 738 | // Turn inputs into a useable string. |
| 739 | $create_a_string = WP2FA::get_wp2fa_setting( 'grace-period' ) . ' ' . WP2FA::get_wp2fa_setting( 'grace-period-denominator' ); |
| 740 | // Turn that string into a time. |
| 741 | $grace_expiry = strtotime( $create_a_string ); |
| 742 | update_user_meta( $user_id, WP_2FA_PREFIX . 'grace_period_expiry', $grace_expiry ); |
| 743 | update_user_meta( $user_id, WP_2FA_PREFIX . 'update_nag_dismissed', true ); |
| 744 | } |
| 745 | $grace_policy = WP2FA::get_wp2fa_setting( 'grace-policy' ); |
| 746 | if ( 'no-grace-period' === $grace_policy ) { |
| 747 | update_user_meta( $user_id, WP_2FA_PREFIX . 'user_enforced_instantly', true ); |
| 748 | // Set this to a known older value so its already expired. |
| 749 | update_user_meta( $user_id, WP_2FA_PREFIX . 'grace_period_expiry', '1609502400' ); |
| 750 | // Get sessions for user with ID $user_id. |
| 751 | $sessions = \WP_Session_Tokens::get_instance( $user_id ); |
| 752 | // Log them out. |
| 753 | $sessions->destroy_all(); |
| 754 | } |
| 755 | } |
| 756 | if ( isset( $get_array['admin_reset'] ) ) { |
| 757 | add_action( 'admin_notices', array( $this, 'admin_deleted_2fa_notice' ) ); |
| 758 | } else { |
| 759 | add_action( 'admin_notices', array( $this, 'user_deleted_2fa_notice' ) ); |
| 760 | } |
| 761 | } |
| 762 | } |
| 763 | |
| 764 | /** |
| 765 | * Send account unlocked notification via email. |
| 766 | * |
| 767 | * @param int $user_id user ID. |
| 768 | * |
| 769 | * @return boolean |
| 770 | */ |
| 771 | public static function send_account_unlocked_email( $user_id ) { |
| 772 | // Bail if the user has not enabled this email. |
| 773 | if ( 'enable_account_unlocked_email' !== WP2FA::get_wp2fa_email_templates( 'send_account_unlocked_email' ) ) { |
| 774 | return false; |
| 775 | } |
| 776 | |
| 777 | // Grab user data. |
| 778 | $user = get_userdata( $user_id ); |
| 779 | // Grab user email. |
| 780 | $email = $user->user_email; |
| 781 | // Setup the email contents. |
| 782 | $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_subject' ) ) ); |
| 783 | $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_body' ), $user_id ) ); |
| 784 | |
| 785 | self::send_email($email, $subject, $message); |
| 786 | } |
| 787 | |
| 788 | /** |
| 789 | * Validate options before saving |
| 790 | * |
| 791 | * @param array $input The settings array. |
| 792 | * |
| 793 | * @return array|void |
| 794 | */ |
| 795 | public function validate_and_sanitize( $input ) { |
| 796 | |
| 797 | // Bail if user doesnt have permissions to be here. |
| 798 | if ( ! current_user_can( 'manage_options' ) || ! isset( $_POST['action'] ) && ! check_admin_referer( 'wp2fa-step-choose-method' ) ) { |
| 799 | return; |
| 800 | } |
| 801 | |
| 802 | $noMethodEnabled = false; |
| 803 | if ( ! isset( $input['enable_totp'] ) && ! isset( $input['enable_email'] ) && ! isset( $_POST['save_step'] ) ) { |
| 804 | add_settings_error( |
| 805 | WP_2FA_SETTINGS_NAME, |
| 806 | esc_attr( 'enable_email_settings_error' ), |
| 807 | esc_html__( 'At least one 2FA method should be enabled.', 'wp-2fa' ), |
| 808 | 'error' |
| 809 | ); |
| 810 | $noMethodEnabled = true; |
| 811 | } |
| 812 | |
| 813 | $simple_settings_we_can_loop = array( |
| 814 | 'enable_totp', |
| 815 | 'enable_email', |
| 816 | 'backup_codes_enabled', |
| 817 | 'grace-policy', |
| 818 | 'enable_grace_cron', |
| 819 | 'enable_destroy_session', |
| 820 | '2fa_settings_last_updated_by', |
| 821 | 'limit_access', |
| 822 | 'delete_data_upon_uninstall', |
| 823 | 'hide_remove_button', |
| 824 | 'redirect-user-custom-page', |
| 825 | 'redirect-user-custom-page-global', |
| 826 | 'superadmins-role-add', |
| 827 | 'superadmins-role-exclude', |
| 828 | ); |
| 829 | |
| 830 | $settings_to_turn_into_bools = array( |
| 831 | 'enable_grace_cron', |
| 832 | 'enable_destroy_session', |
| 833 | 'limit_access', |
| 834 | 'delete_data_upon_uninstall', |
| 835 | 'hide_remove_button' |
| 836 | ); |
| 837 | |
| 838 | $settings_to_turn_into_array = [ |
| 839 | 'enforced_roles', |
| 840 | 'enforced_users', |
| 841 | 'excluded_users', |
| 842 | 'excluded_roles', |
| 843 | 'excluded_sites', |
| 844 | ]; |
| 845 | |
| 846 | foreach ( $simple_settings_we_can_loop as $simple_setting ) { |
| 847 | if ( ! in_array( $simple_setting, $settings_to_turn_into_bools ) ) { |
| 848 | // Is item is not one of our possible settings we want to turn into a bool, process. |
| 849 | $output[ $simple_setting ] = ( isset( $input[ $simple_setting ] ) && ! empty( $input[ $simple_setting ] ) ) ? trim( sanitize_text_field( $input[ $simple_setting ] ) ) : false; |
| 850 | } else { |
| 851 | // This item is one we treat as a bool, so process correctly. |
| 852 | $output[ $simple_setting ] = ( isset( $input[ $simple_setting ] ) && ! empty( $input[ $simple_setting ] ) ) ? true : false; |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | if ( $noMethodEnabled ) { |
| 857 | // No method is enabled, fall back to previous selected one - we don't want to break the logic |
| 858 | $totpEnabled = WP2FA::get_wp2fa_setting( 'enable_totp' ); |
| 859 | $emailEnabled = WP2FA::get_wp2fa_setting( 'enable_email' ); |
| 860 | |
| 861 | if ( $totpEnabled ) { |
| 862 | $output['enable_totp'] = $totpEnabled; |
| 863 | } |
| 864 | if ( $emailEnabled ) { |
| 865 | $output['enable_email'] = $emailEnabled; |
| 866 | } |
| 867 | } |
| 868 | |
| 869 | $output['included_sites'] = []; |
| 870 | if ( isset( $input['included_sites'] ) && is_array($input['included_sites']) && ! empty( $input['included_sites'] ) ) { |
| 871 | foreach ( $input['included_sites'] as &$site ) { |
| 872 | if ( ! filter_var($site, FILTER_VALIDATE_INT) ) { |
| 873 | unset( $site ); |
| 874 | continue; |
| 875 | } |
| 876 | |
| 877 | $output['included_sites'][] = $site; |
| 878 | } |
| 879 | } |
| 880 | unset( $site ); |
| 881 | |
| 882 | foreach ($settings_to_turn_into_array as $setting) { |
| 883 | if ( isset( $input[$setting] ) ) { |
| 884 | $output[$setting] = $input[$setting]; |
| 885 | } else { |
| 886 | $output[$setting] = []; |
| 887 | } |
| 888 | } |
| 889 | |
| 890 | $output['default-text-code-page'] = WP2FA::get_wp2fa_setting('default-text-code-page', false, true); |
| 891 | |
| 892 | if ( isset( $input['default-text-code-page'] ) && '' !== trim( $input['default-text-code-page'] ) ) { |
| 893 | $output['default-text-code-page'] = \strip_tags( $input['default-text-code-page'] ); |
| 894 | } |
| 895 | |
| 896 | $log_content = __( 'The following setting are being saved: ', 'wp-2fa' ) . "\n" . json_encode( $input ) . "\n"; |
| 897 | Debugging::log( $log_content ); |
| 898 | |
| 899 | if ( isset( $input['grace-period'] ) ) { |
| 900 | if ( 0 === (int) $input['grace-period'] ) { |
| 901 | add_settings_error( |
| 902 | WP_2FA_SETTINGS_NAME, |
| 903 | esc_attr( 'grace_settings_error' ), |
| 904 | esc_html__( 'Grace period must be at least 1 day/hour', 'wp-2fa' ), |
| 905 | 'error' |
| 906 | ); |
| 907 | $output['grace-period'] = 1; |
| 908 | } else { |
| 909 | $output['grace-period'] = (int) $input['grace-period']; |
| 910 | } |
| 911 | } |
| 912 | |
| 913 | if ( isset( $input['grace-period-denominator'] ) && 'days' === $input['grace-period-denominator'] || isset( $input['grace-period-denominator'] ) && 'hours' === $input['grace-period-denominator'] || isset( $input['grace-period-denominator'] ) && 'seconds' === $input['grace-period-denominator'] ) { |
| 914 | $output['grace-period-denominator'] = sanitize_text_field( $input['grace-period-denominator'] ); |
| 915 | } |
| 916 | |
| 917 | if ( isset( $input['create-custom-user-page'] ) && 'yes' === $input['create-custom-user-page'] || isset( $input['create-custom-user-page'] ) && 'no' === $input['create-custom-user-page'] ) { |
| 918 | $output['create-custom-user-page'] = sanitize_text_field( $input['create-custom-user-page'] ); |
| 919 | } |
| 920 | |
| 921 | if ( isset( $input['custom-user-page-url'] ) ) { |
| 922 | if ( $input['custom-user-page-url'] !== WP2FA::get_wp2fa_setting( 'custom-user-page-url' ) ) { |
| 923 | if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) { |
| 924 | $updated_post = array( |
| 925 | 'ID' => WP2FA::get_wp2fa_setting( 'custom-user-page-id' ), |
| 926 | 'post_name' => sanitize_title_with_dashes( $input['custom-user-page-url'] ), |
| 927 | ); |
| 928 | wp_update_post( $updated_post ); |
| 929 | $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] ); |
| 930 | $output['custom-user-page-id'] = WP2FA::get_wp2fa_setting( 'custom-user-page-id' ); |
| 931 | } elseif ( 'yes' === $input['create-custom-user-page'] && ! empty( $input['custom-user-page-url'] ) ) { |
| 932 | $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] ); |
| 933 | $create_page = $this->generate_custom_user_profile_page( $output['custom-user-page-url'] ); |
| 934 | $output['custom-user-page-id'] = (int) $create_page; |
| 935 | } |
| 936 | } else { |
| 937 | $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] ); |
| 938 | $output['custom-user-page-id'] = WP2FA::get_wp2fa_setting( 'custom-user-page-id' ); |
| 939 | } |
| 940 | } |
| 941 | |
| 942 | if ( isset( $_REQUEST['page'] ) && 'wp-2fa-setup' !== $_REQUEST['page'] || isset( $_REQUEST[ WP_2FA_SETTINGS_NAME ]['create-custom-user-page'] ) ) { |
| 943 | |
| 944 | if ( isset( $input['create-custom-user-page'] ) && 'no' === $input['create-custom-user-page'] ) { |
| 945 | $output['custom-user-page-url'] = ''; |
| 946 | $output['custom-user-page-id'] = ''; |
| 947 | wp_delete_post( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ), true ); |
| 948 | } |
| 949 | } |
| 950 | |
| 951 | if ( isset( $input['create-custom-user-page'] ) && 'yes' === $input['create-custom-user-page'] && empty( $input['custom-user-page-url'] ) ) { |
| 952 | add_settings_error( |
| 953 | WP_2FA_SETTINGS_NAME, |
| 954 | esc_attr( 'no_page_slug_provided' ), |
| 955 | esc_html__( 'You must provide a new page slug.', 'wp-2fa' ), |
| 956 | 'error' |
| 957 | ); |
| 958 | } |
| 959 | |
| 960 | if ( isset( $input['grace-period'] ) && isset( $input['grace-period-denominator'] ) ) { |
| 961 | // Turn inputs into a useable string. |
| 962 | $create_a_string = $output['grace-period'] . ' ' . $output['grace-period-denominator']; |
| 963 | // Turn that string into a time. |
| 964 | $grace_expiry = strtotime( $create_a_string ); |
| 965 | $output['grace-period-expiry-time'] = sanitize_text_field( $grace_expiry ); |
| 966 | } |
| 967 | |
| 968 | // Process main policy. |
| 969 | if ( isset( $input['enforcement-policy'] ) && in_array( $input['enforcement-policy'], [ 'all-users', 'certain-users-only', 'certain-roles-only', 'do-not-enforce', 'superadmins-only', 'superadmins-siteadmins-only', "enforce-on-multisite" ] ) ) { |
| 970 | |
| 971 | // Clear enforced roles/users if setting has changed. |
| 972 | if ( 'all-users' === $input['enforcement-policy'] || 'do-not-enforce' === $input['enforcement-policy'] ) { |
| 973 | $input['enforced_users'] = []; |
| 974 | $input['enforced_roles'] = []; |
| 975 | $output['enforced_users'] = []; |
| 976 | $output['enforced_roles'] = []; |
| 977 | $output['superadmins-role-add'] = 'no'; |
| 978 | } |
| 979 | |
| 980 | $output['enforcement-policy'] = sanitize_text_field( $input['enforcement-policy'] ); |
| 981 | |
| 982 | if ( 'certain-roles-only' === $input['enforcement-policy'] && empty( $input['enforced_roles'] ) && empty( $input['enforced_users'] ) ) { |
| 983 | add_settings_error( |
| 984 | WP_2FA_SETTINGS_NAME, |
| 985 | esc_attr( 'enforced_roles_settings_error' ), |
| 986 | esc_html__( 'You must specify at least one role or user', 'wp-2fa' ), |
| 987 | 'error' |
| 988 | ); |
| 989 | } |
| 990 | |
| 991 | // If any users are being exluded, delete any wp 2fa data. |
| 992 | if ( isset( $output['excluded_users'] ) && |
| 993 | !empty( array_diff(WP2FA::get_wp2fa_setting( 'excluded_users' ), $output['excluded_users']) ) ) { |
| 994 | // Wipe user 2fa data. |
| 995 | $user_array = $output['excluded_users']; |
| 996 | foreach ( $user_array as $user ) { |
| 997 | if ( ! empty( $user ) ) { |
| 998 | $user_to_wipe = get_user_by( 'login', $user ); |
| 999 | global $wpdb; |
| 1000 | $wpdb->query( |
| 1001 | $wpdb->prepare( |
| 1002 | " |
| 1003 | DELETE FROM $wpdb->usermeta |
| 1004 | WHERE user_id = %d |
| 1005 | AND meta_key LIKE %s |
| 1006 | ", |
| 1007 | [ |
| 1008 | $user_to_wipe->ID, |
| 1009 | 'wp_2fa_%' |
| 1010 | ] |
| 1011 | ) |
| 1012 | ); |
| 1013 | |
| 1014 | } |
| 1015 | } |
| 1016 | } |
| 1017 | } |
| 1018 | |
| 1019 | // Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989. |
| 1020 | global $wp_settings_errors; |
| 1021 | if ( isset( $wp_settings_errors ) ) { |
| 1022 | $errors = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) ); |
| 1023 | $wp_settings_errors = $errors; |
| 1024 | } |
| 1025 | |
| 1026 | // Create a hash for comparison when we interact with a use. |
| 1027 | $settings_hash = SettingsUtils::create_settings_hash( $output ); |
| 1028 | $update_options = SettingsUtils::update_option( WP_2FA_PREFIX . 'settings_hash', $settings_hash ); |
| 1029 | |
| 1030 | $log_content = __( 'Settings saving processes complete', 'wp-2fa' ); |
| 1031 | Debugging::log( $log_content ); |
| 1032 | |
| 1033 | // We have overwridden any defaults by now so can clear this. |
| 1034 | SettingsUtils::delete_option( WP_2FA_PREFIX . 'default_settings_applied' ); |
| 1035 | |
| 1036 | return $output; |
| 1037 | } |
| 1038 | |
| 1039 | /** |
| 1040 | * Hide settings menu item |
| 1041 | */ |
| 1042 | public function hide_settings() { |
| 1043 | $user = wp_get_current_user(); |
| 1044 | |
| 1045 | // Check we have a user before doing anything else. |
| 1046 | if ( is_a( $user, '\WP_User' ) ) { |
| 1047 | $user_id = (int) $user->ID; |
| 1048 | if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) { |
| 1049 | $main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ); |
| 1050 | } else { |
| 1051 | $main_user = ''; |
| 1052 | } |
| 1053 | if ( ! empty( WP2FA::get_wp2fa_setting( 'limit_access' ) ) && $user->ID !== $main_user ) { |
| 1054 | // Remove admin menu item. |
| 1055 | remove_submenu_page( 'options-general.php', 'wp-2fa-settings' ); |
| 1056 | } |
| 1057 | } |
| 1058 | } |
| 1059 | |
| 1060 | /** |
| 1061 | * Add unlock user link to user actions. |
| 1062 | * |
| 1063 | * @param array $links Default row content. |
| 1064 | * |
| 1065 | * @return array |
| 1066 | */ |
| 1067 | public function add_plugin_action_links( $links ) { |
| 1068 | $url = network_admin_url( '/admin.php?page=wp-2fa-settings' ); |
| 1069 | |
| 1070 | $url = Settings::getSettingsPageLink(); |
| 1071 | |
| 1072 | $links = array_merge( |
| 1073 | array( |
| 1074 | '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Configure 2FA Settings', 'wp-2fa' ) . '</a>', |
| 1075 | ), |
| 1076 | $links |
| 1077 | ); |
| 1078 | |
| 1079 | return $links; |
| 1080 | |
| 1081 | } |
| 1082 | |
| 1083 | /** |
| 1084 | * User unlocked notice. |
| 1085 | */ |
| 1086 | public function user_unlocked_notice() { |
| 1087 | ?> |
| 1088 | <div class="notice notice-success is-dismissible"> |
| 1089 | <p><?php esc_html_e( 'User account successfully unlocked. User can login again.', 'wp-2fa' ); ?></p> |
| 1090 | <button type="button" class="notice-dismiss"> |
| 1091 | <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 1092 | </button> |
| 1093 | </div> |
| 1094 | <?php |
| 1095 | } |
| 1096 | |
| 1097 | /** |
| 1098 | * User deleted 2FA settings notification |
| 1099 | */ |
| 1100 | public function user_deleted_2fa_notice() { |
| 1101 | ?> |
| 1102 | <div class="notice notice-success is-dismissible"> |
| 1103 | <p><?php esc_html_e( 'Your 2FA settings have been removed.', 'wp-2fa' ); ?></p> |
| 1104 | <button type="button" class="notice-dismiss"> |
| 1105 | <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 1106 | </button> |
| 1107 | </div> |
| 1108 | <?php |
| 1109 | } |
| 1110 | |
| 1111 | /** |
| 1112 | * Admin deleted user 2FA settings notification |
| 1113 | */ |
| 1114 | public function admin_deleted_2fa_notice() { |
| 1115 | ?> |
| 1116 | <div class="notice notice-success is-dismissible"> |
| 1117 | <p><?php esc_html_e( 'User 2FA settings have been removed.', 'wp-2fa' ); ?></p> |
| 1118 | <button type="button" class="notice-dismiss"> |
| 1119 | <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 1120 | </button> |
| 1121 | </div> |
| 1122 | <?php |
| 1123 | } |
| 1124 | |
| 1125 | public function update_wp2fa_network_options() { |
| 1126 | check_admin_referer( 'wp_2fa_settings-options' ); |
| 1127 | |
| 1128 | if ( isset( $_POST[ WP_2FA_SETTINGS_NAME ] ) ) { |
| 1129 | $options = $this->validate_and_sanitize( wp_unslash( $_POST[ WP_2FA_SETTINGS_NAME ] ) ); |
| 1130 | $settings_errors = get_settings_errors( WP_2FA_SETTINGS_NAME ); |
| 1131 | if ( ! empty( $settings_errors ) ) { |
| 1132 | |
| 1133 | // redirect back to our options page. |
| 1134 | wp_safe_redirect( |
| 1135 | add_query_arg( |
| 1136 | array( |
| 1137 | 'page' => 'wp-2fa-settings', |
| 1138 | 'wp_2fa_network_settings_error' => urlencode_deep( $settings_errors[ 0 ]['message'] ), |
| 1139 | ), |
| 1140 | network_admin_url( 'settings.php' ) |
| 1141 | ) |
| 1142 | ); |
| 1143 | exit; |
| 1144 | |
| 1145 | } |
| 1146 | $update_options = SettingsUtils::update_option( WP_2FA_SETTINGS_NAME, $options ); |
| 1147 | } |
| 1148 | |
| 1149 | // redirect back to our options page. |
| 1150 | wp_safe_redirect( |
| 1151 | add_query_arg( |
| 1152 | array( |
| 1153 | 'page' => 'wp-2fa-settings', |
| 1154 | 'wp_2fa_network_settings_updated' => 'true', |
| 1155 | ), |
| 1156 | network_admin_url( 'admin.php' ) |
| 1157 | ) |
| 1158 | ); |
| 1159 | exit; |
| 1160 | } |
| 1161 | |
| 1162 | /** |
| 1163 | * Handle saving email options to the network main site options. |
| 1164 | */ |
| 1165 | public function update_wp2fa_network_email_options() { |
| 1166 | if ( isset( $_POST['email_from_setting'] ) ) { |
| 1167 | $options = $this->validate_and_sanitize_email( wp_unslash( $_POST ) ); |
| 1168 | |
| 1169 | if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_display_name'] ) && empty( $_POST['custom_from_display_name'] ) || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_email_address'] ) && empty( $_POST['custom_from_email_address'] ) ) { |
| 1170 | // redirect back to our options page. |
| 1171 | wp_safe_redirect( |
| 1172 | add_query_arg( |
| 1173 | array( |
| 1174 | 'page' => 'wp-2fa-settings', |
| 1175 | 'wp_2fa_network_settings_updated' => 'false', |
| 1176 | 'tab' => 'email-settings', |
| 1177 | ), |
| 1178 | network_admin_url( 'admin.php' ) |
| 1179 | ) |
| 1180 | ); |
| 1181 | exit; |
| 1182 | } |
| 1183 | |
| 1184 | $update_options = SettingsUtils::update_option( WP_2FA_EMAIL_SETTINGS_NAME, $options ); |
| 1185 | } |
| 1186 | |
| 1187 | // redirect back to our options page. |
| 1188 | wp_safe_redirect( |
| 1189 | add_query_arg( |
| 1190 | array( |
| 1191 | 'page' => 'wp-2fa-settings', |
| 1192 | 'wp_2fa_network_settings_updated' => 'true', |
| 1193 | 'tab' => 'email-settings', |
| 1194 | ), |
| 1195 | network_admin_url( 'admin.php' ) |
| 1196 | ) |
| 1197 | ); |
| 1198 | exit; |
| 1199 | } |
| 1200 | |
| 1201 | /** |
| 1202 | * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed. |
| 1203 | */ |
| 1204 | public function settings_saved_network_admin_notice() { |
| 1205 | if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && $_GET['wp_2fa_network_settings_updated'] == 'true' ) : |
| 1206 | ?> |
| 1207 | <div class="notice notice-success is-dismissible"> |
| 1208 | <p><?php esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p> |
| 1209 | <button type="button" class="notice-dismiss"> |
| 1210 | <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 1211 | </button> |
| 1212 | </div> |
| 1213 | <?php |
| 1214 | endif; |
| 1215 | if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && $_GET['wp_2fa_network_settings_updated'] == 'false' ) : |
| 1216 | ?> |
| 1217 | <div class="notice notice-error is-dismissible"> |
| 1218 | <p><?php esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p> |
| 1219 | <button type="button" class="notice-dismiss"> |
| 1220 | <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 1221 | </button> |
| 1222 | </div> |
| 1223 | <?php |
| 1224 | endif; |
| 1225 | if ( isset( $_GET['wp_2fa_network_settings_error'] ) ) : |
| 1226 | ?> |
| 1227 | <div class="notice notice-error is-dismissible"> |
| 1228 | <p><?php echo urldecode_deep( $_GET['wp_2fa_network_settings_error'] ); ?></p> |
| 1229 | <button type="button" class="notice-dismiss"> |
| 1230 | <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 1231 | </button> |
| 1232 | </div> |
| 1233 | <?php |
| 1234 | endif; |
| 1235 | } |
| 1236 | |
| 1237 | /** |
| 1238 | * Email settings |
| 1239 | */ |
| 1240 | private function email_from_settings() { |
| 1241 | ?> |
| 1242 | <h3><?php esc_html_e( 'Which email address should the plugin use as a from address?', 'wp-2fa' ); ?></h3> |
| 1243 | <p class="description"> |
| 1244 | <?php esc_html_e( 'Use these settings to customize the "from" name and email address for all correspondence sent from our plugin.', 'wp-2fa' ); ?> |
| 1245 | </p> |
| 1246 | <table class="form-table"> |
| 1247 | <tbody> |
| 1248 | <tr> |
| 1249 | <th><label for="2fa-method"><?php esc_html_e( 'From email & name', 'wp-2fa' ); ?></label> |
| 1250 | </th> |
| 1251 | <td> |
| 1252 | <fieldset class="contains-hidden-inputs"> |
| 1253 | <label for="use-defaults"> |
| 1254 | <input type="radio" name="email_from_setting" id="use-defaults" value="use-defaults" |
| 1255 | <?php checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-defaults' ); ?> |
| 1256 | > |
| 1257 | <span><?php esc_html_e( 'Use the email address from the WordPress general settings.', 'wp-2fa' ); ?></span> |
| 1258 | </label> |
| 1259 | |
| 1260 | <br/> |
| 1261 | <label for="use-custom-email"> |
| 1262 | <input type="radio" name="email_from_setting" id="use-custom-email" value="use-custom-email" |
| 1263 | <?php checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-custom-email' ); ?> |
| 1264 | data-unhide-when-checked=".custom-from-inputs"> |
| 1265 | <span><?php esc_html_e( 'Use another email address', 'wp-2fa' ); ?></span> |
| 1266 | </label> |
| 1267 | <fieldset class="hidden custom-from-inputs"> |
| 1268 | <br/> |
| 1269 | <span><?php esc_html_e( 'Email Address:', 'wp-2fa' ); ?></span> <input type="text" id="custom_from_email_address" name="custom_from_email_address" value="<?php echo WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ); ?>"><br><br> |
| 1270 | <span><?php esc_html_e( 'Display Name:', 'wp-2fa' ); ?></span> <input type="text" id="custom_from_display_name" name="custom_from_display_name" value="<?php echo WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ); ?>"> |
| 1271 | </fieldset> |
| 1272 | |
| 1273 | </fieldset> |
| 1274 | </td> |
| 1275 | </tr> |
| 1276 | </tbody> |
| 1277 | </table> |
| 1278 | |
| 1279 | <br> |
| 1280 | <hr> |
| 1281 | |
| 1282 | <h3><?php esc_html_e( 'Email delivery test', 'wp-2fa' ); ?></h3> |
| 1283 | <p class="description"> |
| 1284 | <?php esc_html_e( 'The plugin sends emails with one-time codes, blocked account notifications and more. Use the button below to confirm the plugin can successfully send emails.', 'wp-2fa' ); ?> |
| 1285 | </p> |
| 1286 | <p> |
| 1287 | <button type="button" name="test_email_config_test" |
| 1288 | class="button js-button-test-email-trigger" |
| 1289 | data-email-id="config_test" |
| 1290 | data-nonce="<?php echo esc_attr( wp_create_nonce( 'wp-2fa-email-test-config_test' ) ); ?>"> |
| 1291 | <?php esc_html_e( 'Test email delivery', 'wp-2fa' ); ?> |
| 1292 | </button> |
| 1293 | </p> |
| 1294 | |
| 1295 | <br> |
| 1296 | <hr> |
| 1297 | |
| 1298 | <?php |
| 1299 | } |
| 1300 | |
| 1301 | /** |
| 1302 | * Creates the email notification definitions. |
| 1303 | * |
| 1304 | * @return EmailTemplate[] |
| 1305 | */ |
| 1306 | public function get_email_notification_definitions( ) { |
| 1307 | $result = [ |
| 1308 | new EmailTemplate( |
| 1309 | 'login_code', |
| 1310 | esc_html__( 'Login code email', 'wp-2fa' ), |
| 1311 | esc_html__( 'This is the email sent to a user when a login code is required.', 'wp-2fa' ) |
| 1312 | ), |
| 1313 | new EmailTemplate( |
| 1314 | 'account_locked', |
| 1315 | esc_html__( 'User account locked email', 'wp-2fa' ), |
| 1316 | esc_html__( 'This is the email sent to a user upon grace period expiry.', 'wp-2fa' ) |
| 1317 | ), |
| 1318 | new EmailTemplate( |
| 1319 | 'account_unlocked', |
| 1320 | esc_html__( 'User account unlocked email', 'wp-2fa' ), |
| 1321 | esc_html__( 'This is the email sent to a user when the user\'s account has been unlocked.', 'wp-2fa' ) |
| 1322 | ) |
| 1323 | ]; |
| 1324 | |
| 1325 | $result[0]->setCanBeToggled(false); |
| 1326 | $result[1]->setEmailContentId('user_account_locked'); |
| 1327 | $result[2]->setEmailContentId('user_account_unlocked'); |
| 1328 | return $result; |
| 1329 | } |
| 1330 | /** |
| 1331 | * Email settings |
| 1332 | */ |
| 1333 | private function email_settings() { |
| 1334 | $custom_user_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' ); |
| 1335 | $email_template_definitions = $this->get_email_notification_definitions(); |
| 1336 | ?> |
| 1337 | <h1><?php esc_html_e( 'Email Templates', 'wp-2fa' ); ?></h1> |
| 1338 | <?php foreach ($email_template_definitions as $email_template) : ?> |
| 1339 | <?php $template_id = $email_template->getId(); ?> |
| 1340 | <h3><?php echo $email_template->getTitle(); ?></h3> |
| 1341 | <p class="description"><?php echo $email_template->getDescription(); ?></p> |
| 1342 | <table class="form-table"> |
| 1343 | <tbody> |
| 1344 | <?php if ($email_template->canBeToggled()): ?> |
| 1345 | <tr> |
| 1346 | <th><label for="send_<?php echo $template_id; ?>_email"><?php esc_html_e( 'Send this email', 'wp-2fa' ); ?></label></th> |
| 1347 | <td> |
| 1348 | <fieldset> |
| 1349 | <input type="checkbox" id="send_<?php echo $template_id; ?>_email" name="send_<?php echo $template_id; ?>_email" value="enable_<?php echo $template_id; ?>_email" |
| 1350 | <?php checked( 'enable_' . $template_id . '_email', WP2FA::get_wp2fa_email_templates( 'send_' . $template_id . '_email' )); ?> |
| 1351 | > |
| 1352 | <label for="send_<?php echo $template_id; ?>_email"><?php esc_html_e( 'Uncheck to disable this message.', 'wp-2fa' ); ?></label> |
| 1353 | </fieldset> |
| 1354 | </td> |
| 1355 | </tr> |
| 1356 | <?php endif; ?> |
| 1357 | <?php $template_id = $email_template->getEmailContentId(); ?> |
| 1358 | <tr> |
| 1359 | <th><label for="<?php echo $template_id; ?>_email_subject"><?php esc_html_e( 'Email subject', 'wp-2fa' ); ?></label></th> |
| 1360 | <td> |
| 1361 | <fieldset> |
| 1362 | <input type="text" id="<?php echo $template_id; ?>_email_subject" name="<?php echo $template_id; ?>_email_subject" class="large-text" value="<?php esc_html_e( WP2FA::get_wp2fa_email_templates( $template_id . '_email_subject' ) ); ?>"> |
| 1363 | </fieldset> |
| 1364 | </td> |
| 1365 | </tr> |
| 1366 | <tr> |
| 1367 | <th> |
| 1368 | <label for="<?php echo $template_id; ?>_email_body"><?php esc_html_e( 'Email body', 'wp-2fa' ); ?></label> |
| 1369 | </br> |
| 1370 | <label for="<?php echo $template_id; ?>_email_tags" style="font-weight: 400;"><?php esc_html_e( 'Available template tags:', 'wp-2fa' ); ?></label> |
| 1371 | </br> |
| 1372 | </br> |
| 1373 | <span style="font-weight: 400;"> |
| 1374 | {site_url}</br> |
| 1375 | {site_name}</br> |
| 1376 | {grace_period}</br> |
| 1377 | {user_login_name}</br> |
| 1378 | {user_first_name}</br> |
| 1379 | {user_last_name}</br> |
| 1380 | {user_display_name}</br> |
| 1381 | {login_code} |
| 1382 | <?php |
| 1383 | if ( ! empty( $custom_user_page_id ) ) { |
| 1384 | echo '</br>{2fa_settings_page_url}'; |
| 1385 | } |
| 1386 | ?> |
| 1387 | </span> |
| 1388 | </th> |
| 1389 | <td> |
| 1390 | <fieldset> |
| 1391 | <?php |
| 1392 | $message = WP2FA::get_wp2fa_email_templates( $template_id . '_email_body' ); |
| 1393 | $content = $message; |
| 1394 | $editor_id = $template_id . '_email_body'; |
| 1395 | $settings = array( |
| 1396 | 'media_buttons' => false, |
| 1397 | 'editor_height' => 200, |
| 1398 | ); |
| 1399 | wp_editor( $content, $editor_id, $settings ); |
| 1400 | ?> |
| 1401 | </fieldset> |
| 1402 | <p> |
| 1403 | <button type="button" name="test_email_<?php echo esc_attr( $template_id ); ?>" |
| 1404 | class="button js-button-test-email-trigger" |
| 1405 | data-email-id="<?php echo esc_attr( $template_id ); ?>" |
| 1406 | data-nonce="<?php echo esc_attr( wp_create_nonce( 'wp-2fa-email-test-' . $template_id ) ); ?>"> |
| 1407 | <?php esc_html_e( 'Send test email', 'wp-2fa' ); ?> |
| 1408 | </button> |
| 1409 | </p> |
| 1410 | </td> |
| 1411 | </tr> |
| 1412 | </tbody> |
| 1413 | </table> |
| 1414 | |
| 1415 | <br> |
| 1416 | <hr> |
| 1417 | <?php endforeach; ?> |
| 1418 | <?php |
| 1419 | } |
| 1420 | |
| 1421 | /** |
| 1422 | * Validate email templates before saving |
| 1423 | * |
| 1424 | * @param array $input The settings array. |
| 1425 | */ |
| 1426 | public function validate_and_sanitize_email( $input ) { |
| 1427 | |
| 1428 | // Bail if user doesnt have permissions to be here. |
| 1429 | if ( ! current_user_can( 'manage_options' ) ) { |
| 1430 | return; |
| 1431 | } |
| 1432 | |
| 1433 | if ( empty( $_POST ) || ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX .'email_settings-options' ) && ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX .'settings-options' ) || ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'email_settings-options' ) && ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'settings-options' ) ) { |
| 1434 | die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 1435 | } |
| 1436 | |
| 1437 | if ( isset( $_POST['email_from_setting'] ) && 'use-defaults' === $_POST['email_from_setting'] || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] ) { |
| 1438 | $output['email_from_setting'] = sanitize_text_field( wp_unslash( $_POST['email_from_setting'] ) ); |
| 1439 | } |
| 1440 | |
| 1441 | if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_email_address'] ) && empty( $_POST['custom_from_email_address'] ) ) { |
| 1442 | add_settings_error( |
| 1443 | WP_2FA_SETTINGS_NAME, |
| 1444 | esc_attr( 'email_from_settings_error' ), |
| 1445 | esc_html__( 'Please provide an email address', 'wp-2fa' ), |
| 1446 | 'error' |
| 1447 | ); |
| 1448 | $output['custom_from_email_address'] = ''; |
| 1449 | } |
| 1450 | |
| 1451 | if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_display_name'] ) && empty( $_POST['custom_from_display_name'] ) ) { |
| 1452 | add_settings_error( |
| 1453 | WP_2FA_SETTINGS_NAME, |
| 1454 | esc_attr( 'display_name_settings_error' ), |
| 1455 | esc_html__( 'Please provide a display name.', 'wp-2fa' ), |
| 1456 | 'error' |
| 1457 | ); |
| 1458 | $output['custom_from_email_address'] = ''; |
| 1459 | } |
| 1460 | |
| 1461 | if ( isset( $_POST['custom_from_email_address'] ) && ! empty( $_POST['custom_from_email_address'] ) ) { |
| 1462 | if ( ! filter_var( $_POST['custom_from_email_address'], FILTER_VALIDATE_EMAIL ) ) { |
| 1463 | add_settings_error( |
| 1464 | WP_2FA_SETTINGS_NAME, |
| 1465 | esc_attr( 'email_invalid_settings_error' ), |
| 1466 | esc_html__( 'Please provide a valid email address. Your email address has not been updated.', 'wp-2fa' ), |
| 1467 | 'error' |
| 1468 | ); |
| 1469 | } |
| 1470 | $output['custom_from_email_address'] = sanitize_email( wp_unslash( $_POST['custom_from_email_address'] ) ); |
| 1471 | } |
| 1472 | |
| 1473 | if ( isset( $_POST['custom_from_display_name'] ) && ! empty( $_POST['custom_from_display_name'] ) ) { |
| 1474 | // Check if the string contains HTML/tags. |
| 1475 | preg_match( "/<\/?\w+((\s+\w+(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/", $_POST['custom_from_display_name'], $matches ); |
| 1476 | if ( count( $matches ) > 0 ) { |
| 1477 | add_settings_error( |
| 1478 | WP_2FA_SETTINGS_NAME, |
| 1479 | esc_attr( 'display_name_invalid_settings_error' ), |
| 1480 | esc_html__( 'Please only use alphanumeric text. Your display name has not been updated.', 'wp-2fa' ), |
| 1481 | 'error' |
| 1482 | ); |
| 1483 | } else { |
| 1484 | $output['custom_from_display_name'] = sanitize_text_field( wp_unslash( $_POST['custom_from_display_name'] ) ); |
| 1485 | } |
| 1486 | } |
| 1487 | |
| 1488 | if ( isset( $_POST['login_code_email_subject'] ) ) { |
| 1489 | $output['login_code_email_subject'] = wp_kses_post( wp_unslash( $_POST['login_code_email_subject'] ) ); |
| 1490 | } |
| 1491 | |
| 1492 | if ( isset( $_POST['login_code_email_body'] ) ) { |
| 1493 | $output['login_code_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['login_code_email_body'] ) ) ); |
| 1494 | } |
| 1495 | |
| 1496 | if ( isset( $_POST['user_account_locked_email_subject'] ) ) { |
| 1497 | $output['user_account_locked_email_subject'] = wp_kses_post( wp_unslash( $_POST['user_account_locked_email_subject'] ) ); |
| 1498 | } |
| 1499 | |
| 1500 | if ( isset( $_POST['user_account_locked_email_body'] ) ) { |
| 1501 | $output['user_account_locked_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['user_account_locked_email_body'] ) ) ); |
| 1502 | } |
| 1503 | |
| 1504 | if ( isset( $_POST['user_account_unlocked_email_subject'] ) ) { |
| 1505 | $output['user_account_unlocked_email_subject'] = wp_kses_post( wp_unslash( $_POST['user_account_unlocked_email_subject'] ) ); |
| 1506 | } |
| 1507 | |
| 1508 | if ( isset( $_POST['user_account_unlocked_email_body'] ) ) { |
| 1509 | $output['user_account_unlocked_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['user_account_unlocked_email_body'] ) ) ); |
| 1510 | } |
| 1511 | |
| 1512 | if ( isset( $_POST['send_account_locked_email'] ) && 'enable_account_locked_email' === $_POST['send_account_locked_email'] ) { |
| 1513 | $output['send_account_locked_email'] = sanitize_text_field( $_POST['send_account_locked_email'] ); |
| 1514 | } |
| 1515 | |
| 1516 | if ( isset( $_POST['send_account_unlocked_email'] ) && 'enable_account_unlocked_email' === $_POST['send_account_unlocked_email'] ) { |
| 1517 | $output['send_account_unlocked_email'] = sanitize_text_field( $_POST['send_account_unlocked_email'] ); |
| 1518 | } |
| 1519 | |
| 1520 | // Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989. |
| 1521 | global $wp_settings_errors; |
| 1522 | if ( isset( $wp_settings_errors ) ) { |
| 1523 | $errors = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) ); |
| 1524 | $wp_settings_errors = $errors; |
| 1525 | } |
| 1526 | |
| 1527 | if ( isset( $output ) ) { |
| 1528 | return $output; |
| 1529 | } else { |
| 1530 | return; |
| 1531 | } |
| 1532 | |
| 1533 | } |
| 1534 | |
| 1535 | /** |
| 1536 | * Creates a new page with our shortcode present. |
| 1537 | */ |
| 1538 | public function generate_custom_user_profile_page( $page_slug ) { |
| 1539 | // Bail if user doesnt have permissions to be here. |
| 1540 | if ( ! current_user_can( 'manage_options' ) ) { |
| 1541 | return; |
| 1542 | } |
| 1543 | |
| 1544 | // Check if a page with slug exists. |
| 1545 | $page_exists = $this->get_post_by_post_name( $page_slug, 'page' ); |
| 1546 | if ( $page_exists ) { |
| 1547 | // Seeing as the page exisits, return its ID. |
| 1548 | return $page_exists->ID; |
| 1549 | } |
| 1550 | |
| 1551 | $generated_by_message = '<p>'.esc_html__( 'Page generated by', 'wp-2fa' ); |
| 1552 | $generated_by_message .= ' <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">'.esc_html__( 'WP 2FA Plugin', 'wp-2fa' ).'</a>'; |
| 1553 | $generated_by_message .= '</p>'; |
| 1554 | |
| 1555 | $user = wp_get_current_user(); |
| 1556 | $post_data = array( |
| 1557 | 'post_title' => 'WP 2FA User Profile', |
| 1558 | 'post_name' => $page_slug, |
| 1559 | 'post_content' => '[wp-2fa-setup-form] ' . $generated_by_message, |
| 1560 | 'post_status' => 'publish', |
| 1561 | 'post_author' => $user->ID, |
| 1562 | 'post_type' => 'page', |
| 1563 | ); |
| 1564 | |
| 1565 | // Lets insert the post now. |
| 1566 | $result = wp_insert_post( $post_data ); |
| 1567 | |
| 1568 | if ( $result && ! is_wp_error( $result ) ) { |
| 1569 | $post_id = $result; |
| 1570 | set_transient( WP_2FA_PREFIX . 'new_custom_page_created', true, 60 ); |
| 1571 | set_site_transient( WP_2FA_PREFIX .'new_custom_page_created', true, 60 ); |
| 1572 | return $post_id; |
| 1573 | } |
| 1574 | } |
| 1575 | |
| 1576 | /** |
| 1577 | * Check if page with slug exisits. |
| 1578 | */ |
| 1579 | public function get_post_by_post_name( $slug = '', $post_type = '' ) { |
| 1580 | if ( ! $slug || ! $post_type ) { |
| 1581 | return false; |
| 1582 | } |
| 1583 | |
| 1584 | $post_object = get_page_by_path( $slug, OBJECT, $post_type ); |
| 1585 | |
| 1586 | if ( ! $post_object ) { |
| 1587 | return false; |
| 1588 | } |
| 1589 | |
| 1590 | return $post_object; |
| 1591 | } |
| 1592 | |
| 1593 | /** |
| 1594 | * Add our custom state to our created page. |
| 1595 | */ |
| 1596 | public function add_display_post_states( $post_states, $post ) { |
| 1597 | if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) { |
| 1598 | if ( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) === $post->ID ) { |
| 1599 | $post_states['wp_2fa_page_for_user'] = __( 'WP 2FA User Page', 'wp-2fa' ); |
| 1600 | } |
| 1601 | } |
| 1602 | |
| 1603 | return $post_states; |
| 1604 | } |
| 1605 | |
| 1606 | /** |
| 1607 | * Handles sending of an email. It sets necessary header such as content type and custom from email address and name. |
| 1608 | * |
| 1609 | * @param string $recipient_email Email address to send message to. |
| 1610 | * @param string $subject Email subject. |
| 1611 | * @param string $message Message contents. |
| 1612 | * |
| 1613 | * @return bool Whether the email contents were sent successfully. |
| 1614 | */ |
| 1615 | public static function send_email( $recipient_email, $subject, $message ) { |
| 1616 | |
| 1617 | // Specify our desired headers. |
| 1618 | $headers = 'Content-type: text/html;charset=utf-8' . "\r\n"; |
| 1619 | |
| 1620 | if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) { |
| 1621 | $headers .= 'From: ' . WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) . ' <' . WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) . '>' . "\r\n"; |
| 1622 | } else { |
| 1623 | $headers .= 'From: ' . get_bloginfo( 'name' ) . ' <' . get_bloginfo('admin_email') . '>' . "\r\n"; |
| 1624 | } |
| 1625 | |
| 1626 | // Fire our email. |
| 1627 | return wp_mail( $recipient_email, $subject, $message, $headers ); |
| 1628 | |
| 1629 | } |
| 1630 | |
| 1631 | /** |
| 1632 | * Turns user roles data in any form and shape to an array of strings. |
| 1633 | * |
| 1634 | * @param mixed $value User role names (slugs) as raw value. |
| 1635 | * |
| 1636 | * @return string[] List of user role names (slugs). |
| 1637 | */ |
| 1638 | public static function extract_roles_from_input( $value ) { |
| 1639 | if ( is_array( $value ) ) { |
| 1640 | return $value; |
| 1641 | } |
| 1642 | |
| 1643 | if ( is_string( $value ) && ! empty( $value ) ) { |
| 1644 | return explode( ',', $value ); |
| 1645 | } |
| 1646 | |
| 1647 | return []; |
| 1648 | } |
| 1649 | |
| 1650 | /** |
| 1651 | * Determine if any BG processes are currently running. |
| 1652 | * |
| 1653 | * @return int|false Number of jobs. |
| 1654 | */ |
| 1655 | public function get_current_number_of_active_bg_processes() { |
| 1656 | global $wpdb; |
| 1657 | |
| 1658 | $bg_jobs = $wpdb->get_results( |
| 1659 | "SELECT option_value FROM $wpdb->options |
| 1660 | WHERE option_name LIKE '%_2fa_bg_%'" |
| 1661 | ); |
| 1662 | |
| 1663 | return count( $bg_jobs ); |
| 1664 | } |
| 1665 | |
| 1666 | /** |
| 1667 | * Cancel BG processes. |
| 1668 | * |
| 1669 | */ |
| 1670 | public function cancel_bg_processes() { |
| 1671 | global $wpdb; |
| 1672 | $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '%_2fa_bg_%'"); |
| 1673 | |
| 1674 | $cron_hook_identifiers = [ '2fa_check_grace_period_status', '2fa_bg_update_user_meta', '2fa_bg_wipe_all_user_data', '2fa_bg_remove_enabled_methods' ]; |
| 1675 | |
| 1676 | foreach ( $cron_hook_identifiers as $cron_hook_identifier ) { |
| 1677 | $cleared_jobs = wp_clear_scheduled_hook( $wpdb->prefix.$cron_hook_identifier ); |
| 1678 | } |
| 1679 | |
| 1680 | wp_send_json_success( $cleared_jobs ); |
| 1681 | } |
| 1682 | |
| 1683 | /** |
| 1684 | * Checks if the backup codes option is globally enabled |
| 1685 | * |
| 1686 | * @return bool |
| 1687 | */ |
| 1688 | public static function are_backup_codes_enabled() { |
| 1689 | |
| 1690 | if ( null === self::$backupCodesEnabled ) { |
| 1691 | self::$backupCodesEnabled = false; |
| 1692 | |
| 1693 | if ( 'yes' === WP2FA::get_wp2fa_setting( 'backup_codes_enabled' ) ) { |
| 1694 | self::$backupCodesEnabled = true; |
| 1695 | } |
| 1696 | } |
| 1697 | |
| 1698 | return self::$backupCodesEnabled; |
| 1699 | } |
| 1700 | } |
| 1701 |