PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.1.0
Defender Security – Malware Scanner, Login Security & Firewall v3.1.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / controller / blacklist.php

blacklist.php in Defender Security – Malware Scanner, Login Security & Firewall 3.1.0, at src/controller/blacklist.php

545 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WP_Defender\Controller;
4
5 use Calotes\Component\Request;
6 use Calotes\Component\Response;
7 use WP_Defender\Component\Config\Config_Hub_Helper;
8 use WP_Defender\Controller;
9 use WP_Defender\Model\Lockout_Ip;
10 use WP_Defender\Model\Setting\Blacklist_Lockout as Model_Blacklist_Lockout;
11 use WP_Defender\Traits\Country;
12 use WP_Defender\Traits\IP;
13 use WP_Defender\Integrations\MaxMind_Geolocation;
14
15 /**
16 * Class Blacklist
17 *
18 * @package WP_Defender\Controller
19 */
20 class Blacklist extends Controller {
21 use IP, Country;
22
23 /**
24 * @var string
25 */
26 protected $slug = 'wdf-ip-lockout';
27
28 /**
29 * @var Model_Blacklist_Lockout
30 */
31 protected $model;
32
33 /**
34 * @var \WP_Defender\Component\Blacklist_Lockout
35 */
36 protected $service;
37
38 public function __construct() {
39 $this->register_routes();
40 add_action( 'defender_enqueue_assets', array( &$this, 'enqueue_assets' ) );
41 $this->model = wd_di()->get( Model_Blacklist_Lockout::class );
42 $this->service = wd_di()->get( \WP_Defender\Component\Blacklist_Lockout::class );
43 add_action( 'wd_blacklist_this_ip', array( &$this, 'blacklist_an_ip' ) );
44 // Update MaxMind's DB.
45 if ( ! empty( $this->model->maxmind_license_key ) ) {
46 if ( ! wp_next_scheduled( 'wpdef_update_geoip' ) ) {
47 wp_schedule_event( strtotime( 'next Thursday' ), 'weekly', 'wpdef_update_geoip' );
48 }
49 // @since 2.8.0 Allows update or remove the database of MaxMind automatic and periodically (MaxMind's TOS).
50 $bind_updater = (bool) apply_filters( 'wd_update_maxmind_database', true );
51 // Bind to the scheduled updater action.
52 if ( $bind_updater ) {
53 add_action( 'wpdef_update_geoip', array( &$this, 'update_database' ) );
54 }
55 }
56 }
57
58 /**
59 * Add an IP into blacklist.
60 *
61 * @param $ip
62 */
63 public function blacklist_an_ip( $ip ) {
64 $this->model->add_to_list( $ip, 'blocklist' );
65 }
66
67 public function enqueue_assets() {
68 if ( ! $this->is_page_active() ) {
69 return;
70 }
71 wp_localize_script( 'def-iplockout', 'blacklist', $this->data_frontend() );
72 }
73
74 /**
75 * All the variables that we will show on frontend, both in the main page, or dashboard widget.
76 *
77 * @return array
78 */
79 public function data_frontend() {
80 $user_ip = $this->get_user_ip();
81 $arr_model = $this->model->export();
82 $exist_geodb = $this->service->is_geodb_downloaded();
83 // If MaxMind GeoIP DB is downloaded then display the required data.
84 if ( $exist_geodb ) {
85 $country_list = $this->countries_list();
86 $blacklist_countries = array_merge( array( 'all' => __( 'Block all', 'wpdef' ) ), $country_list );
87 $whitelist_countries = array_merge( array( 'all' => __( 'Allow all', 'wpdef' ) ), $country_list );
88 } else {
89 $blacklist_countries = $whitelist_countries = array();
90 }
91
92 return array_merge(
93 array(
94 'model' => $arr_model,
95 'misc' => array(
96 'user_ip' => $user_ip,
97 'is_geodb_downloaded' => $exist_geodb,
98 'blacklist_countries' => $blacklist_countries,
99 'whitelist_countries' => $whitelist_countries,
100 'current_country' => $this->get_current_country( $user_ip ),
101 'no_ips' => '' === $arr_model['ip_blacklist'] && '' === $arr_model['ip_whitelist'],
102 ),
103 ),
104 $this->dump_routes_and_nonces()
105 );
106 }
107
108 /**
109 * @param Request $request
110 *
111 * @return Response
112 * @defender_route
113 */
114 public function save_settings( Request $request ) {
115 $data = $request->get_data(
116 array(
117 'country_blacklist' => array(
118 'type' => 'array',
119 ),
120 'country_whitelist' => array(
121 'type' => 'array',
122 ),
123 'ip_blacklist' => array(
124 'type' => 'string',
125 'sanitize' => 'sanitize_textarea_field',
126 ),
127 'ip_whitelist' => array(
128 'type' => 'string',
129 'sanitize' => 'sanitize_textarea_field',
130 ),
131 'ip_lockout_message' => array(
132 'type' => 'string',
133 'sanitize' => 'sanitize_textarea_field',
134 ),
135 )
136 );
137 $this->model->import( $data );
138 if ( $this->model->validate() ) {
139 $this->model->save();
140 Config_Hub_Helper::set_clear_active_flag();
141
142 return new Response(
143 true,
144 array_merge(
145 array(
146 'message' => __( 'Your settings have been updated.', 'wpdef' ),
147 ),
148 $this->data_frontend()
149 )
150 );
151 }
152
153 return new Response(
154 false,
155 array_merge(
156 array( 'message' => $this->model->get_formatted_errors() ),
157 $this->data_frontend()
158 )
159 );
160 }
161
162 /**
163 * Download the GEODB IP from Maxmind.
164 *
165 * @param Request $request
166 *
167 * @return Response
168 * @defender_route
169 */
170 public function download_geodb( Request $request ) {
171 $data = $request->get_data( [
172 'license_key' => [
173 'type' => 'string',
174 'sanitize' => 'sanitize_text_field'
175 ]
176 ] );
177 $license_key = $data['license_key'];
178 $service_geo = wd_di()->get( MaxMind_Geolocation::class );
179 $tmp = $service_geo->get_downloaded_url( $license_key );
180 if ( ! is_wp_error( $tmp ) ) {
181 $phar = new \PharData( $tmp );
182 $path = $this->get_tmp_path() . DIRECTORY_SEPARATOR . 'maxmind';
183 if ( ! is_dir( $path ) ) {
184 wp_mkdir_p( $path );
185 }
186 $phar->extractTo( $path, null, true );
187 // Todo: move logic for the path to MaxMind_Geolocation class.
188 $this->model->geodb_path = $path . DIRECTORY_SEPARATOR . $phar->current()->getFileName() . DIRECTORY_SEPARATOR . $service_geo->get_db_full_name();
189 // Save because we'll check for a saved path.
190 $this->model->save();
191
192 if ( file_exists( $tmp ) ) {
193 unlink( $tmp );
194 }
195
196 $country = $this->get_current_country( $this->get_user_ip() );
197 $current_country = '';
198 if ( ! empty( $country ) && ! empty( $country['iso'] ) ) {
199 $current_country = $country['iso'];
200 $this->model = $this->service->add_default_whitelisted_country( $this->model, $country['iso'] );
201 }
202 $this->model->maxmind_license_key = $license_key;
203 $this->model->save();
204
205 return new Response( true, [
206 'message' => __(
207 'You have successfully downloaded Geo IP Database. You can now use this feature to ban any countries to access any area of your website.',
208 'wpdef'
209 ),
210 'is_geodb_downloaded' => $this->service->is_geodb_downloaded(),
211 'current_country' => $current_country,
212 ] );
213 } else {
214 $this->log( 'Error from MaxMind: ' . $tmp->get_error_message() );
215 $string = sprintf(
216 /* translators: ... */
217 __(
218 'You have entered an invalid <a target="_blank" href="%s">license key</a>. If you just created the key, please wait 5 minutes before trying to activate it.',
219 'wpdef'
220 ),
221 'https://www.maxmind.com/en/accounts/current/license-key'
222 );
223
224 if ( ( new \WP_Defender\Behavior\WPMUDEV() )->show_support_links() ) {
225 $string .= sprintf(
226 /* translators: ... */
227 __( ' Still having trouble? <a target="_blank" href="%s">Open a support ticket</a>.', 'wpdef' ),
228 WP_DEFENDER_SUPPORT_LINK
229 );
230 }
231
232 return new Response( false, array( 'invalid_text' => $string ) );
233 }
234 }
235
236 /**
237 * @defender_route
238 */
239 public function export_ips() {
240 $data = array();
241
242 foreach ( $this->model->get_list( 'blocklist' ) as $ip ) {
243 $data[] = array(
244 'ip' => $ip,
245 'type' => 'blocklist',
246 );
247 }
248 foreach ( $this->model->get_list( 'allowlist' ) as $ip ) {
249 $data[] = array(
250 'ip' => $ip,
251 'type' => 'allowlist',
252 );
253 }
254
255 $fp = fopen( 'php://memory', 'w' );
256 foreach ( $data as $fields ) {
257 fputcsv( $fp, $fields );
258 }
259 $filename = 'wdf-ips-export-' . gmdate( 'ymdHis' ) . '.csv';
260 fseek( $fp, 0 );
261 header( 'Content-Type: text/csv' );
262 header( 'Content-Disposition: attachment; filename="' . $filename . '";' );
263 // Make php send the generated csv lines to the browser.
264 fpassthru( $fp );
265 exit();
266 }
267
268 /**
269 * @param Request $request
270 *
271 * @return Response
272 * @throws \Exception
273 * @defender_route
274 */
275 public function ip_action( Request $request ) {
276 $data = $request->get_data( [
277 'ip' => [
278 'type' => 'string',
279 'sanitize' => 'sanitize_text_field'
280 ],
281 'behavior' => [
282 'type' => 'string',
283 'sanitize' => 'sanitize_text_field'
284 ],
285 ] );
286
287 $ip = $data['ip'];
288 $action = $data['behavior'];
289 $models = Lockout_Ip::get( $ip, $action, true );
290
291 foreach( $models as $model ) {
292 if ( 'unban' === $action ) {
293 $model->status = Lockout_Ip::STATUS_NORMAL;
294 $model->save();
295 } elseif ( 'ban' === $action ) {
296 $model->status = Lockout_Ip::STATUS_BLOCKED;
297 $model->save();
298 }
299 }
300
301 $this->query_locked_ips( $request );
302 }
303
304 /**
305 * Bulk ban or unban IPs.
306 * @param Request $request
307 *
308 * @return Response
309 * @throws \Exception
310 * @defender_route
311 */
312 public function bulk_ip_action( Request $request ) {
313 $data = $request->get_data( [
314 'behavior' => [
315 'type' => 'string',
316 'sanitize' => 'sanitize_text_field'
317 ],
318 'ips' => [
319 'type' => 'string',
320 'sanitize' => 'sanitize_text_field'
321 ],
322 ] );
323
324 $status = 'unban' === $data['behavior'] ? Lockout_Ip::STATUS_BLOCKED : Lockout_Ip::STATUS_NORMAL;
325 $ips = null;
326 $bulk_ips = null;
327 $limit = 50;
328
329 if ( ! empty( $data['ips'] ) ) {
330 $ips = json_decode( $data['ips'] );
331 $first_nth_ips = array_slice( $ips, 0, $limit );
332 $bulk_ips = wp_list_pluck( $first_nth_ips, 'ip' );
333 }
334
335 try {
336 $models = Lockout_Ip::get_bulk( $status, $bulk_ips, $limit );
337 foreach( $models as $model ) {
338 $model->status = ( 'unban' === $data['behavior'] ) ? Lockout_Ip::STATUS_NORMAL : Lockout_Ip::STATUS_BLOCKED;
339 $model->save();
340 }
341 // While bulk banning the IPs, needs to slice the IPs array for next iteration.
342 if ( 'ban' === $data['behavior'] ) {
343 $ips = array_slice( $ips, $limit );
344 }
345 // If the queried models are less than the limit it means we are on the last set of IPs.
346 if ( (is_array($models) || $models instanceof \Countable ? count( $models ) : 0) < $limit ) {
347 return new Response( true, [
348 'status' => 'done'
349 ] );
350 }
351 } catch( \Exception $e ) {
352 return new Response( true, [
353 'status' => 'error'
354 ] );
355 }
356
357 return new Response( true, [
358 'status' => 'continue',
359 'ips' => $ips
360 ] );
361 }
362
363 /**
364 * @param Request $request
365 *
366 * @return Response
367 * @throws \Exception
368 * @defender_route
369 */
370 public function query_locked_ips( Request $request ) {
371 $results = \WP_Defender\Model\Lockout_Ip::query_locked_ip();
372 $locked_ips = array();
373 if ( ! empty( $results ) ) {
374 foreach ( $results as $key => $locked_ip ) {
375 $locked_ips[] = array(
376 'id' => $locked_ip->id,
377 'ip' => $locked_ip->ip,
378 'status' => $locked_ip->status,
379 );
380 }
381 }
382
383 return new Response( true, [
384 'ips' => $locked_ips
385 ] );
386 }
387
388 /**
389 * Export the data of this module, we will use this for export to HUB, create a preset etc.
390 */
391 public function to_array() {}
392
393 /**
394 * @param array $data
395 *
396 * @return array
397 */
398 private function adapt_data( $data ) {
399 $adapted_data = array(
400 'ip_blacklist' => $data['ip_blacklist'],
401 'ip_whitelist' => $data['ip_whitelist'],
402 'ip_lockout_message' => $data['ip_lockout_message'],
403 );
404 if ( isset( $data['geoIP_db'] ) && file_exists( $data['geoIP_db'] ) ) {
405 $adapted_data['geodb_path'] = $data['geoIP_db'];
406 if ( isset( $data['country_blacklist'] ) ) {
407 $adapted_data['country_blacklist'] = $data['country_blacklist'];
408 }
409 if ( isset( $data['country_whitelist'] ) ) {
410 $adapted_data['country_whitelist'] = $data['country_whitelist'];
411 }
412 }
413
414 return array_merge( $data, $adapted_data );
415 }
416
417 /**
418 * Import the data of other source into this, it can be when HUB trigger the import, or user apply a preset.
419 * @param array $data
420 *
421 * @return void
422 */
423 public function import_data( $data ) {
424 if ( ! empty( $data ) ) {
425 // Upgrade for old versions.
426 $data = $this->adapt_data( $data );
427 } else {
428 return;
429 }
430
431 $model = $this->model;
432 $model->import( $data );
433 if ( $model->validate() ) {
434 $model->save();
435 }
436 }
437
438 /**
439 * Remove all settings, configs generated in this container runtime.
440 */
441 public function remove_settings() {}
442
443 /**
444 * Remove all data.
445 */
446 public function remove_data() {}
447
448 /**
449 * @return array
450 */
451 public function export_strings() {
452 return [];
453 }
454
455 /**
456 * Importing IPs from exporter.
457 *
458 * @param Request $request
459 *
460 * @defender_route
461 * @return Response
462 */
463 public function import_ips( Request $request ) {
464 $data = $request->get_data(
465 array(
466 'id' => array(
467 'type' => 'int',
468 ),
469 )
470 );
471 $attached_id = $data['id'];
472 if ( ! is_object( get_post( $attached_id ) ) ) {
473 return new Response(
474 false,
475 array(
476 'message' => __( 'Your file is invalid!', 'wpdef' ),
477 )
478 );
479 }
480
481 $file = get_attached_file( $attached_id );
482 if ( ! is_file( $file ) ) {
483 return new Response(
484 false,
485 array(
486 'message' => __( 'Your file is invalid!', 'wpdef' ),
487 )
488 );
489 }
490
491 $data = $this->service->verify_import_file( $file );
492 if ( ! $data ) {
493 return new Response(
494 false,
495 array(
496 'message' => __( 'Your file content is invalid!', 'wpdef' ),
497 )
498 );
499 }
500
501 // All good, start to import.
502 foreach ( $data as $line ) {
503 $this->model->add_to_list( $line[0], $line[1] );
504 }
505
506 return new Response(
507 true,
508 array(
509 'message' => __( 'Your allowlist/blocklist has been successfully imported.', 'wpdef' ),
510 'interval' => 1,
511 )
512 );
513 }
514
515 /**
516 * Update the geolocation database.
517 *
518 * @since 2.8.0
519 *
520 * @return void
521 */
522 public function update_database() {
523 if ( empty( $this->model->maxmind_license_key ) ) {
524 return;
525 }
526
527 $service_geo = wd_di()->get( MaxMind_Geolocation::class );
528 $service_geo->delete_database();
529
530 $tmp = $service_geo->get_downloaded_url( $this->model->maxmind_license_key );
531 if ( is_wp_error( $tmp ) ) {
532 $this->log( 'CRON error downloading from MaxMind: ' . $tmp->get_error_message() );
533 return;
534 }
535
536 $geodb_path = $service_geo->extract_db_archive( $tmp );
537 if ( is_wp_error( $geodb_path ) ) {
538 $this->log( 'CRON error extracting MaxMind archive: ' . $geodb_path->get_error_message() );
539 return;
540 }
541 $this->model->geodb_path = $geodb_path;
542 $this->model->save();
543 }
544 }
545