PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.78
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.78
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.78, at includes/widgets/Login_Register_Form/Security_Dashboard.php

799 lines 32.9 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 <span class="ka-v3-segmented-icon" aria-hidden="true"></span>
276 <?php esc_html_e('Auto', 'king-addons'); ?>
277 </button>
278 </div>
279 </div>
280 </div>
281
282 <!-- Features -->
283 <div class="ka-features-grid">
284 <div class="ka-feature-card">
285 <span class="dashicons dashicons-shield-alt"></span>
286 <h4><?php esc_html_e('Rate Limiting', 'king-addons'); ?></h4>
287 <p><?php esc_html_e('Auto-blocks IPs after failed attempts', 'king-addons'); ?></p>
288 </div>
289 <div class="ka-feature-card">
290 <span class="dashicons dashicons-upload"></span>
291 <h4><?php esc_html_e('File Security', 'king-addons'); ?></h4>
292 <p><?php esc_html_e('Validates uploads & scans content', 'king-addons'); ?></p>
293 </div>
294 <div class="ka-feature-card">
295 <span class="dashicons dashicons-admin-users"></span>
296 <h4><?php esc_html_e('Anti-Enumeration', 'king-addons'); ?></h4>
297 <p><?php esc_html_e('Unified error messages', 'king-addons'); ?></p>
298 </div>
299 <div class="ka-feature-card">
300 <span class="dashicons dashicons-share"></span>
301 <h4><?php esc_html_e('Social Login', 'king-addons'); ?></h4>
302 <p><?php esc_html_e('Enhanced OAuth validation', 'king-addons'); ?></p>
303 </div>
304 </div>
305
306 <!-- Stats -->
307 <div class="ka-stats-grid">
308 <div class="ka-stat-card">
309 <h3 class="ka-stat-title"><?php esc_html_e('Failed Logins (24h)', 'king-addons'); ?></h3>
310 <div class="ka-stat-number"><?php echo esc_html($stats['failed_logins_24h']); ?></div>
311 </div>
312 <div class="ka-stat-card">
313 <h3 class="ka-stat-title"><?php esc_html_e('Blocked IPs', 'king-addons'); ?></h3>
314 <div class="ka-stat-number"><?php echo esc_html($stats['blocked_ips']); ?></div>
315 </div>
316 <div class="ka-stat-card">
317 <h3 class="ka-stat-title"><?php esc_html_e('Suspicious Registrations', 'king-addons'); ?></h3>
318 <div class="ka-stat-number"><?php echo esc_html($stats['suspicious_registrations']); ?></div>
319 </div>
320 <div class="ka-stat-card">
321 <h3 class="ka-stat-title"><?php esc_html_e('Upload Blocks', 'king-addons'); ?></h3>
322 <div class="ka-stat-number"><?php echo esc_html($stats['file_upload_blocks']); ?></div>
323 </div>
324 </div>
325
326 <!-- Blocked IPs -->
327 <?php if (!empty($blocked_ips)): ?>
328 <div class="ka-card">
329 <div class="ka-card-header">
330 <span class="dashicons dashicons-dismiss" style="color: #ef4444;"></span>
331 <h2><?php esc_html_e('Blocked IPs', 'king-addons'); ?></h2>
332 </div>
333 <div class="ka-card-body" style="padding:0">
334 <table class="ka-table">
335 <thead>
336 <tr>
337 <th><?php esc_html_e('IP Address', 'king-addons'); ?></th>
338 <th><?php esc_html_e('Attempts', 'king-addons'); ?></th>
339 <th><?php esc_html_e('Last Attempt', 'king-addons'); ?></th>
340 <th><?php esc_html_e('Expires', 'king-addons'); ?></th>
341 <th><?php esc_html_e('Actions', 'king-addons'); ?></th>
342 </tr>
343 </thead>
344 <tbody>
345 <?php foreach ($blocked_ips as $ip_data): ?>
346 <tr>
347 <td><?php echo esc_html($ip_data['ip']); ?></td>
348 <td><?php echo esc_html($ip_data['attempts']); ?></td>
349 <td><?php echo esc_html(human_time_diff($ip_data['last_attempt'], time()) . ' ago'); ?></td>
350 <td><?php echo esc_html(human_time_diff(time(), $ip_data['expires']) . ' remaining'); ?></td>
351 <td>
352 <button class="ka-action-btn unblock-ip" data-ip="<?php echo esc_attr($ip_data['ip']); ?>">
353 <?php esc_html_e('Unblock', 'king-addons'); ?>
354 </button>
355 </td>
356 </tr>
357 <?php endforeach; ?>
358 </tbody>
359 </table>
360 </div>
361 </div>
362 <?php endif; ?>
363
364 <!-- Settings -->
365 <div class="ka-card">
366 <div class="ka-card-header">
367 <span class="dashicons dashicons-admin-settings" style="color: #ef4444;"></span>
368 <h2><?php esc_html_e('Security Settings', 'king-addons'); ?></h2>
369 </div>
370 <div class="ka-card-body">
371 <form method="post" action="options.php">
372 <?php settings_fields('king_addons_security_settings'); ?>
373 <div class="ka-row">
374 <div class="ka-row-label"><?php esc_html_e('Max Login Attempts', 'king-addons'); ?></div>
375 <div class="ka-row-field">
376 <input type="number" name="king_addons_max_login_attempts"
377 value="<?php echo esc_attr(get_option('king_addons_max_login_attempts', 5)); ?>" min="1" max="20" />
378 <p class="ka-row-desc"><?php esc_html_e('Failed attempts before IP is blocked. Recommended: 3-5', 'king-addons'); ?></p>
379 </div>
380 </div>
381 <div class="ka-row">
382 <div class="ka-row-label"><?php esc_html_e('Lockout Duration', 'king-addons'); ?></div>
383 <div class="ka-row-field">
384 <input type="number" name="king_addons_lockout_duration"
385 value="<?php echo esc_attr(get_option('king_addons_lockout_duration', 15)); ?>" min="1" max="1440" />
386 <span style="color:#86868b;margin-left:6px"><?php esc_html_e('minutes', 'king-addons'); ?></span>
387 <p class="ka-row-desc"><?php esc_html_e('Duration to block an IP after exceeding attempts. Recommended: 15-30', 'king-addons'); ?></p>
388 </div>
389 </div>
390 <div class="ka-row">
391 <div class="ka-row-label"><?php esc_html_e('Security Logging', 'king-addons'); ?></div>
392 <div class="ka-row-field">
393 <label class="ka-toggle">
394 <input type="checkbox" name="king_addons_enable_security_logging" value="1"
395 <?php checked(get_option('king_addons_enable_security_logging', 1)); ?> />
396 <span class="ka-toggle-slider"></span>
397 <span class="ka-toggle-label"><?php esc_html_e('Log security events', 'king-addons'); ?></span>
398 </label>
399 <p class="ka-row-desc"><?php esc_html_e('Record failed attempts, blocks, and suspicious activity', 'king-addons'); ?></p>
400 </div>
401 </div>
402
403 <div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid rgba(0,0,0,0.04);">
404 <button type="submit" class="ka-btn ka-btn-primary"><?php esc_html_e('Save Settings', 'king-addons'); ?></button>
405 </div>
406 </form>
407 </div>
408 </div>
409
410 <!-- Actions -->
411 <div class="ka-card">
412 <div class="ka-card-header">
413 <span class="dashicons dashicons-admin-tools" style="color: #ef4444;"></span>
414 <h2><?php esc_html_e('Management Actions', 'king-addons'); ?></h2>
415 </div>
416 <div class="ka-card-body">
417 <div class="ka-actions-grid">
418 <div class="ka-action-card">
419 <h4><?php esc_html_e('Clear Security Logs', 'king-addons'); ?></h4>
420 <p><?php esc_html_e('Remove all logs and reset blocked IPs', 'king-addons'); ?></p>
421 <button class="ka-action-btn" id="clear-security-logs">
422 <span class="dashicons dashicons-trash"></span>
423 <?php esc_html_e('Clear Logs', 'king-addons'); ?>
424 </button>
425 </div>
426 <div class="ka-action-card">
427 <h4><?php esc_html_e('Export Report', 'king-addons'); ?></h4>
428 <p><?php esc_html_e('Download security report as JSON', 'king-addons'); ?></p>
429 <button class="ka-action-btn" id="export-security-report">
430 <span class="dashicons dashicons-download"></span>
431 <?php esc_html_e('Export', 'king-addons'); ?>
432 </button>
433 </div>
434 </div>
435 </div>
436 </div>
437 </div>
438
439 <script>
440 (function() {
441 const segment = document.getElementById('ka-v3-theme-segment');
442 if (!segment) {
443 return;
444 }
445
446 const ajaxUrl = '<?php echo esc_url(admin_url('admin-ajax.php')); ?>';
447 const nonce = '<?php echo esc_js(wp_create_nonce('king_addons_dashboard_ui')); ?>';
448 const buttons = segment.querySelectorAll('.ka-v3-segmented-btn');
449
450 const mql = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
451 let mode = (segment.getAttribute('data-active') || 'dark').toString();
452 let mqlHandler = null;
453
454 function setPressedState(activeMode) {
455 segment.setAttribute('data-active', activeMode);
456 buttons.forEach((btn) => {
457 const theme = btn.getAttribute('data-theme');
458 btn.setAttribute('aria-pressed', theme === activeMode ? 'true' : 'false');
459 });
460 }
461
462 function saveUISetting(key, value) {
463 try {
464 const body = new URLSearchParams();
465 body.set('action', 'king_addons_save_dashboard_ui');
466 body.set('nonce', nonce);
467 body.set('key', key);
468 body.set('value', value);
469
470 fetch(ajaxUrl, {
471 method: 'POST',
472 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
473 body: body.toString(),
474 credentials: 'same-origin'
475 });
476 } catch (e) {}
477 }
478
479 function applyTheme(isDark) {
480 document.body.classList.toggle('ka-v3-dark', isDark);
481 document.documentElement.classList.toggle('ka-v3-dark', isDark);
482 }
483
484 function setThemeMode(nextMode, save) {
485 mode = nextMode;
486 setPressedState(nextMode);
487
488 if (mqlHandler && mql) {
489 if (mql.removeEventListener) {
490 mql.removeEventListener('change', mqlHandler);
491 } else if (mql.removeListener) {
492 mql.removeListener(mqlHandler);
493 }
494 mqlHandler = null;
495 }
496
497 if (nextMode === 'auto') {
498 applyTheme(!!(mql && mql.matches));
499 mqlHandler = (e) => {
500 if (mode !== 'auto') {
501 return;
502 }
503 applyTheme(!!e.matches);
504 };
505 if (mql) {
506 if (mql.addEventListener) {
507 mql.addEventListener('change', mqlHandler);
508 } else if (mql.addListener) {
509 mql.addListener(mqlHandler);
510 }
511 }
512 } else {
513 applyTheme(nextMode === 'dark');
514 }
515
516 if (save) {
517 saveUISetting('theme_mode', nextMode);
518 }
519 }
520
521 // Optional global for any legacy handlers
522 window.kaV3ToggleDark = function() {
523 const isDark = document.body.classList.contains('ka-v3-dark');
524 setThemeMode(isDark ? 'light' : 'dark', true);
525 };
526
527 segment.addEventListener('click', (e) => {
528 const btn = e.target && e.target.closest ? e.target.closest('.ka-v3-segmented-btn') : null;
529 if (!btn) {
530 return;
531 }
532 e.preventDefault();
533 const theme = (btn.getAttribute('data-theme') || 'dark').toString();
534 setThemeMode(theme, true);
535 });
536
537 setThemeMode(mode, false);
538 })();
539
540 jQuery(document).ready(function($) {
541 // Unblock IP functionality
542 $('.unblock-ip').on('click', function() {
543 const ip = $(this).data('ip');
544 if (confirm('<?php echo esc_js(__('Are you sure you want to unblock this IP?', 'king-addons')); ?>')) {
545 $.post(ajaxurl, {
546 action: 'king_addons_unblock_ip',
547 ip: ip,
548 nonce: '<?php echo wp_create_nonce('king_addons_security_nonce'); ?>'
549 }, function(response) {
550 if (response.success) {
551 location.reload();
552 } else {
553 alert('<?php echo esc_js(__('Failed to unblock IP', 'king-addons')); ?>');
554 }
555 });
556 }
557 });
558
559 // Clear security logs
560 $('#clear-security-logs').on('click', function() {
561 if (confirm('<?php echo esc_js(__('Are you sure you want to clear all security logs?', 'king-addons')); ?>')) {
562 $.post(ajaxurl, {
563 action: 'king_addons_clear_security_logs',
564 nonce: '<?php echo wp_create_nonce('king_addons_security_nonce'); ?>'
565 }, function(response) {
566 if (response.success) {
567 location.reload();
568 } else {
569 alert('<?php echo esc_js(__('Failed to clear logs', 'king-addons')); ?>');
570 }
571 });
572 }
573 });
574
575 // Export security report
576 $('#export-security-report').on('click', function() {
577 const $button = $(this);
578 const originalText = $button.html();
579 $button.prop('disabled', true).text('<?php echo esc_js(__('Exporting...', 'king-addons')); ?>');
580
581 $.post(ajaxurl, {
582 action: 'king_addons_export_security_report',
583 nonce: '<?php echo wp_create_nonce('king_addons_security_nonce'); ?>'
584 }, function(response) {
585 if (response.success) {
586 const link = document.createElement('a');
587 link.href = response.data.download_url;
588 link.download = response.data.filename;
589 document.body.appendChild(link);
590 link.click();
591 document.body.removeChild(link);
592 } else {
593 alert('<?php echo esc_js(__('Failed to generate report', 'king-addons')); ?>');
594 }
595 }).always(function() {
596 $button.prop('disabled', false).html(originalText);
597 });
598 });
599 });
600 </script>
601 <?php
602 }
603
604 /**
605 * Get security statistics
606 */
607 private static function get_security_statistics()
608 {
609 global $wpdb;
610
611 $stats = [
612 'failed_logins_24h' => 0,
613 'blocked_ips' => 0,
614 'suspicious_registrations' => 0,
615 'file_upload_blocks' => 0
616 ];
617
618 // Count blocked IPs
619 $transients = $wpdb->get_results(
620 "SELECT option_name FROM {$wpdb->options}
621 WHERE option_name LIKE '_transient_king_addons_%_attempts_%'
622 AND option_value >= 3"
623 );
624 $stats['blocked_ips'] = count($transients);
625
626 // Get failed attempts from error log (simplified - would need actual log parsing)
627 $log_file = ini_get('error_log');
628 if ($log_file && file_exists($log_file)) {
629 $log_content = file_get_contents($log_file);
630 $stats['failed_logins_24h'] = substr_count($log_content, 'King Addons Security: Failed login');
631 $stats['suspicious_registrations'] = substr_count($log_content, 'Suspicious registration pattern');
632 $stats['file_upload_blocks'] = substr_count($log_content, 'File upload blocked');
633 }
634
635 return $stats;
636 }
637
638 /**
639 * Get currently blocked IPs
640 */
641 private static function get_blocked_ips()
642 {
643 global $wpdb;
644
645 $blocked_ips = [];
646
647 $transients = $wpdb->get_results(
648 "SELECT option_name, option_value
649 FROM {$wpdb->options}
650 WHERE option_name LIKE '_transient_king_addons_%_attempts_%'"
651 );
652
653 foreach ($transients as $transient) {
654 $attempts = intval($transient->option_value);
655 if ($attempts >= Security_Manager::MAX_LOGIN_ATTEMPTS) {
656 // Extract IP from transient name
657 preg_match('/_transient_king_addons_\w+_attempts_(.+)/', $transient->option_name, $matches);
658 if (isset($matches[1])) {
659 $ip_hash = $matches[1];
660
661 // Get expiration time
662 $timeout_option = '_transient_timeout_' . str_replace('_transient_', '', $transient->option_name);
663 $expires = get_option($timeout_option, 0);
664
665 $blocked_ips[] = [
666 'ip' => 'IP Hash: ' . substr($ip_hash, 0, 8) . '...', // Don't expose full IPs
667 'attempts' => $attempts,
668 'last_attempt' => time() - 300, // Approximate
669 'expires' => $expires
670 ];
671 }
672 }
673 }
674
675 return $blocked_ips;
676 }
677
678 /**
679 * Get recent failed attempts from logs
680 */
681 private static function get_recent_failed_attempts()
682 {
683 $attempts = [];
684
685 // This would parse actual log files in a real implementation
686 // For now, return sample data structure
687
688 return $attempts;
689 }
690
691 /**
692 * AJAX handler to clear security logs
693 */
694 public static function clear_security_logs()
695 {
696 if (!wp_verify_nonce($_POST['nonce'], 'king_addons_security_nonce')) {
697 wp_send_json_error(['message' => 'Invalid nonce']);
698 }
699
700 if (!current_user_can('manage_options')) {
701 wp_send_json_error(['message' => 'Insufficient permissions']);
702 }
703
704 // Clear all rate limiting transients
705 global $wpdb;
706 $wpdb->query(
707 "DELETE FROM {$wpdb->options}
708 WHERE option_name LIKE '_transient_king_addons_%_attempts_%'
709 OR option_name LIKE '_transient_timeout_king_addons_%_attempts_%'"
710 );
711
712 wp_send_json_success(['message' => 'Security logs cleared successfully']);
713 }
714
715 /**
716 * AJAX handler to unblock IP
717 */
718 public static function unblock_ip()
719 {
720 if (!wp_verify_nonce($_POST['nonce'], 'king_addons_security_nonce')) {
721 wp_send_json_error(['message' => 'Invalid nonce']);
722 }
723
724 if (!current_user_can('manage_options')) {
725 wp_send_json_error(['message' => 'Insufficient permissions']);
726 }
727
728 $ip = sanitize_text_field($_POST['ip']);
729 if (empty($ip)) {
730 wp_send_json_error(['message' => 'Invalid IP address']);
731 }
732
733 // Clear attempts for this IP (simplified)
734 global $wpdb;
735 $ip_hash = md5($ip);
736 $wpdb->query($wpdb->prepare(
737 "DELETE FROM {$wpdb->options}
738 WHERE option_name LIKE %s
739 OR option_name LIKE %s",
740 '%_king_addons_%_attempts_' . $ip_hash,
741 '%_king_addons_%_attempts_' . $ip_hash . '%'
742 ));
743
744 wp_send_json_success(['message' => 'IP unblocked successfully']);
745 }
746
747 /**
748 * AJAX handler to export security report
749 */
750 public static function export_security_report()
751 {
752 if (!wp_verify_nonce($_POST['nonce'], 'king_addons_security_nonce')) {
753 wp_send_json_error(['message' => 'Invalid nonce']);
754 }
755
756 if (!current_user_can('manage_options')) {
757 wp_send_json_error(['message' => 'Insufficient permissions']);
758 }
759
760 // Generate security report
761 $stats = self::get_security_statistics();
762 $blocked_ips = self::get_blocked_ips();
763
764 $report = [
765 'generated_at' => current_time('Y-m-d H:i:s'),
766 'site_url' => get_site_url(),
767 'plugin_version' => defined('KING_ADDONS_VERSION') ? KING_ADDONS_VERSION : 'Unknown',
768 'statistics' => $stats,
769 'blocked_ips' => $blocked_ips,
770 'security_settings' => [
771 'max_login_attempts' => get_option('king_addons_max_login_attempts', 5),
772 'lockout_duration' => get_option('king_addons_lockout_duration', 15),
773 'security_logging_enabled' => get_option('king_addons_enable_security_logging', 1),
774 ]
775 ];
776
777 // Convert to JSON
778 $json_report = json_encode($report, JSON_PRETTY_PRINT);
779
780 // Create filename
781 $filename = 'king-addons-security-report-' . date('Y-m-d-H-i-s') . '.json';
782
783 // Return download URL
784 $upload_dir = wp_upload_dir();
785 $report_path = $upload_dir['path'] . '/' . $filename;
786
787 // Save file
788 if (file_put_contents($report_path, $json_report)) {
789 $download_url = $upload_dir['url'] . '/' . $filename;
790 wp_send_json_success([
791 'message' => 'Security report generated successfully',
792 'download_url' => $download_url,
793 'filename' => $filename
794 ]);
795 } else {
796 wp_send_json_error(['message' => 'Failed to generate report file']);
797 }
798 }
799 }