UI Server: factor out authentication code
[e-mobility-charging-stations-simulator.git] / src / charging-station / ui-server / AbstractUIServer.ts
1 import { type IncomingMessage, Server, type ServerResponse } from 'http';
2
3 import type { WebSocket } from 'ws';
4
5 import type { ChargingStationData } from '../../types/ChargingStationWorker';
6 import type { UIServerConfiguration } from '../../types/ConfigurationData';
7 import {
8 AuthenticationType,
9 ProcedureName,
10 ProtocolRequest,
11 ProtocolResponse,
12 ProtocolVersion,
13 RequestPayload,
14 ResponsePayload,
15 } from '../../types/UIProtocol';
16 import type AbstractUIService from './ui-services/AbstractUIService';
17 import UIServiceFactory from './ui-services/UIServiceFactory';
18
19 export abstract class AbstractUIServer {
20 public readonly chargingStations: Map<string, ChargingStationData>;
21 protected httpServer: Server;
22 protected responseHandlers: Map<string, ServerResponse | WebSocket>;
23 protected readonly uiServices: Map<ProtocolVersion, AbstractUIService>;
24
25 public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) {
26 this.chargingStations = new Map<string, ChargingStationData>();
27 this.httpServer = new Server();
28 this.responseHandlers = new Map<string, ServerResponse | WebSocket>();
29 this.uiServices = new Map<ProtocolVersion, AbstractUIService>();
30 }
31
32 public buildProtocolRequest(
33 id: string,
34 procedureName: ProcedureName,
35 requestPayload: RequestPayload
36 ): ProtocolRequest {
37 return [id, procedureName, requestPayload];
38 }
39
40 public buildProtocolResponse(id: string, responsePayload: ResponsePayload): ProtocolResponse {
41 return [id, responsePayload];
42 }
43
44 public stop(): void {
45 this.chargingStations.clear();
46 }
47
48 protected registerProtocolVersionUIService(version: ProtocolVersion): void {
49 if (this.uiServices.has(version) === false) {
50 this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this));
51 }
52 }
53
54 protected isBasicAuthEnabled(): boolean {
55 return (
56 this.uiServerConfiguration.authentication?.enabled === true &&
57 this.uiServerConfiguration.authentication?.type === AuthenticationType.BASIC_AUTH
58 );
59 }
60
61 protected isValidBasicAuth(req: IncomingMessage): boolean {
62 const authorizationHeader = req.headers.authorization ?? '';
63 const authorizationToken = authorizationHeader.split(/\s+/).pop() ?? '';
64 const authentication = Buffer.from(authorizationToken, 'base64').toString();
65 const authenticationParts = authentication.split(/:/);
66 const username = authenticationParts.shift();
67 const password = authenticationParts.join(':');
68 return (
69 this.uiServerConfiguration.authentication?.username === username &&
70 this.uiServerConfiguration.authentication?.password === password
71 );
72 }
73
74 protected authenticate(req: IncomingMessage, next: (err?: Error) => void): void {
75 if (this.isBasicAuthEnabled() === true) {
76 if (this.isValidBasicAuth(req) === false) {
77 next(new Error('Unauthorized'));
78 }
79 next();
80 }
81 next();
82 }
83
84 public abstract start(): void;
85 public abstract sendRequest(request: ProtocolRequest): void;
86 public abstract sendResponse(response: ProtocolResponse): void;
87 public abstract logPrefix(
88 moduleName?: string,
89 methodName?: string,
90 prefixSuffix?: string
91 ): string;
92 }