PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.86
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.86
51.1.86 51.1.84 51.1.85 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 All 40 releases
← All changes | includes/widgets/Login_Register_Form/Security_Dashboard.php +799 -0 51.1.2 → 51.1.86 View file →
@@ -1,0 +1,799 @@
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 = 'auto';
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 +}