fix(client-guardian): Restore original menu path
This commit is contained in:
parent
e3d7873d7b
commit
0e5965dd6c
@ -2,16 +2,8 @@
|
||||
'require view';
|
||||
'require dom';
|
||||
'require poll';
|
||||
'require uci';
|
||||
'require ui';
|
||||
'require rpc';
|
||||
'require client-guardian/nav as CgNav';
|
||||
'require secubox-portal/header as SbHeader';
|
||||
|
||||
var callGetStatus = rpc.declare({
|
||||
object: 'luci.client-guardian',
|
||||
method: 'status'
|
||||
});
|
||||
'require ui';
|
||||
|
||||
var callGetClients = rpc.declare({
|
||||
object: 'luci.client-guardian',
|
||||
@ -19,12 +11,6 @@ var callGetClients = rpc.declare({
|
||||
expect: { clients: [] }
|
||||
});
|
||||
|
||||
var callGetZones = rpc.declare({
|
||||
object: 'luci.client-guardian',
|
||||
method: 'zones',
|
||||
expect: { zones: [] }
|
||||
});
|
||||
|
||||
var callApproveClient = rpc.declare({
|
||||
object: 'luci.client-guardian',
|
||||
method: 'approve_client',
|
||||
@ -37,297 +23,149 @@ var callBanClient = rpc.declare({
|
||||
params: ['mac', 'reason']
|
||||
});
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (!bytes || bytes === 0) return '0 B';
|
||||
var units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
var i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
i = Math.min(i, units.length - 1);
|
||||
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + units[i];
|
||||
}
|
||||
|
||||
function getDeviceIcon(hostname, mac) {
|
||||
hostname = (hostname || '').toLowerCase();
|
||||
mac = (mac || '').toLowerCase();
|
||||
if (hostname.match(/android|iphone|ipad|mobile|phone|samsung|xiaomi|huawei/)) return '📱';
|
||||
if (hostname.match(/pc|laptop|desktop|macbook|imac|windows|linux|ubuntu/)) return '💻';
|
||||
if (hostname.match(/camera|bulb|switch|sensor|thermostat|doorbell|lock/)) return '📷';
|
||||
if (hostname.match(/tv|roku|chromecast|firestick|appletv|media/)) return '📺';
|
||||
if (hostname.match(/playstation|xbox|nintendo|switch|steam/)) return '🎮';
|
||||
if (hostname.match(/router|switch|ap|access[-_]?point|bridge/)) return '🌐';
|
||||
if (hostname.match(/printer|print|hp-|canon-|epson-/)) return '🖨️';
|
||||
return '🔌';
|
||||
}
|
||||
var callUnbanClient = rpc.declare({
|
||||
object: 'luci.client-guardian',
|
||||
method: 'unban_client',
|
||||
params: ['mac']
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
callGetStatus(),
|
||||
callGetClients(),
|
||||
callGetZones(),
|
||||
uci.load('client-guardian')
|
||||
]);
|
||||
return callGetClients();
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var status = data[0];
|
||||
var clients = Array.isArray(data[1]) ? data[1] : (data[1].clients || []);
|
||||
var zones = Array.isArray(data[2]) ? data[2] : (data[2].zones || []);
|
||||
var clients = Array.isArray(data) ? data : (data.clients || []);
|
||||
var online = clients.filter(function(c) { return c.online; }).length;
|
||||
var approved = clients.filter(function(c) { return c.status === 'approved'; }).length;
|
||||
var banned = clients.filter(function(c) { return c.status === 'banned'; }).length;
|
||||
|
||||
var onlineClients = clients.filter(function(c) { return c.online; });
|
||||
var approvedClients = clients.filter(function(c) { return c.status === 'approved'; });
|
||||
var quarantineClients = clients.filter(function(c) { return c.status === 'unknown' || c.zone === 'quarantine'; });
|
||||
var bannedClients = clients.filter(function(c) { return c.status === 'banned'; });
|
||||
var view = E('div', { 'class': 'cbi-map' }, [
|
||||
E('h2', {}, 'Client Guardian'),
|
||||
E('div', { 'class': 'cbi-map-descr' }, 'Network client management'),
|
||||
|
||||
// Main wrapper with SecuBox header
|
||||
var wrapper = E('div', { 'class': 'secubox-page-wrapper' });
|
||||
wrapper.appendChild(SbHeader.render());
|
||||
|
||||
var view = E('div', { 'class': 'client-guardian-dashboard' }, [
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/common.css') }),
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('client-guardian/dashboard.css') }),
|
||||
CgNav.renderTabs('overview'),
|
||||
|
||||
// Chip Header
|
||||
this.renderChipHeader(onlineClients.length, approvedClients.length, quarantineClients.length,
|
||||
bannedClients.length, clients.filter(function(c) { return c.has_threats; }).length, zones.length),
|
||||
|
||||
// Recent Clients Card
|
||||
E('div', { 'class': 'cg-card' }, [
|
||||
E('div', { 'class': 'cg-card-header' }, [
|
||||
E('div', { 'class': 'cg-card-title' }, [
|
||||
E('span', { 'class': 'cg-card-title-icon' }, '⚡'),
|
||||
'Clients Récents'
|
||||
]),
|
||||
E('span', { 'class': 'cg-card-badge' }, 'Temps réel')
|
||||
// Stats
|
||||
E('div', { 'style': 'display:flex;gap:20px;margin:20px 0;' }, [
|
||||
E('div', { 'style': 'padding:15px;background:#22c55e22;border-radius:8px;' }, [
|
||||
E('strong', { 'style': 'font-size:24px;color:#22c55e;' }, String(online)),
|
||||
E('div', {}, 'Online')
|
||||
]),
|
||||
E('div', { 'class': 'cg-card-body' }, [
|
||||
E('div', { 'class': 'cg-client-list' },
|
||||
onlineClients.slice(0, 5).map(L.bind(this.renderClientItem, this, false))
|
||||
)
|
||||
E('div', { 'style': 'padding:15px;background:#3b82f622;border-radius:8px;' }, [
|
||||
E('strong', { 'style': 'font-size:24px;color:#3b82f6;' }, String(approved)),
|
||||
E('div', {}, 'Approved')
|
||||
]),
|
||||
E('div', { 'style': 'padding:15px;background:#ef444422;border-radius:8px;' }, [
|
||||
E('strong', { 'style': 'font-size:24px;color:#ef4444;' }, String(banned)),
|
||||
E('div', {}, 'Banned')
|
||||
])
|
||||
]),
|
||||
|
||||
// Pending Approval Card
|
||||
quarantineClients.length > 0 ? E('div', { 'class': 'cg-card' }, [
|
||||
E('div', { 'class': 'cg-card-header' }, [
|
||||
E('div', { 'class': 'cg-card-title' }, [
|
||||
E('span', { 'class': 'cg-card-title-icon' }, '⏳'),
|
||||
'En Attente d\'Approbation'
|
||||
]),
|
||||
E('span', { 'class': 'cg-card-badge' }, quarantineClients.length + ' clients')
|
||||
]),
|
||||
E('div', { 'class': 'cg-card-body' }, [
|
||||
E('div', { 'class': 'cg-client-list' },
|
||||
quarantineClients.map(L.bind(this.renderClientItem, this, true))
|
||||
)
|
||||
])
|
||||
]) : E('div')
|
||||
]);
|
||||
|
||||
// Setup auto-refresh polling based on UCI settings
|
||||
var autoRefresh = uci.get('client-guardian', 'config', 'auto_refresh');
|
||||
var refreshInterval = parseInt(uci.get('client-guardian', 'config', 'refresh_interval') || '10');
|
||||
|
||||
if (autoRefresh === '1') {
|
||||
poll.add(L.bind(function() {
|
||||
return this.handleRefresh();
|
||||
}, this), refreshInterval);
|
||||
}
|
||||
|
||||
wrapper.appendChild(view);
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
renderHeaderChip: function(stat) {
|
||||
return E('div', { 'class': 'sh-header-chip' + (stat.tone ? ' ' + stat.tone : '') }, [
|
||||
E('span', { 'class': 'sh-chip-icon' }, stat.icon || '•'),
|
||||
E('div', { 'class': 'sh-chip-text' }, [
|
||||
E('span', { 'class': 'sh-chip-label' }, stat.label),
|
||||
E('strong', {}, String(stat.value))
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
renderChipHeader: function(online, approved, quarantine, banned, threats, zones) {
|
||||
var stats = [
|
||||
{ icon: '📱', label: _('Online'), value: online, tone: online > 0 ? 'success' : '' },
|
||||
{ icon: '✅', label: _('Approved'), value: approved },
|
||||
{ icon: '⏳', label: _('Quarantine'), value: quarantine, tone: quarantine > 0 ? 'warn' : '' },
|
||||
{ icon: '🚫', label: _('Banned'), value: banned, tone: banned > 0 ? 'error' : '' },
|
||||
{ icon: '⚠️', label: _('Threats'), value: threats, tone: threats > 0 ? 'error' : '' },
|
||||
{ icon: '🌐', label: _('Zones'), value: zones }
|
||||
];
|
||||
|
||||
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
|
||||
E('div', {}, [
|
||||
E('h2', { 'class': 'sh-page-title' }, [
|
||||
E('span', { 'class': 'sh-page-title-icon' }, '🛡️'),
|
||||
_('Client Guardian')
|
||||
]),
|
||||
E('p', { 'class': 'sh-page-subtitle' },
|
||||
_('Device protection · Access control · Threat monitoring'))
|
||||
]),
|
||||
E('div', { 'class': 'sh-header-meta' }, stats.map(L.bind(this.renderHeaderChip, this)))
|
||||
]);
|
||||
},
|
||||
|
||||
renderStatCard: function(icon, value, label) {
|
||||
return E('div', { 'class': 'cg-stat-card' }, [
|
||||
E('div', { 'class': 'cg-stat-icon' }, icon),
|
||||
E('div', { 'class': 'cg-stat-value' }, String(value)),
|
||||
E('div', { 'class': 'cg-stat-label' }, label)
|
||||
]);
|
||||
},
|
||||
|
||||
renderClientItem: function(showActions, client) {
|
||||
var statusClass = client.online ? 'online' : 'offline';
|
||||
if (client.status === 'unknown' || client.zone === 'quarantine')
|
||||
statusClass += ' quarantine';
|
||||
if (client.status === 'banned')
|
||||
statusClass += ' banned';
|
||||
|
||||
var deviceIcon = getDeviceIcon(client.hostname || client.name, client.mac);
|
||||
var zoneClass = (client.zone || 'unknown').replace('lan_', '');
|
||||
|
||||
var item = E('div', { 'class': 'cg-client-item ' + statusClass }, [
|
||||
E('div', { 'class': 'cg-client-avatar' }, deviceIcon),
|
||||
E('div', { 'class': 'cg-client-info' }, [
|
||||
E('div', { 'class': 'cg-client-name' }, [
|
||||
client.online ? E('span', { 'class': 'online-indicator' }) : E('span'),
|
||||
client.name || client.hostname || 'Unknown',
|
||||
client.has_threats ? E('span', {
|
||||
'class': 'cg-threat-badge',
|
||||
'title': (client.threat_count || 0) + ' menace(s) active(s), score de risque: ' + (client.risk_score || 0),
|
||||
'style': 'margin-left: 8px; color: #ef4444; font-size: 16px; cursor: help;'
|
||||
}, '⚠️') : E('span')
|
||||
]),
|
||||
E('div', { 'class': 'cg-client-meta' }, [
|
||||
E('span', {}, client.mac),
|
||||
E('span', {}, client.ip || 'N/A'),
|
||||
client.has_threats ? E('span', {
|
||||
'style': 'color: #ef4444; font-weight: 500; margin-left: 8px;'
|
||||
}, 'Risque: ' + (client.risk_score || 0) + '%') : E('span')
|
||||
])
|
||||
]),
|
||||
E('span', { 'class': 'cg-client-zone ' + zoneClass }, client.zone || 'unknown'),
|
||||
E('div', { 'class': 'cg-client-traffic' }, [
|
||||
E('div', { 'class': 'cg-client-traffic-value' }, '↓ ' + formatBytes(client.rx_bytes || 0)),
|
||||
E('div', { 'class': 'cg-client-traffic-label' }, '↑ ' + formatBytes(client.tx_bytes || 0))
|
||||
// Client Table
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('table', { 'class': 'table', 'id': 'client-table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, 'Status'),
|
||||
E('th', { 'class': 'th' }, 'Name'),
|
||||
E('th', { 'class': 'th' }, 'MAC'),
|
||||
E('th', { 'class': 'th' }, 'IP'),
|
||||
E('th', { 'class': 'th' }, 'Actions')
|
||||
])
|
||||
].concat(clients.map(L.bind(this.renderClientRow, this))))
|
||||
])
|
||||
]);
|
||||
|
||||
if (showActions) {
|
||||
var actions = E('div', { 'class': 'cg-client-actions' });
|
||||
poll.add(L.bind(this.refresh, this), 10);
|
||||
return view;
|
||||
},
|
||||
|
||||
if (client.status === 'unknown') {
|
||||
var approveBtn = E('div', {
|
||||
'class': 'cg-client-action approve',
|
||||
'title': 'Approuver',
|
||||
'data-mac': client.mac
|
||||
}, '✅');
|
||||
approveBtn.addEventListener('click', L.bind(this.handleApprove, this));
|
||||
actions.appendChild(approveBtn);
|
||||
}
|
||||
|
||||
if (client.status !== 'banned') {
|
||||
var banBtn = E('div', {
|
||||
'class': 'cg-client-action ban',
|
||||
'title': 'Bannir',
|
||||
'data-mac': client.mac
|
||||
}, '🚫');
|
||||
banBtn.addEventListener('click', L.bind(this.handleBan, this));
|
||||
actions.appendChild(banBtn);
|
||||
}
|
||||
|
||||
item.appendChild(actions);
|
||||
renderClientRow: function(client) {
|
||||
var statusIcon = client.online ? '🟢' : '⚪';
|
||||
var statusStyle = '';
|
||||
if (client.status === 'banned') {
|
||||
statusIcon = '🔴';
|
||||
statusStyle = 'background:#fee2e2;';
|
||||
}
|
||||
|
||||
return item;
|
||||
return E('tr', { 'class': 'tr', 'style': statusStyle, 'data-mac': client.mac }, [
|
||||
E('td', { 'class': 'td' }, statusIcon),
|
||||
E('td', { 'class': 'td' }, client.name || client.hostname || '-'),
|
||||
E('td', { 'class': 'td', 'style': 'font-family:monospace;' }, client.mac),
|
||||
E('td', { 'class': 'td' }, client.ip || '-'),
|
||||
E('td', { 'class': 'td' }, this.renderActions(client))
|
||||
]);
|
||||
},
|
||||
|
||||
renderActions: function(client) {
|
||||
var actions = E('div', { 'style': 'display:flex;gap:8px;' });
|
||||
|
||||
if (client.status !== 'approved') {
|
||||
var approveBtn = E('button', {
|
||||
'class': 'cbi-button cbi-button-positive',
|
||||
'style': 'padding:4px 12px;',
|
||||
'data-mac': client.mac
|
||||
}, '✓ Approve');
|
||||
approveBtn.addEventListener('click', L.bind(this.handleApprove, this));
|
||||
actions.appendChild(approveBtn);
|
||||
}
|
||||
|
||||
if (client.status === 'banned') {
|
||||
var unbanBtn = E('button', {
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'style': 'padding:4px 12px;',
|
||||
'data-mac': client.mac
|
||||
}, 'Unban');
|
||||
unbanBtn.addEventListener('click', L.bind(this.handleUnban, this));
|
||||
actions.appendChild(unbanBtn);
|
||||
} else {
|
||||
var banBtn = E('button', {
|
||||
'class': 'cbi-button cbi-button-negative',
|
||||
'style': 'padding:4px 12px;',
|
||||
'data-mac': client.mac
|
||||
}, 'Ban');
|
||||
banBtn.addEventListener('click', L.bind(this.handleBan, this));
|
||||
actions.appendChild(banBtn);
|
||||
}
|
||||
|
||||
return actions;
|
||||
},
|
||||
|
||||
handleApprove: function(ev) {
|
||||
var mac = ev.currentTarget.dataset.mac;
|
||||
var self = this;
|
||||
|
||||
ui.showModal(_('Approuver le Client'), [
|
||||
E('p', {}, _('Choisissez une zone pour ce client:')),
|
||||
E('select', { 'id': 'approve-zone', 'class': 'cg-select' }, [
|
||||
E('option', { 'value': 'lan_private' }, 'LAN Privé'),
|
||||
E('option', { 'value': 'iot' }, 'IoT'),
|
||||
E('option', { 'value': 'kids' }, 'Enfants'),
|
||||
E('option', { 'value': 'guest' }, 'Invités')
|
||||
]),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'cg-btn',
|
||||
'click': ui.hideModal
|
||||
}, _('Annuler')),
|
||||
E('button', {
|
||||
'class': 'cg-btn cg-btn-success',
|
||||
'click': L.bind(function() {
|
||||
var zone = document.getElementById('approve-zone').value;
|
||||
callApproveClient(mac, '', zone, '').then(L.bind(function() {
|
||||
ui.hideModal();
|
||||
ui.addNotification(null, E('p', _('Client approved successfully')), 'success');
|
||||
this.handleRefresh();
|
||||
}, this));
|
||||
}, this)
|
||||
}, _('Approuver'))
|
||||
])
|
||||
]);
|
||||
callApproveClient(mac, '', 'lan_private', '').then(L.bind(function() {
|
||||
ui.addNotification(null, E('p', 'Client approved'), 'info');
|
||||
this.refresh();
|
||||
}, this));
|
||||
},
|
||||
|
||||
handleBan: function(ev) {
|
||||
var mac = ev.currentTarget.dataset.mac;
|
||||
|
||||
ui.showModal(_('Bannir le Client'), [
|
||||
E('p', {}, _('Voulez-vous vraiment bannir ce client?')),
|
||||
E('p', {}, E('strong', {}, mac)),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'cg-btn',
|
||||
'click': ui.hideModal
|
||||
}, _('Annuler')),
|
||||
E('button', {
|
||||
'class': 'cg-btn cg-btn-danger',
|
||||
'click': L.bind(function() {
|
||||
callBanClient(mac, 'Manual ban').then(L.bind(function() {
|
||||
ui.hideModal();
|
||||
ui.addNotification(null, E('p', _('Client banned successfully')), 'info');
|
||||
this.handleRefresh();
|
||||
}, this));
|
||||
}, this)
|
||||
}, _('Bannir'))
|
||||
])
|
||||
]);
|
||||
if (confirm('Ban this client?\n' + mac)) {
|
||||
callBanClient(mac, 'Manual ban').then(L.bind(function() {
|
||||
ui.addNotification(null, E('p', 'Client banned'), 'info');
|
||||
this.refresh();
|
||||
}, this));
|
||||
}
|
||||
},
|
||||
|
||||
handleRefresh: function() {
|
||||
return Promise.all([
|
||||
callGetStatus(),
|
||||
callGetClients(),
|
||||
callGetZones()
|
||||
]).then(L.bind(function(data) {
|
||||
var container = document.querySelector('.client-guardian-dashboard');
|
||||
if (container) {
|
||||
var statusBadge = document.querySelector('.cg-status-badge');
|
||||
if (statusBadge) {
|
||||
statusBadge.classList.add('loading');
|
||||
}
|
||||
var newView = this.render(data);
|
||||
dom.content(container.parentNode, newView);
|
||||
if (statusBadge) {
|
||||
statusBadge.classList.remove('loading');
|
||||
}
|
||||
handleUnban: function(ev) {
|
||||
var mac = ev.currentTarget.dataset.mac;
|
||||
callUnbanClient(mac).then(L.bind(function() {
|
||||
ui.addNotification(null, E('p', 'Client unbanned'), 'info');
|
||||
this.refresh();
|
||||
}, this));
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
return callGetClients().then(L.bind(function(data) {
|
||||
var clients = Array.isArray(data) ? data : (data.clients || []);
|
||||
var table = document.getElementById('client-table');
|
||||
if (table) {
|
||||
while (table.rows.length > 1) table.deleteRow(1);
|
||||
clients.forEach(L.bind(function(client) {
|
||||
table.appendChild(this.renderClientRow(client));
|
||||
}, this));
|
||||
}
|
||||
}, this)).catch(function(err) {
|
||||
console.error('Failed to refresh Client Guardian dashboard:', err);
|
||||
});
|
||||
},
|
||||
|
||||
handleLeave: function() {
|
||||
poll.stop();
|
||||
}, this));
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
|
||||
@ -1,189 +1,40 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require client-guardian/api as API';
|
||||
'require client-guardian/nav as CgNav';
|
||||
'require secubox-portal/header as SbHeader';
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
API.getPolicy(),
|
||||
API.getStatus(),
|
||||
L.resolveDefault(uci.load('client-guardian'), {})
|
||||
]);
|
||||
return uci.load('client-guardian');
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var policy = data[0] || {};
|
||||
var status = data[1] || {};
|
||||
|
||||
render: function() {
|
||||
var m, s, o;
|
||||
|
||||
m = new form.Map('client-guardian', _('Client Guardian Settings'),
|
||||
_('Configure default network access policy and client management.'));
|
||||
m = new form.Map('client-guardian', 'Client Guardian Settings',
|
||||
'Basic network client management.');
|
||||
|
||||
// General Settings
|
||||
s = m.section(form.NamedSection, 'config', 'client-guardian', _('General Settings'));
|
||||
s = m.section(form.NamedSection, 'config', 'client-guardian', 'General');
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enable Client Guardian'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
o.description = _('Enable or disable the Client Guardian access control system. WARNING: Enabling with restrictive policies may block network access!');
|
||||
o = s.option(form.Flag, 'enabled', 'Enable');
|
||||
o.default = '1';
|
||||
|
||||
o = s.option(form.ListValue, 'default_policy', _('Default Policy'));
|
||||
o.value('open', _('Open - Allow all clients (Recommended)'));
|
||||
o.value('quarantine', _('Quarantine - Require approval (Restrictive)'));
|
||||
o.value('whitelist', _('Whitelist Only - Block unknown clients (Very Restrictive)'));
|
||||
o = s.option(form.ListValue, 'default_policy', 'Default Policy');
|
||||
o.value('open', 'Open - Auto-approve new clients');
|
||||
o.value('closed', 'Closed - Require approval');
|
||||
o.default = 'open';
|
||||
o.rmempty = false;
|
||||
o.description = _('Default behavior for new/unknown clients. WARNING: Quarantine and Whitelist modes will block new devices from accessing the network!');
|
||||
|
||||
o = s.option(form.Flag, 'auto_approve', _('Auto-Approve Known Devices'));
|
||||
o.default = '0';
|
||||
o.depends('default_policy', 'whitelist');
|
||||
o.description = _('Automatically approve devices that have connected before');
|
||||
|
||||
o = s.option(form.Value, 'session_timeout', _('Session Timeout'));
|
||||
o.datatype = 'uinteger';
|
||||
o.default = '86400';
|
||||
o.placeholder = '86400';
|
||||
o.description = _('Maximum session duration in seconds (default: 86400 = 24 hours)');
|
||||
|
||||
// Advanced Settings
|
||||
s = m.section(form.NamedSection, 'config', 'client-guardian', _('Advanced Settings'));
|
||||
|
||||
o = s.option(form.Flag, 'mac_filtering', _('Enable MAC Filtering'));
|
||||
o = s.option(form.Flag, 'auto_approve', 'Auto-approve known devices');
|
||||
o.default = '1';
|
||||
o.description = _('Use MAC addresses for client identification and blocking');
|
||||
|
||||
o = s.option(form.Flag, 'log_connections', _('Log Connection Events'));
|
||||
o.default = '1';
|
||||
o.description = _('Log client connections and authentication events');
|
||||
o = s.option(form.ListValue, 'log_level', 'Log Level');
|
||||
o.value('error', 'Error');
|
||||
o.value('warn', 'Warning');
|
||||
o.value('info', 'Info');
|
||||
o.value('debug', 'Debug');
|
||||
o.default = 'info';
|
||||
|
||||
o = s.option(form.Value, 'log_retention', _('Log Retention (days)'));
|
||||
o.datatype = 'uinteger';
|
||||
o.default = '30';
|
||||
o.depends('log_connections', '1');
|
||||
o.description = _('Number of days to keep connection logs');
|
||||
|
||||
o = s.option(form.Flag, 'block_tor', _('Block Tor Exit Nodes'));
|
||||
o.default = '0';
|
||||
o.description = _('Automatically block known Tor exit nodes');
|
||||
|
||||
o = s.option(form.Flag, 'block_vpn', _('Block VPN Detection'));
|
||||
o.default = '0';
|
||||
o.description = _('Attempt to detect and block VPN connections');
|
||||
|
||||
// Dashboard Reactiveness
|
||||
s = m.section(form.NamedSection, 'config', 'client-guardian', _('Dashboard Reactiveness'));
|
||||
|
||||
o = s.option(form.Flag, 'auto_refresh', _('Enable Auto-Refresh'),
|
||||
_('Automatically refresh dashboard every few seconds'));
|
||||
o.default = o.enabled;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.ListValue, 'refresh_interval', _('Refresh Interval'),
|
||||
_('How often to poll for updates'));
|
||||
o.value('5', _('Every 5 seconds'));
|
||||
o.value('10', _('Every 10 seconds (recommended)'));
|
||||
o.value('30', _('Every 30 seconds'));
|
||||
o.value('60', _('Every 60 seconds'));
|
||||
o.default = '10';
|
||||
o.depends('auto_refresh', '1');
|
||||
|
||||
// Threat Intelligence Integration
|
||||
s = m.section(form.NamedSection, 'threat_policy', 'threat_policy', _('Threat Intelligence Integration'));
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enable Threat Intelligence'),
|
||||
_('Correlate clients with Security Threats Dashboard data'));
|
||||
o.default = o.enabled;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'auto_ban_threshold', _('Auto-Ban Threshold'),
|
||||
_('Automatically ban clients with threat score above this value (0-100)'));
|
||||
o.datatype = 'range(1,100)';
|
||||
o.placeholder = '80';
|
||||
o.default = '80';
|
||||
o.depends('enabled', '1');
|
||||
|
||||
o = s.option(form.Value, 'auto_quarantine_threshold', _('Auto-Quarantine Threshold'),
|
||||
_('Automatically quarantine clients with threat score above this value (0-100)'));
|
||||
o.datatype = 'range(1,100)';
|
||||
o.placeholder = '60';
|
||||
o.default = '60';
|
||||
o.depends('enabled', '1');
|
||||
|
||||
o = s.option(form.Value, 'threat_check_interval', _('Threat Check Interval'),
|
||||
_('How often to check for threats (seconds)'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = '60';
|
||||
o.default = '60';
|
||||
o.depends('enabled', '1');
|
||||
|
||||
return m.render().then(function(rendered) {
|
||||
// Policy display names
|
||||
var policyNames = {
|
||||
'open': _('Open'),
|
||||
'quarantine': _('Quarantine'),
|
||||
'whitelist': _('Whitelist Only')
|
||||
};
|
||||
var currentPolicy = policy.default_policy || 'quarantine';
|
||||
var policyDisplay = policyNames[currentPolicy] || currentPolicy;
|
||||
|
||||
// Add policy info box at the top
|
||||
var infoBox = E('div', {
|
||||
'class': 'cbi-section',
|
||||
'style': 'background: var(--cg-bg-secondary, #151b23); border-left: 4px solid var(--cg-accent, #6366f1); padding: 1em; margin-bottom: 1em; border-radius: 8px;'
|
||||
}, [
|
||||
E('h3', { 'style': 'margin-top: 0; color: var(--cg-text-primary, #e6edf3);' }, [
|
||||
_('Current Policy: '),
|
||||
E('span', { 'style': 'color: var(--cg-accent, #6366f1); font-weight: 600;' }, policyDisplay)
|
||||
]),
|
||||
E('div', { 'style': 'margin-top: 0.5em; color: var(--cg-text-secondary, #8b949e);' }, [
|
||||
E('strong', {}, _('Session Timeout: ')),
|
||||
E('span', {}, (policy.session_timeout || 86400) + ' ' + _('seconds'))
|
||||
]),
|
||||
E('div', { 'style': 'margin-top: 1em; padding: 0.75em; background: var(--cg-bg-tertiary, #1e2632); border-radius: 4px;' }, [
|
||||
E('strong', { 'style': 'color: var(--cg-text-primary, #e6edf3);' }, _('Policy Descriptions:')),
|
||||
E('ul', { 'style': 'margin: 0.5em 0; color: var(--cg-text-secondary, #8b949e);' }, [
|
||||
E('li', {}, [
|
||||
E('strong', { 'style': 'color: #22c55e;' }, _('Open: ')),
|
||||
_('All clients can access the network without authentication. Not recommended for public networks.')
|
||||
]),
|
||||
E('li', {}, [
|
||||
E('strong', { 'style': 'color: #f59e0b;' }, _('Quarantine: ')),
|
||||
_('New clients are placed in quarantine and require manual approval. Recommended for secure networks.')
|
||||
]),
|
||||
E('li', {}, [
|
||||
E('strong', { 'style': 'color: #ef4444;' }, _('Whitelist Only: ')),
|
||||
_('Only explicitly approved clients can access the network. Highest security.')
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
rendered.insertBefore(infoBox, rendered.firstChild);
|
||||
|
||||
// Main wrapper with SecuBox header
|
||||
var wrapper = E('div', { 'class': 'secubox-page-wrapper' });
|
||||
wrapper.appendChild(SbHeader.render());
|
||||
|
||||
var view = E('div', { 'class': 'client-guardian-dashboard' }, [
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('client-guardian/dashboard.css') }),
|
||||
CgNav.renderTabs('settings'),
|
||||
rendered
|
||||
]);
|
||||
|
||||
wrapper.appendChild(view);
|
||||
return wrapper;
|
||||
});
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
|
||||
@ -9,73 +9,17 @@
|
||||
"acl": ["luci-app-client-guardian"]
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/overview": {
|
||||
"title": "Overview",
|
||||
"admin/secubox/security/guardian/clients": {
|
||||
"title": "Clients",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/overview"
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/wizard": {
|
||||
"title": "Setup Wizard",
|
||||
"order": 15,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/wizard"
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/clients": {
|
||||
"title": "Clients",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/clients"
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/zones": {
|
||||
"title": "Network Zones",
|
||||
"order": 25,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/zones"
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/autozoning": {
|
||||
"title": "Auto-Zoning Rules",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/autozoning"
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/logs": {
|
||||
"title": "Event Logs",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/logs"
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/alerts": {
|
||||
"title": "Alerts & Notifications",
|
||||
"order": 50,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/alerts"
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/parental": {
|
||||
"title": "Parental Controls",
|
||||
"order": 60,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/parental"
|
||||
}
|
||||
},
|
||||
"admin/secubox/security/guardian/settings": {
|
||||
"title": "Settings",
|
||||
"order": 90,
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "client-guardian/settings"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user