PluginProbe
Atomic Edge Security – Firewall, Malware Scan and Login Security / 2.2.2
Atomic Edge Security – Firewall, Malware Scan and Login Security v2.2.2
trunk 2.0.0 2.1.0 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 All 29 releases
atomic-edge-security / includes / class-atomicedge-admin.php

class-atomicedge-admin.php in Atomic Edge Security – Firewall, Malware Scan and Login Security 2.2.2, at includes/class-atomicedge-admin.php

640 lines 17.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * AtomicEdge Admin Pages
4 *
5 * Handles all WordPress admin interface functionality.
6 *
7 * @package AtomicEdge
8 */
9
10 // Prevent direct access.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 /**
16 * Class AtomicEdge_Admin
17 *
18 * Manages admin pages and menus.
19 */
20 class AtomicEdge_Admin {
21
22 /**
23 * API client instance.
24 *
25 * @var AtomicEdge_API
26 */
27 private $api;
28
29 /**
30 * Constructor.
31 *
32 * @param AtomicEdge_API $api API client instance.
33 */
34 public function __construct( AtomicEdge_API $api ) {
35 $this->api = $api;
36 $this->init_hooks();
37 }
38
39 /**
40 * Initialize hooks.
41 *
42 * @return void
43 */
44 private function init_hooks() {
45 add_action( 'admin_menu', array( $this, 'register_menu' ) );
46 add_action( 'admin_init', array( $this, 'handle_form_submissions' ) );
47 add_action( 'admin_notices', array( $this, 'display_admin_notices' ) );
48 add_action( 'admin_notices', array( $this, 'display_conflicting_plugin_notice' ) );
49 }
50
51 /**
52 * Register admin menu.
53 *
54 * @return void
55 */
56 public function register_menu() {
57 // Main menu.
58 add_menu_page(
59 __( 'Atomic Edge Security', 'atomic-edge-security' ),
60 __( 'Atomic Edge', 'atomic-edge-security' ),
61 'manage_options',
62 'atomic-edge-security',
63 array( $this, 'render_dashboard_page' ),
64 'dashicons-shield',
65 30
66 );
67
68 // Dashboard submenu (same as main).
69 add_submenu_page(
70 'atomic-edge-security',
71 __( 'Dashboard', 'atomic-edge-security' ),
72 __( 'Dashboard', 'atomic-edge-security' ),
73 'manage_options',
74 'atomic-edge-security',
75 array( $this, 'render_dashboard_page' )
76 );
77
78 // Analytics submenu.
79 add_submenu_page(
80 'atomic-edge-security',
81 __( 'Analytics', 'atomic-edge-security' ),
82 __( 'Analytics', 'atomic-edge-security' ),
83 'manage_options',
84 'atomicedge-analytics',
85 array( $this, 'render_analytics_page' )
86 );
87
88 // Adaptive Defense submenu.
89 add_submenu_page(
90 'atomic-edge-security',
91 __( 'Adaptive Defense', 'atomic-edge-security' ),
92 __( 'Adaptive Defense', 'atomic-edge-security' ),
93 'manage_options',
94 'atomicedge-adaptive-defense',
95 array( $this, 'render_adaptive_defense_page' )
96 );
97
98 // WAF Logs submenu.
99 add_submenu_page(
100 'atomic-edge-security',
101 __( 'WAF Logs', 'atomic-edge-security' ),
102 __( 'WAF Logs', 'atomic-edge-security' ),
103 'manage_options',
104 'atomicedge-waf-logs',
105 array( $this, 'render_waf_logs_page' )
106 );
107
108 // Access Control submenu.
109 add_submenu_page(
110 'atomic-edge-security',
111 __( 'Access Control', 'atomic-edge-security' ),
112 __( 'Access Control', 'atomic-edge-security' ),
113 'manage_options',
114 'atomicedge-access-control',
115 array( $this, 'render_access_control_page' )
116 );
117
118 // Malware Scanner submenu.
119 add_submenu_page(
120 'atomic-edge-security',
121 __( 'Malware Scanner', 'atomic-edge-security' ),
122 __( 'Malware Scanner', 'atomic-edge-security' ),
123 'manage_options',
124 'atomicedge-scanner',
125 array( $this, 'render_scanner_page' )
126 );
127
128 // Vulnerability Scanner submenu.
129 add_submenu_page(
130 'atomic-edge-security',
131 __( 'Vulnerability Scanner', 'atomic-edge-security' ),
132 __( 'Vulnerability Scanner', 'atomic-edge-security' ),
133 'manage_options',
134 'atomicedge-vulnerabilities',
135 array( $this, 'render_vulnerability_scanner_page' )
136 );
137
138 // CDN submenu.
139 add_submenu_page(
140 'atomic-edge-security',
141 __( 'CDN', 'atomic-edge-security' ),
142 __( 'CDN', 'atomic-edge-security' ),
143 'manage_options',
144 'atomicedge-cdn',
145 array( $this, 'render_cdn_page' )
146 );
147
148 // 2FA submenu (unified page with tabs).
149 add_submenu_page(
150 'atomic-edge-security',
151 __( '2FA', 'atomic-edge-security' ),
152 __( '2FA', 'atomic-edge-security' ),
153 'manage_options',
154 'atomicedge-2fa',
155 array( $this, 'render_2fa_settings_page' )
156 );
157
158 // Settings submenu.
159 add_submenu_page(
160 'atomic-edge-security',
161 __( 'Settings', 'atomic-edge-security' ),
162 __( 'Settings', 'atomic-edge-security' ),
163 'manage_options',
164 'atomicedge-settings',
165 array( $this, 'render_settings_page' )
166 );
167 }
168
169 /**
170 * Handle form submissions.
171 *
172 * @return void
173 */
174 public function handle_form_submissions() {
175 // Handle settings form.
176 if ( isset( $_POST['atomicedge_save_settings'], $_POST['_wpnonce'] ) &&
177 wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'atomicedge_settings' ) ) {
178 $this->handle_settings_save();
179 }
180
181 // Handle connection.
182 if ( isset( $_POST['atomicedge_connect'], $_POST['_wpnonce'] ) &&
183 wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'atomicedge_connect' ) ) {
184 $this->handle_connect();
185 }
186
187 // Handle disconnection.
188 if ( isset( $_POST['atomicedge_disconnect'], $_POST['_wpnonce'] ) &&
189 wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'atomicedge_disconnect' ) ) {
190 $this->handle_disconnect();
191 }
192 }
193
194 /**
195 * Handle settings save.
196 *
197 * @return void
198 */
199 private function handle_settings_save() {
200 // Verify nonce.
201 if ( ! isset( $_POST['_wpnonce'] ) ||
202 ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'atomicedge_settings' ) ) {
203 $this->add_admin_notice( 'error', __( 'Security check failed. Please try again.', 'atomic-edge-security' ) );
204 return;
205 }
206
207 // Check capabilities.
208 if ( ! current_user_can( 'manage_options' ) ) {
209 $this->add_admin_notice( 'error', __( 'You do not have permission to change settings.', 'atomic-edge-security' ) );
210 return;
211 }
212
213 // Save API URL.
214 if ( isset( $_POST['atomicedge_api_url'] ) ) {
215 $api_url = esc_url_raw( wp_unslash( $_POST['atomicedge_api_url'] ) );
216 update_option( 'atomicedge_api_url', $api_url );
217 }
218
219 $this->add_admin_notice( 'success', __( 'Settings saved successfully.', 'atomic-edge-security' ) );
220 }
221
222 /**
223 * Handle connect action.
224 *
225 * @return void
226 */
227 private function handle_connect() {
228 // Verify nonce.
229 if ( ! isset( $_POST['_wpnonce'] ) ||
230 ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'atomicedge_connect' ) ) {
231 $this->add_admin_notice( 'error', __( 'Security check failed. Please try again.', 'atomic-edge-security' ) );
232 return;
233 }
234
235 // Check capabilities.
236 if ( ! current_user_can( 'manage_options' ) ) {
237 $this->add_admin_notice( 'error', __( 'You do not have permission to connect.', 'atomic-edge-security' ) );
238 return;
239 }
240
241 // Get and validate API key.
242 $api_key = isset( $_POST['atomicedge_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['atomicedge_api_key'] ) ) : '';
243 $api_key = trim( $api_key );
244
245 if ( empty( $api_key ) ) {
246 $this->add_admin_notice( 'error', __( 'Please enter an API key.', 'atomic-edge-security' ) );
247 return;
248 }
249
250 // AtomicEdge keys are 32-64 alphanumeric characters (no prefixes).
251 if ( ! preg_match( '/^[A-Za-z0-9]{32,64}$/', $api_key ) ) {
252 $this->add_admin_notice(
253 'error',
254 __( 'Invalid API key format. Paste the key exactly as shown in the Atomic Edge dashboard (32–64 letters/numbers, no prefix).', 'atomic-edge-security' )
255 );
256 return;
257 }
258
259 // Attempt connection.
260 $result = $this->api->connect( $api_key );
261
262 if ( $result['success'] ) {
263 $this->add_admin_notice( 'success', $result['message'] );
264 } else {
265 $this->add_admin_notice( 'error', $result['error'] );
266 }
267 }
268
269 /**
270 * Handle disconnect action.
271 *
272 * @return void
273 */
274 private function handle_disconnect() {
275 // Verify nonce.
276 if ( ! isset( $_POST['_wpnonce'] ) ||
277 ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'atomicedge_disconnect' ) ) {
278 $this->add_admin_notice( 'error', __( 'Security check failed. Please try again.', 'atomic-edge-security' ) );
279 return;
280 }
281
282 // Check capabilities.
283 if ( ! current_user_can( 'manage_options' ) ) {
284 $this->add_admin_notice( 'error', __( 'You do not have permission to disconnect.', 'atomic-edge-security' ) );
285 return;
286 }
287
288 $result = $this->api->disconnect();
289 $this->add_admin_notice( 'success', $result['message'] );
290 }
291
292 /**
293 * Add admin notice to be displayed.
294 *
295 * @param string $type Notice type (success, error, warning, info).
296 * @param string $message Notice message.
297 * @return void
298 */
299 private function add_admin_notice( $type, $message ) {
300 $notices = get_transient( 'atomicedge_admin_notices' );
301 $notices = is_array( $notices ) ? $notices : array();
302 $notices[] = array(
303 'type' => $type,
304 'message' => $message,
305 );
306 set_transient( 'atomicedge_admin_notices', $notices, 60 );
307 }
308
309 /**
310 * Display admin notices.
311 *
312 * @return void
313 */
314 public function display_admin_notices() {
315 $notices = get_transient( 'atomicedge_admin_notices' );
316 if ( ! is_array( $notices ) || empty( $notices ) ) {
317 return;
318 }
319
320 foreach ( $notices as $notice ) {
321 $class = 'notice-' . esc_attr( $notice['type'] );
322 printf(
323 '<div class="notice %s is-dismissible"><p>%s</p></div>',
324 esc_attr( $class ),
325 esc_html( $notice['message'] )
326 );
327 }
328
329 delete_transient( 'atomicedge_admin_notices' );
330 }
331
332 /**
333 * Display notice if Shift8 CDN plugin is active.
334 *
335 * @return void
336 */
337 public function display_conflicting_plugin_notice() {
338 if ( ! current_user_can( 'activate_plugins' ) ) {
339 return;
340 }
341
342 if ( ! function_exists( 'is_plugin_active' ) ) {
343 $file_php = ABSPATH . 'wp-admin/includes/plugin.php';
344 if ( file_exists( $file_php ) ) {
345 require_once $file_php;
346 }
347 }
348
349 if ( ! function_exists( 'is_plugin_active' ) ) {
350 return;
351 }
352
353 $conflicting_plugin = 'shift8-cdn/shift8-cdn.php';
354 if ( ! is_plugin_active( $conflicting_plugin ) ) {
355 return;
356 }
357
358 $deactivate_url = wp_nonce_url(
359 admin_url( 'plugins.php?action=deactivate&plugin=' . rawurlencode( $conflicting_plugin ) ),
360 'deactivate-plugin_' . $conflicting_plugin
361 );
362
363 $message = sprintf(
364 /* translators: 1: plugin name, 2: deactivate link */
365 __( '%1$s is active. Atomic Edge Security should not run alongside the retired Shift8 CDN plugin. %2$s', 'atomic-edge-security' ),
366 esc_html__( 'Shift8 CDN', 'atomic-edge-security' ),
367 sprintf(
368 '<a href="%s">%s</a>',
369 esc_url( $deactivate_url ),
370 esc_html__( 'Deactivate Shift8 CDN', 'atomic-edge-security' )
371 )
372 );
373
374 printf(
375 '<div class="notice notice-warning"><p>%s</p></div>',
376 wp_kses_post( $message )
377 );
378 }
379
380 /**
381 * Render dashboard page.
382 *
383 * @return void
384 */
385 public function render_dashboard_page() {
386 if ( ! current_user_can( 'manage_options' ) ) {
387 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
388 }
389
390 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/dashboard.php';
391 }
392
393 /**
394 * Render analytics page.
395 *
396 * @return void
397 */
398 public function render_analytics_page() {
399 if ( ! current_user_can( 'manage_options' ) ) {
400 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
401 }
402
403 if ( ! $this->api->is_connected() ) {
404 $this->render_not_connected_notice();
405 return;
406 }
407
408 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/analytics.php';
409 }
410
411 /**
412 * Render WAF logs page.
413 *
414 * @return void
415 */
416 public function render_waf_logs_page() {
417 if ( ! current_user_can( 'manage_options' ) ) {
418 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
419 }
420
421 if ( ! $this->api->is_connected() ) {
422 $this->render_not_connected_notice();
423 return;
424 }
425
426 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/waf-logs.php';
427 }
428
429 /**
430 * Render access control page.
431 *
432 * @return void
433 */
434 public function render_access_control_page() {
435 if ( ! current_user_can( 'manage_options' ) ) {
436 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
437 }
438
439 if ( ! $this->api->is_connected() ) {
440 $this->render_not_connected_notice();
441 return;
442 }
443
444 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/access-control.php';
445 }
446
447 /**
448 * Render scanner page.
449 *
450 * @return void
451 */
452 public function render_scanner_page() {
453 if ( ! current_user_can( 'manage_options' ) ) {
454 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
455 }
456
457 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/scanner.php';
458 }
459
460 /**
461 * Render vulnerability scanner page.
462 *
463 * @return void
464 */
465 public function render_vulnerability_scanner_page() {
466 if ( ! current_user_can( 'manage_options' ) ) {
467 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
468 }
469
470 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/vulnerability-scanner.php';
471 }
472
473 /**
474 * Render CDN page.
475 *
476 * Handles form submission for CDN settings before rendering.
477 *
478 * @return void
479 */
480 public function render_cdn_page() {
481 if ( ! current_user_can( 'manage_options' ) ) {
482 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
483 }
484
485 // Handle CDN settings form submission.
486 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce check first.
487 if ( isset( $_POST['atomicedge_save_cdn_settings'] ) ) {
488 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized immediately.
489 $nonce = isset( $_POST['atomicedge_cdn_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['atomicedge_cdn_nonce'] ) ) : '';
490 if ( wp_verify_nonce( $nonce, 'atomicedge_cdn_settings' ) ) {
491 // Save CDN optimization settings to WP options.
492 update_option( 'atomicedge_cdn_brotli', isset( $_POST['atomicedge_cdn_brotli'] ) ? true : false );
493 update_option( 'atomicedge_cdn_js_minification', isset( $_POST['atomicedge_cdn_js_minification'] ) ? true : false );
494 update_option( 'atomicedge_cdn_css_minification', isset( $_POST['atomicedge_cdn_css_minification'] ) ? true : false );
495 update_option( 'atomicedge_cdn_image_optimization', isset( $_POST['atomicedge_cdn_image_optimization'] ) ? true : false );
496
497 add_settings_error( 'atomicedge_cdn', 'settings_saved', __( 'CDN settings saved.', 'atomic-edge-security' ), 'success' );
498 } else {
499 add_settings_error( 'atomicedge_cdn', 'nonce_failed', __( 'Security check failed.', 'atomic-edge-security' ), 'error' );
500 }
501 settings_errors( 'atomicedge_cdn' );
502 }
503
504 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/cdn.php';
505 }
506
507 /**
508 * Render settings page.
509 *
510 * @return void
511 */
512 public function render_settings_page() {
513 if ( ! current_user_can( 'manage_options' ) ) {
514 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
515 }
516
517 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/settings.php';
518 }
519
520 /**
521 * Render Adaptive Defense page.
522 *
523 * AI-powered threat detection with tabbed interface.
524 *
525 * @return void
526 */
527 public function render_adaptive_defense_page() {
528 if ( ! current_user_can( 'manage_options' ) ) {
529 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
530 }
531
532 if ( ! $this->api->is_connected() ) {
533 $this->render_not_connected_notice();
534 return;
535 }
536
537 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/adaptive-defense.php';
538 }
539
540 /**
541 * Render the unified 2FA settings page with tabs.
542 *
543 * @return void
544 */
545 public function render_2fa_settings_page() {
546 if ( ! current_user_can( 'manage_options' ) ) {
547 wp_die( esc_html__( 'You do not have permission to access this page.', 'atomic-edge-security' ) );
548 }
549
550 // Handle export action (from audit tab).
551 if ( isset( $_GET['action'], $_GET['tab'] ) && 'export' === $_GET['action'] && 'audit' === $_GET['tab'] ) {
552 // Verify nonce.
553 if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ?? '' ) ), 'atomicedge_export_audit' ) ) {
554 wp_die( esc_html__( 'Security check failed.', 'atomic-edge-security' ) );
555 }
556
557 $this->export_audit_csv();
558 return;
559 }
560
561 include ATOMICEDGE_PLUGIN_DIR . 'admin/views/2fa-settings.php';
562 }
563
564 /**
565 * Export audit log as CSV.
566 *
567 * @return void
568 */
569 private function export_audit_csv() {
570 $entries = AtomicEdge_2FA_Audit::export( 1000 );
571
572 $filename = 'atomicedge-2fa-audit-' . gmdate( 'Y-m-d' ) . '.csv';
573
574 header( 'Content-Type: text/csv; charset=utf-8' );
575 header( 'Content-Disposition: attachment; filename=' . $filename );
576 header( 'Pragma: no-cache' );
577 header( 'Expires: 0' );
578
579 $output = fopen( 'php://output', 'w' );
580
581 // CSV header.
582 fputcsv( $output, array( 'Date/Time', 'User', 'Email', 'Event', 'IP Address', 'Admin' ) );
583
584 // CSV data.
585 foreach ( $entries as $entry ) {
586 fputcsv( $output, array(
587 $entry['date'],
588 $entry['user'],
589 $entry['user_email'],
590 $entry['event'],
591 $entry['ip_address'],
592 $entry['admin'],
593 ) );
594 }
595
596 fclose( $output );
597 exit;
598 }
599
600 /**
601 * Render not connected notice.
602 *
603 * @return void
604 */
605 private function render_not_connected_notice() {
606 ?>
607 <div class="wrap atomicedge-wrap">
608 <h1><img src="<?php echo esc_url( ATOMICEDGE_PLUGIN_URL . 'admin/images/logo.svg' ); ?>" alt="<?php esc_attr_e( 'Atomic Edge', 'atomic-edge-security' ); ?>" class="atomicedge-logo" /></h1>
609 <div class="notice notice-warning">
610 <p>
611 <?php
612 printf(
613 wp_kses(
614 /* translators: %s: Settings page URL */
615 __( 'Please <a href="%s">connect your site</a> to Atomic Edge to access this feature.', 'atomic-edge-security' ),
616 array( 'a' => array( 'href' => array() ) )
617 ),
618 esc_url( admin_url( 'admin.php?page=atomicedge' ) )
619 );
620 ?>
621 </p>
622 </div>
623 </div>
624 <?php
625 }
626
627 /**
628 * Get masked API key for display.
629 *
630 * @return string Masked API key or empty string.
631 */
632 public function get_masked_api_key() {
633 $api_key = $this->api->get_api_key();
634 if ( ! $api_key || strlen( $api_key ) < 8 ) {
635 return '';
636 }
637 return str_repeat( '', strlen( $api_key ) - 4 ) . substr( $api_key, -4 );
638 }
639 }
640