Files
dcrouter/ts_web/elements/ops-view-security.ts

530 lines
14 KiB
TypeScript

import * as plugins from '../plugins.js';
import * as shared from './shared/index.js';
import * as appstate from '../appstate.js';
import {
DeesElement,
customElement,
html,
state,
css,
cssManager,
} from '@design.estate/dees-element';
import { type IStatsTile } from '@design.estate/dees-catalog';
@customElement('ops-view-security')
export class OpsViewSecurity extends DeesElement {
@state()
private statsState: appstate.IStatsState = {
serverStats: null,
emailStats: null,
dnsStats: null,
securityMetrics: null,
lastUpdated: 0,
isLoading: false,
error: null,
};
@state()
private selectedTab: 'overview' | 'blocked' | 'authentication' | 'email-security' = 'overview';
constructor() {
super();
const subscription = appstate.statsStatePart
.select((stateArg) => stateArg)
.subscribe((statsState) => {
this.statsState = statsState;
});
this.rxSubscriptions.push(subscription);
}
public static styles = [
cssManager.defaultStyles,
shared.viewHostCss,
css`
.tabs {
display: flex;
gap: 8px;
margin-bottom: 24px;
border-bottom: 2px solid ${cssManager.bdTheme('#e9ecef', '#333')};
}
.tab {
padding: 12px 24px;
background: none;
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
font-size: 16px;
color: ${cssManager.bdTheme('#666', '#999')};
transition: all 0.2s ease;
}
.tab:hover {
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.tab.active {
color: ${cssManager.bdTheme('#2196F3', '#4a90e2')};
border-bottom-color: ${cssManager.bdTheme('#2196F3', '#4a90e2')};
}
h2 {
margin: 32px 0 16px 0;
font-size: 24px;
font-weight: 600;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
dees-statsgrid {
margin-bottom: 32px;
}
.securityCard {
background: ${cssManager.bdTheme('#fff', '#222')};
border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
border-radius: 8px;
padding: 24px;
position: relative;
overflow: hidden;
}
.securityCard.alert {
border-color: ${cssManager.bdTheme('#f44336', '#ff6666')};
background: ${cssManager.bdTheme('#ffebee', '#4a1f1f')};
}
.securityCard.warning {
border-color: ${cssManager.bdTheme('#ff9800', '#ffaa33')};
background: ${cssManager.bdTheme('#fff3e0', '#4a3a1f')};
}
.securityCard.success {
border-color: ${cssManager.bdTheme('#4caf50', '#66cc66')};
background: ${cssManager.bdTheme('#e8f5e9', '#1f3f1f')};
}
.cardHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.cardTitle {
font-size: 18px;
font-weight: 600;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.cardStatus {
font-size: 14px;
padding: 4px 12px;
border-radius: 16px;
font-weight: 500;
}
.status-critical {
background: ${cssManager.bdTheme('#f44336', '#ff6666')};
color: ${cssManager.bdTheme('#fff', '#fff')};
}
.status-warning {
background: ${cssManager.bdTheme('#ff9800', '#ffaa33')};
color: ${cssManager.bdTheme('#fff', '#fff')};
}
.status-good {
background: ${cssManager.bdTheme('#4caf50', '#66cc66')};
color: ${cssManager.bdTheme('#fff', '#fff')};
}
.metricValue {
font-size: 32px;
font-weight: 700;
margin-bottom: 8px;
}
.metricLabel {
font-size: 14px;
color: ${cssManager.bdTheme('#666', '#999')};
}
.actionButton {
margin-top: 16px;
}
.blockedIpList {
max-height: 400px;
overflow-y: auto;
}
.blockedIpItem {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
border-bottom: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
}
.blockedIpItem:last-child {
border-bottom: none;
}
.ipAddress {
font-family: 'Consolas', 'Monaco', monospace;
font-weight: 600;
}
.blockReason {
font-size: 14px;
color: ${cssManager.bdTheme('#666', '#999')};
}
.blockTime {
font-size: 12px;
color: ${cssManager.bdTheme('#999', '#666')};
}
`,
];
public render() {
return html`
<ops-sectionheading>Security</ops-sectionheading>
<div class="tabs">
<button
class="tab ${this.selectedTab === 'overview' ? 'active' : ''}"
@click=${() => this.selectedTab = 'overview'}
>
Overview
</button>
<button
class="tab ${this.selectedTab === 'blocked' ? 'active' : ''}"
@click=${() => this.selectedTab = 'blocked'}
>
Blocked IPs
</button>
<button
class="tab ${this.selectedTab === 'authentication' ? 'active' : ''}"
@click=${() => this.selectedTab = 'authentication'}
>
Authentication
</button>
<button
class="tab ${this.selectedTab === 'email-security' ? 'active' : ''}"
@click=${() => this.selectedTab = 'email-security'}
>
Email Security
</button>
</div>
${this.renderTabContent()}
`;
}
private renderTabContent() {
const metrics = this.statsState.securityMetrics;
if (!metrics) {
return html`
<div class="loadingMessage">
<p>Loading security metrics...</p>
</div>
`;
}
switch(this.selectedTab) {
case 'overview':
return this.renderOverview(metrics);
case 'blocked':
return this.renderBlockedIPs(metrics);
case 'authentication':
return this.renderAuthentication(metrics);
case 'email-security':
return this.renderEmailSecurity(metrics);
}
}
private renderOverview(metrics: any) {
const threatLevel = this.calculateThreatLevel(metrics);
const threatScore = this.getThreatScore(metrics);
const tiles: IStatsTile[] = [
{
id: 'threatLevel',
title: 'Threat Level',
value: threatScore,
type: 'gauge',
icon: 'shield',
gaugeOptions: {
min: 0,
max: 100,
thresholds: [
{ value: 0, color: '#ef4444' },
{ value: 30, color: '#f59e0b' },
{ value: 70, color: '#22c55e' },
],
},
description: `Status: ${threatLevel.toUpperCase()}`,
},
{
id: 'blockedThreats',
title: 'Blocked Threats',
value: metrics.blockedIPs.length + metrics.spamDetected,
type: 'number',
icon: 'userShield',
color: '#ef4444',
description: 'Total threats blocked today',
},
{
id: 'activeSessions',
title: 'Active Sessions',
value: 0,
type: 'number',
icon: 'users',
color: '#22c55e',
description: 'Current authenticated sessions',
},
{
id: 'authFailures',
title: 'Auth Failures',
value: metrics.authenticationFailures,
type: 'number',
icon: 'lockOpen',
color: metrics.authenticationFailures > 10 ? '#ef4444' : '#f59e0b',
description: 'Failed login attempts today',
},
];
return html`
<dees-statsgrid
.tiles=${tiles}
.minTileWidth=${200}
></dees-statsgrid>
<h2>Recent Security Events</h2>
<dees-table
.heading1=${'Security Events'}
.heading2=${'Last 24 hours'}
.data=${this.getSecurityEvents(metrics)}
.displayFunction=${(item) => ({
'Time': new Date(item.timestamp).toLocaleTimeString(),
'Event': item.event,
'Severity': item.severity,
'Details': item.details,
})}
></dees-table>
`;
}
private renderBlockedIPs(metrics: any) {
return html`
<div class="securityCard">
<div class="cardHeader">
<h3 class="cardTitle">Blocked IP Addresses</h3>
<dees-button @click=${() => this.clearBlockedIPs()}>
Clear All
</dees-button>
</div>
<div class="blockedIpList">
${metrics.blockedIPs && metrics.blockedIPs.length > 0 ? metrics.blockedIPs.map((ipAddress, index) => html`
<div class="blockedIpItem">
<div>
<div class="ipAddress">${ipAddress}</div>
<div class="blockReason">Suspicious activity</div>
<div class="blockTime">Blocked</div>
</div>
<dees-button @click=${() => this.unblockIP(ipAddress)}>
Unblock
</dees-button>
</div>
`) : html`
<p>No blocked IPs</p>
`}
</div>
</div>
`;
}
private renderAuthentication(metrics: any) {
const tiles: IStatsTile[] = [
{
id: 'authFailures',
title: 'Authentication Failures',
value: metrics.authenticationFailures,
type: 'number',
icon: 'lockOpen',
color: metrics.authenticationFailures > 10 ? '#ef4444' : '#f59e0b',
description: 'Failed authentication attempts today',
},
{
id: 'successfulLogins',
title: 'Successful Logins',
value: 0,
type: 'number',
icon: 'lock',
color: '#22c55e',
description: 'Successful logins today',
},
];
return html`
<dees-statsgrid
.tiles=${tiles}
.minTileWidth=${200}
></dees-statsgrid>
<h2>Recent Login Attempts</h2>
<dees-table
.heading1=${'Login History'}
.heading2=${'Recent authentication attempts'}
.data=${[]}
.displayFunction=${(item) => ({
'Time': new Date(item.timestamp).toLocaleString(),
'Username': item.username,
'IP Address': item.ipAddress,
'Status': item.success ? 'Success' : 'Failed',
'Reason': item.reason || '-',
})}
></dees-table>
`;
}
private renderEmailSecurity(metrics: any) {
const tiles: IStatsTile[] = [
{
id: 'malware',
title: 'Malware Detection',
value: metrics.malwareDetected,
type: 'number',
icon: 'virusSlash',
color: metrics.malwareDetected > 0 ? '#ef4444' : '#22c55e',
description: 'Malware detected',
},
{
id: 'phishing',
title: 'Phishing Detection',
value: metrics.phishingDetected,
type: 'number',
icon: 'fishFins',
color: metrics.phishingDetected > 0 ? '#ef4444' : '#22c55e',
description: 'Phishing attempts detected',
},
{
id: 'suspicious',
title: 'Suspicious Activities',
value: metrics.suspiciousActivities,
type: 'number',
icon: 'triangleExclamation',
color: metrics.suspiciousActivities > 5 ? '#ef4444' : '#f59e0b',
description: 'Suspicious activities detected',
},
{
id: 'spam',
title: 'Spam Detection',
value: metrics.spamDetected,
type: 'number',
icon: 'ban',
color: '#f59e0b',
description: 'Spam emails blocked',
},
];
return html`
<dees-statsgrid
.tiles=${tiles}
.minTileWidth=${200}
></dees-statsgrid>
<h2>Email Security Configuration</h2>
<div class="securityCard">
<dees-form>
<dees-input-checkbox
.key=${'enableSPF'}
.label=${'Enable SPF checking'}
.value=${true}
></dees-input-checkbox>
<dees-input-checkbox
.key=${'enableDKIM'}
.label=${'Enable DKIM validation'}
.value=${true}
></dees-input-checkbox>
<dees-input-checkbox
.key=${'enableDMARC'}
.label=${'Enable DMARC policy enforcement'}
.value=${true}
></dees-input-checkbox>
<dees-input-checkbox
.key=${'enableSpamFilter'}
.label=${'Enable spam filtering'}
.value=${true}
></dees-input-checkbox>
</dees-form>
<dees-button
class="actionButton"
type="highlighted"
@click=${() => this.saveEmailSecuritySettings()}
>
Save Settings
</dees-button>
</div>
`;
}
private calculateThreatLevel(metrics: any): string {
const score = this.getThreatScore(metrics);
if (score < 30) return 'alert';
if (score < 70) return 'warning';
return 'success';
}
private getThreatScore(metrics: any): number {
// Simple scoring algorithm
let score = 100;
score -= metrics.blockedIPs.length * 2;
score -= metrics.authenticationFailures * 1;
score -= metrics.spamDetected * 0.5;
score -= metrics.malwareDetected * 3;
score -= metrics.phishingDetected * 3;
score -= metrics.suspiciousActivities * 2;
return Math.max(0, Math.min(100, Math.round(score)));
}
private getSecurityEvents(metrics: any): any[] {
// Mock data - in real implementation, this would come from the server
return [
{
timestamp: Date.now() - 1000 * 60 * 5,
event: 'Multiple failed login attempts',
severity: 'warning',
details: 'IP: 192.168.1.100',
},
{
timestamp: Date.now() - 1000 * 60 * 15,
event: 'SPF check failed',
severity: 'medium',
details: 'Domain: example.com',
},
{
timestamp: Date.now() - 1000 * 60 * 30,
event: 'IP blocked due to spam',
severity: 'high',
details: 'IP: 10.0.0.1',
},
];
}
private async clearBlockedIPs() {
console.log('Clear blocked IPs');
}
private async unblockIP(ip: string) {
console.log('Unblock IP:', ip);
}
private async saveEmailSecuritySettings() {
console.log('Save email security settings');
}
}