@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,221 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { HttpMethod, HttpStatus, HUBJECT_DEFAULT_AUTH_TOKEN, HUBJECT_DEFAULT_BASEURL, HUBJECT_DEFAULT_CLIENTID, HUBJECT_DEFAULT_CLIENTSECRET, HUBJECT_DEFAULT_TOKENURL, } from '@citrineos/base';
|
|
5
|
+
import { Logger } from 'tslog';
|
|
6
|
+
import { createPemBlock } from '../CertificateUtil.js';
|
|
7
|
+
export class Hubject {
|
|
8
|
+
_baseUrl;
|
|
9
|
+
_tokenUrl;
|
|
10
|
+
_clientId;
|
|
11
|
+
_clientSecret;
|
|
12
|
+
_logger;
|
|
13
|
+
_cache;
|
|
14
|
+
static AUTH_TOKEN_CACHE_KEY = 'HUBJECT_AUTH_TOKEN';
|
|
15
|
+
static AUTH_TOKEN_CACHE_NAMESPACE = 'hubject';
|
|
16
|
+
constructor(config, cache, logger) {
|
|
17
|
+
const hubjectConfig = config.util.certificateAuthority.v2gCA.hubject;
|
|
18
|
+
if (!hubjectConfig) {
|
|
19
|
+
throw new Error('Missing Hubject configuration');
|
|
20
|
+
}
|
|
21
|
+
this._baseUrl = hubjectConfig.baseUrl;
|
|
22
|
+
this._tokenUrl = hubjectConfig.tokenUrl;
|
|
23
|
+
this._clientId = hubjectConfig.clientId;
|
|
24
|
+
this._clientSecret = hubjectConfig.clientSecret;
|
|
25
|
+
this._cache = cache;
|
|
26
|
+
this._logger = logger
|
|
27
|
+
? logger.getSubLogger({ name: this.constructor.name })
|
|
28
|
+
: new Logger({ name: this.constructor.name });
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves a signed certificate based on the provided CSR.
|
|
32
|
+
* DOC: https://hubject.stoplight.io/docs/open-plugncharge/486f0b8b3ded4-simple-enroll-iso-15118-2-and-iso-15118-20
|
|
33
|
+
*
|
|
34
|
+
* @param {string} csrString - The certificate signing request from SignCertificateRequest.
|
|
35
|
+
* @return {Promise<string>} The signed certificate without header and footer.
|
|
36
|
+
*/
|
|
37
|
+
async getSignedCertificate(csrString) {
|
|
38
|
+
return this._makeAuthenticatedRequest(async () => {
|
|
39
|
+
const url = `${this._baseUrl}/.well-known/cpo/simpleenroll`;
|
|
40
|
+
return fetch(url, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
Accept: 'application/pkcs10',
|
|
44
|
+
Authorization: await this._getAuthorizationToken(),
|
|
45
|
+
'Content-Type': 'application/pkcs10',
|
|
46
|
+
},
|
|
47
|
+
body: csrString,
|
|
48
|
+
});
|
|
49
|
+
}, 'Get signed certificate response is unexpected');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Retrieves the CA certificates including sub CAs and root CA.
|
|
53
|
+
* DOC: https://hubject.stoplight.io/docs/open-plugncharge/e246aa213bc22-obtaining-ca-certificates-iso-15118-2-and-iso-15118-20
|
|
54
|
+
*
|
|
55
|
+
* @return {Promise<string>} The CA certificates.
|
|
56
|
+
*/
|
|
57
|
+
async getCACertificates() {
|
|
58
|
+
return this._makeAuthenticatedRequest(async () => {
|
|
59
|
+
const url = `${this._baseUrl}/.well-known/cpo/cacerts`;
|
|
60
|
+
return fetch(url, {
|
|
61
|
+
method: 'GET',
|
|
62
|
+
headers: {
|
|
63
|
+
Accept: 'application/pkcs10, application/pkcs7',
|
|
64
|
+
Authorization: await this._getAuthorizationToken(),
|
|
65
|
+
'Content-Transfer-Encoding': 'application/pkcs10',
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}, 'Get CA certificates response is unexpected');
|
|
69
|
+
}
|
|
70
|
+
async getSignedContractData(xsdMsgDefNamespace, certificateInstallationReq) {
|
|
71
|
+
const responseText = await this._makeAuthenticatedRequest(async () => {
|
|
72
|
+
const url = `${this._baseUrl}/v1/ccp/signedContractData`;
|
|
73
|
+
return fetch(url, {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: {
|
|
76
|
+
Accept: 'application/json',
|
|
77
|
+
Authorization: await this._getAuthorizationToken(),
|
|
78
|
+
'Content-Type': 'application/json',
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
certificateInstallationReq: certificateInstallationReq,
|
|
82
|
+
xsdMsgDefNamespace: xsdMsgDefNamespace,
|
|
83
|
+
}),
|
|
84
|
+
});
|
|
85
|
+
}, 'Get signed contract data response is unexpected');
|
|
86
|
+
const contractData = JSON.parse(responseText);
|
|
87
|
+
let certificateInstallationRes;
|
|
88
|
+
if (contractData.CCPResponse.emaidContent && contractData.CCPResponse.emaidContent.length > 0) {
|
|
89
|
+
for (const emaidContent of contractData.CCPResponse.emaidContent) {
|
|
90
|
+
if (emaidContent.messageDef && emaidContent.messageDef.certificateInstallationRes) {
|
|
91
|
+
certificateInstallationRes = emaidContent.messageDef.certificateInstallationRes;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!certificateInstallationRes) {
|
|
96
|
+
throw new Error('Failed to find CertificateInstallationRes in response.');
|
|
97
|
+
}
|
|
98
|
+
return certificateInstallationRes;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Retrieves all root certificates from Hubject.
|
|
102
|
+
* Refer to https://hubject.stoplight.io/docs/open-plugncharge/fdc9bdfdd4fb2-get-all-root-certificates
|
|
103
|
+
*
|
|
104
|
+
* @return {Promise<string[]>} Array of root certificate.
|
|
105
|
+
*/
|
|
106
|
+
async getRootCertificates() {
|
|
107
|
+
const responseText = await this._makeAuthenticatedRequest(async () => {
|
|
108
|
+
const url = `${this._baseUrl}/v1/root/rootCerts`;
|
|
109
|
+
return fetch(url, {
|
|
110
|
+
method: 'GET',
|
|
111
|
+
headers: {
|
|
112
|
+
Accept: 'application/json',
|
|
113
|
+
Authorization: await this._getAuthorizationToken(),
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}, 'Get root certificates response is unexpected');
|
|
117
|
+
const certificates = [];
|
|
118
|
+
const rootCertificatesResponse = JSON.parse(responseText);
|
|
119
|
+
for (const root of rootCertificatesResponse.RootCertificateCollection.rootCertificates) {
|
|
120
|
+
certificates.push(createPemBlock(root.caCertificate));
|
|
121
|
+
}
|
|
122
|
+
return certificates;
|
|
123
|
+
}
|
|
124
|
+
async _getAuthorizationToken(retryCount = 0) {
|
|
125
|
+
if (this._baseUrl === HUBJECT_DEFAULT_BASEURL &&
|
|
126
|
+
this._tokenUrl === HUBJECT_DEFAULT_TOKENURL &&
|
|
127
|
+
this._clientId === HUBJECT_DEFAULT_CLIENTID &&
|
|
128
|
+
this._clientSecret === HUBJECT_DEFAULT_CLIENTSECRET) {
|
|
129
|
+
this._logger.warn('Using default Hubject credentials. Please set them in the configuration if needed.');
|
|
130
|
+
return `Bearer ${HUBJECT_DEFAULT_AUTH_TOKEN}`;
|
|
131
|
+
}
|
|
132
|
+
const MAX_RETRIES = 10;
|
|
133
|
+
if (retryCount >= MAX_RETRIES) {
|
|
134
|
+
throw new Error(`Max retries (${MAX_RETRIES}) exceeded while waiting for auth token. ` +
|
|
135
|
+
`Another instance may be holding the lock or experiencing issues.`);
|
|
136
|
+
}
|
|
137
|
+
const cachedToken = await this._cache.get(Hubject.AUTH_TOKEN_CACHE_KEY, Hubject.AUTH_TOKEN_CACHE_NAMESPACE);
|
|
138
|
+
if (cachedToken) {
|
|
139
|
+
return cachedToken;
|
|
140
|
+
}
|
|
141
|
+
// Try to acquire lock
|
|
142
|
+
const lockKey = `${Hubject.AUTH_TOKEN_CACHE_KEY}_LOCK`;
|
|
143
|
+
const lockAcquired = await this._cache.setIfNotExist(lockKey, 'locked', Hubject.AUTH_TOKEN_CACHE_NAMESPACE, 30);
|
|
144
|
+
if (!lockAcquired) {
|
|
145
|
+
// Another instance is fetching, wait for it
|
|
146
|
+
const waitMs = 1000 + retryCount * 500; // 1s, 1.5s, 2s, 2.5s...
|
|
147
|
+
this._logger.debug(`Lock not acquired, waiting ${waitMs}ms (retry ${retryCount}/${MAX_RETRIES})`);
|
|
148
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
149
|
+
return this._getAuthorizationToken(retryCount + 1); // Recursive retry
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
// Double-check cache in case another instance populated it
|
|
153
|
+
// between initial check and lock acquisition
|
|
154
|
+
const tokenAfterLock = await this._cache.get(Hubject.AUTH_TOKEN_CACHE_KEY, Hubject.AUTH_TOKEN_CACHE_NAMESPACE);
|
|
155
|
+
if (tokenAfterLock) {
|
|
156
|
+
return tokenAfterLock;
|
|
157
|
+
}
|
|
158
|
+
// Fetch and cache token
|
|
159
|
+
const token = await this._fetchNewToken();
|
|
160
|
+
return token;
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
// Always release lock
|
|
164
|
+
const removed = await this._cache.remove(lockKey, Hubject.AUTH_TOKEN_CACHE_NAMESPACE);
|
|
165
|
+
if (!removed) {
|
|
166
|
+
this._logger.warn('Failed to remove lock, it may have already expired');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async _fetchNewToken() {
|
|
171
|
+
const body = new URLSearchParams({
|
|
172
|
+
grant_type: 'client_credentials',
|
|
173
|
+
client_id: this._clientId,
|
|
174
|
+
client_secret: this._clientSecret,
|
|
175
|
+
audience: this._baseUrl,
|
|
176
|
+
});
|
|
177
|
+
const response = await fetch(this._tokenUrl, {
|
|
178
|
+
method: HttpMethod.Post,
|
|
179
|
+
headers: {
|
|
180
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
181
|
+
},
|
|
182
|
+
body: body.toString(),
|
|
183
|
+
});
|
|
184
|
+
if (!response.ok) {
|
|
185
|
+
throw new Error(`Get token response is unexpected: ${response.status}: ${await response.text()}`);
|
|
186
|
+
}
|
|
187
|
+
const tokenResponse = await response.json();
|
|
188
|
+
if (!tokenResponse.access_token) {
|
|
189
|
+
this._logger.error('Error fetching token - no access_token in response');
|
|
190
|
+
throw new Error('Error while making call for hubject auth token');
|
|
191
|
+
}
|
|
192
|
+
const token = `Bearer ${tokenResponse.access_token}`;
|
|
193
|
+
// Cache with expiration buffer
|
|
194
|
+
const expiresIn = tokenResponse.expires_in || 3600;
|
|
195
|
+
await this._cache.set(Hubject.AUTH_TOKEN_CACHE_KEY, token, Hubject.AUTH_TOKEN_CACHE_NAMESPACE, expiresIn - 60);
|
|
196
|
+
return token;
|
|
197
|
+
}
|
|
198
|
+
async _makeAuthenticatedRequest(requestFn, errorPrefix) {
|
|
199
|
+
try {
|
|
200
|
+
let response = await requestFn();
|
|
201
|
+
// If 401/403, clear cache and retry once
|
|
202
|
+
if (response.status === HttpStatus.FORBIDDEN || response.status === HttpStatus.UNAUTHORIZED) {
|
|
203
|
+
this._logger.warn(`Received ${response.status}, clearing auth token cache and retrying...`);
|
|
204
|
+
const removed = await this._cache.remove(Hubject.AUTH_TOKEN_CACHE_KEY, Hubject.AUTH_TOKEN_CACHE_NAMESPACE);
|
|
205
|
+
this._logger.debug(`Cache ${Hubject.AUTH_TOKEN_CACHE_KEY} removed: ${removed}`);
|
|
206
|
+
response = await requestFn();
|
|
207
|
+
}
|
|
208
|
+
if (response.status !== HttpStatus.OK) {
|
|
209
|
+
const msg = `${errorPrefix}: ${response.status}: ${await response.text()}`;
|
|
210
|
+
this._logger.error(msg);
|
|
211
|
+
throw new Error(msg);
|
|
212
|
+
}
|
|
213
|
+
return await response.text();
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
this._logger.error('Request failed:', error);
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=hubject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubject.js","sourceRoot":"","sources":["../../../src/certificate/client/hubject.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface IV2GCertificateAuthorityClient {
|
|
2
|
+
getSignedCertificate(csrString: string): Promise<string>;
|
|
3
|
+
getCACertificates(): Promise<string>;
|
|
4
|
+
getSignedContractData(certificateInstallationReq: string, xsdMsgDefNamespace: string): Promise<string>;
|
|
5
|
+
getRootCertificates(): Promise<string[]>;
|
|
6
|
+
}
|
|
7
|
+
export interface IChargingStationCertificateAuthorityClient {
|
|
8
|
+
getRootCACertificate(): Promise<string>;
|
|
9
|
+
getCertificateChain(csrString: string): Promise<string>;
|
|
10
|
+
signCertificateByExternalCA(csrString: string): Promise<string>;
|
|
11
|
+
updateCertificateChainKeyMap(serverId: string, certificateChain: string, privateKey: string): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../../src/certificate/client/interface.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,sCAAsC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/certificate/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,sCAAsC;AAEtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ftpServer.js","sourceRoot":"","sources":["../../src/files/ftpServer.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,sCAAsC;AAItC,MAAM,OAAO,SAAS;IACpB,UAAU;QACR,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { BootstrapConfig, ConfigStore, SystemConfig } from '@citrineos/base';
|
|
2
|
+
import type { ILogObj } from 'tslog';
|
|
3
|
+
import { Logger } from 'tslog';
|
|
4
|
+
export declare class GcpCloudStorage implements ConfigStore {
|
|
5
|
+
protected readonly _logger: Logger<ILogObj>;
|
|
6
|
+
private storageClient;
|
|
7
|
+
private configBucketName;
|
|
8
|
+
private configFileName;
|
|
9
|
+
constructor(config: BootstrapConfig['fileAccess']['gcp'], configFileName: string, configDir?: string, logger?: Logger<ILogObj>);
|
|
10
|
+
/**
|
|
11
|
+
* Save a raw file buffer into GCS.
|
|
12
|
+
*
|
|
13
|
+
* @param fileName - Object key / blob name.
|
|
14
|
+
* @param content - File data.
|
|
15
|
+
* @param filePath - Optional bucket name, falls back to configBucketName.
|
|
16
|
+
*/
|
|
17
|
+
saveFile(fileName: string, content: Buffer, filePath?: string): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Read a file from GCS and return its contents as UTF-8 string.
|
|
20
|
+
*
|
|
21
|
+
* @param id - Object key / blob name.
|
|
22
|
+
* @param filePath - Optional bucket name, falls back to configBucketName.
|
|
23
|
+
*/
|
|
24
|
+
getFile(id: string, filePath?: string): Promise<string | undefined>;
|
|
25
|
+
/**
|
|
26
|
+
* Load JSON config from GCS and parse as SystemConfig.
|
|
27
|
+
*/
|
|
28
|
+
fetchConfig(): Promise<SystemConfig | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Serialize and save SystemConfig JSON to GCS.
|
|
31
|
+
*/
|
|
32
|
+
saveConfig(config: SystemConfig): Promise<void>;
|
|
33
|
+
private getBucket;
|
|
34
|
+
private createBucket;
|
|
35
|
+
/**
|
|
36
|
+
* Normalize "not found" checks across GCS error shapes.
|
|
37
|
+
*/
|
|
38
|
+
private isNotFoundError;
|
|
39
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { Bucket, Storage } from '@google-cloud/storage';
|
|
5
|
+
import { Logger } from 'tslog';
|
|
6
|
+
export class GcpCloudStorage {
|
|
7
|
+
_logger;
|
|
8
|
+
storageClient;
|
|
9
|
+
configBucketName;
|
|
10
|
+
configFileName;
|
|
11
|
+
constructor(config, configFileName, configDir, logger) {
|
|
12
|
+
if (!config) {
|
|
13
|
+
throw new Error('GCP Cloud Storage config missing.');
|
|
14
|
+
}
|
|
15
|
+
this.storageClient = new Storage({
|
|
16
|
+
projectId: config.projectId,
|
|
17
|
+
credentials: config.credentials,
|
|
18
|
+
});
|
|
19
|
+
this.configBucketName = configDir || 'default';
|
|
20
|
+
this.configFileName = configFileName;
|
|
21
|
+
this._logger = logger
|
|
22
|
+
? logger.getSubLogger({ name: this.constructor.name })
|
|
23
|
+
: new Logger({ name: this.constructor.name });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Save a raw file buffer into GCS.
|
|
27
|
+
*
|
|
28
|
+
* @param fileName - Object key / blob name.
|
|
29
|
+
* @param content - File data.
|
|
30
|
+
* @param filePath - Optional bucket name, falls back to configBucketName.
|
|
31
|
+
*/
|
|
32
|
+
async saveFile(fileName, content, filePath) {
|
|
33
|
+
const bucketName = filePath ? filePath : this.configBucketName;
|
|
34
|
+
const bucket = this.getBucket(bucketName);
|
|
35
|
+
const file = bucket.file(fileName);
|
|
36
|
+
try {
|
|
37
|
+
await file.save(content, {
|
|
38
|
+
contentType: 'application/octet-stream',
|
|
39
|
+
resumable: false,
|
|
40
|
+
});
|
|
41
|
+
return fileName;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (this.isNotFoundError(error)) {
|
|
45
|
+
this._logger.warn(`Bucket "${bucketName}" not found. Creating it...`);
|
|
46
|
+
await this.createBucket(bucketName);
|
|
47
|
+
this._logger.info(`Bucket "${bucketName}" created. Retrying file save...`);
|
|
48
|
+
return this.saveFile(fileName, content, filePath);
|
|
49
|
+
}
|
|
50
|
+
this._logger.error('Error saving file to GCP Cloud Storage:', error);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Read a file from GCS and return its contents as UTF-8 string.
|
|
56
|
+
*
|
|
57
|
+
* @param id - Object key / blob name.
|
|
58
|
+
* @param filePath - Optional bucket name, falls back to configBucketName.
|
|
59
|
+
*/
|
|
60
|
+
async getFile(id, filePath) {
|
|
61
|
+
const bucketName = filePath ? filePath : this.configBucketName;
|
|
62
|
+
const bucket = this.getBucket(bucketName);
|
|
63
|
+
const file = bucket.file(id);
|
|
64
|
+
try {
|
|
65
|
+
const [exists] = await file.exists();
|
|
66
|
+
if (!exists)
|
|
67
|
+
return;
|
|
68
|
+
const [contents] = await file.download();
|
|
69
|
+
return contents.toString('utf-8');
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (this.isNotFoundError(error)) {
|
|
73
|
+
// Treat missing file like S3's NoSuchKey
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this._logger.error('Error reading file from GCP Cloud Storage:', error);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Load JSON config from GCS and parse as SystemConfig.
|
|
82
|
+
*/
|
|
83
|
+
async fetchConfig() {
|
|
84
|
+
try {
|
|
85
|
+
const configString = await this.getFile(this.configFileName, this.configBucketName);
|
|
86
|
+
if (!configString)
|
|
87
|
+
return null;
|
|
88
|
+
return JSON.parse(configString);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (this.isNotFoundError(error)) {
|
|
92
|
+
this._logger.warn('Config not found in GCP Cloud Storage.');
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
this._logger.error('Error fetching config from GCP Cloud Storage:', error);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Serialize and save SystemConfig JSON to GCS.
|
|
101
|
+
*/
|
|
102
|
+
async saveConfig(config) {
|
|
103
|
+
await this.saveFile(this.configFileName, Buffer.from(JSON.stringify(config, null, 2)), this.configBucketName);
|
|
104
|
+
this._logger.info('Config saved to GCP Cloud Storage.');
|
|
105
|
+
}
|
|
106
|
+
getBucket(name) {
|
|
107
|
+
return this.storageClient.bucket(name);
|
|
108
|
+
}
|
|
109
|
+
async createBucket(bucketName) {
|
|
110
|
+
try {
|
|
111
|
+
await this.storageClient.createBucket(bucketName);
|
|
112
|
+
this._logger.info(`Bucket "${bucketName}" created successfully.`);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
this._logger.error(`Failed to create bucket "${bucketName}" in GCP Cloud Storage:`, error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Normalize "not found" checks across GCS error shapes.
|
|
121
|
+
*/
|
|
122
|
+
isNotFoundError(error) {
|
|
123
|
+
return (error?.code === 404 ||
|
|
124
|
+
(typeof error?.message === 'string' &&
|
|
125
|
+
(error.message.includes('No such object') ||
|
|
126
|
+
error.message.includes('Not Found') ||
|
|
127
|
+
error.message.includes('could not find'))));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=gcpCloudStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcpCloudStorage.js","sourceRoot":"","sources":["../../src/files/gcpCloudStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ConfigStore, SystemConfig } from '@citrineos/base';
|
|
2
|
+
import type { ILogObj } from 'tslog';
|
|
3
|
+
import { Logger } from 'tslog';
|
|
4
|
+
export declare class LocalStorage implements ConfigStore {
|
|
5
|
+
protected readonly _logger: Logger<ILogObj>;
|
|
6
|
+
private defaultFilePath;
|
|
7
|
+
private configFileName;
|
|
8
|
+
private configDir;
|
|
9
|
+
constructor(defaultFilePath: string, configFileName: string, configDir?: string, logger?: Logger<ILogObj>);
|
|
10
|
+
saveFile(fileName: string, content: Buffer, filePath?: string): Promise<string>;
|
|
11
|
+
getFile(id: string, filePath?: string): Promise<string | undefined>;
|
|
12
|
+
fetchConfig(): Promise<SystemConfig | null>;
|
|
13
|
+
saveConfig(config: SystemConfig): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { Logger } from 'tslog';
|
|
7
|
+
export class LocalStorage {
|
|
8
|
+
_logger;
|
|
9
|
+
defaultFilePath;
|
|
10
|
+
configFileName;
|
|
11
|
+
configDir;
|
|
12
|
+
constructor(defaultFilePath, configFileName, configDir, logger) {
|
|
13
|
+
this.defaultFilePath = defaultFilePath;
|
|
14
|
+
this.configFileName = configFileName;
|
|
15
|
+
this.configDir = configDir;
|
|
16
|
+
this._logger = logger
|
|
17
|
+
? logger.getSubLogger({ name: this.constructor.name })
|
|
18
|
+
: new Logger({ name: this.constructor.name });
|
|
19
|
+
}
|
|
20
|
+
async saveFile(fileName, content, filePath) {
|
|
21
|
+
const relativePath = path.join(filePath ? filePath : this.defaultFilePath, fileName);
|
|
22
|
+
const absoluteFilePath = path.join(process.cwd(), relativePath);
|
|
23
|
+
this._logger.debug(`Saving file to ${absoluteFilePath}`);
|
|
24
|
+
fs.writeFileSync(absoluteFilePath, content, 'utf-8');
|
|
25
|
+
return filePath ? relativePath : fileName;
|
|
26
|
+
}
|
|
27
|
+
async getFile(id, filePath) {
|
|
28
|
+
const absoluteFilePath = path.join(process.cwd(), filePath ? filePath : this.defaultFilePath, id);
|
|
29
|
+
this._logger.debug(`Getting file from ${absoluteFilePath}`);
|
|
30
|
+
if (!fs.existsSync(absoluteFilePath)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
return fs.readFileSync(absoluteFilePath, 'utf-8');
|
|
34
|
+
}
|
|
35
|
+
async fetchConfig() {
|
|
36
|
+
try {
|
|
37
|
+
const configString = await this.getFile(this.configFileName, this.configDir);
|
|
38
|
+
if (!configString)
|
|
39
|
+
return null;
|
|
40
|
+
return JSON.parse(configString);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
this._logger.error('Error fetching config from local storage:', error);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async saveConfig(config) {
|
|
48
|
+
try {
|
|
49
|
+
await this.saveFile(this.configFileName, Buffer.from(JSON.stringify(config, null, 2)), this.configDir);
|
|
50
|
+
this._logger.info('Config saved locally.');
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
this._logger.error('Error saving config to local storage:', error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=localStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localStorage.js","sourceRoot":"","sources":["../../src/files/localStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BootstrapConfig, ConfigStore, SystemConfig } from '@citrineos/base';
|
|
2
|
+
import type { ILogObj } from 'tslog';
|
|
3
|
+
import { Logger } from 'tslog';
|
|
4
|
+
export declare class S3Storage implements ConfigStore {
|
|
5
|
+
protected readonly _logger: Logger<ILogObj>;
|
|
6
|
+
private s3Client;
|
|
7
|
+
private defaultBucketName;
|
|
8
|
+
private configFileName;
|
|
9
|
+
private configBucketName;
|
|
10
|
+
constructor(config: BootstrapConfig['fileAccess']['s3'], configFileName: string, configDir?: string, logger?: Logger<ILogObj>);
|
|
11
|
+
saveFile(fileName: string, content: Buffer, filePath?: string): Promise<string>;
|
|
12
|
+
getFile(id: string, filePath?: string): Promise<string | undefined>;
|
|
13
|
+
fetchConfig(): Promise<SystemConfig | null>;
|
|
14
|
+
saveConfig(config: SystemConfig): Promise<void>;
|
|
15
|
+
private createBucket;
|
|
16
|
+
private static streamToString;
|
|
17
|
+
}
|