PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.0
WP 2FA – Two-factor authentication for WordPress v2.9.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / Helpers / class-ajax-helper.php
wp-2fa / includes / classes / Admin / Helpers Last commit date
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