class-ajax-helper.php
11 months ago
class-classes-helper.php
11 months ago
class-file-writer.php
11 months ago
class-methods-helper.php
11 months ago
class-php-helper.php
11 months ago
class-user-helper.php
11 months ago
class-wp-helper.php
11 months ago
index.php
11 months ago
class-ajax-helper.php
424 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Responsible for the AJAX calls. |
| 4 | * |
| 5 | * @package wp2fa |
| 6 | * @subpackage helpers |
| 7 | * |
| 8 | * @since 2.6.0 |
| 9 | * |
| 10 | * @copyright 2025 Melapress |
| 11 | * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 |
| 12 | * |
| 13 | * @see https://wordpress.org/plugins/wp-2fa/ |
| 14 | */ |
| 15 | |
| 16 | declare(strict_types=1); |
| 17 | |
| 18 | namespace WP2FA\Admin\Helpers; |
| 19 | |
| 20 | use WP2FA\WP2FA; |
| 21 | use WP2FA\Utils\User_Utils; |
| 22 | use WP2FA\Admin\Settings_Page; |
| 23 | use WP2FA\Utils\Settings_Utils; |
| 24 | use WP2FA\Admin\Helpers\WP_Helper; |
| 25 | use WP2FA\Admin\SettingsPages\Settings_Page_Email; |
| 26 | |
| 27 | // Exit if accessed directly. |
| 28 | if ( ! defined( 'ABSPATH' ) ) { |
| 29 | exit; |
| 30 | } |
| 31 | |
| 32 | if ( ! class_exists( '\WP2FA\Admin\Helpers\Ajax_Helper' ) ) { |
| 33 | /** |
| 34 | * Responsible for the proper AJAX calls and responses. |
| 35 | */ |
| 36 | class Ajax_Helper { |
| 37 | |
| 38 | /** |
| 39 | * Get all users in AJAX matter and returns them |
| 40 | * |
| 41 | * @since 2.6.0 |
| 42 | */ |
| 43 | public static function get_all_users() { |
| 44 | // Check user permissions. |
| 45 | if ( ! \current_user_can( 'manage_options' ) ) { |
| 46 | \wp_send_json_error( 'Access Denied.' ); |
| 47 | } |
| 48 | |
| 49 | // Verify nonce. |
| 50 | $nonce = isset( $_GET['wp_2fa_nonce'] ) ? \sanitize_text_field( \wp_unslash( $_GET['wp_2fa_nonce'] ) ) : null; |
| 51 | if ( null === $nonce || false === $nonce || ! \wp_verify_nonce( $nonce, 'wp-2fa-settings-nonce' ) ) { |
| 52 | \wp_send_json_error( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 53 | } |
| 54 | |
| 55 | $term = isset( $_GET['term'] ) ? \sanitize_text_field( \wp_unslash( $_GET['term'] ) ) : null; |
| 56 | |
| 57 | if ( null === $term || false === $term ) { |
| 58 | \wp_send_json_error( \esc_html__( 'Invalid term.', 'wp-2fa' ) ); |
| 59 | } |
| 60 | |
| 61 | $users_args = array( |
| 62 | 'fields' => array( 'ID', 'user_login' ), |
| 63 | ); |
| 64 | if ( WP_Helper::is_multisite() ) { |
| 65 | $users_args['blog_id'] = 0; |
| 66 | } |
| 67 | $users_data = User_Utils::get_all_user_ids_and_login_names( 'query', $users_args ); |
| 68 | |
| 69 | // Create final array which we will fill in below. |
| 70 | $users = array(); |
| 71 | |
| 72 | foreach ( $users_data as $user ) { |
| 73 | if ( stripos( $user['user_login'], $term ) !== false ) { |
| 74 | $users[] = array( |
| 75 | 'value' => $user['user_login'], |
| 76 | 'label' => $user['user_login'], |
| 77 | ); |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | echo wp_json_encode( $users ); |
| 82 | exit; |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * Get all network sites in AJAX way |
| 87 | * |
| 88 | * @since 2.6.0 |
| 89 | */ |
| 90 | public static function get_all_network_sites() { |
| 91 | // Check user permissions. |
| 92 | if ( ! \current_user_can( 'manage_options' ) ) { |
| 93 | \wp_send_json_error( 'Access Denied.' ); |
| 94 | } |
| 95 | |
| 96 | // Verify nonce. |
| 97 | $nonce = isset( $_GET['wp_2fa_nonce'] ) ? \sanitize_text_field( \wp_unslash( $_GET['wp_2fa_nonce'] ) ) : null; |
| 98 | if ( null === $nonce || false === $nonce || ! \wp_verify_nonce( $nonce, 'wp-2fa-settings-nonce' ) ) { |
| 99 | \wp_send_json_error( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 100 | } |
| 101 | |
| 102 | $term = isset( $_GET['term'] ) ? \sanitize_text_field( \wp_unslash( $_GET['term'] ) ) : null; |
| 103 | |
| 104 | if ( null === $term || false === $term ) { |
| 105 | \wp_send_json_error( \esc_html__( 'Invalid term.', 'wp-2fa' ) ); |
| 106 | } |
| 107 | // Fetch sites. |
| 108 | $sites_found = array(); |
| 109 | |
| 110 | foreach ( WP_Helper::get_multi_sites() as $site ) { |
| 111 | if ( false !== stripos( $site->blogname, $term ) ) { |
| 112 | $sites_found[] = array( |
| 113 | 'label' => $site->blog_id, |
| 114 | 'value' => $site->blogname, |
| 115 | ); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | echo \wp_json_encode( $sites_found ); |
| 120 | exit; |
| 121 | } |
| 122 | |
| 123 | /** |
| 124 | * Unlock users accounts if they have overrun grace period it is also used in AJAX calls |
| 125 | * |
| 126 | * @param int $user_id User ID. |
| 127 | * |
| 128 | * @since 2.6.0 |
| 129 | */ |
| 130 | public static function unlock_account( $user_id ) { |
| 131 | // Check user permissions. |
| 132 | if ( ! \current_user_can( 'manage_options' ) ) { |
| 133 | \wp_send_json_error( 'Access Denied.' ); |
| 134 | } |
| 135 | |
| 136 | $nonce = isset( $_GET['wp_2fa_nonce'] ) ? \sanitize_text_field( \wp_unslash( $_GET['wp_2fa_nonce'] ) ) : null; |
| 137 | if ( null === $nonce || false === $nonce || ! \wp_verify_nonce( $nonce, 'wp-2fa-unlock-account-nonce' ) ) { |
| 138 | \wp_send_json_error( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 139 | } |
| 140 | |
| 141 | $grace_period = Settings_Utils::get_setting_role( User_Helper::get_user_role( $user_id ), 'grace-period' ); |
| 142 | $grace_period_denominator = Settings_Utils::get_setting_role( User_Helper::get_user_role( $user_id ), 'grace-period-denominator' ); |
| 143 | $create_a_string = $grace_period . ' ' . $grace_period_denominator; |
| 144 | // Turn that string into a time. |
| 145 | $grace_expiry = strtotime( $create_a_string ); |
| 146 | |
| 147 | $user_id = isset( $_GET['user_id'] ) ? \intval( \sanitize_text_field( \wp_unslash( $_GET['user_id'] ) ) ) : null; |
| 148 | |
| 149 | if ( isset( $user_id ) ) { |
| 150 | |
| 151 | User_Helper::remove_meta( WP_2FA_PREFIX . 'locked_account_notification', $user_id ); |
| 152 | User_Helper::remove_grace_period( $user_id ); |
| 153 | User_Helper::remove_meta( User_Helper::USER_LOCKED_STATUS ); |
| 154 | |
| 155 | User_Helper::set_user_expiry_date( (string) $grace_expiry, $user_id ); |
| 156 | Settings_Page::send_account_unlocked_email( $user_id ); |
| 157 | |
| 158 | /* |
| 159 | * Fires after the user is unlocked. |
| 160 | * |
| 161 | * @param \WP_User $user - The user for which the method has been set. |
| 162 | * |
| 163 | * @since 2.6.0 |
| 164 | */ |
| 165 | \do_action( WP_2FA_PREFIX . 'user_is_unlocked', User_Helper::get_user( $user_id ) ); |
| 166 | |
| 167 | \add_action( 'admin_notices', array( __CLASS__, 'user_unlocked_notice' ) ); |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * Sets the salt key into the wp-config.php file via AJAX request. |
| 173 | * |
| 174 | * @return void |
| 175 | * |
| 176 | * @since 2.4.0 |
| 177 | */ |
| 178 | public static function set_salt_key() { |
| 179 | if ( \wp_doing_ajax() ) { |
| 180 | if ( isset( $_REQUEST['_wpnonce'] ) ) { |
| 181 | $nonce_check = \wp_verify_nonce( \sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'wp-2fa-set-salt-nonce' ); |
| 182 | if ( ! $nonce_check ) { |
| 183 | \wp_send_json_error( new \WP_Error( 500, esc_html__( 'Nonce checking failed', 'wp-2fa' ) ), 400 ); |
| 184 | } elseif ( \current_user_can( 'manage_options' ) ) { |
| 185 | if ( ! File_Writer::can_write_to_file( File_Writer::get_wp_config_file_path() ) ) { |
| 186 | \wp_send_json_error( |
| 187 | new \WP_Error( |
| 188 | 500, |
| 189 | \esc_html__( 'Unable to write to wp-config.php', 'wp-2fa' ) |
| 190 | ), |
| 191 | 400 |
| 192 | ); |
| 193 | } else { |
| 194 | $secret_key = Settings_Utils::get_option( 'secret_key' ); |
| 195 | if ( ! empty( $secret_key ) ) { |
| 196 | File_Writer::save_secret_key( $secret_key ); |
| 197 | Settings_Utils::delete_option( 'secret_key' ); |
| 198 | \wp_send_json_success( |
| 199 | \esc_html__( |
| 200 | 'wp-config.php successfully update, global setting deleted', |
| 201 | 'wp-2fa' |
| 202 | ) |
| 203 | ); |
| 204 | } else { |
| 205 | \wp_send_json_error( |
| 206 | new \WP_Error( |
| 207 | 500, |
| 208 | \esc_html__( 'Unable to find global secret key', 'wp-2fa' ) |
| 209 | ), |
| 210 | 400 |
| 211 | ); |
| 212 | } |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Removes the salt key unable to store in the wp-config file notification. |
| 221 | * |
| 222 | * @return void |
| 223 | * |
| 224 | * @since 3.0.0 |
| 225 | */ |
| 226 | public static function unset_salt_key(): void { |
| 227 | if ( \wp_doing_ajax() ) { |
| 228 | if ( isset( $_REQUEST['_wpnonce'] ) ) { |
| 229 | $nonce_check = \wp_verify_nonce( \sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'wp-2fa-unset-salt-nonce' ); |
| 230 | if ( ! $nonce_check ) { |
| 231 | \wp_send_json_error( new \WP_Error( 500, esc_html__( 'Nonce checking failed', 'wp-2fa' ) ), 400 ); |
| 232 | } elseif ( \current_user_can( 'manage_options' ) ) { |
| 233 | |
| 234 | Settings_Utils::update_option( 'remove_store_salt_in_wp_config_message', true ); |
| 235 | |
| 236 | \wp_send_json_success( |
| 237 | \esc_html__( |
| 238 | 'message is set for removal', |
| 239 | 'wp-2fa' |
| 240 | ) |
| 241 | ); |
| 242 | |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | /** |
| 249 | * Remove user 2fa config |
| 250 | * |
| 251 | * @param int $user_id User ID. |
| 252 | * |
| 253 | * @since 2.6.0 |
| 254 | */ |
| 255 | public static function remove_user_2fa( $user_id ) { |
| 256 | |
| 257 | // Verify nonce. |
| 258 | $nonce = isset( $_GET['wp_2fa_nonce'] ) ? \sanitize_text_field( \wp_unslash( $_GET['wp_2fa_nonce'] ) ) : null; |
| 259 | if ( null === $nonce || false === $nonce || ! \wp_verify_nonce( $nonce, 'wp-2fa-remove-user-2fa-nonce' ) ) { |
| 260 | \wp_send_json_error( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 261 | } |
| 262 | |
| 263 | $user_id = isset( $_GET['user_id'] ) ? \intval( \sanitize_text_field( \wp_unslash( $_GET['user_id'] ) ) ) : null; |
| 264 | |
| 265 | if ( isset( $user_id ) ) { |
| 266 | |
| 267 | if ( ! \current_user_can( 'manage_options' ) && \get_current_user_id() !== $user_id ) { |
| 268 | \wp_send_json_error( 'Access Denied.' ); |
| 269 | } |
| 270 | |
| 271 | User_Helper::remove_2fa_for_user( $user_id ); |
| 272 | |
| 273 | if ( isset( $get_array['admin_reset'] ) ) { |
| 274 | \add_action( 'admin_notices', array( __CLASS__, 'admin_deleted_2fa_notice' ) ); |
| 275 | } else { |
| 276 | \add_action( 'admin_notices', array( __CLASS__, 'user_deleted_2fa_notice' ) ); |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | /** |
| 282 | * Returns the user roles in AJAX matter. |
| 283 | * |
| 284 | * @return void |
| 285 | * |
| 286 | * @since 2.6.0 |
| 287 | */ |
| 288 | public static function get_ajax_user_roles() { |
| 289 | if ( \wp_doing_ajax() ) { |
| 290 | // Verify nonce. |
| 291 | $nonce = isset( $_GET['wp_2fa_nonce'] ) ? \sanitize_text_field( \wp_unslash( $_GET['wp_2fa_nonce'] ) ) : null; |
| 292 | if ( null === $nonce || false === $nonce || ! \wp_verify_nonce( $nonce, 'wp-2fa-settings-nonce' ) ) { |
| 293 | \wp_send_json_error( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 294 | } |
| 295 | |
| 296 | $roles = array(); |
| 297 | |
| 298 | $term = isset( $_GET['term'] ) ? \sanitize_text_field( \wp_unslash( $_GET['term'] ) ) : null; |
| 299 | if ( null === $term || false === $term ) { |
| 300 | \wp_send_json_error( esc_html__( 'Invalid term.', 'wp-2fa' ) ); |
| 301 | } |
| 302 | |
| 303 | foreach ( WP_Helper::get_roles_wp() as $role => $human_readable ) { |
| 304 | if ( stripos( $human_readable, $term ) !== false ) { |
| 305 | $roles[] = array( |
| 306 | 'label' => \sanitize_text_field( $role ), |
| 307 | 'value' => \sanitize_text_field( $human_readable ), |
| 308 | ); |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | echo \wp_json_encode( $roles ); |
| 313 | exit; |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | /** |
| 318 | * Handles AJAX calls for sending test emails. |
| 319 | * |
| 320 | * @return void |
| 321 | * |
| 322 | * @since 2.6.0 |
| 323 | */ |
| 324 | public static function handle_send_test_email_ajax() { |
| 325 | // Check user permissions. |
| 326 | if ( ! \current_user_can( 'manage_options' ) ) { |
| 327 | \wp_send_json_error( \esc_html__( 'Access Denied.', 'wp-2fa' ) ); |
| 328 | } |
| 329 | |
| 330 | // Check email ID. |
| 331 | $email_id = isset( $_POST['email_id'] ) ? \sanitize_text_field( \wp_unslash( $_POST['email_id'] ) ) : null; |
| 332 | if ( null === $email_id || false === $email_id ) { |
| 333 | \wp_send_json_error( \esc_html__( 'Invalid email ID.', 'wp-2fa' ) ); |
| 334 | } |
| 335 | |
| 336 | // Verify nonce. |
| 337 | $nonce = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : null; |
| 338 | if ( null === $nonce || false === $nonce || ! wp_verify_nonce( $nonce, 'wp-2fa-email-test-' . $email_id ) ) { |
| 339 | wp_send_json_error( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) ); |
| 340 | } |
| 341 | |
| 342 | $user_id = \get_current_user_id(); |
| 343 | $user = \get_userdata( $user_id ); |
| 344 | $email = $user->user_email; |
| 345 | |
| 346 | if ( 'config_test' === $email_id ) { |
| 347 | $email_sent = Settings_Page::send_email( |
| 348 | $email, |
| 349 | \esc_html__( 'Test email from WP 2FA', 'wp-2fa' ), |
| 350 | \esc_html__( 'This email was sent by the WP 2FA plugin to test the email delivery.', 'wp-2fa' ) |
| 351 | ); |
| 352 | if ( $email_sent ) { |
| 353 | \wp_send_json_success( esc_html__( 'Test email was successfully sent to ', 'wp-2fa' ) . '<strong>' . esc_html( $email ) . '</strong>' ); |
| 354 | } |
| 355 | |
| 356 | \wp_send_json_error( esc_html__( 'Failed to send test email.', 'wp-2fa' ) ); |
| 357 | } |
| 358 | |
| 359 | $email_templates = Settings_Page_Email::get_email_notification_definitions(); |
| 360 | foreach ( $email_templates as $email_template ) { |
| 361 | if ( $email_id === $email_template->get_email_content_id() ) { |
| 362 | $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( $email_id . '_email_subject' ) ) ); |
| 363 | $message = \wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( $email_id . '_email_body' ), $user_id ) ); |
| 364 | |
| 365 | $email_sent = Settings_Page::send_email( $email, $subject, $message ); |
| 366 | if ( $email_sent ) { |
| 367 | \wp_send_json_success( esc_html__( 'Test email ', 'wp-2fa' ) . '<strong>' . \esc_html( $email_template->get_title() ) . '</strong>' . esc_html__( ' was successfully sent to ', 'wp-2fa' ) . '<strong>' . \esc_html( $email ) . '</strong>' ); |
| 368 | } |
| 369 | |
| 370 | \wp_send_json_error( esc_html__( 'Failed to send test email.', 'wp-2fa' ) ); |
| 371 | } |
| 372 | } |
| 373 | } |
| 374 | |
| 375 | /** |
| 376 | * User deleted 2FA settings notification |
| 377 | * |
| 378 | * @since 2.6.0 |
| 379 | */ |
| 380 | public static function user_deleted_2fa_notice() { |
| 381 | ?> |
| 382 | <div class="notice notice-success is-dismissible"> |
| 383 | <p><?php \esc_html_e( '2FA settings have been removed.', 'wp-2fa' ); ?></p> |
| 384 | <button type="button" class="notice-dismiss"> |
| 385 | <span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 386 | </button> |
| 387 | </div> |
| 388 | <?php |
| 389 | } |
| 390 | |
| 391 | /** |
| 392 | * Admin deleted user 2FA settings notification |
| 393 | * |
| 394 | * @since 2.6.0 |
| 395 | */ |
| 396 | public static function admin_deleted_2fa_notice() { |
| 397 | ?> |
| 398 | <div class="notice notice-success is-dismissible"> |
| 399 | <p><?php \esc_html_e( 'User 2FA settings have been removed.', 'wp-2fa' ); ?></p> |
| 400 | <button type="button" class="notice-dismiss"> |
| 401 | <span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 402 | </button> |
| 403 | </div> |
| 404 | <?php |
| 405 | } |
| 406 | |
| 407 | /** |
| 408 | * User unlocked notice. |
| 409 | * |
| 410 | * @since 2.6.0 |
| 411 | */ |
| 412 | public static function user_unlocked_notice() { |
| 413 | ?> |
| 414 | <div class="notice notice-success is-dismissible"> |
| 415 | <p><?php \esc_html_e( 'User account successfully unlocked. User can login again.', 'wp-2fa' ); ?></p> |
| 416 | <button type="button" class="notice-dismiss"> |
| 417 | <span class="screen-reader-text"><?php \esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span> |
| 418 | </button> |
| 419 | </div> |
| 420 | <?php |
| 421 | } |
| 422 | } |
| 423 | } |
| 424 |