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 / CloudApp.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
CloudApp.php
478 lines
1 <?php
2
3 namespace LLAR\Core;
4
5 use Exception;
6 use LLAR\Core\Http\Http;
7
8 if ( ! defined( 'ABSPATH' ) ) exit;
9
10 class CloudApp
11 {
12 /**
13 * @var null|string
14 */
15 private $id = null;
16
17 /**
18 * @var null|string
19 */
20 private $api = null;
21
22 /**
23 * @var array
24 */
25 private $config = array();
26
27 /**
28 * @var array
29 */
30 private $login_errors = array();
31
32 /**
33 * @var null
34 */
35 public $last_response_code = null;
36
37 /**
38 * @var string|null
39 */
40 public $last_error_message = null;
41
42 /**
43 * @var array
44 */
45 private $stats_cache = array();
46
47 /**
48 * App constructor.
49 * @param array $config
50 */
51 public function __construct( array $config )
52 {
53 if ( empty( $config ) ) {
54 return false;
55 }
56
57 $this->id = 'app_' . $config['id'];
58 $this->api = $config['api'];
59 $this->config = $config;
60 }
61
62 /**
63 * @param $error
64 * @return bool
65 */
66 public function add_error( $error )
67 {
68 if ( ! $error ) {
69 return false;
70 }
71
72 $this->login_errors[] = $error;
73 }
74
75 /**
76 * @return array
77 */
78 public function get_errors()
79 {
80 return $this->login_errors;
81 }
82
83 /**
84 * @return null|string
85 */
86 public function get_id()
87 {
88 return $this->id;
89 }
90
91 /**
92 * @return array
93 */
94 public function get_config()
95 {
96 return $this->config;
97 }
98
99 /**
100 * @param $link
101 * @return false[]
102 */
103 public static function setup( $link )
104 {
105 $return = array(
106 'success' => false,
107 );
108
109 if ( empty( $link ) ) {
110 return $return;
111 }
112
113 $link = 'https://' . $link;
114
115 $domain = parse_url( home_url( '/' ) );
116 $link = add_query_arg( 'domain', $domain['host'], $link );
117
118 $plugin_data = get_plugin_data( LLA_PLUGIN_DIR . 'limit-login-attempts-reloaded.php' );
119 $link = add_query_arg( 'version', $plugin_data['Version'], $link );
120
121 $setup_response = Http::get( $link );
122
123 $response_data = isset( $setup_response['data'] ) ? $setup_response['data'] : '{}';
124 $setup_response_body = json_decode( $response_data, true );
125
126 if ( ! empty( $setup_response['error'] ) ) {
127
128 $return['error'] = $setup_response['error'];
129
130 } elseif( $setup_response['status'] === 200 ) {
131
132 $return['success'] = true;
133 $return['app_config'] = $setup_response_body;
134
135 } else {
136
137 $return['error'] = ( ! empty( $setup_response_body['message'] ) )
138 ? $setup_response_body['message']
139 : __( 'The endpoint is not responding. Please contact your app provider to settle that.', 'limit-login-attempts-reloaded' );
140
141 $return['response_code'] = $setup_response['status'];
142 }
143
144 return $return;
145 }
146
147 public static function activate_license_key( $setup_code )
148 {
149 $link = strrev( $setup_code );
150 $setup_result = self::setup( $link );
151
152 if ( $setup_result['success'] ) {
153
154 if ( $setup_result['app_config'] ) {
155
156 Helpers::cloud_app_update_config( $setup_result['app_config'], true );
157
158 Config::update( Config::OPTION_ACTIVE_APP, 'custom' );
159 Config::update( 'app_setup_code', $setup_code );
160
161 $setup_result['app_config']['messages']['setup_success'] =
162 ! empty( $setup_result['app_config']['messages']['setup_success'] )
163 ? $setup_result['app_config']['messages']['setup_success']
164 : __( 'The app has been successfully imported.', 'limit-login-attempts-reloaded' );
165
166 return $setup_result;
167 }
168 } else {
169
170 return $setup_result;
171 }
172
173 return $setup_result;
174 }
175
176 /**
177 * @return bool|mixed
178 * @throws Exception
179 */
180 public function stats()
181 {
182 if ( empty( $this->stats_cache ) ) {
183 $this->stats_cache = $this->request( 'stats' );
184 }
185
186 return $this->stats_cache;
187 }
188
189 /**
190 * @return bool|mixed
191 */
192 public static function stats_global()
193 {
194 $response = Http::get( 'https://api.limitloginattempts.com/v1/global-stats' );
195
196 if ( $response['status'] !== 200 ) {
197 return false;
198 }
199
200 return json_decode( $response['data'], true );
201 }
202
203 /**
204 * @param $data
205 *
206 * @return bool|mixed
207 * @throws Exception
208 */
209 public function acl_check( $data )
210 {
211 $this->prepare_settings( 'acl', $data );
212
213 return $this->request( 'acl', 'post', $data );
214 }
215
216 /**
217 * @param $data
218 *
219 * @return bool|mixed
220 * @throws Exception
221 */
222 public function acl( $data )
223 {
224 return $this->request( 'acl', 'get', $data );
225 }
226
227 /**
228 * @return bool|mixed
229 * @throws Exception
230 */
231 public function info()
232 {
233 $info = $this->request( 'info' );
234 if ( ! $info && ! defined( 'LLAR_FAILED_INFO_NOTICE_SHOWN' )) {
235 define( 'LLAR_FAILED_INFO_NOTICE_SHOWN', true );
236 $details = array();
237
238 if ( null !== $this->last_response_code ) {
239 $details[] = sprintf(
240 /* translators: %d: HTTP response code. */
241 __( 'Code: %d', 'limit-login-attempts-reloaded' ),
242 intval( $this->last_response_code )
243 );
244 }
245
246 if ( ! empty( $this->last_error_message ) ) {
247 $details[] = sprintf(
248 /* translators: %s: technical error reason. */
249 __( 'Reason: %s', 'limit-login-attempts-reloaded' ),
250 esc_html( $this->get_readable_error_reason( $this->last_error_message ) )
251 );
252 }
253
254 $message = __( 'Oops, it looks like the site is having a temporary network issue.', 'limit-login-attempts-reloaded' );
255
256 if ( ! empty( $details ) ) {
257 $message .= ' (' . implode( '; ', $details ) . ')';
258 }
259
260 echo '<div class="notice notice-error" style="display: block;"><p>' . $message . '</p>';
261 echo '<p><a href="javascript:void(0);" onclick="window.location.reload();" class="button button-primary">' . __( 'Click here to refresh the page', 'limit-login-attempts-reloaded' ) . '</a></p></div>';
262 }
263
264 return $info;
265 }
266
267 /**
268 * @param $data
269 *
270 * @return bool|mixed
271 * @throws Exception
272 */
273 public function acl_create( $data )
274 {
275 return $this->request( 'acl/create', 'post', $data );
276 }
277
278 /**
279 * @param $data
280 *
281 * @return bool|mixed
282 * @throws Exception
283 */
284 public function acl_delete( $data )
285 {
286 return $this->request( 'acl/delete', 'post', $data );
287 }
288
289 /**
290 * @return bool|mixed
291 * @throws Exception
292 */
293 public function country()
294 {
295 return $this->request( 'country', 'get' );
296 }
297
298 /**
299 * @param $data
300 * @return bool|mixed
301 * @throws Exception
302 */
303 public function country_add( $data )
304 {
305 return $this->request( 'country/add', 'post', $data );
306 }
307
308 /**
309 * @param $data
310 * @return bool|mixed
311 * @throws Exception
312 */
313 public function country_remove( $data )
314 {
315 return $this->request( 'country/remove', 'post', $data );
316 }
317
318 /**
319 * @param $data
320 *
321 * @return bool|mixed
322 * @throws Exception
323 */
324 public function country_rule( $data )
325 {
326 return $this->request( 'country/rule', 'post', $data );
327 }
328
329 /**
330 * @param $data
331 *
332 * @return bool|mixed
333 * @throws Exception
334 */
335 public function lockout_check( $data )
336 {
337 $this->prepare_settings( 'lockout', $data );
338
339 return $this->request( 'lockout', 'post', $data );
340 }
341
342 /**
343 * @param int $limit
344 * @param string $offset
345 *
346 * @return bool|mixed
347 * @throws Exception
348 */
349 public function log($limit = 25, $offset = '')
350 {
351 $data = array();
352
353 $data['limit'] = $limit;
354 $data['offset'] = $offset;
355 $data['is_short'] = 1;
356
357 return $this->request( 'log', 'get', $data );
358 }
359
360
361 /**
362 * @param int $limit
363 * @param string $offset
364 *
365 * @return bool|mixed
366 * @throws Exception
367 */
368 public function get_login($limit = 25, $offset = '')
369 {
370 $data = array();
371
372 $data['limit'] = $limit;
373 $data['offset'] = $offset;
374 $data['is_short'] = 1;
375
376 return $this->request( 'login', 'get', $data );
377 }
378
379
380 /**
381 * @param int $limit
382 * @param string $offset
383 *
384 * @return bool|mixed
385 * @throws Exception
386 */
387 public function get_lockouts($limit = 25, $offset = '')
388 {
389 $data = array();
390
391 $data['limit'] = $limit;
392 $data['offset'] = $offset;
393
394 return $this->request( 'lockout', 'get', $data );
395 }
396
397 /**
398 * Prepare settings for API request
399 *
400 * @param $method
401 */
402 public function prepare_settings( $method, &$data )
403 {
404 $settings = array();
405
406 if ( ! empty( $this->config['settings'] ) ) {
407
408 foreach ( $this->config['settings'] as $setting_name => $setting_data ) {
409
410 if ( in_array( $method, $setting_data['methods'] ) ) {
411 $settings[$setting_name] = $setting_data['value'];
412 }
413 }
414 }
415
416 if ( $settings ) {
417 $data['settings'] = $settings;
418 }
419 }
420
421 /**
422 * @param $method
423 * @param string $type
424 * @param null $data
425 * @return bool|mixed
426 * @throws Exception
427 */
428 public function request( $method, $type = 'get', $data = null )
429 {
430 if ( ! $method ) {
431 throw new Exception( 'You must specify API method.' );
432 }
433
434 $headers = array();
435 $headers[] = "{$this->config['header']}: {$this->config['key']}";
436
437 $response = Http::$type( $this->api . '/' . $method, array(
438 'data' => $data,
439 'headers' => $headers
440 ) );
441
442 $this->last_response_code = !empty( $response['status'] ) ? $response['status'] : 0;
443 $this->last_error_message = ! empty( $response['error'] ) ? $response['error'] : null;
444
445 if ( 200 !== $response['status'] ) {
446 error_log( 'LLAR: CloudApp request failed: ' . $this->api . '/' . $method . ' ' . $this->last_response_code );
447 return false;
448 }
449
450 $decoded = json_decode( $response['data'], true );
451
452 return Helpers::sanitize_stripslashes_deep( $decoded );
453 }
454
455 /**
456 * Tries to keep technical reason readable and avoid leaking full URL.
457 *
458 * @param string $error_message
459 * @return string
460 */
461 private function get_readable_error_reason( $error_message ) {
462 $reason = trim( (string) $error_message );
463
464 if ( false !== strpos( $reason, 'Failed to open stream:' ) ) {
465 $parts = explode( 'Failed to open stream:', $reason, 2 );
466 if ( ! empty( $parts[1] ) ) {
467 $reason = trim( $parts[1] );
468 }
469 }
470
471 if ( 220 < strlen( $reason ) ) {
472 $reason = substr( $reason, 0, 217 ) . '...';
473 }
474
475 return $reason;
476 }
477 }
478