PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.49
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.49
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Login_Register_Form / Security_Dashboard.php

Security_Dashboard.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.49, at includes/widgets/Login_Register_Form/Security_Dashboard.php

798 lines 32.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace King_Addons\Widgets\Login_Register_Form;
4
5 if (!defined('ABSPATH')) {
6 exit; // Exit if accessed directly.
7 }
8
9 // Include Security Manager
10 require_once KING_ADDONS_PATH . 'includes/widgets/Login_Register_Form/Security_Manager.php';
11
12 /**
13 * Security Dashboard for Login Register Form widget
14 * Provides administrative interface for monitoring security events
15 */
16 class Security_Dashboard
17 {
18 /**
19 * Initialize the security dashboard
20 */
21 public static function init()
22 {
23 // Add admin menu
24 add_action('admin_menu', [__CLASS__, 'add_admin_menu'], 20);
25
26 // Add security logs capability check
27 add_action('admin_init', [__CLASS__, 'check_capabilities']);
28
29 // Add AJAX handlers for dashboard
30 add_action('wp_ajax_king_addons_clear_security_logs', [__CLASS__, 'clear_security_logs']);
31 add_action('wp_ajax_king_addons_unblock_ip', [__CLASS__, 'unblock_ip']);
32 add_action('wp_ajax_king_addons_export_security_report', [__CLASS__, 'export_security_report']);
33 }
34
35 /**
36 * Add admin menu for security dashboard
37 */
38 public static function add_admin_menu()
39 {
40 add_submenu_page(
41 'king-addons',
42 esc_html__('Login Security', 'king-addons'),
43 esc_html__('Login Security', 'king-addons'),
44 'manage_options',
45 'king-addons-login-security',
46 [__CLASS__, 'render_dashboard']
47 );
48 }
49
50 /**
51 * Check if user has capabilities to view security dashboard
52 */
53 public static function check_capabilities()
54 {
55 if (isset($_GET['page']) && $_GET['page'] === 'king-addons-login-security') {
56 if (!current_user_can('manage_options')) {
57 wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'king-addons'));
58 }
59 }
60 }
61
62 /**
63 * Render the security dashboard - V3 Premium style inspired Design
64 */
65 public static function render_dashboard()
66 {
67 // Get security statistics
68 $stats = self::get_security_statistics();
69 $blocked_ips = self::get_blocked_ips();
70 $recent_attempts = self::get_recent_failed_attempts();
71
72 // Theme mode is per-user
73 $theme_mode = get_user_meta(get_current_user_id(), 'king_addons_theme_mode', true);
74 $allowed_theme_modes = ['dark', 'light', 'auto'];
75 if (!in_array($theme_mode, $allowed_theme_modes, true)) {
76 $theme_mode = 'dark';
77 }
78
79 // Enqueue shared V3 styles
80 wp_enqueue_style(
81 'king-addons-admin-v3',
82 KING_ADDONS_URL . 'includes/admin/layouts/shared/admin-v3-styles.css',
83 [],
84 KING_ADDONS_VERSION
85 );
86 ?>
87 <script>
88 (function() {
89 document.body && document.body.classList.add('ka-admin-v3');
90 const mode = '<?php echo esc_js($theme_mode); ?>';
91 const mql = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
92 const isDark = mode === 'auto' ? !!(mql && mql.matches) : mode === 'dark';
93 document.documentElement.classList.toggle('ka-v3-dark', isDark);
94 document.body && document.body.classList.toggle('ka-v3-dark', isDark);
95 })();
96 </script>
97
98 <style>
99 /* Security Dashboard V3 - Additional styles */
100 .ka-security-v3 .ka-features-grid {
101 display: grid;
102 grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
103 gap: 16px;
104 margin-bottom: 24px;
105 }
106
107 .ka-security-v3 .ka-feature-card {
108 background: #fff;
109 border-radius: 16px;
110 padding: 24px;
111 text-align: center;
112 border: 1px solid rgba(0, 0, 0, 0.04);
113 transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
114 }
115
116 body.ka-v3-dark .ka-security-v3 .ka-feature-card {
117 background: #1c1c1e;
118 border-color: rgba(255, 255, 255, 0.06);
119 }
120
121 .ka-security-v3 .ka-feature-card:hover {
122 transform: translateY(-4px);
123 box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
124 }
125
126 body.ka-v3-dark .ka-security-v3 .ka-feature-card:hover {
127 box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
128 }
129
130 .ka-security-v3 .ka-feature-card .dashicons {
131 font-size: 32px;
132 width: 32px;
133 height: 32px;
134 color: #ef4444;
135 margin-bottom: 12px;
136 }
137
138 .ka-security-v3 .ka-feature-card h4 {
139 margin: 0 0 6px;
140 font-size: 15px;
141 font-weight: 600;
142 color: #1d1d1f;
143 }
144
145 body.ka-v3-dark .ka-security-v3 .ka-feature-card h4,
146 body.ka-v3-dark .ka-security-v3 .ka-stat-card h3 {
147 color: #f5f5f7;
148 }
149
150 .ka-security-v3 .ka-feature-card p {
151 margin: 0;
152 font-size: 13px;
153 color: #86868b;
154 }
155
156 /* Stats override for security color */
157 .ka-security-v3 .ka-stat-card .ka-stat-number {
158 color: #ef4444;
159 }
160
161 /* Actions grid */
162 .ka-security-v3 .ka-actions-grid {
163 display: grid;
164 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
165 gap: 20px;
166 }
167
168 .ka-security-v3 .ka-action-card {
169 background: rgba(0, 0, 0, 0.02);
170 border: 1px solid rgba(0, 0, 0, 0.04);
171 border-radius: 16px;
172 padding: 24px;
173 text-align: center;
174 transition: all 0.3s;
175 }
176
177 body.ka-v3-dark .ka-security-v3 .ka-action-card {
178 background: rgba(255, 255, 255, 0.04);
179 border-color: rgba(255, 255, 255, 0.06);
180 }
181
182 .ka-security-v3 .ka-action-card h4 {
183 margin: 0 0 8px;
184 font-size: 15px;
185 font-weight: 600;
186 color: #1d1d1f;
187 }
188
189 body.ka-v3-dark .ka-security-v3 .ka-action-card h4 {
190 color: #f5f5f7;
191 }
192
193 .ka-security-v3 .ka-action-card p {
194 margin: 0 0 16px;
195 font-size: 13px;
196 color: #86868b;
197 }
198
199 .ka-security-v3 .ka-action-btn {
200 display: inline-flex;
201 align-items: center;
202 gap: 8px;
203 background: #fff;
204 border: 1px solid rgba(0, 0, 0, 0.1);
205 padding: 10px 20px;
206 border-radius: 980px;
207 font-size: 14px;
208 color: #1d1d1f;
209 cursor: pointer;
210 transition: all 0.2s;
211 }
212
213 body.ka-v3-dark .ka-security-v3 .ka-action-btn {
214 background: #2c2c2e;
215 border-color: rgba(255, 255, 255, 0.1);
216 color: #f5f5f7;
217 }
218
219 .ka-security-v3 .ka-action-btn:hover {
220 border-color: #ef4444;
221 color: #ef4444;
222 }
223
224 body.ka-v3-dark .ka-security-v3 .ka-action-btn:hover {
225 border-color: #ef4444;
226 color: #ef4444;
227 }
228
229 .ka-security-v3 .ka-action-btn .dashicons {
230 font-size: 16px;
231 width: 16px;
232 height: 16px;
233 }
234
235 /* Security specific input focus */
236 .ka-security-v3 input:focus {
237 border-color: #ef4444 !important;
238 box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.1) !important;
239 }
240
241 body.ka-v3-dark .ka-security-v3 input:focus {
242 box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.2) !important;
243 }
244
245 /* Security toggle color */
246 .ka-security-v3 .ka-toggle input:checked + .ka-toggle-slider {
247 background: #ef4444 !important;
248 }
249 </style>
250
251 <div class="ka-admin-wrap ka-security-v3">
252 <!-- Header -->
253 <div class="ka-admin-header">
254 <div class="ka-admin-header-left">
255 <div class="ka-admin-header-icon red">
256 <span class="dashicons dashicons-shield"></span>
257 </div>
258 <div>
259 <h1 class="ka-admin-title"><?php esc_html_e('Login Security', 'king-addons'); ?></h1>
260 <p class="ka-admin-subtitle"><?php esc_html_e('Monitor and protect Login Register Form widgets', 'king-addons'); ?></p>
261 </div>
262 </div>
263 <div class="ka-admin-header-actions">
264 <div class="ka-v3-segmented" id="ka-v3-theme-segment" role="radiogroup" aria-label="<?php echo esc_attr(esc_html__('Theme', 'king-addons')); ?>" data-active="<?php echo esc_attr($theme_mode); ?>">
265 <span class="ka-v3-segmented-indicator" aria-hidden="true"></span>
266 <button type="button" class="ka-v3-segmented-btn" data-theme="light" aria-pressed="<?php echo $theme_mode === 'light' ? 'true' : 'false'; ?>">
267 <span class="ka-v3-segmented-icon" aria-hidden="true">☀︎</span>
268 <?php esc_html_e('Light', 'king-addons'); ?>
269 </button>
270 <button type="button" class="ka-v3-segmented-btn" data-theme="dark" aria-pressed="<?php echo $theme_mode === 'dark' ? 'true' : 'false'; ?>">
271 <span class="ka-v3-segmented-icon" aria-hidden="true"></span>
272 <?php esc_html_e('Dark', 'king-addons'); ?>
273 </button>
274 <button type="button" class="ka-v3-segmented-btn" data-theme="auto" aria-pressed="<?php echo $theme_mode === 'auto' ? 'true' : 'false'; ?>">
275 <?php esc_html_e('Auto', 'king-addons'); ?>
276 </button>
277 </div>
278 </div>
279 </div>
280
281 <!-- Features -->
282 <div class="ka-features-grid">
283 <div class="ka-feature-card">
284 <span class="dashicons dashicons-shield-alt"></span>
285 <h4><?php esc_html_e('Rate Limiting', 'king-addons'); ?></h4>
286 <p><?php esc_html_e('Auto-blocks IPs after failed attempts', 'king-addons'); ?></p>
287 </div>
288 <div class="ka-feature-card">
289 <span class="dashicons dashicons-upload"></span>
290 <h4><?php esc_html_e('File Security', 'king-addons'); ?></h4>
291 <p><?php esc_html_e('Validates uploads & scans content', 'king-addons'); ?></p>
292 </div>
293 <div class="ka-feature-card">
294 <span class="dashicons dashicons-admin-users"></span>
295 <h4><?php esc_html_e('Anti-Enumeration', 'king-addons'); ?></h4>
296 <p><?php esc_html_e('Unified error messages', 'king-addons'); ?></p>
297 </div>
298 <div class="ka-feature-card">
299 <span class="dashicons dashicons-share"></span>
300 <h4><?php esc_html_e('Social Login', 'king-addons'); ?></h4>
301 <p><?php esc_html_e('Enhanced OAuth validation', 'king-addons'); ?></p>
302 </div>
303 </div>
304
305 <!-- Stats -->
306 <div class="ka-stats-grid">
307 <div class="ka-stat-card">
308 <h3 class="ka-stat-title"><?php esc_html_e('Failed Logins (24h)', 'king-addons'); ?></h3>
309 <div class="ka-stat-number"><?php echo esc_html($stats['failed_logins_24h']); ?></div>
310 </div>
311 <div class="ka-stat-card">
312 <h3 class="ka-stat-title"><?php esc_html_e('Blocked IPs', 'king-addons'); ?></h3>
313 <div class="ka-stat-number"><?php echo esc_html($stats['blocked_ips']); ?></div>
314 </div>
315 <div class="ka-stat-card">
316 <h3 class="ka-stat-title"><?php esc_html_e('Suspicious Registrations', 'king-addons'); ?></h3>
317 <div class="ka-stat-number"><?php echo esc_html($stats['suspicious_registrations']); ?></div>
318 </div>
319 <div class="ka-stat-card">
320 <h3 class="ka-stat-title"><?php esc_html_e('Upload Blocks', 'king-addons'); ?></h3>
321 <div class="ka-stat-number"><?php echo esc_html($stats['file_upload_blocks']); ?></div>
322 </div>
323 </div>
324
325 <!-- Blocked IPs -->
326 <?php if (!empty($blocked_ips)): ?>
327 <div class="ka-card">
328 <div class="ka-card-header">
329 <span class="dashicons dashicons-dismiss" style="color: #ef4444;"></span>
330 <h2><?php esc_html_e('Blocked IPs', 'king-addons'); ?></h2>
331 </div>
332 <div class="ka-card-body" style="padding:0">
333 <table class="ka-table">
334 <thead>
335 <tr>
336 <th><?php esc_html_e('IP Address', 'king-addons'); ?></th>
337 <th><?php esc_html_e('Attempts', 'king-addons'); ?></th>
338 <th><?php esc_html_e('Last Attempt', 'king-addons'); ?></th>
339 <th><?php esc_html_e('Expires', 'king-addons'); ?></th>
340 <th><?php esc_html_e('Actions', 'king-addons'); ?></th>
341 </tr>
342 </thead>
343 <tbody>
344 <?php foreach ($blocked_ips as $ip_data): ?>
345 <tr>
346 <td><?php echo esc_html($ip_data['ip']); ?></td>
347 <td><?php echo esc_html($ip_data['attempts']); ?></td>
348 <td><?php echo esc_html(human_time_diff($ip_data['last_attempt'], time()) . ' ago'); ?></td>
349 <td><?php echo esc_html(human_time_diff(time(), $ip_data['expires']) . ' remaining'); ?></td>
350 <td>
351 <button class="ka-action-btn unblock-ip" data-ip="<?php echo esc_attr($ip_data['ip']); ?>">
352 <?php esc_html_e('Unblock', 'king-addons'); ?>
353 </button>
354 </td>
355 </tr>
356 <?php endforeach; ?>
357 </tbody>
358 </table>
359 </div>
360 </div>
361 <?php endif; ?>
362
363 <!-- Settings -->
364 <div class="ka-card">
365 <div class="ka-card-header">
366 <span class="dashicons dashicons-admin-settings" style="color: #ef4444;"></span>
367 <h2><?php esc_html_e('Security Settings', 'king-addons'); ?></h2>
368 </div>
369 <div class="ka-card-body">
370 <form method="post" action="options.php">
371 <?php settings_fields('king_addons_security_settings'); ?>
372 <div class="ka-row">
373 <div class="ka-row-label"><?php esc_html_e('Max Login Attempts', 'king-addons'); ?></div>
374 <div class="ka-row-field">
375 <input type="number" name="king_addons_max_login_attempts"
376 value="<?php echo esc_attr(get_option('king_addons_max_login_attempts', 5)); ?>" min="1" max="20" />
377 <p class="ka-row-desc"><?php esc_html_e('Failed attempts before IP is blocked. Recommended: 3-5', 'king-addons'); ?></p>
378 </div>
379 </div>
380 <div class="ka-row">
381 <div class="ka-row-label"><?php esc_html_e('Lockout Duration', 'king-addons'); ?></div>
382 <div class="ka-row-field">
383 <input type="number" name="king_addons_lockout_duration"
384 value="<?php echo esc_attr(get_option('king_addons_lockout_duration', 15)); ?>" min="1" max="1440" />
385 <span style="color:#86868b;margin-left:6px"><?php esc_html_e('minutes', 'king-addons'); ?></span>
386 <p class="ka-row-desc"><?php esc_html_e('Duration to block an IP after exceeding attempts. Recommended: 15-30', 'king-addons'); ?></p>
387 </div>
388 </div>
389 <div class="ka-row">
390 <div class="ka-row-label"><?php esc_html_e('Security Logging', 'king-addons'); ?></div>
391 <div class="ka-row-field">
392 <label class="ka-toggle">
393 <input type="checkbox" name="king_addons_enable_security_logging" value="1"
394 <?php checked(get_option('king_addons_enable_security_logging', 1)); ?> />
395 <span class="ka-toggle-slider"></span>
396 <span class="ka-toggle-label"><?php esc_html_e('Log security events', 'king-addons'); ?></span>
397 </label>
398 <p class="ka-row-desc"><?php esc_html_e('Record failed attempts, blocks, and suspicious activity', 'king-addons'); ?></p>
399 </div>
400 </div>
401
402 <div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid rgba(0,0,0,0.04);">
403 <button type="submit" class="ka-btn ka-btn-primary"><?php esc_html_e('Save Settings', 'king-addons'); ?></button>
404 </div>
405 </form>
406 </div>
407 </div>
408
409 <!-- Actions -->
410 <div class="ka-card">
411 <div class="ka-card-header">
412 <span class="dashicons dashicons-admin-tools" style="color: #ef4444;"></span>
413 <h2><?php esc_html_e('Management Actions', 'king-addons'); ?></h2>
414 </div>
415 <div class="ka-card-body">
416 <div class="ka-actions-grid">
417 <div class="ka-action-card">
418 <h4><?php esc_html_e('Clear Security Logs', 'king-addons'); ?></h4>
419 <p><?php esc_html_e('Remove all logs and reset blocked IPs', 'king-addons'); ?></p>
420 <button class="ka-action-btn" id="clear-security-logs">
421 <span class="dashicons dashicons-trash"></span>
422 <?php esc_html_e('Clear Logs', 'king-addons'); ?>
423 </button>
424 </div>
425 <div class="ka-action-card">
426 <h4><?php esc_html_e('Export Report', 'king-addons'); ?></h4>
427 <p><?php esc_html_e('Download security report as JSON', 'king-addons'); ?></p>
428 <button class="ka-action-btn" id="export-security-report">
429 <span class="dashicons dashicons-download"></span>
430 <?php esc_html_e('Export', 'king-addons'); ?>
431 </button>
432 </div>
433 </div>
434 </div>
435 </div>
436 </div>
437
438 <script>
439 (function() {
440 const segment = document.getElementById('ka-v3-theme-segment');
441 if (!segment) {
442 return;
443 }
444
445 const ajaxUrl = '<?php echo esc_url(admin_url('admin-ajax.php')); ?>';
446 const nonce = '<?php echo esc_js(wp_create_nonce('king_addons_dashboard_ui')); ?>';
447 const buttons = segment.querySelectorAll('.ka-v3-segmented-btn');
448
449 const mql = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
450 let mode = (segment.getAttribute('data-active') || 'dark').toString();
451 let mqlHandler = null;
452
453 function setPressedState(activeMode) {
454 segment.setAttribute('data-active', activeMode);
455 buttons.forEach((btn) => {
456 const theme = btn.getAttribute('data-theme');
457 btn.setAttribute('aria-pressed', theme === activeMode ? 'true' : 'false');
458 });
459 }
460
461 function saveUISetting(key, value) {
462 try {
463 const body = new URLSearchParams();
464 body.set('action', 'king_addons_save_dashboard_ui');
465 body.set('nonce', nonce);
466 body.set('key', key);
467 body.set('value', value);
468
469 fetch(ajaxUrl, {
470 method: 'POST',
471 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
472 body: body.toString(),
473 credentials: 'same-origin'
474 });
475 } catch (e) {}
476 }
477
478 function applyTheme(isDark) {
479 document.body.classList.toggle('ka-v3-dark', isDark);
480 document.documentElement.classList.toggle('ka-v3-dark', isDark);
481 }
482
483 function setThemeMode(nextMode, save) {
484 mode = nextMode;
485 setPressedState(nextMode);
486
487 if (mqlHandler && mql) {
488 if (mql.removeEventListener) {
489 mql.removeEventListener('change', mqlHandler);
490 } else if (mql.removeListener) {
491 mql.removeListener(mqlHandler);
492 }
493 mqlHandler = null;
494 }
495
496 if (nextMode === 'auto') {
497 applyTheme(!!(mql && mql.matches));
498 mqlHandler = (e) => {
499 if (mode !== 'auto') {
500 return;
501 }
502 applyTheme(!!e.matches);
503 };
504 if (mql) {
505 if (mql.addEventListener) {
506 mql.addEventListener('change', mqlHandler);
507 } else if (mql.addListener) {
508 mql.addListener(mqlHandler);
509 }
510 }
511 } else {
512 applyTheme(nextMode === 'dark');
513 }
514
515 if (save) {
516 saveUISetting('theme_mode', nextMode);
517 }
518 }
519
520 // Optional global for any legacy handlers
521 window.kaV3ToggleDark = function() {
522 const isDark = document.body.classList.contains('ka-v3-dark');
523 setThemeMode(isDark ? 'light' : 'dark', true);
524 };
525
526 segment.addEventListener('click', (e) => {
527 const btn = e.target && e.target.closest ? e.target.closest('.ka-v3-segmented-btn') : null;
528 if (!btn) {
529 return;
530 }
531 e.preventDefault();
532 const theme = (btn.getAttribute('data-theme') || 'dark').toString();
533 setThemeMode(theme, true);
534 });
535
536 setThemeMode(mode, false);
537 })();
538
539 jQuery(document).ready(function($) {
540 // Unblock IP functionality
541 $('.unblock-ip').on('click', function() {
542 const ip = $(this).data('ip');
543 if (confirm('<?php echo esc_js(__('Are you sure you want to unblock this IP?', 'king-addons')); ?>')) {
544 $.post(ajaxurl, {
545 action: 'king_addons_unblock_ip',
546 ip: ip,
547 nonce: '<?php echo wp_create_nonce('king_addons_security_nonce'); ?>'
548 }, function(response) {
549 if (response.success) {
550 location.reload();
551 } else {
552 alert('<?php echo esc_js(__('Failed to unblock IP', 'king-addons')); ?>');
553 }
554 });
555 }
556 });
557
558 // Clear security logs
559 $('#clear-security-logs').on('click', function() {
560 if (confirm('<?php echo esc_js(__('Are you sure you want to clear all security logs?', 'king-addons')); ?>')) {
561 $.post(ajaxurl, {
562 action: 'king_addons_clear_security_logs',
563 nonce: '<?php echo wp_create_nonce('king_addons_security_nonce'); ?>'
564 }, function(response) {
565 if (response.success) {
566 location.reload();
567 } else {
568 alert('<?php echo esc_js(__('Failed to clear logs', 'king-addons')); ?>');
569 }
570 });
571 }
572 });
573
574 // Export security report
575 $('#export-security-report').on('click', function() {
576 const $button = $(this);
577 const originalText = $button.html();
578 $button.prop('disabled', true).text('<?php echo esc_js(__('Exporting...', 'king-addons')); ?>');
579
580 $.post(ajaxurl, {
581 action: 'king_addons_export_security_report',
582 nonce: '<?php echo wp_create_nonce('king_addons_security_nonce'); ?>'
583 }, function(response) {
584 if (response.success) {
585 const link = document.createElement('a');
586 link.href = response.data.download_url;
587 link.download = response.data.filename;
588 document.body.appendChild(link);
589 link.click();
590 document.body.removeChild(link);
591 } else {
592 alert('<?php echo esc_js(__('Failed to generate report', 'king-addons')); ?>');
593 }
594 }).always(function() {
595 $button.prop('disabled', false).html(originalText);
596 });
597 });
598 });
599 </script>
600 <?php
601 }
602
603 /**
604 * Get security statistics
605 */
606 private static function get_security_statistics()
607 {
608 global $wpdb;
609
610 $stats = [
611 'failed_logins_24h' => 0,
612 'blocked_ips' => 0,
613 'suspicious_registrations' => 0,
614 'file_upload_blocks' => 0
615 ];
616
617 // Count blocked IPs
618 $transients = $wpdb->get_results(
619 "SELECT option_name FROM {$wpdb->options}
620 WHERE option_name LIKE '_transient_king_addons_%_attempts_%'
621 AND option_value >= 3"
622 );
623 $stats['blocked_ips'] = count($transients);
624
625 // Get failed attempts from error log (simplified - would need actual log parsing)
626 $log_file = ini_get('error_log');
627 if ($log_file && file_exists($log_file)) {
628 $log_content = file_get_contents($log_file);
629 $stats['failed_logins_24h'] = substr_count($log_content, 'King Addons Security: Failed login');
630 $stats['suspicious_registrations'] = substr_count($log_content, 'Suspicious registration pattern');
631 $stats['file_upload_blocks'] = substr_count($log_content, 'File upload blocked');
632 }
633
634 return $stats;
635 }
636
637 /**
638 * Get currently blocked IPs
639 */
640 private static function get_blocked_ips()
641 {
642 global $wpdb;
643
644 $blocked_ips = [];
645
646 $transients = $wpdb->get_results(
647 "SELECT option_name, option_value
648 FROM {$wpdb->options}
649 WHERE option_name LIKE '_transient_king_addons_%_attempts_%'"
650 );
651
652 foreach ($transients as $transient) {
653 $attempts = intval($transient->option_value);
654 if ($attempts >= Security_Manager::MAX_LOGIN_ATTEMPTS) {
655 // Extract IP from transient name
656 preg_match('/_transient_king_addons_\w+_attempts_(.+)/', $transient->option_name, $matches);
657 if (isset($matches[1])) {
658 $ip_hash = $matches[1];
659
660 // Get expiration time
661 $timeout_option = '_transient_timeout_' . str_replace('_transient_', '', $transient->option_name);
662 $expires = get_option($timeout_option, 0);
663
664 $blocked_ips[] = [
665 'ip' => 'IP Hash: ' . substr($ip_hash, 0, 8) . '...', // Don't expose full IPs
666 'attempts' => $attempts,
667 'last_attempt' => time() - 300, // Approximate
668 'expires' => $expires
669 ];
670 }
671 }
672 }
673
674 return $blocked_ips;
675 }
676
677 /**
678 * Get recent failed attempts from logs
679 */
680 private static function get_recent_failed_attempts()
681 {
682 $attempts = [];
683
684 // This would parse actual log files in a real implementation
685 // For now, return sample data structure
686
687 return $attempts;
688 }
689
690 /**
691 * AJAX handler to clear security logs
692 */
693 public static function clear_security_logs()
694 {
695 if (!wp_verify_nonce($_POST['nonce'], 'king_addons_security_nonce')) {
696 wp_send_json_error(['message' => 'Invalid nonce']);
697 }
698
699 if (!current_user_can('manage_options')) {
700 wp_send_json_error(['message' => 'Insufficient permissions']);
701 }
702
703 // Clear all rate limiting transients
704 global $wpdb;
705 $wpdb->query(
706 "DELETE FROM {$wpdb->options}
707 WHERE option_name LIKE '_transient_king_addons_%_attempts_%'
708 OR option_name LIKE '_transient_timeout_king_addons_%_attempts_%'"
709 );
710
711 wp_send_json_success(['message' => 'Security logs cleared successfully']);
712 }
713
714 /**
715 * AJAX handler to unblock IP
716 */
717 public static function unblock_ip()
718 {
719 if (!wp_verify_nonce($_POST['nonce'], 'king_addons_security_nonce')) {
720 wp_send_json_error(['message' => 'Invalid nonce']);
721 }
722
723 if (!current_user_can('manage_options')) {
724 wp_send_json_error(['message' => 'Insufficient permissions']);
725 }
726
727 $ip = sanitize_text_field($_POST['ip']);
728 if (empty($ip)) {
729 wp_send_json_error(['message' => 'Invalid IP address']);
730 }
731
732 // Clear attempts for this IP (simplified)
733 global $wpdb;
734 $ip_hash = md5($ip);
735 $wpdb->query($wpdb->prepare(
736 "DELETE FROM {$wpdb->options}
737 WHERE option_name LIKE %s
738 OR option_name LIKE %s",
739 '%_king_addons_%_attempts_' . $ip_hash,
740 '%_king_addons_%_attempts_' . $ip_hash . '%'
741 ));
742
743 wp_send_json_success(['message' => 'IP unblocked successfully']);
744 }
745
746 /**
747 * AJAX handler to export security report
748 */
749 public static function export_security_report()
750 {
751 if (!wp_verify_nonce($_POST['nonce'], 'king_addons_security_nonce')) {
752 wp_send_json_error(['message' => 'Invalid nonce']);
753 }
754
755 if (!current_user_can('manage_options')) {
756 wp_send_json_error(['message' => 'Insufficient permissions']);
757 }
758
759 // Generate security report
760 $stats = self::get_security_statistics();
761 $blocked_ips = self::get_blocked_ips();
762
763 $report = [
764 'generated_at' => current_time('Y-m-d H:i:s'),
765 'site_url' => get_site_url(),
766 'plugin_version' => defined('KING_ADDONS_VERSION') ? KING_ADDONS_VERSION : 'Unknown',
767 'statistics' => $stats,
768 'blocked_ips' => $blocked_ips,
769 'security_settings' => [
770 'max_login_attempts' => get_option('king_addons_max_login_attempts', 5),
771 'lockout_duration' => get_option('king_addons_lockout_duration', 15),
772 'security_logging_enabled' => get_option('king_addons_enable_security_logging', 1),
773 ]
774 ];
775
776 // Convert to JSON
777 $json_report = json_encode($report, JSON_PRETTY_PRINT);
778
779 // Create filename
780 $filename = 'king-addons-security-report-' . date('Y-m-d-H-i-s') . '.json';
781
782 // Return download URL
783 $upload_dir = wp_upload_dir();
784 $report_path = $upload_dir['path'] . '/' . $filename;
785
786 // Save file
787 if (file_put_contents($report_path, $json_report)) {
788 $download_url = $upload_dir['url'] . '/' . $filename;
789 wp_send_json_success([
790 'message' => 'Security report generated successfully',
791 'download_url' => $download_url,
792 'filename' => $filename
793 ]);
794 } else {
795 wp_send_json_error(['message' => 'Failed to generate report file']);
796 }
797 }
798 }