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 / audit-logging.php

audit-logging.php in Defender Security – Malware Scanner, Login Security & Firewall 3.0.1, at src/controller/audit-logging.php

556 lines 14.0 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 Calotes\Helper\HTTP;
8 use WP_Defender\Component\Audit;
9 use WP_Defender\Component\Config\Config_Hub_Helper;
10 use WP_Defender\Controller;
11 use WP_Defender\Model\Audit_Log;
12 use WP_Defender\Model\Notification\Audit_Report;
13 use WP_Defender\Traits\Formats;
14 use WP_Defender\Traits\User;
15
16 class Audit_Logging extends Controller {
17 use User, Formats;
18
19 public $slug = 'wdf-logging';
20
21 /**
22 * Use for cache.
23 *
24 * @var \WP_Defender\Model\Setting\Audit_Logging
25 */
26 public $model;
27
28 /**
29 * @var Audit
30 */
31 public $service;
32
33 public function __construct() {
34 $this->register_page(
35 esc_html__( 'Audit Logging', 'wpdef' ),
36 $this->slug,
37 array(
38 &$this,
39 'main_view',
40 ),
41 $this->parent_slug
42 );
43 add_action( 'defender_enqueue_assets', array( &$this, 'enqueue_assets' ) );
44 $this->model = wd_di()->get( \WP_Defender\Model\Setting\Audit_Logging::class );
45 $this->service = new Audit();
46 $this->register_routes();
47 if ( $this->model->is_active() ) {
48 $this->service->enqueue_event_listener();
49 add_action( 'shutdown', array( &$this, 'cache_audit_logs' ) );
50 /**
51 * We will schedule the time for flush data into cloud.
52 */
53 if ( ! wp_next_scheduled( 'audit_sync_events' ) ) {
54 wp_schedule_event( time() + 15, 'hourly', 'audit_sync_events' );
55 }
56 add_action( 'audit_sync_events', array( &$this, 'sync_events' ) );
57
58 /**
59 * We will schedule the time to clean up old logs.
60 */
61 if ( ! wp_next_scheduled( 'audit_clean_up_logs' ) ) {
62 wp_schedule_event( time(), 'hourly', 'audit_clean_up_logs' );
63 }
64 add_action( 'audit_clean_up_logs', array( &$this, 'clean_up_audit_logs' ) );
65 }
66 }
67
68 /**
69 * Sync all the events into cloud, this will happen per hourly basis.
70 */
71 public function sync_events() {
72 $this->service->flush();
73 }
74
75 /**
76 * Clean up all the old logs from the local storage, this will happen per hourly basis.
77 */
78 public function clean_up_audit_logs() {
79 $this->service->audit_clean_up_logs();
80 }
81
82 /**
83 * @throws \Exception
84 * @defender_route
85 */
86 public function export_as_csv() {
87 $date_from = HTTP::get(
88 'date_from',
89 date( 'Y-m-d H:i:s', strtotime( '-7 days', current_time( 'timestamp' ) ) )
90 );
91 $date_to = HTTP::get( 'date_to', date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) );
92 $date_from = ( new \DateTime( $date_from ) )->setTime( 0, 0, 0 )->getTimestamp();
93 $date_to = ( new \DateTime( $date_to ) )->setTime( 23, 59, 59 )->getTimestamp();
94 $username = HTTP::get( 'term', '' );
95 $user_id = '';
96 $user = get_user_by( 'login', $username );
97 $events = HTTP::get( 'event_type', array() );
98 if ( is_object( $user ) ) {
99 $user_id = $user->ID;
100 }
101
102 $handler = new Audit();
103 $ip_address = HTTP::get( 'ip_address', '' );
104 $result = $handler->fetch( $date_from, $date_to, $events, $user_id, $ip_address, false );
105 // Have data, now prepare to flush.
106 $fp = fopen( 'php://memory', 'w' );
107 $headers = array(
108 __( 'Summary', 'wpdef' ),
109 __( 'Date / Time', 'wpdef' ),
110 __( 'Context', 'wpdef' ),
111 __( 'Type', 'wpdef' ),
112 __( 'IP address', 'wpdef' ),
113 __( 'User', 'wpdef' ),
114 );
115 fputcsv( $fp, $headers );
116 foreach ( $result as $log ) {
117 $fields = $log->export();
118 $vars = array(
119 $fields['msg'],
120 is_array( $fields['timestamp'] )
121 ? $this->format_date_time( date( 'Y-m-d H:i:s', $fields['timestamp'][0] ) )
122 : $this->format_date_time( date( 'Y-m-d H:i:s', $fields['timestamp'] ) ),
123 $fields['context'],
124 $fields['action_type'],
125 $fields['ip'],
126 $this->get_user_display( $fields['user_id'] ),
127 );
128 fputcsv( $fp, $vars );
129 }
130 $filename = 'wdf-audit-logs-export-' . date( 'ymdHis' ) . '.csv';
131 fseek( $fp, 0 );
132 header( 'Content-Type: text/csv' );
133 header( 'Content-Disposition: attachment; filename="' . $filename . '";' );
134 // Make php send the generated csv lines to the browser.
135 fpassthru( $fp );
136 exit();
137 }
138
139 /**
140 * We'll pass all the event logs into the db handler, so it writes down to db.
141 * Do it in shutdown runtime, so no delay time.
142 *
143 */
144 public function cache_audit_logs() {
145 $audit = new Audit();
146 $audit->log_audit_events();
147 }
148
149 /**
150 * Pull the logs from db cached:
151 * - date_from: the start of the date we will run the query, as mysql time format,
152 * - date_to: similar to the above,
153 * others will refer to Audit.
154 *
155 * @param Request $request
156 *
157 * @return Response
158 * @throws \Exception
159 * @defender_route
160 */
161 public function pull_logs( Request $request ) {
162 $data = $request->get_data(
163 array(
164 'date_from' => array(
165 'type' => 'string',
166 'sanitize' => 'sanitize_text_field',
167 ),
168 'date_to' => array(
169 'type' => 'string',
170 'sanitize' => 'sanitize_text_field',
171 ),
172 'username' => array(
173 'type' => 'string',
174 'sanitize' => 'sanitize_text_field',
175 ),
176 'events' => array(
177 'type' => 'array',
178 'sanitize' => 'sanitize_text_field',
179 ),
180 'ip_address' => array(
181 'type' => 'string',
182 'sanitize' => 'sanitize_text_field',
183 ),
184 'paged' => array(
185 'type' => 'int',
186 'sanitize' => 'sanitize_text_field',
187 ),
188 )
189 );
190
191 $date_from = $data['date_from'];
192 $date_from = new \DateTime( $date_from );
193 $date_to = $data['date_to'];
194 $date_to = new \DateTime( $date_to );
195
196 if ( ! is_object( $date_from ) || ! is_object( $date_to ) ) {
197 return new Response(
198 false,
199 array(
200 'message' => __( 'Invalid data', 'wpdef' ),
201 )
202 );
203 }
204 $username = $data['username'] ?? '';
205 $user_id = '';
206 $user = get_user_by( 'login', $username );
207 $events = $data['events'] ?? array();
208 if ( is_object( $user ) ) {
209 $user_id = $user->ID;
210 }
211 $ip_address = $data['ip_address'] ?? '';
212 $paged = $data['paged'] ?? 1;
213
214 $result = $this->service->fetch(
215 $date_from->setTime( 0, 0, 0 )->getTimestamp(),
216 $date_to->setTime( 23, 59, 59 )->getTimestamp(),
217 $events,
218 $user_id,
219 $ip_address,
220 $paged
221 );
222
223 if ( is_wp_error( $result ) ) {
224 return new Response(
225 false,
226 array(
227 'message' => $result->get_error_message(),
228 )
229 );
230 }
231 $logs = array();
232 foreach ( $result as $item ) {
233 $logs[] = array_merge(
234 $item->export(),
235 array(
236 'user' => $this->get_user_display( $item->user_id ),
237 'user_url' => (int) $item->user_id > 0 ? get_edit_user_link( $item->user_id ) : '',
238 'log_date' => $this->get_date( $item->timestamp ),
239 'format_date' => $this->format_date_time( gmdate( 'Y-m-d H:i:s', $item->timestamp ) ),
240 )
241 );
242 }
243 // @since 3.0.0 If no logs then $count = 0.
244 if ( empty( $logs ) ) {
245 $count = 0;
246 } else {
247 $count = Audit_Log::count(
248 $date_from->setTime( 0, 0, 0 )->getTimestamp(),
249 $date_to->setTime( 23, 59, 59 )->getTimestamp(),
250 $events,
251 $user_id,
252 $ip_address
253 );
254 }
255 $per_page = 20;
256
257 // Get the week count.
258 return new Response(
259 true,
260 array(
261 'logs' => $logs,
262 'total_items' => $count,
263 'total_pages' => ceil( $count / $per_page ),
264 'per_page' => $per_page,
265 )
266 );
267 }
268
269 public function get_frequency_text( Audit_Report $audit_report ) {
270 $text = '';
271 switch ( $audit_report->frequency ) {
272 case 'daily':
273 $text = ucfirst( $audit_report->day ) . 's at ' . $audit_report->time;
274 break;
275 case 'weekly':
276 case 'monthly':
277 $text = ucfirst( $audit_report->frequency ) . ' on ' . ucfirst( $audit_report->day ) . 's at ' . $audit_report->time;
278 break;
279 default:
280 break;
281 }
282
283 return $text;
284 }
285
286 public function enqueue_assets() {
287 if ( ! $this->is_page_active() ) {
288 return;
289 }
290
291 wp_enqueue_script( 'def-moment', defender_asset_url( '/assets/js/vendor/moment/moment.min.js' ) );
292 wp_enqueue_script(
293 'def-daterangepicker',
294 defender_asset_url( '/assets/js/vendor/daterangepicker/daterangepicker.js' )
295 );
296 wp_localize_script(
297 'def-audit',
298 'audit',
299 $this->data_frontend()
300 );
301 wp_enqueue_script( 'def-audit' );
302 $this->enqueue_main_assets();
303 }
304
305 /**
306 * Render the root element for frontend.
307 */
308 public function main_view() {
309 $this->render( 'main' );
310 }
311
312 /**
313 * @throws \Exception
314 * @defender_route
315 */
316 public function summary() {
317 $response = $this->model->is_active() ? $this->summary_data() : array();
318 wp_send_json_success( $response );
319 }
320
321 /**
322 * @param bool $for_hub. Default 'false' because it's displayed on site summary sections.
323 *
324 * @return array
325 */
326 public function summary_data( $for_hub = false) {
327 $date_from = ( new \DateTime( date( 'Y-m-d', strtotime( '-30 days' ) ) ) )->setTime(
328 0,
329 0,
330 0
331 )->getTimestamp();
332 $date_to = ( new \DateTime( date( 'Y-m-d' ) ) )->setTime( 23, 59, 59 )->getTimestamp();
333 $month_count = Audit_Log::count( $date_from, $date_to );
334 $date_from = ( new \DateTime( date( 'Y-m-d', strtotime( '-7 days' ) ) ) )->setTime( 0, 0, 0 )->getTimestamp();
335 $week_count = Audit_Log::count( $date_from, $date_to );
336 $last = Audit_Log::get_last();
337 $date_from = ( new \DateTime( 'now', wp_timezone() ) )->modify( '-24 hours' )->setTime( 0, 0, 0 )->getTimestamp();
338 $day_count = Audit_Log::count( $date_from, $date_to );
339 if ( is_object( $last ) ) {
340 $last = $for_hub
341 ? $this->persistent_hub_datetime_format( $last->timestamp )
342 : $this->format_date_time( $last->timestamp );
343 } else {
344 $last = 'n/a';
345 }
346
347 return array(
348 'monthCount' => $month_count,
349 'weekCount' => $week_count,
350 'dayCount' => $day_count,
351 'lastEvent' => $last,
352 );
353 }
354
355 /**
356 * Save settings.
357 * @param Request $request
358 *
359 * @return Response
360 * @defender_route
361 */
362 public function save_settings( Request $request ) {
363 $data = $request->get_data_by_model( $this->model );
364 if ( false === $data['enabled'] && $data['enabled'] !== $this->model->is_active() ) {
365 // Toggle off, so we need to flush everything to cloud.
366 $this->service->flush();
367 }
368
369 $this->model->import( $data );
370 if ( $this->model->validate() ) {
371 $this->model->save();
372 }
373
374 Config_Hub_Helper::set_clear_active_flag();
375
376 return new Response(
377 true,
378 array_merge(
379 $this->data_frontend(),
380 array(
381 'message' => __( 'Your settings have been updated.', 'wpdef' ),
382 )
383 )
384 );
385 }
386
387 /**
388 * @return array
389 */
390 public function to_array() {
391 return array_merge(
392 array(
393 'enabled' => $this->model->is_active(),
394 'report' => true,
395 ),
396 $this->dump_routes_and_nonces()
397 );
398 }
399
400 public function remove_settings() {
401 ( new \WP_Defender\Model\Setting\Audit_Logging() )->delete();
402 }
403
404 /**
405 * Delete all the data & the cache.
406 */
407 public function remove_data() {
408 Audit_Log::truncate();
409 delete_site_option( Audit::CACHE_LAST_CHECKPOINT );
410 }
411
412 /**
413 * Setup config for audit.
414 * Todo: need?
415 */
416 public function optimize_configs() {
417 $settings = new \WP_Defender\Model\Setting\Audit_Logging();
418 $settings->enabled = true;
419 $settings->save();
420
421 $report = new Audit_Report();
422 $report->save();
423 }
424
425 /**
426 * All the variables that we will show on frontend, both in the main page, or dashboard widget.
427 *
428 * @return array
429 */
430 public function data_frontend() {
431 $logs = array();
432 $count = 0;
433 $per_page = 20;
434 $total_page = 1;
435 if ( $this->model->is_active() ) {
436 $date_from = ( new \DateTime() )
437 ->setTimezone( wp_timezone() )
438 ->sub( new \DateInterval( 'P7D' ) )->setTime( 0, 0, 0 );
439 $date_to = ( new \DateTime() )->setTimezone( wp_timezone() )->setTime( 23, 59, 59 );
440
441 $result = $this->service->fetch(
442 $date_from->getTimestamp(),
443 $date_to->getTimestamp(),
444 array(),
445 '',
446 '',
447 1
448 );
449 if ( ! is_wp_error( $result ) ) {
450 foreach ( $result as $item ) {
451 $logs[] = array_merge(
452 $item->export(),
453 array(
454 'user' => $this->get_user_display( $item->user_id ),
455 'user_url' => (int) $item->user_id > 0 ? get_edit_user_link( $item->user_id ) : '',
456 'log_date' => $this->get_date( $item->timestamp ),
457 'format_date' => $this->format_date_time( gmdate( 'Y-m-d H:i:s', $item->timestamp ) ),
458 )
459 );
460 }
461 $count = Audit_Log::count( $date_from->getTimestamp(), $date_to->getTimestamp() );
462 $total_page = ceil( $count / $per_page );
463 }
464 }
465
466 return array_merge(
467 array(
468 'model' => $this->model->export(),
469 'logs' => $logs,
470 'events_type' => Audit_Log::allowed_events(),
471 'summary' => array(
472 'count_7_days' => $count,
473 'report' => wd_di()->get( Audit_Report::class )->to_string(),
474 ),
475 'paging' => array(
476 'paged' => 1,
477 'total_pages' => $total_page,
478 'count' => $count,
479 ),
480 ),
481 $this->dump_routes_and_nonces()
482 );
483 }
484
485 /**
486 * @param array $data
487 */
488 public function import_data( $data ) {
489 $model = $this->model;
490
491 $model->import( $data );
492 if ( $model->validate() ) {
493 $model->save();
494 }
495 }
496
497 /**
498 * @return array
499 */
500 public function export_strings() {
501 if ( ! ( new \WP_Defender\Behavior\WPMUDEV() )->is_pro() ) {
502 return array(
503 sprintf( __( 'Inactive %s', 'wpdef' ), '<span class="sui-tag sui-tag-pro">Pro</span>' ),
504 );
505 }
506
507 if ( $this->model->is_active() ) {
508 $strings = array( __( 'Active', 'wpdef' ) );
509 $audit_report = new \WP_Defender\Model\Notification\Audit_Report();
510 if ( 'enabled' === $audit_report->status ) {
511 $strings[] = sprintf(
512 __( 'Email reports sending %s', 'wpdef' ),
513 $audit_report->frequency
514 );
515 }
516 } else {
517 $strings = array( __( 'Inactive', 'wpdef' ) );
518 }
519
520 return $strings;
521 }
522
523 /**
524 * @param array $config
525 * @param bool $is_pro
526 *
527 * @return array
528 */
529 public function config_strings( $config, $is_pro ) {
530 if ( $is_pro ) {
531 if ( $config['enabled'] ) {
532 $strings = array( __( 'Active', 'wpdef' ) );
533 if ( isset( $config['report'] ) && 'enabled' === $config['report'] ) {
534 $strings[] = sprintf(
535 /* translators: %s - option frequency */
536 __( 'Email reports sending %s', 'wpdef' ),
537 $config['frequency']
538 );
539 }
540 } else {
541 $strings = array( __( 'Inactive', 'wpdef' ) );
542 }
543 } else {
544 $strings = array(
545 sprintf(
546 /* translators: ... */
547 __( 'Inactive %s', 'wpdef' ),
548 '<span class="sui-tag sui-tag-pro">Pro</span>'
549 )
550 );
551 }
552
553 return $strings;
554 }
555 }
556