@zetra/citrineos-util 1.8.3-fork.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authorization/ApiAuthPlugin.d.ts +52 -0
- package/dist/authorization/ApiAuthPlugin.js +122 -0
- package/dist/authorization/ApiAuthPlugin.js.map +1 -0
- package/dist/authorization/OidcTokenProvider.d.ts +15 -0
- package/dist/authorization/OidcTokenProvider.js +47 -0
- package/dist/authorization/OidcTokenProvider.js.map +1 -0
- package/dist/authorization/index.d.ts +4 -0
- package/dist/authorization/index.js +8 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/authorization/provider/LocalByPassAuthProvider.d.ts +34 -0
- package/dist/authorization/provider/LocalByPassAuthProvider.js +62 -0
- package/dist/authorization/provider/LocalByPassAuthProvider.js.map +1 -0
- package/dist/authorization/provider/OIDCAuthProvider.d.ts +62 -0
- package/dist/authorization/provider/OIDCAuthProvider.js +173 -0
- package/dist/authorization/provider/OIDCAuthProvider.js.map +1 -0
- package/dist/authorization/rbac/RbacRulesLoader.d.ts +32 -0
- package/dist/authorization/rbac/RbacRulesLoader.js +105 -0
- package/dist/authorization/rbac/RbacRulesLoader.js.map +1 -0
- package/dist/authorization/rbac/UrlMatcher.d.ts +14 -0
- package/dist/authorization/rbac/UrlMatcher.js +44 -0
- package/dist/authorization/rbac/UrlMatcher.js.map +1 -0
- package/dist/authorizer/RealTimeAuthorizer.d.ts +28 -0
- package/dist/authorizer/RealTimeAuthorizer.js +152 -0
- package/dist/authorizer/RealTimeAuthorizer.js.map +1 -0
- package/dist/authorizer/index.d.ts +1 -0
- package/dist/authorizer/index.js +5 -0
- package/dist/authorizer/index.js.map +1 -0
- package/dist/cache/memory.d.ts +19 -0
- package/dist/cache/memory.js +147 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/redis.d.ts +16 -0
- package/dist/cache/redis.js +120 -0
- package/dist/cache/redis.js.map +1 -0
- package/dist/certificate/CertificateAuthority.d.ts +38 -0
- package/dist/certificate/CertificateAuthority.js +233 -0
- package/dist/certificate/CertificateAuthority.js.map +1 -0
- package/dist/certificate/CertificateUtil.d.ts +60 -0
- package/dist/certificate/CertificateUtil.js +317 -0
- package/dist/certificate/CertificateUtil.js.map +1 -0
- package/dist/certificate/client/acme.d.ts +37 -0
- package/dist/certificate/client/acme.js +138 -0
- package/dist/certificate/client/acme.js.map +1 -0
- package/dist/certificate/client/hubject.d.ts +41 -0
- package/dist/certificate/client/hubject.js +221 -0
- package/dist/certificate/client/hubject.js.map +1 -0
- package/dist/certificate/client/interface.d.ts +12 -0
- package/dist/certificate/client/interface.js +5 -0
- package/dist/certificate/client/interface.js.map +1 -0
- package/dist/certificate/index.d.ts +2 -0
- package/dist/certificate/index.js +6 -0
- package/dist/certificate/index.js.map +1 -0
- package/dist/files/ftpServer.d.ts +4 -0
- package/dist/files/ftpServer.js +9 -0
- package/dist/files/ftpServer.js.map +1 -0
- package/dist/files/gcpCloudStorage.d.ts +39 -0
- package/dist/files/gcpCloudStorage.js +130 -0
- package/dist/files/gcpCloudStorage.js.map +1 -0
- package/dist/files/localStorage.d.ts +14 -0
- package/dist/files/localStorage.js +57 -0
- package/dist/files/localStorage.js.map +1 -0
- package/dist/files/s3Storage.d.ts +17 -0
- package/dist/files/s3Storage.js +118 -0
- package/dist/files/s3Storage.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/networkconnection/WebsocketNetworkConnection.d.ts +135 -0
- package/dist/networkconnection/WebsocketNetworkConnection.js +474 -0
- package/dist/networkconnection/WebsocketNetworkConnection.js.map +1 -0
- package/dist/networkconnection/authenticator/Authenticator.d.ts +20 -0
- package/dist/networkconnection/authenticator/Authenticator.js +39 -0
- package/dist/networkconnection/authenticator/Authenticator.js.map +1 -0
- package/dist/networkconnection/authenticator/AuthenticatorFilter.d.ts +11 -0
- package/dist/networkconnection/authenticator/AuthenticatorFilter.js +30 -0
- package/dist/networkconnection/authenticator/AuthenticatorFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/BasicAuthenticationFilter.d.ts +17 -0
- package/dist/networkconnection/authenticator/BasicAuthenticationFilter.js +51 -0
- package/dist/networkconnection/authenticator/BasicAuthenticationFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/ConnectedStationFilter.d.ts +14 -0
- package/dist/networkconnection/authenticator/ConnectedStationFilter.js +25 -0
- package/dist/networkconnection/authenticator/ConnectedStationFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/NetworkProfileFilter.d.ts +16 -0
- package/dist/networkconnection/authenticator/NetworkProfileFilter.js +84 -0
- package/dist/networkconnection/authenticator/NetworkProfileFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/UnknownStationFilter.d.ts +16 -0
- package/dist/networkconnection/authenticator/UnknownStationFilter.js +25 -0
- package/dist/networkconnection/authenticator/UnknownStationFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/errors/AuthenticationError.d.ts +6 -0
- package/dist/networkconnection/authenticator/errors/AuthenticationError.js +25 -0
- package/dist/networkconnection/authenticator/errors/AuthenticationError.js.map +1 -0
- package/dist/networkconnection/authenticator/errors/IUpgradeError.d.ts +9 -0
- package/dist/networkconnection/authenticator/errors/IUpgradeError.js +5 -0
- package/dist/networkconnection/authenticator/errors/IUpgradeError.js.map +1 -0
- package/dist/networkconnection/authenticator/errors/UnknownError.d.ts +6 -0
- package/dist/networkconnection/authenticator/errors/UnknownError.js +24 -0
- package/dist/networkconnection/authenticator/errors/UnknownError.js.map +1 -0
- package/dist/networkconnection/index.d.ts +5 -0
- package/dist/networkconnection/index.js +9 -0
- package/dist/networkconnection/index.js.map +1 -0
- package/dist/queue/index.d.ts +4 -0
- package/dist/queue/index.js +8 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/kafka/receiver.d.ts +35 -0
- package/dist/queue/kafka/receiver.js +179 -0
- package/dist/queue/kafka/receiver.js.map +1 -0
- package/dist/queue/kafka/sender.d.ts +53 -0
- package/dist/queue/kafka/sender.js +189 -0
- package/dist/queue/kafka/sender.js.map +1 -0
- package/dist/queue/rabbit-mq/receiver.d.ts +89 -0
- package/dist/queue/rabbit-mq/receiver.js +472 -0
- package/dist/queue/rabbit-mq/receiver.js.map +1 -0
- package/dist/queue/rabbit-mq/sender.d.ts +90 -0
- package/dist/queue/rabbit-mq/sender.js +251 -0
- package/dist/queue/rabbit-mq/sender.js.map +1 -0
- package/dist/security/SignedMeterValuesUtil.d.ts +44 -0
- package/dist/security/SignedMeterValuesUtil.js +135 -0
- package/dist/security/SignedMeterValuesUtil.js.map +1 -0
- package/dist/security/authentication.d.ts +2 -0
- package/dist/security/authentication.js +26 -0
- package/dist/security/authentication.js.map +1 -0
- package/dist/util/RequestOperations.d.ts +14 -0
- package/dist/util/RequestOperations.js +25 -0
- package/dist/util/RequestOperations.js.map +1 -0
- package/dist/util/StringOperations.d.ts +1 -0
- package/dist/util/StringOperations.js +8 -0
- package/dist/util/StringOperations.js.map +1 -0
- package/dist/util/emaidCheckDigitCalculator.d.ts +15 -0
- package/dist/util/emaidCheckDigitCalculator.js +179 -0
- package/dist/util/emaidCheckDigitCalculator.js.map +1 -0
- package/dist/util/idGenerator.d.ts +7 -0
- package/dist/util/idGenerator.js +10 -0
- package/dist/util/idGenerator.js.map +1 -0
- package/dist/util/parser.d.ts +31 -0
- package/dist/util/parser.js +60 -0
- package/dist/util/parser.js.map +1 -0
- package/dist/util/swagger.d.ts +5 -0
- package/dist/util/swagger.js +154 -0
- package/dist/util/swagger.js.map +1 -0
- package/dist/util/validator.d.ts +110 -0
- package/dist/util/validator.js +534 -0
- package/dist/util/validator.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { CacheNamespace, createIdentifier, getStationIdFromIdentifier, getTenantIdFromIdentifier, } from '@citrineos/base';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import * as http from 'http';
|
|
4
|
+
import * as https from 'https';
|
|
5
|
+
import { Duplex } from 'stream';
|
|
6
|
+
import { Logger } from 'tslog';
|
|
7
|
+
import { WebSocket, WebSocketServer } from 'ws';
|
|
8
|
+
export class WebsocketNetworkConnection {
|
|
9
|
+
_cache;
|
|
10
|
+
_config;
|
|
11
|
+
_logger;
|
|
12
|
+
_identifierConnections = new Map();
|
|
13
|
+
// websocketServers id as key and http server as value
|
|
14
|
+
_httpServersMap;
|
|
15
|
+
_authenticator;
|
|
16
|
+
_router;
|
|
17
|
+
_doesChargingStationExistByStationId;
|
|
18
|
+
constructor(config, cache, authenticator, router, logger, doesChargingStationExistByStationId) {
|
|
19
|
+
this._cache = cache;
|
|
20
|
+
this._config = config;
|
|
21
|
+
this._doesChargingStationExistByStationId = doesChargingStationExistByStationId;
|
|
22
|
+
this._logger = logger
|
|
23
|
+
? logger.getSubLogger({ name: this.constructor.name })
|
|
24
|
+
: new Logger({ name: this.constructor.name });
|
|
25
|
+
this._authenticator = authenticator;
|
|
26
|
+
router.networkHook = this.sendMessage.bind(this);
|
|
27
|
+
this._router = router;
|
|
28
|
+
this._httpServersMap = new Map();
|
|
29
|
+
this._config.util.networkConnection.websocketServers.forEach(async (websocketServerConfig) => {
|
|
30
|
+
const _httpServer = await this._createAndStartWebsocketServer(websocketServerConfig);
|
|
31
|
+
this._httpServersMap.set(websocketServerConfig.id, _httpServer);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Send a message to the charging station specified by the identifier.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} identifier - The identifier of the client.
|
|
38
|
+
* @param {string} message - The message to send.
|
|
39
|
+
* @return {void} rejects the promise if message fails to send, otherwise returns void.
|
|
40
|
+
*/
|
|
41
|
+
sendMessage(identifier, message) {
|
|
42
|
+
return new Promise(async (resolve, reject) => {
|
|
43
|
+
try {
|
|
44
|
+
const clientConnection = await this._cache.get(identifier, CacheNamespace.Connections);
|
|
45
|
+
if (clientConnection) {
|
|
46
|
+
const websocketConnection = this._identifierConnections.get(identifier);
|
|
47
|
+
if (websocketConnection && websocketConnection.readyState === WebSocket.OPEN) {
|
|
48
|
+
websocketConnection.send(message, (error) => {
|
|
49
|
+
if (error) {
|
|
50
|
+
reject(error); // Reject the promise with the error
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
resolve(); // Resolve the promise with true indicating success
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const errorMsg = 'Websocket connection is not ready - ' + identifier;
|
|
59
|
+
this._logger.fatal(errorMsg);
|
|
60
|
+
websocketConnection?.close(1011, errorMsg);
|
|
61
|
+
reject(new Error(errorMsg)); // Reject with a new error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const errorMsg = 'Cannot identify client connection for ' + identifier;
|
|
66
|
+
// This can happen when a charging station disconnects in the moment a message is trying to send.
|
|
67
|
+
// Retry logic on the message sender might not suffice as charging station might connect to different instance.
|
|
68
|
+
this._logger.error(errorMsg);
|
|
69
|
+
this._identifierConnections
|
|
70
|
+
.get(identifier)
|
|
71
|
+
?.close(1011, 'Failed to get connection information for ' + identifier);
|
|
72
|
+
reject(new Error(errorMsg)); // Reject with a new error
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
reject(error);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
bindNetworkHook() {
|
|
81
|
+
return (identifier, message) => this.sendMessage(identifier, message);
|
|
82
|
+
}
|
|
83
|
+
async disconnect(tenantId, stationId) {
|
|
84
|
+
const identifier = createIdentifier(tenantId, stationId);
|
|
85
|
+
const websocketConnection = this._identifierConnections.get(identifier);
|
|
86
|
+
if (!websocketConnection) {
|
|
87
|
+
this._logger.warn(`No websocket connection found for tenantId ${tenantId} and stationId ${stationId}, will still deregister from router.`);
|
|
88
|
+
}
|
|
89
|
+
websocketConnection?.close(1000, 'Disconnected by admin request');
|
|
90
|
+
const deregistered = await this._router?.deregisterConnection(tenantId, stationId);
|
|
91
|
+
return !!websocketConnection && deregistered;
|
|
92
|
+
}
|
|
93
|
+
async shutdown() {
|
|
94
|
+
this._httpServersMap.forEach((server) => server.close());
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Updates certificates for a specific server with the provided TLS key, certificate chain, and optional
|
|
98
|
+
* root CA.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} serverId - The ID of the server to update.
|
|
101
|
+
* @param {string} tlsKey - The TLS key to set.
|
|
102
|
+
* @param {string} tlsCertificateChain - The TLS certificate chain to set.
|
|
103
|
+
* @param {string} [rootCA] - The root CA to set (optional).
|
|
104
|
+
* @return {void} void
|
|
105
|
+
*/
|
|
106
|
+
updateTlsCertificates(serverId, tlsKey, tlsCertificateChain, rootCA) {
|
|
107
|
+
let httpsServer = this._httpServersMap.get(serverId);
|
|
108
|
+
if (httpsServer && httpsServer instanceof https.Server) {
|
|
109
|
+
const secureContextOptions = {
|
|
110
|
+
key: tlsKey,
|
|
111
|
+
cert: tlsCertificateChain,
|
|
112
|
+
};
|
|
113
|
+
if (rootCA) {
|
|
114
|
+
secureContextOptions.ca = rootCA;
|
|
115
|
+
}
|
|
116
|
+
httpsServer.setSecureContext(secureContextOptions);
|
|
117
|
+
this._logger.info(`Updated TLS certificates in SecureContextOptions for server ${serverId}`);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
throw new TypeError(`Server ${serverId} is not a https server.`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Dynamically adds a new websocket server at runtime and starts it.
|
|
125
|
+
*
|
|
126
|
+
* @param {WebsocketServerConfig} websocketServerConfig
|
|
127
|
+
* @returns {Promise<void>}
|
|
128
|
+
*/
|
|
129
|
+
async addWebsocketServer(websocketServerConfig) {
|
|
130
|
+
const httpServer = await this._createAndStartWebsocketServer(websocketServerConfig);
|
|
131
|
+
this._httpServersMap.set(websocketServerConfig.id, httpServer);
|
|
132
|
+
}
|
|
133
|
+
_onHttpRequest(req, res) {
|
|
134
|
+
if (req.method === 'GET' && req.url === '/health') {
|
|
135
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
136
|
+
res.end(JSON.stringify({ status: 'healthy' }));
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
140
|
+
res.end(JSON.stringify({
|
|
141
|
+
message: `Route ${req.method}:${req.url} not found`,
|
|
142
|
+
error: 'Not Found',
|
|
143
|
+
statusCode: 404,
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Method to validate websocket upgrade requests and pass them to the socket server.
|
|
149
|
+
*
|
|
150
|
+
* @param {IncomingMessage} req - The request object.
|
|
151
|
+
* @param {Duplex} socket - Websocket duplex stream.
|
|
152
|
+
* @param {Buffer} head - Websocket buffer.
|
|
153
|
+
* @param {WebSocketServer} wss - Websocket server.
|
|
154
|
+
* @param {WebsocketServerConfig} websocketServerConfig - websocket server config.
|
|
155
|
+
*/
|
|
156
|
+
async _upgradeRequest(req, socket, head, wss, websocketServerConfig) {
|
|
157
|
+
// Failed mTLS and TLS requests are rejected by the server before getting this far
|
|
158
|
+
this._logger.debug('On upgrade request', req.method, req.url, req.headers, websocketServerConfig);
|
|
159
|
+
try {
|
|
160
|
+
// Resolve tenant at upgrade time (query param, path segment, header),
|
|
161
|
+
// falling back to the server-configured tenant if none provided.
|
|
162
|
+
const resolvedTenantId = websocketServerConfig.dynamicTenantResolution
|
|
163
|
+
? this._extractTenantIdFromRequest(req, websocketServerConfig) ??
|
|
164
|
+
websocketServerConfig.tenantId
|
|
165
|
+
: websocketServerConfig.tenantId;
|
|
166
|
+
// Attach resolved tenant to request so downstream handlers (connection) can use it
|
|
167
|
+
req.__resolvedTenantId = resolvedTenantId;
|
|
168
|
+
const { identifier } = await this._authenticator.authenticate(req, resolvedTenantId, {
|
|
169
|
+
securityProfile: websocketServerConfig.securityProfile,
|
|
170
|
+
allowUnknownChargingStations: websocketServerConfig.allowUnknownChargingStations,
|
|
171
|
+
});
|
|
172
|
+
this._logger.debug('Successfully registered websocket client', identifier);
|
|
173
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
174
|
+
wss.emit('connection', ws, req);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
/**
|
|
179
|
+
* See {@link IUpgradeError.terminateConnection}
|
|
180
|
+
**/
|
|
181
|
+
error?.terminateConnection?.(socket) || this._terminateConnectionInternalError(socket);
|
|
182
|
+
this._logger.warn(error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Utility function to reject websocket upgrade requests with 500 status code.
|
|
187
|
+
* @param socket - Websocket duplex stream.
|
|
188
|
+
*/
|
|
189
|
+
_terminateConnectionInternalError(socket) {
|
|
190
|
+
socket.write('HTTP/1.1 500 Internal Server Error\r\n');
|
|
191
|
+
socket.write('\r\n');
|
|
192
|
+
socket.end();
|
|
193
|
+
socket.destroy();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Internal method to handle new client connection and ensures supported protocols are used.
|
|
197
|
+
*
|
|
198
|
+
* @param {Set<string>} protocols - The set of protocols to handle.
|
|
199
|
+
* @param {IncomingMessage} _req - The request object.
|
|
200
|
+
* @param {string} wsServerProtocol - The websocket server protocol.
|
|
201
|
+
* @return {boolean|string} - Returns the protocol version if successful, otherwise false.
|
|
202
|
+
*/
|
|
203
|
+
_handleProtocols(protocols, _req, wsServerProtocol) {
|
|
204
|
+
// Only supports configured protocol version
|
|
205
|
+
if (protocols.has(wsServerProtocol)) {
|
|
206
|
+
return wsServerProtocol;
|
|
207
|
+
}
|
|
208
|
+
this._logger.error(`Protocol mismatch. Charger supports: [${[...protocols].join(', ')}], but server expects: '${wsServerProtocol}'.`);
|
|
209
|
+
// Reject the client trying to connect
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Internal method to handle the connection event when a WebSocket connection is established.
|
|
214
|
+
* This happens after successful protocol exchange with client.
|
|
215
|
+
*
|
|
216
|
+
* @param {WebSocket} ws - The WebSocket object representing the connection.
|
|
217
|
+
* @param {WebsocketServerConfig} websocketServerConfig - The websocket server configuration.
|
|
218
|
+
* @param {number} pingInterval - The ping interval in seconds.
|
|
219
|
+
* @param {IncomingMessage} req - The request object associated with the connection.
|
|
220
|
+
* @return {void}
|
|
221
|
+
*/
|
|
222
|
+
async _onConnection(ws, websocketServerConfig, pingInterval, req) {
|
|
223
|
+
if (!ws.protocol) {
|
|
224
|
+
this._logger.debug('Websocket connection without protocol');
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
// Pause the WebSocket event emitter until broker is established
|
|
229
|
+
ws.pause();
|
|
230
|
+
const stationId = this._getClientIdFromUrl(req.url);
|
|
231
|
+
// Prefer tenant resolved during upgrade; fallback to server-configured tenant.
|
|
232
|
+
const tenantId = req.__resolvedTenantId ?? websocketServerConfig.tenantId;
|
|
233
|
+
const checker = this._doesChargingStationExistByStationId ??
|
|
234
|
+
this._router.doesChargingStationExistByStationId?.bind(this._router);
|
|
235
|
+
if (!checker) {
|
|
236
|
+
throw new Error('No method available to check if charging station exists');
|
|
237
|
+
}
|
|
238
|
+
const exists = await checker(tenantId, stationId);
|
|
239
|
+
if (!exists && !websocketServerConfig.allowUnknownChargingStations) {
|
|
240
|
+
this._logger.error('Rejecting connection: station %s not found in tenant %s', stationId, tenantId);
|
|
241
|
+
ws.close(1011, 'Unknown charging station');
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const identifier = createIdentifier(tenantId, stationId);
|
|
245
|
+
// Enforce optional per-tenant connection limit if configured
|
|
246
|
+
const maxConnections = websocketServerConfig.maxConnectionsPerTenant;
|
|
247
|
+
if (typeof maxConnections === 'number' && maxConnections > 0) {
|
|
248
|
+
const currentCount = [...this._identifierConnections.keys()].filter((k) => getTenantIdFromIdentifier(k) === tenantId).length;
|
|
249
|
+
if (currentCount >= maxConnections) {
|
|
250
|
+
this._logger.warn(`Tenant ${tenantId} exceeded max connections (${maxConnections}), rejecting ${identifier}`);
|
|
251
|
+
ws.close(1013, 'Tenant connection limit exceeded');
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
this._identifierConnections.set(identifier, ws);
|
|
256
|
+
try {
|
|
257
|
+
// Get IP address of client
|
|
258
|
+
const ip = req.headers['x-forwarded-for']?.toString().split(',')[0].trim() ||
|
|
259
|
+
req.socket.remoteAddress ||
|
|
260
|
+
'N/A';
|
|
261
|
+
const port = req.socket.remotePort;
|
|
262
|
+
const connLogger = this._logger.getSubLogger({
|
|
263
|
+
name: `T${tenantId}:${stationId}`,
|
|
264
|
+
});
|
|
265
|
+
connLogger.info('Client websocket connected', identifier, ip, port, ws.protocol);
|
|
266
|
+
// Register client
|
|
267
|
+
const websocketConnection = {
|
|
268
|
+
id: websocketServerConfig.id,
|
|
269
|
+
protocol: ws.protocol,
|
|
270
|
+
};
|
|
271
|
+
let registered = await this._cache.set(identifier, JSON.stringify(websocketConnection), CacheNamespace.Connections);
|
|
272
|
+
registered =
|
|
273
|
+
registered && (await this._router.registerConnection(tenantId, stationId, ws.protocol));
|
|
274
|
+
if (!registered) {
|
|
275
|
+
connLogger.fatal('Failed to register websocket client', identifier);
|
|
276
|
+
throw new Error('Failed to register websocket client');
|
|
277
|
+
}
|
|
278
|
+
connLogger.info('Successfully connected new charging station.', identifier);
|
|
279
|
+
// Register all websocket events
|
|
280
|
+
this._registerWebsocketEvents(identifier, ws, pingInterval);
|
|
281
|
+
// Resume the WebSocket event emitter after events have been subscribed to
|
|
282
|
+
ws.resume();
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
this._logger.fatal('Failed to subscribe to message broker for ', identifier);
|
|
286
|
+
ws.close(1011, 'Failed to subscribe to message broker for ' + identifier);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Internal method to register event listeners for the WebSocket connection.
|
|
292
|
+
*
|
|
293
|
+
* @param {string} identifier - The unique identifier of the connection, i.e. the combination of tenantId and stationId.
|
|
294
|
+
* @param {WebSocket} ws - The WebSocket object representing the connection.
|
|
295
|
+
* @param {number} pingInterval - The ping interval in seconds.
|
|
296
|
+
* @return {void} This function does not return anything.
|
|
297
|
+
*/
|
|
298
|
+
_registerWebsocketEvents(identifier, ws, pingInterval) {
|
|
299
|
+
ws.onerror = (event) => {
|
|
300
|
+
this._logger.error('Connection error encountered for', identifier, event.error, event.message, event.type);
|
|
301
|
+
ws.close(1011, event.message);
|
|
302
|
+
};
|
|
303
|
+
ws.onmessage = (event) => {
|
|
304
|
+
this._onMessage(identifier, event.data.toString(), ws.protocol);
|
|
305
|
+
};
|
|
306
|
+
ws.once('close', () => {
|
|
307
|
+
// Unregister client
|
|
308
|
+
this._logger.info('Connection closed for', identifier);
|
|
309
|
+
this._cache.remove(identifier, CacheNamespace.Connections);
|
|
310
|
+
this._identifierConnections.delete(identifier);
|
|
311
|
+
this._router.deregisterConnection(getTenantIdFromIdentifier(identifier), getStationIdFromIdentifier(identifier));
|
|
312
|
+
});
|
|
313
|
+
ws.on('ping', async (message) => {
|
|
314
|
+
this._logger.debug(`Ping received for ${identifier} with message ${JSON.stringify(message)}`);
|
|
315
|
+
ws.pong(message);
|
|
316
|
+
});
|
|
317
|
+
ws.on('pong', async () => {
|
|
318
|
+
this._logger.debug('Pong received for', identifier);
|
|
319
|
+
const clientConnection = await this._cache.get(identifier, CacheNamespace.Connections);
|
|
320
|
+
if (clientConnection) {
|
|
321
|
+
// Remove expiration for connection and send ping to client in pingInterval seconds.
|
|
322
|
+
await this._cache.set(identifier, clientConnection, CacheNamespace.Connections);
|
|
323
|
+
this._ping(identifier, ws, pingInterval);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
this._logger.debug('Pong received for', identifier, 'but client is not alive');
|
|
327
|
+
ws.close(1011, 'Client is not alive');
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
this._ping(identifier, ws, pingInterval);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Internal method to handle the incoming message from the websocket client.
|
|
334
|
+
*
|
|
335
|
+
* @param {string} identifier - The client identifier.
|
|
336
|
+
* @param {string} message - The incoming message from the client.
|
|
337
|
+
* @param {OCPPVersionType} protocol - The OCPP protocol version of the client, 'ocpp1.6' or 'ocpp2.0.1'.
|
|
338
|
+
* @return {void} This function does not return anything.
|
|
339
|
+
*/
|
|
340
|
+
_onMessage(identifier, message, protocol) {
|
|
341
|
+
this._router.onMessage(identifier, message, new Date(), protocol);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Internal method to handle the error event for the WebSocket server.
|
|
345
|
+
*
|
|
346
|
+
* @param {WebSocketServer} wss - The WebSocket server instance.
|
|
347
|
+
* @param {Error} error - The error object.
|
|
348
|
+
* @return {void} This function does not return anything.
|
|
349
|
+
*/
|
|
350
|
+
_onError(wss, error) {
|
|
351
|
+
this._logger.error(error);
|
|
352
|
+
// TODO: Try to recover the Websocket server
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Internal method to handle the event when the WebSocketServer is closed.
|
|
356
|
+
*
|
|
357
|
+
* @param {WebSocketServer} wss - The WebSocketServer instance.
|
|
358
|
+
* @return {void} This function does not return anything.
|
|
359
|
+
*/
|
|
360
|
+
_onClose(wss) {
|
|
361
|
+
this._logger.debug('Websocket Server closed');
|
|
362
|
+
// TODO: Try to recover the Websocket server
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Internal method to execute a ping operation on a WebSocket connection after a delay of 60 seconds.
|
|
366
|
+
*
|
|
367
|
+
* @param {string} identifier - The identifier of the client connection.
|
|
368
|
+
* @param {WebSocket} ws - The WebSocket connection to ping.
|
|
369
|
+
* @param {number} pingInterval - The ping interval in milliseconds.
|
|
370
|
+
* @return {void} This function does not return anything.
|
|
371
|
+
*/
|
|
372
|
+
async _ping(identifier, ws, pingInterval) {
|
|
373
|
+
setTimeout(async () => {
|
|
374
|
+
const clientConnection = await this._cache.get(identifier, CacheNamespace.Connections);
|
|
375
|
+
if (clientConnection) {
|
|
376
|
+
this._logger.debug('Pinging client', identifier);
|
|
377
|
+
// Set connection expiration and send ping to client
|
|
378
|
+
await this._cache.set(identifier, clientConnection, CacheNamespace.Connections, pingInterval * 2);
|
|
379
|
+
ws.ping();
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
ws.close(1011, 'Client is not alive');
|
|
383
|
+
}
|
|
384
|
+
}, pingInterval * 1000);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
*
|
|
388
|
+
* @param url Http upgrade request url used by charger
|
|
389
|
+
* @returns Charger identifier
|
|
390
|
+
*/
|
|
391
|
+
_getClientIdFromUrl(url) {
|
|
392
|
+
// Remove query string first
|
|
393
|
+
const pathOnly = url.split('?')[0];
|
|
394
|
+
return pathOnly.split('/').pop();
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Extract tenant id from the incoming upgrade request.
|
|
398
|
+
* Supported sources (in order): query `tenant`/`tenantId`, header `x-tenant-id`,
|
|
399
|
+
* path segment (second-last segment if URL is `/tenant/station`).
|
|
400
|
+
*/
|
|
401
|
+
_extractTenantIdFromRequest(req, config) {
|
|
402
|
+
try {
|
|
403
|
+
const rawUrl = req.url ?? '';
|
|
404
|
+
const url = new URL(rawUrl, 'http://localhost');
|
|
405
|
+
const segments = url.pathname.split('/').filter(Boolean);
|
|
406
|
+
// Path segment mapping: assume /.../{pathSegment}/{station}
|
|
407
|
+
// We look for a mapping of pathSegment to tenantId.
|
|
408
|
+
if (segments.length >= 2 && config.tenantPathMapping) {
|
|
409
|
+
const pathSegment = segments[segments.length - 2];
|
|
410
|
+
if (config.tenantPathMapping[pathSegment]) {
|
|
411
|
+
return config.tenantPathMapping[pathSegment];
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
this._logger.debug(`No mapping found for path segment: ${pathSegment}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
catch (err) {
|
|
419
|
+
// If parsing fails, ignore and fall back to server-configured tenant
|
|
420
|
+
this._logger.debug('Failed to extract tenant from request', err);
|
|
421
|
+
}
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
_generateServerOptions(config) {
|
|
425
|
+
const serverOptions = {
|
|
426
|
+
key: fs.readFileSync(config.tlsKeyFilePath),
|
|
427
|
+
cert: fs.readFileSync(config.tlsCertificateChainFilePath),
|
|
428
|
+
};
|
|
429
|
+
if (config.rootCACertificateFilePath) {
|
|
430
|
+
serverOptions.ca = fs.readFileSync(config.rootCACertificateFilePath);
|
|
431
|
+
}
|
|
432
|
+
if (config.securityProfile > 2) {
|
|
433
|
+
serverOptions.requestCert = true;
|
|
434
|
+
serverOptions.rejectUnauthorized = true;
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
serverOptions.rejectUnauthorized = false;
|
|
438
|
+
}
|
|
439
|
+
return serverOptions;
|
|
440
|
+
}
|
|
441
|
+
_createAndStartWebsocketServer(wsConfig) {
|
|
442
|
+
return new Promise((resolve) => {
|
|
443
|
+
let httpServer;
|
|
444
|
+
switch (wsConfig.securityProfile) {
|
|
445
|
+
case 3: // mTLS
|
|
446
|
+
case 2: // TLS
|
|
447
|
+
httpServer = https.createServer(this._generateServerOptions(wsConfig), this._onHttpRequest.bind(this));
|
|
448
|
+
break;
|
|
449
|
+
case 1:
|
|
450
|
+
case 0:
|
|
451
|
+
default:
|
|
452
|
+
httpServer = http.createServer(this._onHttpRequest.bind(this));
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
const wss = new WebSocketServer({
|
|
456
|
+
noServer: true,
|
|
457
|
+
handleProtocols: (protocols, req) => this._handleProtocols(protocols, req, wsConfig.protocol),
|
|
458
|
+
clientTracking: false,
|
|
459
|
+
});
|
|
460
|
+
wss.on('connection', (ws, req) => this._onConnection(ws, wsConfig, wsConfig.pingInterval, req));
|
|
461
|
+
wss.on('error', (server, error) => this._onError(server, error));
|
|
462
|
+
wss.on('close', (server) => this._onClose(server));
|
|
463
|
+
httpServer.on('upgrade', (req, socket, head) => this._upgradeRequest(req, socket, head, wss, wsConfig));
|
|
464
|
+
httpServer.on('error', (error) => wss.emit('error', error));
|
|
465
|
+
httpServer.on('close', () => wss.emit('close'));
|
|
466
|
+
const protocol = wsConfig.securityProfile > 1 ? 'wss' : 'ws';
|
|
467
|
+
httpServer.listen(wsConfig.port, wsConfig.host, () => {
|
|
468
|
+
this._logger.info(`WebsocketServer running on ${protocol}://${wsConfig.host}:${wsConfig.port}/`);
|
|
469
|
+
resolve(httpServer);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=WebsocketNetworkConnection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebsocketNetworkConnection.js","sourceRoot":"","sources":["../../src/networkconnection/WebsocketNetworkConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AuthenticationOptions, IAuthenticator } from '@citrineos/base';
|
|
2
|
+
import type { ILogObj } from 'tslog';
|
|
3
|
+
import { Logger } from 'tslog';
|
|
4
|
+
import { IncomingMessage } from 'http';
|
|
5
|
+
import { UnknownStationFilter } from './UnknownStationFilter.js';
|
|
6
|
+
import { BasicAuthenticationFilter } from './BasicAuthenticationFilter.js';
|
|
7
|
+
import { ConnectedStationFilter } from './ConnectedStationFilter.js';
|
|
8
|
+
import { NetworkProfileFilter } from './NetworkProfileFilter.js';
|
|
9
|
+
export declare class Authenticator implements IAuthenticator {
|
|
10
|
+
protected _logger: Logger<ILogObj>;
|
|
11
|
+
private _unknownStationFilter;
|
|
12
|
+
private _connectedStationFilter;
|
|
13
|
+
private _networkProfileFilter;
|
|
14
|
+
private _basicAuthenticationFilter;
|
|
15
|
+
constructor(unknownStationFilter: UnknownStationFilter, connectedStationFilter: ConnectedStationFilter, networkProfileFilter: NetworkProfileFilter, basicAuthenticationFilter: BasicAuthenticationFilter, logger?: Logger<ILogObj>);
|
|
16
|
+
authenticate(request: IncomingMessage, tenantId: number, options: AuthenticationOptions): Promise<{
|
|
17
|
+
identifier: string;
|
|
18
|
+
}>;
|
|
19
|
+
private _getClientIdFromUrl;
|
|
20
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { Logger } from 'tslog';
|
|
5
|
+
import { IncomingMessage } from 'http';
|
|
6
|
+
import { UnknownStationFilter } from './UnknownStationFilter.js';
|
|
7
|
+
import { BasicAuthenticationFilter } from './BasicAuthenticationFilter.js';
|
|
8
|
+
import { ConnectedStationFilter } from './ConnectedStationFilter.js';
|
|
9
|
+
import { NetworkProfileFilter } from './NetworkProfileFilter.js';
|
|
10
|
+
export class Authenticator {
|
|
11
|
+
_logger;
|
|
12
|
+
_unknownStationFilter;
|
|
13
|
+
_connectedStationFilter;
|
|
14
|
+
_networkProfileFilter;
|
|
15
|
+
_basicAuthenticationFilter;
|
|
16
|
+
constructor(unknownStationFilter, connectedStationFilter, networkProfileFilter, basicAuthenticationFilter, logger) {
|
|
17
|
+
this._unknownStationFilter = unknownStationFilter;
|
|
18
|
+
this._connectedStationFilter = connectedStationFilter;
|
|
19
|
+
this._networkProfileFilter = networkProfileFilter;
|
|
20
|
+
this._basicAuthenticationFilter = basicAuthenticationFilter;
|
|
21
|
+
this._logger = logger
|
|
22
|
+
? logger.getSubLogger({ name: this.constructor.name })
|
|
23
|
+
: new Logger({ name: this.constructor.name });
|
|
24
|
+
}
|
|
25
|
+
async authenticate(request, tenantId, options) {
|
|
26
|
+
const identifier = this._getClientIdFromUrl(request.url);
|
|
27
|
+
this._logger.debug(`Starting authentication for identifier: ${identifier}`);
|
|
28
|
+
await this._unknownStationFilter.authenticate(tenantId, identifier, request, options);
|
|
29
|
+
await this._connectedStationFilter.authenticate(tenantId, identifier, request, options);
|
|
30
|
+
await this._networkProfileFilter.authenticate(tenantId, identifier, request, options);
|
|
31
|
+
await this._basicAuthenticationFilter.authenticate(tenantId, identifier, request, options);
|
|
32
|
+
this._logger.debug(`Authentication successful for identifier: ${identifier}`);
|
|
33
|
+
return { identifier };
|
|
34
|
+
}
|
|
35
|
+
_getClientIdFromUrl(url) {
|
|
36
|
+
return url.split('/').pop();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=Authenticator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Authenticator.js","sourceRoot":"","sources":["../../../src/networkconnection/authenticator/Authenticator.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,sCAAsC;AAItC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,OAAO,aAAa;IACd,OAAO,CAAkB;IAC3B,qBAAqB,CAAuB;IAC5C,uBAAuB,CAAyB;IAChD,qBAAqB,CAAuB;IAC5C,0BAA0B,CAA4B;IAE9D,YACE,oBAA0C,EAC1C,sBAA8C,EAC9C,oBAA0C,EAC1C,yBAAoD,EACpD,MAAwB;QAExB,IAAI,CAAC,qBAAqB,GAAG,oBAAoB,CAAC;QAClD,IAAI,CAAC,uBAAuB,GAAG,sBAAsB,CAAC;QACtD,IAAI,CAAC,qBAAqB,GAAG,oBAAoB,CAAC;QAClD,IAAI,CAAC,0BAA0B,GAAG,yBAAyB,CAAC;QAC5D,IAAI,CAAC,OAAO,GAAG,MAAM;YACnB,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACtD,CAAC,CAAC,IAAI,MAAM,CAAU,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,OAAwB,EACxB,QAAgB,EAChB,OAA8B;QAE9B,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,GAAa,CAAC,CAAC;QACnE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,2CAA2C,UAAU,EAAE,CAAC,CAAC;QAE5E,MAAM,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACtF,MAAM,IAAI,CAAC,uBAAuB,CAAC,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACxF,MAAM,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACtF,MAAM,IAAI,CAAC,0BAA0B,CAAC,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAE3F,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,6CAA6C,UAAU,EAAE,CAAC,CAAC;QAC9E,OAAO,EAAE,UAAU,EAAE,CAAC;IACxB,CAAC;IAEO,mBAAmB,CAAC,GAAW;QACrC,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAY,CAAC;IACxC,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IncomingMessage } from 'http';
|
|
2
|
+
import type { ILogObj } from 'tslog';
|
|
3
|
+
import { Logger } from 'tslog';
|
|
4
|
+
import type { AuthenticationOptions } from '@citrineos/base';
|
|
5
|
+
export declare abstract class AuthenticatorFilter {
|
|
6
|
+
protected _logger: Logger<ILogObj>;
|
|
7
|
+
protected constructor(logger?: Logger<ILogObj>);
|
|
8
|
+
protected abstract shouldFilter(options: AuthenticationOptions): boolean;
|
|
9
|
+
protected abstract filter(tenantId: number, identifier: string, request: IncomingMessage, options?: AuthenticationOptions): Promise<void>;
|
|
10
|
+
authenticate(tenantId: number, identifier: string, request: IncomingMessage, options: AuthenticationOptions): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { IncomingMessage } from 'http';
|
|
5
|
+
import { Logger } from 'tslog';
|
|
6
|
+
export class AuthenticatorFilter {
|
|
7
|
+
_logger;
|
|
8
|
+
constructor(logger) {
|
|
9
|
+
this._logger = logger
|
|
10
|
+
? logger.getSubLogger({ name: this.constructor.name })
|
|
11
|
+
: new Logger({ name: this.constructor.name });
|
|
12
|
+
}
|
|
13
|
+
async authenticate(tenantId, identifier, request, options) {
|
|
14
|
+
if (this.shouldFilter(options)) {
|
|
15
|
+
this._logger.debug(`Applying filter for: ${identifier}`);
|
|
16
|
+
try {
|
|
17
|
+
await this.filter(tenantId, identifier, request, options);
|
|
18
|
+
this._logger.debug(`Filter passed for: ${identifier}`);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
this._logger.warn(`Filter failed for: ${identifier}`);
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this._logger.debug(`Filter skipped for: ${identifier}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=AuthenticatorFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthenticatorFilter.js","sourceRoot":"","sources":["../../../src/networkconnection/authenticator/AuthenticatorFilter.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,sCAAsC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAG/B,MAAM,OAAgB,mBAAmB;IAC7B,OAAO,CAAkB;IAEnC,YAAsB,MAAwB;QAC5C,IAAI,CAAC,OAAO,GAAG,MAAM;YACnB,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACtD,CAAC,CAAC,IAAI,MAAM,CAAU,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAUD,KAAK,CAAC,YAAY,CAChB,QAAgB,EAChB,UAAkB,EAClB,OAAwB,EACxB,OAA8B;QAE9B,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC1D,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,sBAAsB,UAAU,EAAE,CAAC,CAAC;YACzD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,sBAAsB,UAAU,EAAE,CAAC,CAAC;gBACtD,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ILogObj } from 'tslog';
|
|
2
|
+
import { Logger } from 'tslog';
|
|
3
|
+
import type { IDeviceModelRepository } from '@citrineos/data';
|
|
4
|
+
import { IncomingMessage } from 'http';
|
|
5
|
+
import { AuthenticatorFilter } from './AuthenticatorFilter.js';
|
|
6
|
+
import type { AuthenticationOptions } from '@citrineos/base';
|
|
7
|
+
/**
|
|
8
|
+
* Filter used to authenticate incoming HTTP requests based on basic authorization header.
|
|
9
|
+
* It only applies when the security profile is set to 1 or 2.
|
|
10
|
+
*/
|
|
11
|
+
export declare class BasicAuthenticationFilter extends AuthenticatorFilter {
|
|
12
|
+
private _deviceModelRepository;
|
|
13
|
+
constructor(deviceModelRepository: IDeviceModelRepository, logger?: Logger<ILogObj>);
|
|
14
|
+
protected shouldFilter(options: AuthenticationOptions): boolean;
|
|
15
|
+
protected filter(tenantId: number, identifier: string, request: IncomingMessage): Promise<void>;
|
|
16
|
+
private _isPasswordValid;
|
|
17
|
+
}
|