PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.0.1
Defender Security – Malware Scanner, Login Security & Firewall v3.0.1
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.0.1, at src/controller/blacklist.php

547 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 __(
228 ' Still having trouble? <a target="_blank" href="%s">Open a support ticket</a>.',
229 'wpdef'
230 ),
231 'https://wpmudev.com/hub2/support/#get-support'
232 );
233 }
234 return new Response( false, array( 'invalid_text' => $string ) );
235 }
236 }
237
238 /**
239 * @defender_route
240 */
241 public function export_ips() {
242 $data = array();
243
244 foreach ( $this->model->get_list( 'blocklist' ) as $ip ) {
245 $data[] = array(
246 'ip' => $ip,
247 'type' => 'blocklist',
248 );
249 }
250 foreach ( $this->model->get_list( 'allowlist' ) as $ip ) {
251 $data[] = array(
252 'ip' => $ip,
253 'type' => 'allowlist',
254 );
255 }
256
257 $fp = fopen( 'php://memory', 'w' );
258 foreach ( $data as $fields ) {
259 fputcsv( $fp, $fields );
260 }
261 $filename = 'wdf-ips-export-' . gmdate( 'ymdHis' ) . '.csv';
262 fseek( $fp, 0 );
263 header( 'Content-Type: text/csv' );
264 header( 'Content-Disposition: attachment; filename="' . $filename . '";' );
265 // Make php send the generated csv lines to the browser.
266 fpassthru( $fp );
267 exit();
268 }
269
270 /**
271 * @param Request $request
272 *
273 * @return Response
274 * @throws \Exception
275 * @defender_route
276 */
277 public function ip_action( Request $request ) {
278 $data = $request->get_data( [
279 'ip' => [
280 'type' => 'string',
281 'sanitize' => 'sanitize_text_field'
282 ],
283 'behavior' => [
284 'type' => 'string',
285 'sanitize' => 'sanitize_text_field'
286 ],
287 ] );
288
289 $ip = $data['ip'];
290 $action = $data['behavior'];
291 $models = Lockout_Ip::get( $ip, $action, true );
292
293 foreach( $models as $model ) {
294 if ( 'unban' === $action ) {
295 $model->status = Lockout_Ip::STATUS_NORMAL;
296 $model->save();
297 } elseif ( 'ban' === $action ) {
298 $model->status = Lockout_Ip::STATUS_BLOCKED;
299 $model->save();
300 }
301 }
302
303 $this->query_locked_ips( $request );
304 }
305
306 /**
307 * Bulk ban or unban IPs.
308 * @param Request $request
309 *
310 * @return Response
311 * @throws \Exception
312 * @defender_route
313 */
314 public function bulk_ip_action( Request $request ) {
315 $data = $request->get_data( [
316 'behavior' => [
317 'type' => 'string',
318 'sanitize' => 'sanitize_text_field'
319 ],
320 'ips' => [
321 'type' => 'string',
322 'sanitize' => 'sanitize_text_field'
323 ],
324 ] );
325
326 $status = 'unban' === $data['behavior'] ? Lockout_Ip::STATUS_BLOCKED : Lockout_Ip::STATUS_NORMAL;
327 $ips = null;
328 $bulk_ips = null;
329 $limit = 50;
330
331 if ( ! empty( $data['ips'] ) ) {
332 $ips = json_decode( $data['ips'] );
333 $first_nth_ips = array_slice( $ips, 0, $limit );
334 $bulk_ips = wp_list_pluck( $first_nth_ips, 'ip' );
335 }
336
337 try {
338 $models = Lockout_Ip::get_bulk( $status, $bulk_ips, $limit );
339 foreach( $models as $model ) {
340 $model->status = ( 'unban' === $data['behavior'] ) ? Lockout_Ip::STATUS_NORMAL : Lockout_Ip::STATUS_BLOCKED;
341 $model->save();
342 }
343 // While bulk banning the IPs, needs to slice the IPs array for next iteration.
344 if ( 'ban' === $data['behavior'] ) {
345 $ips = array_slice( $ips, $limit );
346 }
347 // If the queried models are less than the limit it means we are on the last set of IPs.
348 if ( (is_array($models) || $models instanceof \Countable ? count( $models ) : 0) < $limit ) {
349 return new Response( true, [
350 'status' => 'done'
351 ] );
352 }
353 } catch( \Exception $e ) {
354 return new Response( true, [
355 'status' => 'error'
356 ] );
357 }
358
359 return new Response( true, [
360 'status' => 'continue',
361 'ips' => $ips
362 ] );
363 }
364
365 /**
366 * @param Request $request
367 *
368 * @return Response
369 * @throws \Exception
370 * @defender_route
371 */
372 public function query_locked_ips( Request $request ) {
373 $results = \WP_Defender\Model\Lockout_Ip::query_locked_ip();
374 $locked_ips = array();
375 if ( ! empty( $results ) ) {
376 foreach ( $results as $key => $locked_ip ) {
377 $locked_ips[] = array(
378 'id' => $locked_ip->id,
379 'ip' => $locked_ip->ip,
380 'status' => $locked_ip->status,
381 );
382 }
383 }
384
385 return new Response( true, [
386 'ips' => $locked_ips
387 ] );
388 }
389
390 /**
391 * Export the data of this module, we will use this for export to HUB, create a preset etc.
392 */
393 public function to_array() {}
394
395 /**
396 * @param array $data
397 *
398 * @return array
399 */
400 private function adapt_data( $data ) {
401 $adapted_data = array(
402 'ip_blacklist' => $data['ip_blacklist'],
403 'ip_whitelist' => $data['ip_whitelist'],
404 'ip_lockout_message' => $data['ip_lockout_message'],
405 );
406 if ( isset( $data['geoIP_db'] ) && file_exists( $data['geoIP_db'] ) ) {
407 $adapted_data['geodb_path'] = $data['geoIP_db'];
408 if ( isset( $data['country_blacklist'] ) ) {
409 $adapted_data['country_blacklist'] = $data['country_blacklist'];
410 }
411 if ( isset( $data['country_whitelist'] ) ) {
412 $adapted_data['country_whitelist'] = $data['country_whitelist'];
413 }
414 }
415
416 return array_merge( $data, $adapted_data );
417 }
418
419 /**
420 * Import the data of other source into this, it can be when HUB trigger the import, or user apply a preset.
421 * @param array $data
422 *
423 * @return void
424 */
425 public function import_data( $data ) {
426 if ( ! empty( $data ) ) {
427 // Upgrade for old versions.
428 $data = $this->adapt_data( $data );
429 } else {
430 return;
431 }
432
433 $model = $this->model;
434 $model->import( $data );
435 if ( $model->validate() ) {
436 $model->save();
437 }
438 }
439
440 /**
441 * Remove all settings, configs generated in this container runtime.
442 */
443 public function remove_settings() {}
444
445 /**
446 * Remove all data.
447 */
448 public function remove_data() {}
449
450 /**
451 * @return array
452 */
453 public function export_strings() {
454 return [];
455 }
456
457 /**
458 * Importing IPs from exporter.
459 *
460 * @param Request $request
461 *
462 * @defender_route
463 * @return Response
464 */
465 public function import_ips( Request $request ) {
466 $data = $request->get_data(
467 array(
468 'id' => array(
469 'type' => 'int',
470 ),
471 )
472 );
473 $attached_id = $data['id'];
474 if ( ! is_object( get_post( $attached_id ) ) ) {
475 return new Response(
476 false,
477 array(
478 'message' => __( 'Your file is invalid!', 'wpdef' ),
479 )
480 );
481 }
482
483 $file = get_attached_file( $attached_id );
484 if ( ! is_file( $file ) ) {
485 return new Response(
486 false,
487 array(
488 'message' => __( 'Your file is invalid!', 'wpdef' ),
489 )
490 );
491 }
492
493 $data = $this->service->verify_import_file( $file );
494 if ( ! $data ) {
495 return new Response(
496 false,
497 array(
498 'message' => __( 'Your file content is invalid!', 'wpdef' ),
499 )
500 );
501 }
502
503 // All good, start to import.
504 foreach ( $data as $line ) {
505 $this->model->add_to_list( $line[0], $line[1] );
506 }
507
508 return new Response(
509 true,
510 array(
511 'message' => __( 'Your allowlist/blocklist has been successfully imported.', 'wpdef' ),
512 'interval' => 1,
513 )
514 );
515 }
516
517 /**
518 * Update the geolocation database.
519 *
520 * @since 2.8.0
521 *
522 * @return void
523 */
524 public function update_database() {
525 if ( empty( $this->model->maxmind_license_key ) ) {
526 return;
527 }
528
529 $service_geo = wd_di()->get( MaxMind_Geolocation::class );
530 $service_geo->delete_database();
531
532 $tmp = $service_geo->get_downloaded_url( $this->model->maxmind_license_key );
533 if ( is_wp_error( $tmp ) ) {
534 $this->log( 'CRON error downloading from MaxMind: ' . $tmp->get_error_message() );
535 return;
536 }
537
538 $geodb_path = $service_geo->extract_db_archive( $tmp );
539 if ( is_wp_error( $geodb_path ) ) {
540 $this->log( 'CRON error extracting MaxMind archive: ' . $geodb_path->get_error_message() );
541 return;
542 }
543 $this->model->geodb_path = $geodb_path;
544 $this->model->save();
545 }
546 }
547