Event Service - jastit00/IT-Sec-Projekt GitHub Wiki
Author: Marcel NicklaΓ
The Event Service manages security events and provides the following core features:
- Categorization by severity (Critical, Warning, Normal) with detailed descriptions
- Filtering and searching through event data
- Sorting by various criteria
- IP address extraction and display
The service follows a modern frontend-backend architecture:
βββββββββββββββββββ HTTP/REST βββββββββββββββββββ
β Frontend β βββββββββββββ β Backend β
β (Angular) β β (Django) β
βββββββββββββββββββ βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β Event Service β
βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β Components β
β - All Events β
β - Critical β
βββββββββββββββββββ
Frontend:
- Angular & Angular Material Design
- RxJS
- TypeScript
- CSS
Backend:
- Django REST Framework
- PostgreSQL
- RESTful API Design
Communication:
- HTTP/REST API
- JSON data format
@Injectable({
providedIn: 'root'
})
export class EventService {
private defaultService = inject(DefaultService);
private httpClient = inject(HttpClient);
private updateService = inject(ChartUpdateService);
events: SecurityEvent[] = [];
loading = false;
}-
Loading Events:
loadEventsFromBackend() - Caching: In-memory event storage
- State Management: Handling loading states and errors
Event Mapping:
private mapBackendEventsToSecurityEvents(backendEvents: Events[]): SecurityEvent[] {
return backendEvents.map(backendEvent => ({
id: backendEvent.id ?? Math.floor(Math.random() * 10000),
date: this.formatDate(new Date(backendEvent.timestamp)),
event: this.getDisplayEventType(backendEvent),
status: this.mapSeverityToStatus(backendEvent.severity),
ips: this.extractIPs(backendEvent),
description: this.generateDescription(backendEvent)
}));
}IP Extraction:
private extractIPs(event: Events): string[] {
const ips: string[] = [];
if (event.source_ips && Array.isArray(event.source_ips)) {
event.source_ips.forEach(ip => {
if (ip && typeof ip === 'string' && ip.trim() !== '') {
ips.push(ip.trim());
}
});
}
return ips;
}private generateDescription(event: Events): string {
const eventType = this.getDisplayEventType(event).toLowerCase();
switch (eventType) {
case 'network packet':
const packetInfo = [];
if (event.protocol) packetInfo.push(`Protocol: ${event.protocol}`);
if (event.count) packetInfo.push(`Count: ${event.count}`);
return packetInfo.join(' | ');
case 'login':
const loginInfo = [];
if (event.username) loginInfo.push(`User: ${event.username}`);
if (event.result) loginInfo.push(`Status: ${event.result}`);
return loginInfo.join(' | ');
// More event types...
}
}getAllEvents(): SecurityEvent[]
- Returns all loaded events
- Used in the All Events component
getCriticalEvents(): SecurityEvent[]
- Filters only critical events
- Used in the Critical Events component
refreshEvents(): Observable<SecurityEvent[]>
- Reloads events from the backend
- Triggered by chart updates
Displays a full list of all security events with advanced filtering and search features.
private matchesSearchTerm(event: SecurityEvent): boolean {
if (!this._searchTerm.trim()) return true;
const search = this._searchTerm.toLowerCase();
return event.date.toLowerCase().includes(search) ||
event.event.toLowerCase().includes(search) ||
event.status.toLowerCase().includes(search) ||
event.description.toLowerCase().includes(search) ||
event.ips?.some(ip => ip.toLowerCase().includes(search));
}<mat-form-field appearance="outline" class="sort-field">
<mat-label>Sort by</mat-label>
<mat-select [(ngModel)]="sortBy" (selectionChange)="sortEvents()">
<mat-option value="">No sorting</mat-option>
<mat-option value="normal">Normal Events</mat-option>
<mat-option value="warning">Warning Events</mat-option>
<mat-option value="critical">Critical Events</mat-option>
<mat-option value="login">Login Events</mat-option>
<mat-option value="logout">Logout Events</mat-option>
</mat-select>
</mat-form-field>sortEvents() {
this.filteredEvents.sort((a, b) => {
let comparison = 0;
const dateA = new Date(a.date);
const dateB = new Date(b.date);
comparison = dateA.getTime() - dateB.getTime();
if (comparison === 0) {
comparison = a.event.toLowerCase().localeCompare(b.event.toLowerCase());
}
return this.sortDirection === 'desc' ? -comparison : comparison;
});
}Focuses exclusively on critical security events.
constructor(private eventService: EventService) {
this.events = this.eventService.getCriticalEvents();
this.filteredEvents = [...this.events];
}sortEvents() {
const criticalEvents = this.eventService.getCriticalEvents();
if (!this.searchTerm.trim()) {
this.filteredEvents = [...criticalEvents];
} else {
this.filteredEvents = criticalEvents.filter(event =>
this.matchesSearchTerm(event)
);
}
if (this.sortDirection && this.filteredEvents.length > 0) {
this.filteredEvents.sort((a, b) => {
let comparison = new Date(a.date).getTime() - new Date(b.date).getTime();
if (comparison === 0) {
comparison = a.event.toLowerCase().localeCompare(b.event.toLowerCase());
}
if (comparison === 0) {
comparison = a.description.toLowerCase().localeCompare(b.description.toLowerCase());
}
return this.sortDirection === 'desc' ? -comparison : comparison;
});
}
}export class EventService {
private apiBaseUrl = 'http://localhost:8000/api';
getUnifiedEvents(): Observable<Events[]> {
return this.httpClient.get<Events[]>(`${this.apiBaseUrl}/logfiles/unified-event-log/`);
}
}loadEventsFromBackend(): Observable<SecurityEvent[]> {
this.loading = true;
return this.getUnifiedEvents().pipe(
map(backendEvents => this.mapBackendEventsToSecurityEvents(backendEvents)),
tap(mappedEvents => this.events = mappedEvents),
catchError(this.handleError),
finalize(() => this.loading = false)
);
}private handleError = (error: any): Observable<SecurityEvent[]> => {
console.error('API Error:', error);
this.events = [];
if (error.status === 404) {
console.warn('Event endpoint not found');
} else if (error.status === 500) {
console.error('Server error occurred');
}
return of([]);
};interface Events {
id?: number;
timestamp: string;
event_type: string;
severity: string;
source_ips?: string[];
src_ip_address?: string;
dst_ip_address?: string;
protocol?: string;
username?: string;
result?: string;
incident_type?: string;
reason?: string;
details?: any;
}export interface SecurityEvent {
id: number;
date: string;
event: string;
status: 'Critical' | 'Warning' | 'Normal';
ips: string[];
description: string;
}constructor() {
this.updateSubscription = this.updateService.updateChart$.subscribe(() => {
this.refreshEvents().subscribe();
});
}
refreshEvents(): Observable<SecurityEvent[]> {
return this.loadEventsFromBackend();
}export class AllEventsComponent {
private readonly FORBIDDEN_CHARS = /[<>"';[\]{}()\\\/]/g;
set searchTerm(value: string) {
this._searchTerm = value ? value.replace(this.FORBIDDEN_CHARS, '').trim() : '';
}
}onKeyPress(event: KeyboardEvent) {
const forbiddenChars = '<>"\;[]{}()/';
if (forbiddenChars.includes(event.key)) {
event.preventDefault();
console.log("Blocked forbidden character:", event.key);
}
}<!-- Angular auto-escapes input -->
<input matInput
[(ngModel)]="searchTerm"
(keypress)="onKeyPress($event)"
maxlength="100"
autocomplete="off"
spellcheck="false"><!-- Safe - Angular auto escapes interpolated content -->
<td>{{ event.description }}</td>
<td>{{ event.event }}</td>// Sanitization on user input
private sanitizeInput(input: string): string {
return input
.replace(this.FORBIDDEN_CHARS, '')
.trim()
.substring(0, 100); // Limit length
}