PluginProbe ʕ •ᴥ•ʔ
Limit Login Attempts Security – Login Security, 2FA, Firewall, Brute Force Prevention / 3.2.4
Limit Login Attempts Security – Login Security, 2FA, Firewall, Brute Force Prevention v3.2.4
3.2.4 3.2.3 3.2.2 3.2.1 3.2.0 trunk 2.0.0 2.1.0 2.10.0 2.10.1 2.11.0 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.14.0 2.15.0 2.15.1 2.15.2 2.16.0 2.17.0 2.17.1 2.17.2 2.17.3 2.17.4 2.18.0 2.19.0 2.19.1 2.19.2 2.2.0 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.21.1 2.22.0 2.22.1 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.25.0 2.25.1 2.25.10 2.25.11 2.25.12 2.25.13 2.25.14 2.25.15 2.25.16 2.25.17 2.25.18 2.25.19 2.25.2 2.25.20 2.25.21 2.25.22 2.25.23 2.25.24 2.25.25 2.25.26 2.25.27 2.25.28 2.25.29 2.25.3 2.25.4 2.25.5 2.25.6 2.25.7 2.25.8 2.25.9 2.26.0 2.26.1 2.26.10 2.26.11 2.26.12 2.26.13 2.26.14 2.26.15 2.26.16 2.26.17 2.26.18 2.26.19 2.26.2 2.26.20 2.26.21 2.26.22 2.26.23 2.26.24 2.26.25 2.26.26 2.26.27 2.26.28 2.26.3 2.26.4 2.26.5 2.26.6 2.26.7 2.26.8 2.26.9 2.3.0 2.4.0 2.5.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.8.0 2.8.1 2.9.0 3.0.0 3.0.1 3.0.2 3.1.0
limit-login-attempts-reloaded / core / Ajax.php
limit-login-attempts-reloaded / core Last commit date
http 2 weeks ago integrations 2 weeks ago mfa 2 weeks ago mfa-flow 2 weeks ago AdminNoticesController.php 2 weeks ago Ajax.php 2 weeks ago CloudApp.php 2 weeks ago Config.php 2 weeks ago Helpers.php 2 weeks ago LimitLoginAttempts.php 2 weeks ago LoginFlowTransientStore.php 2 weeks ago MfaConstants.php 2 weeks ago Shortcodes.php 2 weeks ago
Ajax.php
1203 lines
1 <?php
2
3 namespace LLAR\Core;
4
5 use LLAR\Core\Http\Http;
6
7 if ( ! defined( 'ABSPATH' ) ) exit;
8
9 class Ajax
10 {
11
12 /**
13 * Register all ajax requests & handlers
14 */
15 public function register() {
16
17 add_action( 'wp_ajax_limit-login-unlock', array( $this, 'ajax_unlock' ) );
18 add_action( 'wp_ajax_dismiss_review_notice', array( $this, 'dismiss_review_notice_callback' ) );
19 add_action( 'wp_ajax_dismiss_notify_notice', array( $this, 'dismiss_notify_notice_callback' ) );
20 add_action( 'wp_ajax_enable_notify', array( $this, 'enable_notify_callback' ) );
21 add_action( 'wp_ajax_app_config_save', array( $this, 'app_config_save_callback' ) );
22 add_action( 'wp_ajax_app_setup', array( $this, 'app_setup_callback' ) );
23 add_action( 'wp_ajax_app_log_action', array( $this, 'app_log_action_callback' ) );
24 add_action( 'wp_ajax_app_load_log', array( $this, 'app_load_log_callback' ) );
25 add_action( 'wp_ajax_app_load_successful_login', array( $this, 'app_load_successful_login_callback' ) );
26 add_action( 'wp_ajax_app_load_lockouts', array( $this, 'app_load_lockouts_callback' ) );
27 add_action( 'wp_ajax_app_load_acl_rules', array( $this, 'app_load_acl_rules_callback' ) );
28 add_action( 'wp_ajax_app_load_country_access_rules', array( $this, 'app_load_country_access_rules_callback' ) );
29 add_action( 'wp_ajax_app_toggle_country', array( $this, 'app_toggle_country_callback' ) );
30 add_action( 'wp_ajax_app_country_rule', array( $this, 'app_country_rule_callback' ) );
31 add_action( 'wp_ajax_app_acl_add_rule', array( $this, 'app_acl_add_rule_callback' ) );
32 add_action( 'wp_ajax_app_acl_remove_rule', array( $this, 'app_acl_remove_rule_callback' ) );
33 add_action( 'wp_ajax_nopriv_get_remaining_attempts_message', array(
34 $this,
35 'get_remaining_attempts_message_callback'
36 ) );
37 add_action( 'wp_ajax_subscribe_email', array( $this, 'subscribe_email_callback' ) );
38 add_action( 'wp_ajax_strong_account_policies', array( $this, 'strong_account_policies_callback' ) );
39 add_action( 'wp_ajax_block_by_country', array( $this, 'block_by_country_callback' ) );
40 add_action( 'wp_ajax_dismiss_onboarding_popup', array( $this, 'dismiss_onboarding_popup_callback' ) );
41 add_action( 'wp_ajax_onboarding_reset', array( $this, 'onboarding_reset_callback' ) );
42 add_action( 'wp_ajax_close_premium_message', array( $this, 'close_premium_message' ) );
43 add_action( 'wp_ajax_toggle_auto_update', array( $this, 'toggle_auto_update_callback' ) );
44 add_action( 'wp_ajax_activate_micro_cloud', array( $this, 'activate_micro_cloud_callback' ) );
45 add_action( 'wp_ajax_test_email_notifications', array( $this, 'test_email_notifications_callback' ) );
46 add_action( 'wp_ajax_nopriv_llar_mfa_flow_send_code', array( $this, 'mfa_flow_send_code_callback' ) );
47 add_action( 'wp_ajax_llar_mfa_flow_send_code', array( $this, 'mfa_flow_send_code_callback' ) );
48 }
49
50 public function ajax_unlock() {
51
52 $this->check_user_capabilities();
53
54 check_ajax_referer( 'llar-unlock', 'sec' );
55 $ip = (string) @$_POST['ip'];
56
57 $lockouts = (array) Config::get( Config::OPTION_LOCKOUTS );
58
59 if ( isset( $lockouts[ $ip ] ) ) {
60 unset( $lockouts[ $ip ] );
61 Config::update( Config::OPTION_LOCKOUTS, $lockouts );
62 }
63
64 //save to log
65 $user_login = @(string) $_POST['username'];
66 $log = Config::get( Config::OPTION_LOGGED );
67
68 if ( @$log[ $ip ][ $user_login ] ) {
69 if ( ! is_array( $log[ $ip ][ $user_login ] ) ) {
70 $log[ $ip ][ $user_login ] = array(
71 'counter' => $log[ $ip ][ $user_login ],
72 );
73 }
74 $log[ $ip ][ $user_login ]['unlocked'] = true;
75
76 Config::update( Config::OPTION_LOGGED, $log );
77 }
78
79 header( 'Content-Type: application/json' );
80 echo 'true';
81 exit;
82 }
83
84 public function dismiss_review_notice_callback() {
85
86 $this->check_user_capabilities();
87
88 check_ajax_referer( 'llar-dismiss-review', 'sec' );
89
90 $type = isset( $_POST['type'] ) ? sanitize_text_field( $_POST['type'] ) : false;
91
92 if ( $type === 'dismiss' ) {
93
94 Config::update( 'review_notice_shown', true );
95 }
96
97 if ( $type === 'later' ) {
98
99 Config::update( 'activation_timestamp', time() );
100 }
101
102 wp_send_json_success( array() );
103 }
104
105 public function dismiss_notify_notice_callback() {
106
107 $this->check_user_capabilities();
108
109 check_ajax_referer( 'llar-dismiss-notify-notice', 'sec' );
110
111 $type = isset( $_POST['type'] ) ? sanitize_text_field( $_POST['type'] ) : false;
112
113 if ( $type === 'dismiss' ) {
114
115 Config::update( 'enable_notify_notice_shown', true );
116 }
117
118 if ( $type === 'later' ) {
119
120 Config::update( 'notice_enable_notify_timestamp', time() );
121 }
122
123 wp_send_json_success( array() );
124 }
125
126 public function enable_notify_callback() {
127
128 $this->check_user_capabilities();
129
130 check_ajax_referer( 'llar-enable-notify', 'sec' );
131
132 $notify_methods = explode( ',', Config::get( 'lockout_notify' ) );
133
134 if ( ! in_array( 'email', $notify_methods ) ) {
135
136 $notify_methods[] = 'email';
137 }
138
139 Config::update( 'lockout_notify', implode( ',', $notify_methods ) );
140 Config::update( 'enable_notify_notice_shown', true );
141
142 wp_send_json_success( array() );
143 }
144
145 public function app_setup_callback() {
146
147 $this->check_user_capabilities();
148
149 check_ajax_referer( 'llar-app-setup', 'sec' );
150
151 if ( ! empty( $_POST['code'] ) ) {
152
153 $setup_code = sanitize_text_field( $_POST['code'] );
154
155 if ( $key_result = CloudApp::activate_license_key( $setup_code ) ) {
156
157 if ( $key_result['success'] ) {
158
159 wp_send_json_success( array(
160 'msg' => ( $key_result['app_config']['messages']['setup_success'] )
161 ) );
162 } else {
163
164 wp_send_json_error( array(
165 'msg' => ( $key_result['error'] )
166 ) );
167 }
168 } else {
169
170 wp_send_json_error( array(
171 'msg' => $key_result['error']
172 ) );
173 }
174 }
175
176 wp_send_json_error( array(
177 'msg' => __( 'Please specify the Setup Code', 'limit-login-attempts-reloaded' )
178 ) );
179 }
180
181
182 public function app_log_action_callback() {
183
184 $this->check_user_capabilities();
185
186 check_ajax_referer( 'llar-app-log', 'sec' );
187
188 if ( ! empty( $_POST['method'] ) && ! empty( $_POST['params'] ) ) {
189
190 $method = sanitize_text_field( $_POST['method'] );
191 $params = (array) $_POST['params'];
192
193 if ( ! in_array( $method, array( 'lockout/delete', 'acl/create', 'acl/delete' ) ) ) {
194
195 wp_send_json_error( array(
196 'msg' => 'Wrong method.'
197 ) );
198 }
199
200 if ( $response = LimitLoginAttempts::$cloud_app->request( $method, 'post', $params ) ) {
201
202 wp_send_json_success( array(
203 'msg' => $response['message']
204 ) );
205
206 } else {
207
208 wp_send_json_error( array(
209 'msg' => 'The endpoint is not responding. Please contact your app provider to settle that.'
210 ) );
211 }
212 }
213
214 wp_send_json_error( array(
215 'msg' => 'Wrong App id.'
216 ) );
217 }
218
219 public function app_acl_add_rule_callback() {
220
221 $this->check_user_capabilities();
222
223 check_ajax_referer( 'llar-app-acl-add-rule', 'sec' );
224
225 if ( ! empty( $_POST['pattern'] ) && ! empty( $_POST['rule'] ) && ! empty( $_POST['type'] ) ) {
226
227 $pattern = sanitize_text_field( $_POST['pattern'] );
228 $rule = sanitize_text_field( $_POST['rule'] );
229 $type = sanitize_text_field( $_POST['type'] );
230
231 if ( ! in_array( $rule, array( 'pass', 'allow', 'deny' ) ) ) {
232
233 wp_send_json_error( array(
234 'msg' => 'Wrong rule.'
235 ) );
236 }
237
238 if ( $response = LimitLoginAttempts::$cloud_app->acl_create( array(
239 'pattern' => $pattern,
240 'rule' => $rule,
241 'type' => ( $type === 'ip' ) ? 'ip' : 'login',
242 ) ) ) {
243
244 wp_send_json_success( array(
245 'msg' => $response['message']
246 ) );
247
248 } else {
249
250 wp_send_json_error( array(
251 'msg' => 'The endpoint is not responding. Please contact your app provider to settle that.'
252 ) );
253 }
254 }
255
256 wp_send_json_error( array(
257 'msg' => 'Wrong input data.'
258 ) );
259 }
260
261 public function app_acl_remove_rule_callback() {
262
263 $this->check_user_capabilities();
264
265 check_ajax_referer( 'llar-app-acl-remove-rule', 'sec' );
266
267 if ( ! empty( $_POST['pattern'] ) && ! empty( $_POST['type'] ) ) {
268
269 $pattern = sanitize_text_field( $_POST['pattern'] );
270 $type = sanitize_text_field( $_POST['type'] );
271
272 if ( $response = LimitLoginAttempts::$cloud_app->acl_delete( array(
273 'pattern' => $pattern,
274 'type' => ( $type === 'ip' ) ? 'ip' : 'login',
275 ) ) ) {
276
277 wp_send_json_success( array(
278 'msg' => $response['message']
279 ) );
280
281 } else {
282
283 wp_send_json_error( array(
284 'msg' => 'The endpoint is not responding. Please contact your app provider to settle that.'
285 ) );
286 }
287 }
288
289 wp_send_json_error( array(
290 'msg' => 'Wrong input data.'
291 ) );
292 }
293
294 public function app_load_log_callback() {
295
296 $this->check_user_capabilities();
297
298 check_ajax_referer( 'llar-app-load-log', 'sec' );
299
300 $offset = sanitize_text_field( $_POST['offset'] );
301 $limit = sanitize_text_field( $_POST['limit'] );
302
303 $log = LimitLoginAttempts::$cloud_app->log( $limit, $offset );
304
305 if ( $log ) {
306
307 $date_format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
308 $countries_list = Helpers::get_countries_list();
309
310 ob_start();
311 if ( empty( $log['items'] ) && ! empty( $log['offset'] ) ) : ?>
312 <?php elseif ( $log['items'] ) : ?>
313
314 <?php foreach ( $log['items'] as $item ) :
315 $country_name = ! empty( $countries_list[ $item['country_code'] ] ) ? $countries_list[ $item['country_code'] ] : '';
316 ?>
317 <tr>
318 <td class="llar-col-nowrap"><?php echo get_date_from_gmt( date( 'Y-m-d H:i:s', $item['created_at'] ), $date_format ); ?></td>
319 <td>
320 <div class="llar-log-country-flag">
321 <span class="hint_tooltip-parent">
322 <img src="<?php echo LLA_PLUGIN_URL . 'assets/img/flags/' . esc_attr( strtolower( $item['country_code'] ) ) . '.png' ?>">
323 <div class="hint_tooltip">
324 <div class="hint_tooltip-content">
325 <?php echo esc_attr( $country_name ) ?>
326 </div>
327 </div>
328 </span>
329 <span><?php echo esc_html( $item['ip'] ); ?></span></div>
330 </td>
331 <td><?php echo esc_html( $item['gateway'] ); ?></td>
332 <td><?php echo ( is_null( $item['login'] ) ) ? '-' : esc_html( $item['login'] ); ?></td>
333 <td><?php echo ( is_null( $item['result'] ) ) ? '-' : esc_html( $item['result'] ); ?></td>
334 <td><?php echo ( is_null( $item['reason'] ) ) ? '-' : esc_html( $item['reason'] ); ?></td>
335 <td><?php echo ( is_null( $item['pattern'] ) ) ? '-' : esc_html( $item['pattern'] ); ?></td>
336 <td><?php echo ( is_null( $item['attempts_left'] ) ) ? '-' : esc_html( $item['attempts_left'] ); ?></td>
337 <td><?php echo ( is_null( $item['time_left'] ) ) ? '-' : esc_html( $item['time_left'] ) ?></td>
338 <td class="llar-app-log-actions">
339 <?php
340 if ( $item['actions'] ) {
341
342 foreach ( $item['actions'] as $action ) {
343
344 echo '<button class="button llar-app-log-action-btn js-app-log-action"
345 style="color:' . esc_attr( $action['color'] ) . '; border-color:' . esc_attr( $action['color'] ) . '"
346 data-method="' . esc_attr( $action['method'] ) . '"
347 data-params="' . esc_attr( json_encode( $action['data'], JSON_FORCE_OBJECT ) ) . '"
348 href="#" title="' . $action['label'] . '"><i class="dashicons dashicons-' . esc_attr( $action['icon'] ) . '"></i></button>';
349 }
350 } else {
351 echo '-';
352 }
353 ?>
354 </td>
355 </tr>
356 <?php endforeach; ?>
357 <?php else : ?>
358 <?php if ( empty( $offset ) ) : ?>
359 <tr class="empty-row">
360 <td colspan="100%"
361 style="text-align: center"><?php _e( 'No events yet.', 'limit-login-attempts-reloaded' ); ?></td>
362 </tr>
363 <?php endif; ?>
364 <?php endif; ?>
365 <?php
366
367 wp_send_json_success( array(
368 'html' => ob_get_clean(),
369 'offset' => $log['offset'],
370 'total_items' => count( $log['items'] )
371 ) );
372
373 } else {
374
375 wp_send_json_error( array(
376 'msg' => 'The endpoint is not responding. Please contact your app provider to settle that.'
377 ) );
378 }
379 }
380
381
382 public function app_load_successful_login_callback() {
383
384 if ( ! LimitLoginAttempts::$instance->has_capability ) {
385
386 wp_send_json_error( array() );
387 }
388
389 check_ajax_referer( 'llar-app-load-login', 'sec' );
390
391 if ( empty( $_POST['limit'] ) && empty( $_POST['custom'] ) && ! $_POST['offset'] && ! $_POST['url_premium'] ) {
392 wp_send_json_error( array() );
393 }
394
395 $offset = sanitize_text_field( $_POST['offset'] );
396 $limit = sanitize_text_field( $_POST['limit'] );
397 $custom = sanitize_text_field( $_POST['custom'] );
398 $upgrade_premium_url = sanitize_text_field( $_POST['url_premium'] );
399
400 if ( $custom === 'true') {
401
402 $data = LimitLoginAttempts::$cloud_app->get_login( $limit, $offset );
403 } else {
404
405 $local_data = ['at' => time() - 60, 'login' => 'admin', 'ip' => '11.22.33.44', 'location' => ['country_code' => 'US'], 'roles' => ['administrator']];
406 $data['items'] = array_fill(0, 5, $local_data);
407 $data['offset'] = '';
408 }
409
410 if ( $data ) {
411
412 $date_format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
413 $current_date = date('Y-m-d');
414 $current_year = date('Y');
415
416 $countries_list = Helpers::get_countries_list();
417 $continent_list = Helpers::get_continent_list();
418
419 ob_start();
420 if ( empty( $data['items'] ) && ! empty( $data['offset'] ) ) :
421
422 elseif ( $data['items'] ) : ?>
423
424 <?php foreach ( $data['items'] as $item ) :
425
426 $limited = isset( $item['limited'] ) ? filter_var( $item['limited'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ) : false;
427
428 if ( $limited ) :
429 $item['login'] = 'admin';
430 $item['location']['country_code'] = 'ZZ';
431 $item['roles'] = [ 'administrator' ];
432 $item['ip'] = '11.22.33.44';
433 endif;
434
435 $login = ! empty( $item['login'] ) ? $item['login'] : '';
436 $ip = ! empty( $item['ip'] ) ? $item['ip'] : '';
437
438 $country_name = ! empty( $item['location']['country_code'] ) ? $countries_list[ $item['location']['country_code'] ] : '';
439 $continent_name = ! empty( $item['location']['continent_code'] ) ? $continent_list[ $item['location']['continent_code'] ] : '';
440 $long_login = mb_strlen( $login ) > 10;
441 $long_ip = strlen( $ip ) > 15;
442 $login_url = !empty( $item['user_id'] ) ? get_edit_user_link( $item['user_id'] ) : '';
443
444 $latitude = !empty( $item['location']['latitude'] ) ? $item['location']['latitude'] : false;
445 $longitude = !empty( $item['location']['longitude'] ) ? $item['location']['longitude'] : false;
446
447 $log_date_gmt = date('Y-m-d H:i:s', $item['at']);
448 $log_local_date = get_date_from_gmt($log_date_gmt, 'Y-m-d');
449 $log_local_time = get_date_from_gmt($log_date_gmt, get_option('time_format'));
450 $log_year = get_date_from_gmt($log_date_gmt, 'Y');
451
452 if ($log_local_date === $current_date) {
453 $correct_date = __('Today at ', 'limit-login-attempts-reloaded') . $log_local_time;
454 } elseif ($log_year === $current_year) {
455 $log_local_month_day = get_date_from_gmt($log_date_gmt, 'M j');
456 $correct_date = $log_local_month_day . ' at ' . $log_local_time;
457 } else {
458 $correct_date = get_date_from_gmt($log_date_gmt, $date_format);
459 }
460 ?>
461 <tr>
462 <td class="llar-col-nowrap"><?php echo esc_html( $correct_date ) ?></td>
463 <td class="cell-login <?php echo $limited ? 'llar-blur-block-cell' : '' ?>">
464 <span class="hint_tooltip-parent">
465 <a href="<?php echo $login_url ?>" target="_blank"><?php echo esc_html( $login ) ?></a>
466 <?php if ( $long_login ) : ?>
467 <div class="hint_tooltip">
468 <div class="hint_tooltip-content">
469 <?php echo esc_html( $login ) ?>
470 </div>
471 </div>
472 <?php endif; ?>
473 </span>
474 </td>
475 <td>
476 <div class="llar-log-country-flag <?php echo $limited ? 'llar-blur-block-cell' : '' ?>">
477 <span class="hint_tooltip-parent">
478 <img src="<?php echo LLA_PLUGIN_URL . 'assets/img/flags/' . esc_attr( strtolower( $item['location']['country_code'] ) ) . '.png' ?>">
479 <div class="hint_tooltip">
480 <div class="hint_tooltip-content">
481 <?php echo $country_name ?>
482 </div>
483 </div>
484 </span>
485 <span class="cell-id hint_tooltip-parent">
486 <div class="id">
487 <?php echo esc_html( $ip ) ?>
488 </div>
489 <?php if ( $long_ip ) : ?>
490 <div class="hint_tooltip">
491 <div class="hint_tooltip-content">
492 <?php echo esc_html( $ip ) ?>
493 </div>
494 </div>
495 <?php endif; ?>
496 </span>
497 </div>
498 </td>
499 <td class="cell-role <?php echo $limited ? 'llar-blur-block-cell' : '' ?>">
500 <?php if ( isset( $item['roles'] ) && is_array( $item['roles'] ) ) : ?>
501 <span class="hint_tooltip-parent">
502 <?php $admin_key = array_search( 'administrator', $item['roles'] );
503 if ( $admin_key !== false ) : ?>
504 <span><?php echo esc_html( $item['roles'][$admin_key] ) ?></span>
505 <?php unset( $item['roles'][$admin_key] );
506 elseif ( isset($item['roles'][0]) ) :
507 echo esc_html( $item['roles'][0] );
508 unset( $item['roles'][0] );
509 endif;
510 $list_roles = '';
511 foreach ( $item['roles'] as $role ) :
512 $list_roles .= '<li>' . esc_html( $role ) . '</li>';
513 endforeach;
514 if ( ! empty ( $list_roles ) ) : ?>
515 <span class="dashicons dashicons-menu-alt2"></span>
516 <div class="hint_tooltip">
517 <div class="hint_tooltip-content">
518 <ul>
519 <?php echo $list_roles; ?>
520 </ul>
521 </div>
522 </div>
523 <?php endif; ?>
524 </span>
525 <?php endif; ?>
526 </td>
527 <td class="button-open">
528 <button class="button llar-add-login-open">
529 <i class="dashicons dashicons-arrow-down-alt2"></i>
530 </button>
531 </td>
532 </tr>
533 <tr class="hidden-row">
534
535 <?php if ( $limited ) : ?>
536
537 <td colspan="4" style="max-width: 290px">
538 <div>
539 <?php echo sprintf(
540 __( 'Login details could not be populated due to insufficient cloud resources.<br>Please <a class="link__style_unlink llar_turquoise" href="%s" target="_blank">upgrade to Premium</a> to access this data.', 'limit-login-attempts-reloaded' ),
541 $upgrade_premium_url
542 ); ?>
543 </div>
544 </td>
545
546 <?php else : ?>
547 <td colspan="2" style="max-width: 200px">
548
549 <?php if ( !empty( $continent_name ) ) : ?>
550 <div>
551 <span><?php _e( 'Continent: ', 'limit-login-attempts-reloaded' ) ?></span><?php echo $continent_name ?>
552 </div>
553 <?php endif ?>
554
555 <?php if ( !empty( $country_name ) ) :
556
557 $country_code = $item['location']['country_code'] !== 'ZZ' ? ' (' . esc_html( $item['location']['country_code'] ) . ')' : '';
558 ?>
559 <div>
560 <span><?php _e( 'Country: ', 'limit-login-attempts-reloaded' ) ?></span><?php echo esc_html($country_name) . esc_html($country_code) ?>
561 </div>
562 <?php endif ?>
563
564 <?php if ( !empty( $item['location']['stateprov'] ) ) : ?>
565 <div>
566 <span><?php _e( 'State/Province: ', 'limit-login-attempts-reloaded' ) ?></span><?php echo esc_html( $item['location']['stateprov'] ) ?>
567 </div>
568 <?php endif ?>
569
570 <?php if ( !empty( $item['location']['district'] ) ) : ?>
571 <div>
572 <span><?php _e( 'District: ', 'limit-login-attempts-reloaded' ) ?></span><?php echo esc_html( $item['location']['district'] ) ?>
573 </div>
574 <?php endif ?>
575
576 <?php if ( !empty( $item['location']['city'] ) ) : ?>
577 <div>
578 <span><?php _e( 'City: ', 'limit-login-attempts-reloaded' ) ?></span><?php echo esc_html( $item['location']['city'] ) ?>
579 </div>
580 <?php endif ?>
581
582 <?php if ( !empty( $item['location']['zipcode'] ) ) : ?>
583 <div>
584 <span><?php _e( 'Zipcode: ', 'limit-login-attempts-reloaded' ) ?></span><?php echo esc_html( $item['location']['zipcode'] ) ?>
585 </div>
586 <?php endif ?>
587
588 <?php if ( $latitude && $longitude ) : ?>
589 <div>
590 <span><?php _e( 'Latitude, Longitude: ', 'limit-login-attempts-reloaded' ) ?></span>
591 <a href="https://www.limitloginattempts.com/map?lat=<?php echo esc_attr( $latitude ) ?>&lon=<?php echo esc_attr( $longitude ) ?>" target="_blank">
592 <?php echo esc_html( $latitude ) . ', ' . esc_html( $longitude ) ?>
593 </a>
594 </div>
595 <?php endif ?>
596
597 <?php if ( !empty( $item['location']['timezone_name'] ) ) :
598
599 $timezone_offset = '';
600 if ( !empty( $item['location']['timezone_offset'] ) ) {
601
602 $timezone_offset = (int)$item['location']['timezone_offset'] > 0
603 ? '+' . $item['location']['timezone_offset']
604 : $item['location']['timezone_offset'];
605 }
606 ?>
607 <div>
608 <span>
609 <?php _e( 'Timezone: ', 'limit-login-attempts-reloaded' ) ?>
610 </span>
611 <?php echo esc_html( $item['location']['timezone_name'] ) . ' [' . esc_html( $timezone_offset ) . ']' ?>
612 </div>
613 <?php endif ?>
614
615 <?php if ( !empty( $item['location']['isp_name'] ) && !empty( $item['location']['organization_name'] ) ) :
616
617 $usage_type = !empty( $item['location']['usage_type'] ) ? ' (' . $item['location']['usage_type'] . ')' : '';
618
619 $isp_name = $item['location']['isp_name'];
620 $organization_name = $item['location']['organization_name'];
621
622 if ( $isp_name === $organization_name ) {
623
624 $full_name = $organization_name;
625 } elseif ( strpos($isp_name, $organization_name ) !== false ) {
626
627 $full_name = $isp_name;
628 } elseif ( strpos($organization_name, $isp_name ) !== false ) {
629
630 $full_name = $organization_name;
631 } else {
632
633 $full_name = $isp_name . ' / ' . $organization_name;
634 }
635 ?>
636
637 <div>
638 <span><?php _e( 'Internet Provider: ', 'limit-login-attempts-reloaded' ) ?></span><?php echo esc_html( $full_name ) . esc_html( $usage_type ) ?>
639 </div>
640
641 <?php endif ?>
642
643 <?php if ( !empty( $item['location']['connection_type'] ) ) : ?>
644 <div>
645 <span><?php _e( 'Connection Type: ', 'limit-login-attempts-reloaded' ) ?></span><?php echo esc_html( $item['location']['connection_type'] ) ?>
646 </div>
647 <?php endif ?>
648
649 <?php endif ?>
650 </td>
651 <td colspan="3">
652 <?php if ( $latitude && $longitude ) : ?>
653 <iframe class="open_street_map" data-latitude="<?php echo esc_attr( $latitude ) ?>" data-longitude="<?php echo esc_attr( $longitude ) ?>"
654 width="100%" height="100%" frameborder="0" scrolling="no" marginheight="0" marginwidth="0">
655 </iframe>
656 <?php endif; ?>
657 </td>
658 </tr>
659 <?php endforeach; ?>
660 <?php else : ?>
661 <?php if ( empty( $offset ) ) : ?>
662 <tr class="empty-row">
663 <td colspan="100%"
664 style="text-align: center"><?php _e( 'No events yet.', 'limit-login-attempts-reloaded' ); ?></td>
665 </tr>
666 <?php endif; ?>
667 <?php endif; ?>
668 <?php
669
670 wp_send_json_success( array(
671 'html' => ob_get_clean(),
672 'offset' => $data['offset'],
673 'total_items' => count( $data['items'] )
674 ) );
675
676 } else {
677
678 wp_send_json_error( array(
679 'msg' => 'The endpoint is not responding. Please contact your app provider to settle that.'
680 ) );
681 }
682 }
683
684 public function app_load_lockouts_callback() {
685
686 $this->check_user_capabilities();
687
688 check_ajax_referer( 'llar-app-load-lockouts', 'sec' );
689
690 $offset = sanitize_text_field( $_POST['offset'] );
691 $limit = sanitize_text_field( $_POST['limit'] );
692
693 $lockouts = LimitLoginAttempts::$cloud_app->get_lockouts( $limit, $offset );
694
695 if ( $lockouts ) {
696
697 ob_start(); ?>
698
699 <?php if ( $lockouts['items'] ) : ?>
700 <?php foreach ( $lockouts['items'] as $item ) : ?>
701 <tr>
702 <td><?php echo esc_html( $item['ip'] ); ?></td>
703 <td><?php echo ( is_null( $item['login'] ) ) ? '-' : esc_html( implode( ',', $item['login'] ) ); ?></td>
704 <td><?php echo ( is_null( $item['count'] ) ) ? '-' : esc_html( $item['count'] ); ?></td>
705 <td><?php echo ( is_null( $item['ttl'] ) ) ? '-' : esc_html( round( ( $item['ttl'] - time() ) / 60 ) ); ?></td>
706 </tr>
707 <?php endforeach; ?>
708
709 <?php else: ?>
710 <?php if ( empty( $offset ) ) : ?>
711 <tr class="empty-row">
712 <td colspan="4"
713 style="text-align: center"><?php _e( 'No lockouts yet.', 'limit-login-attempts-reloaded' ); ?></td>
714 </tr>
715 <?php endif; ?>
716 <?php endif; ?>
717 <?php
718
719 wp_send_json_success( array(
720 'html' => ob_get_clean(),
721 'offset' => $lockouts['offset']
722 ) );
723
724 } elseif ( intval( LimitLoginAttempts::$cloud_app->last_response_code ) >= 400 && intval( LimitLoginAttempts::$cloud_app->last_response_code ) < 500 ) {
725
726 $app_config = Config::get( 'app_config' );
727
728 wp_send_json_error( array(
729 'error_notice' => '<div class="llar-app-notice">
730 <p>' . $app_config['messages']['sync_error'] . '<br><br>' . sprintf( __( 'Meanwhile, the app falls over to the <a href="%s">default functionality</a>.', 'limit-login-attempts-reloaded' ), admin_url( 'options-general.php?page=limit-login-attempts&tab=logs-local' ) ) . '</p>
731 </div>'
732 ) );
733 } else {
734
735 wp_send_json_error( array(
736 'msg' => 'The endpoint is not responding. Please contact your app provider to settle that.'
737 ) );
738 }
739 }
740
741 public function app_load_acl_rules_callback() {
742
743 $this->check_user_capabilities();
744
745 check_ajax_referer( 'llar-app-load-acl-rules', 'sec' );
746
747 $type = sanitize_text_field( $_POST['type'] );
748 $limit = sanitize_text_field( $_POST['limit'] );
749 $offset = sanitize_text_field( $_POST['offset'] );
750
751 $acl_list = LimitLoginAttempts::$cloud_app->acl( array(
752 'type' => $type,
753 'limit' => $limit,
754 'offset' => $offset
755 ) );
756
757 if ( $acl_list ) {
758
759 ob_start(); ?>
760
761 <?php if ( $acl_list['items'] ) : ?>
762 <?php foreach ( $acl_list['items'] as $item ) : ?>
763 <tr class="llar-app-rule-<?php echo esc_attr( $item['rule'] ); ?>">
764 <td class="rule-pattern" scope="col">
765 <?php echo esc_html( $item['pattern'] ); ?>
766 </td>
767 <td scope="col">
768 <?php echo esc_html( $item['rule'] ); ?><?php echo ( $type === 'ip' ) ? '<span class="origin">' . esc_html( $item['origin'] ) . '</span>' : ''; ?>
769 </td>
770 <td class="llar-app-acl-action-col" scope="col">
771 <button class="button llar-app-acl-remove" data-type="<?php echo esc_attr( $type ); ?>"
772 data-pattern="<?php echo esc_attr( $item['pattern'] ); ?>">
773 <span class="dashicons dashicons-no"></span>
774 </button>
775 </td>
776 </tr>
777 <?php endforeach; ?>
778 <?php else : ?>
779 <tr class="empty-row">
780 <td colspan="3" style="text-align: center">
781 <?php _e( 'No rules yet.', 'limit-login-attempts-reloaded' ); ?>
782 </td>
783 </tr>
784 <?php endif; ?>
785 <?php
786
787 wp_send_json_success( array(
788 'html' => ob_get_clean(),
789 'offset' => $acl_list['offset']
790 ) );
791
792 } else {
793
794 wp_send_json_error( array(
795 'msg' => 'The endpoint is not responding. Please contact your app provider to settle that.'
796 ) );
797 }
798 }
799
800 public function app_load_country_access_rules_callback() {
801
802 $this->check_user_capabilities();
803
804 check_ajax_referer( 'llar-app-load-country-access-rules', 'sec' );
805
806 $country_rules = LimitLoginAttempts::$cloud_app->country();
807
808 if ( $country_rules ) {
809
810 wp_send_json_success( $country_rules );
811 } else {
812
813 wp_send_json_error( array(
814 'msg' => 'Something wrong.'
815 ) );
816 }
817 }
818
819 public function app_toggle_country_callback() {
820
821 $this->check_user_capabilities();
822
823 check_ajax_referer( 'llar-app-toggle-country', 'sec' );
824
825 $code = sanitize_text_field( $_POST['code'] );
826 $action_type = sanitize_text_field( $_POST['type'] );
827
828 if ( ! $code ) {
829
830 wp_send_json_error( array(
831 'msg' => 'Wrong country code.'
832 ) );
833 }
834
835 $result = false;
836
837 if ( $action_type === 'add' ) {
838
839 $result = LimitLoginAttempts::$cloud_app->country_add( array(
840 'code' => $code
841 ) );
842
843 } else if ( $action_type === 'remove' ) {
844
845 $result = LimitLoginAttempts::$cloud_app->country_remove( array(
846 'code' => $code
847 ) );
848 }
849
850 if ( $result ) {
851
852 wp_send_json_success( array() );
853 } else {
854
855 wp_send_json_error( array(
856 'msg' => 'Something wrong.'
857 ) );
858 }
859 }
860
861 public function app_country_rule_callback() {
862
863 $this->check_user_capabilities();
864
865 check_ajax_referer( 'llar-app-country-rule', 'sec' );
866
867 $rule = sanitize_text_field( $_POST['rule'] );
868
869 if ( empty( $rule ) || ! in_array( $rule, array( 'allow', 'deny' ) ) ) {
870
871 wp_send_json_error( array(
872 'msg' => 'Wrong rule.'
873 ) );
874 }
875
876 $result = LimitLoginAttempts::$cloud_app->country_rule( array(
877 'rule' => $rule
878 ) );
879
880 if ( $result ) {
881
882 wp_send_json_success( array() );
883 } else {
884
885 wp_send_json_error( array(
886 'msg' => 'Something wrong.'
887 ) );
888 }
889 }
890
891
892 public function subscribe_email_callback() {
893
894 $this->check_user_capabilities();
895
896 check_ajax_referer( 'llar-subscribe-email', 'sec' );
897
898 $email = sanitize_text_field( trim( $_POST['email'] ) );
899 $is_subscribe_yes = sanitize_text_field( $_POST['is_subscribe_yes'] ) === 'true';
900 $admin_email = ( ! is_multisite() ) ? get_option( 'admin_email' ) : get_site_option( 'admin_email' );
901
902 if ( ! empty( $email ) && is_email( $email ) ) {
903
904 Config::update( 'admin_notify_email', $email );
905 Config::update( 'lockout_notify', 'email' );
906
907 if ( $is_subscribe_yes ) {
908 $response = Http::post( 'https://api.limitloginattempts.com/my/key', array(
909 'data' => array(
910 'email' => $email
911 )
912 ) );
913
914 if ( !empty( $response['error'] ) ) {
915
916 wp_send_json_error( $response['error'] );
917 } else {
918
919 $response_body = json_decode( $response['data'], true );
920
921 if ( ! empty( $response_body['key'] ) ) {
922 Config::update( 'cloud_key', $response_body['key'] );
923 }
924 }
925 }
926
927 wp_send_json_success( array( 'email' => $email, 'is_subscribe_yes' => $is_subscribe_yes ) );
928
929 } else if ( empty( $email ) ) {
930 Config::update( 'admin_notify_email', $admin_email );
931 Config::update( 'lockout_notify', '' );
932
933 wp_send_json_success( array( 'email' => $admin_email, 'is_subscribe_yes' => '' ) );
934 }
935
936 wp_send_json_error( array( 'email' => $email, 'is_subscribe_yes' => $is_subscribe_yes ) );
937 }
938
939
940 public function strong_account_policies_callback() {
941
942 if ( ! LimitLoginAttempts::$instance->has_capability ) {
943
944 wp_send_json_error( array() );
945 }
946
947 check_ajax_referer( 'llar-strong-account-policies', 'sec' );
948
949 $is_checklist = sanitize_text_field( trim( $_POST['is_checklist'] ) );
950
951 Config::update( 'checklist', $is_checklist );
952
953 wp_send_json_success();
954 }
955
956 public function block_by_country_callback() {
957
958 if ( ! LimitLoginAttempts::$instance->has_capability ) {
959
960 wp_send_json_error( array() );
961 }
962
963 check_ajax_referer( 'llar-block_by_country', 'sec' );
964
965 $is_checklist = sanitize_text_field( trim( $_POST['is_checklist'] ) );
966
967 Config::update( 'block_by_country', $is_checklist );
968
969 wp_send_json_success();
970 }
971
972
973 public function dismiss_onboarding_popup_callback() {
974
975 $this->check_user_capabilities();
976
977 check_ajax_referer( 'llar-dismiss-onboarding-popup', 'sec' );
978
979 Config::update( 'onboarding_popup_shown', true );
980
981 wp_send_json_success();
982 }
983
984 public function get_remaining_attempts_message_callback() {
985
986 check_ajax_referer( 'llar-get-remaining-attempts-message', 'sec' );
987
988 $remaining = (int) LoginFlowTransientStore::get( 'login_attempts_left', 0 );
989
990 if ( ! empty( $remaining ) && $remaining > 0 ) {
991
992 $message = sprintf( _n( "<strong>%d</strong> attempt remaining.", "<strong>%d</strong> attempts remaining.", $remaining, 'limit-login-attempts-reloaded' ), $remaining );
993 wp_send_json_success( $message );
994 } else {
995
996 wp_send_json_error();
997 }
998 }
999
1000
1001 public function onboarding_reset_callback() {
1002
1003 if ( ! LimitLoginAttempts::$instance->has_capability ) {
1004
1005 wp_send_json_error( array() );
1006 }
1007
1008 check_ajax_referer( 'llar-action-onboarding-reset', 'sec' );
1009
1010 if ( Config::get( Config::OPTION_ACTIVE_APP ) !== 'local' || ! empty( Config::get( 'app_setup_code' ) ) ) {
1011
1012 wp_send_json_error( array() );
1013 }
1014
1015 Config::update( 'onboarding_popup_shown', 0 );
1016
1017 wp_send_json_success();
1018 }
1019
1020
1021 public function close_premium_message() {
1022
1023 $this->check_user_capabilities();
1024
1025 check_ajax_referer( 'llar-close-premium-message', 'sec' );
1026
1027 Config::update( 'notifications_message_shown', strtotime( '+1 day' ) );
1028
1029 wp_send_json_success();
1030 }
1031
1032
1033 public function activate_micro_cloud_callback() {
1034
1035 if ( ! LimitLoginAttempts::$instance->has_capability ) {
1036
1037 wp_send_json_error( array('msg' => 'Wrong country code.') );
1038 }
1039
1040 check_ajax_referer( 'llar-activate-micro-cloud', 'sec' );
1041 $email = sanitize_text_field( trim( $_POST['email'] ) );
1042
1043 if ( ! empty( $email ) && is_email( $email ) ) {
1044
1045 $url_api = defined( 'LLAR_MC_URL' ) ? LLAR_MC_URL : 'https://api.limitloginattempts.com/checkout/network';
1046
1047 $data = [
1048 'group' => 'free',
1049 'email' => $email
1050 ];
1051
1052 $response = Http::post( $url_api, array(
1053 'data' => $data
1054 ) );
1055
1056 if ( ! empty( $response['error'] ) ) {
1057
1058 wp_send_json_error( $response['error'] );
1059
1060 } else {
1061
1062 $response_body = json_decode( $response['data'], true );
1063
1064 if ( ! empty( $response_body['setup_code'] ) ) {
1065
1066 if ( $key_result = CloudApp::activate_license_key( $response_body['setup_code'] ) ) {
1067
1068 if ( $key_result['success'] ) {
1069
1070 wp_send_json_success( array(
1071 'msg' => ( $key_result )
1072 ) );
1073 } else {
1074
1075 wp_send_json_error( array(
1076 'msg' => ( $key_result )
1077 ) );
1078 }
1079 } else {
1080
1081 wp_send_json_error( array(
1082 'msg' => $key_result['error']
1083 ) );
1084 }
1085 }
1086 }
1087 }
1088
1089 wp_send_json_error( array() );
1090 }
1091
1092
1093 public function toggle_auto_update_callback() {
1094
1095 $this->check_user_capabilities();
1096
1097 if ( Helpers::is_block_automatic_update_disabled() ) {
1098
1099 wp_send_json_error( array( 'msg' => 'Can\'t turn auto-updates on. Please ask your hosting provider or developer for assistance.') );
1100 }
1101
1102 check_ajax_referer( 'llar-toggle-auto-update', 'sec' );
1103
1104 $value = sanitize_text_field( $_POST['value'] );
1105 $auto_update_plugins = get_site_option( 'auto_update_plugins', array() );
1106
1107 if( $value === 'yes' ) {
1108 $auto_update_plugins[] = LLA_PLUGIN_BASENAME;
1109 Config::update( 'auto_update_choice', 1 );
1110
1111 } else if ( $value === 'no' ) {
1112 if ( ( $key = array_search( LLA_PLUGIN_BASENAME, $auto_update_plugins ) ) !== false ) {
1113 unset($auto_update_plugins[$key]);
1114 }
1115 Config::update( 'auto_update_choice', 0 );
1116 }
1117
1118 update_site_option( 'auto_update_plugins', $auto_update_plugins );
1119
1120 wp_send_json_success();
1121 }
1122
1123 public function test_email_notifications_callback() {
1124
1125 $this->check_user_capabilities();
1126
1127 check_ajax_referer('llar-test-email-notifications', 'sec');
1128
1129 $to = sanitize_email( $_POST['email'] );
1130
1131 if( empty( $to ) || !is_email( $to ) ) {
1132
1133 wp_send_json_error( array(
1134 'msg' => __( 'Wrong email format.', 'limit-login-attempts-reloaded' ),
1135 ) );
1136 }
1137
1138 if( wp_mail(
1139 $to,
1140 __( 'LLAR Security Notifications [TEST]', 'limit-login-attempts-reloaded' ),
1141 __( 'Your email notifications for Limit Login Attempts Reloaded are working correctly. If this email is going to spam, please be sure to add this address to your safelist.', 'limit-login-attempts-reloaded' )
1142 ) ) {
1143
1144 wp_send_json_success();
1145 } else {
1146
1147 wp_send_json_error();
1148 }
1149 }
1150
1151 /**
1152 * MFA flow: send code to user email (AJAX fallback when REST API is unavailable).
1153 * POST only: token, secret (send_email secret), code in $_POST.
1154 */
1155 public function mfa_flow_send_code_callback() {
1156 check_ajax_referer( 'llar_mfa_flow_send_code', '_ajax_nonce', true );
1157
1158 $method = isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : '';
1159 if ( 'POST' !== $method ) {
1160 status_header( 405 );
1161 wp_send_json_error( array( 'message' => 'Method not allowed' ) );
1162 }
1163
1164 $token = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '';
1165 $secret = isset( $_POST['secret'] ) ? sanitize_text_field( wp_unslash( $_POST['secret'] ) ) : '';
1166 $code = isset( $_POST['code'] ) ? sanitize_text_field( wp_unslash( $_POST['code'] ) ) : '';
1167 $ip = isset( $_POST['ip'] ) ? sanitize_text_field( wp_unslash( $_POST['ip'] ) ) : '';
1168 $browser = isset( $_POST['browser'] ) ? sanitize_text_field( wp_unslash( $_POST['browser'] ) ) : '';
1169 $location = isset( $_POST['location'] ) ? sanitize_text_field( wp_unslash( $_POST['location'] ) ) : '';
1170 $context = array(
1171 'ip' => is_string( $ip ) ? $ip : '',
1172 'browser' => is_string( $browser ) ? $browser : '',
1173 'location' => is_string( $location ) ? $location : '',
1174 );
1175
1176 if ( '' === $token || '' === $secret ) {
1177 status_header( 403 );
1178 wp_send_json_error( array( 'message' => 'Forbidden' ) );
1179 }
1180
1181 $result = \LLAR\Core\MfaFlow\MfaFlowSendCode::execute( $token, $secret, $code, $context );
1182 $status = isset( $result['http_status'] ) ? (int) $result['http_status'] : 200;
1183 $message = isset( $result['message'] ) ? $result['message'] : '';
1184
1185 status_header( $status );
1186 if ( ! empty( $result['success'] ) ) {
1187 wp_send_json_success();
1188 }
1189 wp_send_json_error( array( 'message' => $message ? $message : 'Forbidden' ) );
1190 }
1191
1192 /**
1193 * Access capabilities checks
1194 */
1195 private function check_user_capabilities() {
1196
1197 if ( ! LimitLoginAttempts::$instance->has_capability ) {
1198
1199 wp_send_json_error( array() );
1200 }
1201 }
1202 }
1203