@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.
Files changed (142) hide show
  1. package/dist/authorization/ApiAuthPlugin.d.ts +52 -0
  2. package/dist/authorization/ApiAuthPlugin.js +122 -0
  3. package/dist/authorization/ApiAuthPlugin.js.map +1 -0
  4. package/dist/authorization/OidcTokenProvider.d.ts +15 -0
  5. package/dist/authorization/OidcTokenProvider.js +47 -0
  6. package/dist/authorization/OidcTokenProvider.js.map +1 -0
  7. package/dist/authorization/index.d.ts +4 -0
  8. package/dist/authorization/index.js +8 -0
  9. package/dist/authorization/index.js.map +1 -0
  10. package/dist/authorization/provider/LocalByPassAuthProvider.d.ts +34 -0
  11. package/dist/authorization/provider/LocalByPassAuthProvider.js +62 -0
  12. package/dist/authorization/provider/LocalByPassAuthProvider.js.map +1 -0
  13. package/dist/authorization/provider/OIDCAuthProvider.d.ts +62 -0
  14. package/dist/authorization/provider/OIDCAuthProvider.js +173 -0
  15. package/dist/authorization/provider/OIDCAuthProvider.js.map +1 -0
  16. package/dist/authorization/rbac/RbacRulesLoader.d.ts +32 -0
  17. package/dist/authorization/rbac/RbacRulesLoader.js +105 -0
  18. package/dist/authorization/rbac/RbacRulesLoader.js.map +1 -0
  19. package/dist/authorization/rbac/UrlMatcher.d.ts +14 -0
  20. package/dist/authorization/rbac/UrlMatcher.js +44 -0
  21. package/dist/authorization/rbac/UrlMatcher.js.map +1 -0
  22. package/dist/authorizer/RealTimeAuthorizer.d.ts +28 -0
  23. package/dist/authorizer/RealTimeAuthorizer.js +152 -0
  24. package/dist/authorizer/RealTimeAuthorizer.js.map +1 -0
  25. package/dist/authorizer/index.d.ts +1 -0
  26. package/dist/authorizer/index.js +5 -0
  27. package/dist/authorizer/index.js.map +1 -0
  28. package/dist/cache/memory.d.ts +19 -0
  29. package/dist/cache/memory.js +147 -0
  30. package/dist/cache/memory.js.map +1 -0
  31. package/dist/cache/redis.d.ts +16 -0
  32. package/dist/cache/redis.js +120 -0
  33. package/dist/cache/redis.js.map +1 -0
  34. package/dist/certificate/CertificateAuthority.d.ts +38 -0
  35. package/dist/certificate/CertificateAuthority.js +233 -0
  36. package/dist/certificate/CertificateAuthority.js.map +1 -0
  37. package/dist/certificate/CertificateUtil.d.ts +60 -0
  38. package/dist/certificate/CertificateUtil.js +317 -0
  39. package/dist/certificate/CertificateUtil.js.map +1 -0
  40. package/dist/certificate/client/acme.d.ts +37 -0
  41. package/dist/certificate/client/acme.js +138 -0
  42. package/dist/certificate/client/acme.js.map +1 -0
  43. package/dist/certificate/client/hubject.d.ts +41 -0
  44. package/dist/certificate/client/hubject.js +221 -0
  45. package/dist/certificate/client/hubject.js.map +1 -0
  46. package/dist/certificate/client/interface.d.ts +12 -0
  47. package/dist/certificate/client/interface.js +5 -0
  48. package/dist/certificate/client/interface.js.map +1 -0
  49. package/dist/certificate/index.d.ts +2 -0
  50. package/dist/certificate/index.js +6 -0
  51. package/dist/certificate/index.js.map +1 -0
  52. package/dist/files/ftpServer.d.ts +4 -0
  53. package/dist/files/ftpServer.js +9 -0
  54. package/dist/files/ftpServer.js.map +1 -0
  55. package/dist/files/gcpCloudStorage.d.ts +39 -0
  56. package/dist/files/gcpCloudStorage.js +130 -0
  57. package/dist/files/gcpCloudStorage.js.map +1 -0
  58. package/dist/files/localStorage.d.ts +14 -0
  59. package/dist/files/localStorage.js +57 -0
  60. package/dist/files/localStorage.js.map +1 -0
  61. package/dist/files/s3Storage.d.ts +17 -0
  62. package/dist/files/s3Storage.js +118 -0
  63. package/dist/files/s3Storage.js.map +1 -0
  64. package/dist/index.d.ts +21 -0
  65. package/dist/index.js +25 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/networkconnection/WebsocketNetworkConnection.d.ts +135 -0
  68. package/dist/networkconnection/WebsocketNetworkConnection.js +474 -0
  69. package/dist/networkconnection/WebsocketNetworkConnection.js.map +1 -0
  70. package/dist/networkconnection/authenticator/Authenticator.d.ts +20 -0
  71. package/dist/networkconnection/authenticator/Authenticator.js +39 -0
  72. package/dist/networkconnection/authenticator/Authenticator.js.map +1 -0
  73. package/dist/networkconnection/authenticator/AuthenticatorFilter.d.ts +11 -0
  74. package/dist/networkconnection/authenticator/AuthenticatorFilter.js +30 -0
  75. package/dist/networkconnection/authenticator/AuthenticatorFilter.js.map +1 -0
  76. package/dist/networkconnection/authenticator/BasicAuthenticationFilter.d.ts +17 -0
  77. package/dist/networkconnection/authenticator/BasicAuthenticationFilter.js +51 -0
  78. package/dist/networkconnection/authenticator/BasicAuthenticationFilter.js.map +1 -0
  79. package/dist/networkconnection/authenticator/ConnectedStationFilter.d.ts +14 -0
  80. package/dist/networkconnection/authenticator/ConnectedStationFilter.js +25 -0
  81. package/dist/networkconnection/authenticator/ConnectedStationFilter.js.map +1 -0
  82. package/dist/networkconnection/authenticator/NetworkProfileFilter.d.ts +16 -0
  83. package/dist/networkconnection/authenticator/NetworkProfileFilter.js +84 -0
  84. package/dist/networkconnection/authenticator/NetworkProfileFilter.js.map +1 -0
  85. package/dist/networkconnection/authenticator/UnknownStationFilter.d.ts +16 -0
  86. package/dist/networkconnection/authenticator/UnknownStationFilter.js +25 -0
  87. package/dist/networkconnection/authenticator/UnknownStationFilter.js.map +1 -0
  88. package/dist/networkconnection/authenticator/errors/AuthenticationError.d.ts +6 -0
  89. package/dist/networkconnection/authenticator/errors/AuthenticationError.js +25 -0
  90. package/dist/networkconnection/authenticator/errors/AuthenticationError.js.map +1 -0
  91. package/dist/networkconnection/authenticator/errors/IUpgradeError.d.ts +9 -0
  92. package/dist/networkconnection/authenticator/errors/IUpgradeError.js +5 -0
  93. package/dist/networkconnection/authenticator/errors/IUpgradeError.js.map +1 -0
  94. package/dist/networkconnection/authenticator/errors/UnknownError.d.ts +6 -0
  95. package/dist/networkconnection/authenticator/errors/UnknownError.js +24 -0
  96. package/dist/networkconnection/authenticator/errors/UnknownError.js.map +1 -0
  97. package/dist/networkconnection/index.d.ts +5 -0
  98. package/dist/networkconnection/index.js +9 -0
  99. package/dist/networkconnection/index.js.map +1 -0
  100. package/dist/queue/index.d.ts +4 -0
  101. package/dist/queue/index.js +8 -0
  102. package/dist/queue/index.js.map +1 -0
  103. package/dist/queue/kafka/receiver.d.ts +35 -0
  104. package/dist/queue/kafka/receiver.js +179 -0
  105. package/dist/queue/kafka/receiver.js.map +1 -0
  106. package/dist/queue/kafka/sender.d.ts +53 -0
  107. package/dist/queue/kafka/sender.js +189 -0
  108. package/dist/queue/kafka/sender.js.map +1 -0
  109. package/dist/queue/rabbit-mq/receiver.d.ts +89 -0
  110. package/dist/queue/rabbit-mq/receiver.js +472 -0
  111. package/dist/queue/rabbit-mq/receiver.js.map +1 -0
  112. package/dist/queue/rabbit-mq/sender.d.ts +90 -0
  113. package/dist/queue/rabbit-mq/sender.js +251 -0
  114. package/dist/queue/rabbit-mq/sender.js.map +1 -0
  115. package/dist/security/SignedMeterValuesUtil.d.ts +44 -0
  116. package/dist/security/SignedMeterValuesUtil.js +135 -0
  117. package/dist/security/SignedMeterValuesUtil.js.map +1 -0
  118. package/dist/security/authentication.d.ts +2 -0
  119. package/dist/security/authentication.js +26 -0
  120. package/dist/security/authentication.js.map +1 -0
  121. package/dist/util/RequestOperations.d.ts +14 -0
  122. package/dist/util/RequestOperations.js +25 -0
  123. package/dist/util/RequestOperations.js.map +1 -0
  124. package/dist/util/StringOperations.d.ts +1 -0
  125. package/dist/util/StringOperations.js +8 -0
  126. package/dist/util/StringOperations.js.map +1 -0
  127. package/dist/util/emaidCheckDigitCalculator.d.ts +15 -0
  128. package/dist/util/emaidCheckDigitCalculator.js +179 -0
  129. package/dist/util/emaidCheckDigitCalculator.js.map +1 -0
  130. package/dist/util/idGenerator.d.ts +7 -0
  131. package/dist/util/idGenerator.js +10 -0
  132. package/dist/util/idGenerator.js.map +1 -0
  133. package/dist/util/parser.d.ts +31 -0
  134. package/dist/util/parser.js +60 -0
  135. package/dist/util/parser.js.map +1 -0
  136. package/dist/util/swagger.d.ts +5 -0
  137. package/dist/util/swagger.js +154 -0
  138. package/dist/util/swagger.js.map +1 -0
  139. package/dist/util/validator.d.ts +110 -0
  140. package/dist/util/validator.js +534 -0
  141. package/dist/util/validator.js.map +1 -0
  142. package/package.json +46 -0
@@ -0,0 +1,105 @@
1
+ // SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import * as fs from 'fs';
5
+ import { Logger } from 'tslog';
6
+ import { RbacRulesSchema } from '@citrineos/base';
7
+ import { UrlMatcher } from './UrlMatcher.js';
8
+ import path from 'path';
9
+ /**
10
+ * Class to load and validate RBAC rules
11
+ */
12
+ export class RbacRulesLoader {
13
+ _rules = {};
14
+ _logger;
15
+ /**
16
+ * Creates a new RBAC rules loader
17
+ *
18
+ * @param rulesFilePath Path to the JSON rules file
19
+ * @param logger Logger instance
20
+ */
21
+ constructor(rulesFilePath, logger) {
22
+ this._logger = logger.getSubLogger({ name: 'RbacRulesLoader' });
23
+ this.loadRules(rulesFilePath);
24
+ }
25
+ /**
26
+ * Load and validate rules from a JSON file
27
+ *
28
+ * @param filePath Path to the JSON rules file
29
+ */
30
+ loadRules(filePath) {
31
+ const absoluteFilePath = path.join(process.cwd(), filePath);
32
+ try {
33
+ if (!fs.existsSync(absoluteFilePath)) {
34
+ this._logger.warn(`Rules file not found at ${absoluteFilePath}, using empty rules`);
35
+ return;
36
+ }
37
+ const rulesContent = fs.readFileSync(absoluteFilePath, 'utf8');
38
+ const parsedRules = JSON.parse(rulesContent);
39
+ // Validate rules against the schema
40
+ const validationResult = RbacRulesSchema.safeParse(parsedRules);
41
+ if (!validationResult.success) {
42
+ this._logger.error('Invalid RBAC rules format:', validationResult.error);
43
+ throw new Error('Invalid RBAC rules format');
44
+ }
45
+ // Store the validated rules
46
+ this._rules = validationResult.data;
47
+ this._logger.info(`Successfully loaded RBAC rules from ${filePath}`);
48
+ this._logger.debug(`Loaded ${Object.keys(this._rules).length} tenants with rules`);
49
+ }
50
+ catch (error) {
51
+ if (error instanceof Error && error.message === 'Invalid RBAC rules format') {
52
+ throw error; // Re-throw validation errors
53
+ }
54
+ this._logger.error('Failed to load RBAC rules:', error);
55
+ throw new Error(`Failed to load RBAC rules: ${error instanceof Error ? error.message : String(error)}`);
56
+ }
57
+ }
58
+ /**
59
+ * Get the required roles for a specific tenant, URL, and HTTP method
60
+ *
61
+ * @param tenantId Tenant identifier
62
+ * @param url URL path
63
+ * @param method HTTP method
64
+ * @returns Array of required roles or null if no matching rule
65
+ */
66
+ getRequiredRoles(tenantId, url, method) {
67
+ // Normalize method to uppercase
68
+ method = method.toUpperCase();
69
+ const cleanUrl = this.normalizeUrl(url);
70
+ const tenantRules = this._rules[tenantId];
71
+ if (!tenantRules) {
72
+ return null;
73
+ }
74
+ // Try exact URL match first
75
+ const exactUrlRules = tenantRules[cleanUrl];
76
+ if (exactUrlRules) {
77
+ return exactUrlRules[method] || exactUrlRules['*'] || null;
78
+ }
79
+ // Pattern matching
80
+ for (const pattern in tenantRules) {
81
+ if (UrlMatcher.match(cleanUrl, pattern)) {
82
+ const methodRules = tenantRules[pattern];
83
+ const roles = methodRules[method] || methodRules['*'];
84
+ if (roles) {
85
+ return roles;
86
+ }
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+ normalizeUrl(url) {
92
+ // Remove query parameters and fragments
93
+ let cleanUrl = url.split('?')[0].split('#')[0];
94
+ // Remove trailing slash (optional, depends on your URL patterns)
95
+ if (cleanUrl.length > 1 && cleanUrl.endsWith('/')) {
96
+ cleanUrl = cleanUrl.slice(0, -1);
97
+ }
98
+ // Ensure it starts with /
99
+ if (!cleanUrl.startsWith('/')) {
100
+ cleanUrl = '/' + cleanUrl;
101
+ }
102
+ return cleanUrl;
103
+ }
104
+ }
105
+ //# sourceMappingURL=RbacRulesLoader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RbacRulesLoader.js","sourceRoot":"","sources":["../../../src/authorization/rbac/RbacRulesLoader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Utility for matching URLs against patterns
3
+ */
4
+ export declare class UrlMatcher {
5
+ /**
6
+ * Check if a URL matches a pattern
7
+ * Supports exact matches, wildcards, and path parameters
8
+ *
9
+ * @param url URL to check
10
+ * @param pattern Pattern to match against
11
+ * @returns True if URL matches pattern
12
+ */
13
+ static match(url: string, pattern: string): boolean;
14
+ }
@@ -0,0 +1,44 @@
1
+ // SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ /**
5
+ * Utility for matching URLs against patterns
6
+ */
7
+ export class UrlMatcher {
8
+ /**
9
+ * Check if a URL matches a pattern
10
+ * Supports exact matches, wildcards, and path parameters
11
+ *
12
+ * @param url URL to check
13
+ * @param pattern Pattern to match against
14
+ * @returns True if URL matches pattern
15
+ */
16
+ static match(url, pattern) {
17
+ // Handle exact matches
18
+ if (url === pattern) {
19
+ return true;
20
+ }
21
+ // Handle wildcard patterns
22
+ if (pattern.endsWith('/*')) {
23
+ const basePattern = pattern.slice(0, -1);
24
+ return url === basePattern || url.startsWith(basePattern);
25
+ }
26
+ // Handle path parameters (e.g., /users/:id)
27
+ const patternSegments = pattern.split('/').filter(Boolean);
28
+ const urlSegments = url.split('/').filter(Boolean);
29
+ if (patternSegments.length !== urlSegments.length) {
30
+ return false;
31
+ }
32
+ for (let i = 0; i < patternSegments.length; i++) {
33
+ // Skip path parameters (starting with :)
34
+ if (patternSegments[i].startsWith(':')) {
35
+ continue;
36
+ }
37
+ if (patternSegments[i] !== urlSegments[i]) {
38
+ return false;
39
+ }
40
+ }
41
+ return true;
42
+ }
43
+ }
44
+ //# sourceMappingURL=UrlMatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UrlMatcher.js","sourceRoot":"","sources":["../../../src/authorization/rbac/UrlMatcher.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,sCAAsC;AACtC;;GAEG;AACH,MAAM,OAAO,UAAU;IACrB;;;;;;;OAOG;IACI,MAAM,CAAC,KAAK,CAAC,GAAW,EAAE,OAAe;QAC9C,uBAAuB;QACvB,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,2BAA2B;QAC3B,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACzC,OAAO,GAAG,KAAK,WAAW,IAAI,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QAC5D,CAAC;QAED,4CAA4C;QAC5C,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3D,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,eAAe,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAChD,yCAAyC;YACzC,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvC,SAAS;YACX,CAAC;YACD,IAAI,eAAe,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1C,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ import { type AuthorizationStatusEnumType, type ConnectorDto, type EvseDto, type IAuthorizer, type IdTokenEnumType, type IMessageContext, type SystemConfig } from '@citrineos/base';
2
+ import type { Authorization, ILocationRepository } from '@citrineos/data';
3
+ import type { ILogObj } from 'tslog';
4
+ import { Logger } from 'tslog';
5
+ export interface RealTimeAuthorizationRequestBody {
6
+ tenantPartnerId: number;
7
+ idToken: string;
8
+ idTokenType: IdTokenEnumType;
9
+ locationId?: string;
10
+ stationId: string;
11
+ evseId: number;
12
+ connectorId: number;
13
+ }
14
+ export interface RealTimeAuthorizationResponse {
15
+ timestamp: string;
16
+ data: {
17
+ allowed: string;
18
+ reason?: string;
19
+ };
20
+ }
21
+ export declare class RealTimeAuthorizer implements IAuthorizer {
22
+ private _locationRepository;
23
+ private _config;
24
+ private readonly _logger;
25
+ private readonly _oidcTokenProvider?;
26
+ constructor(locationRepository: ILocationRepository, config: SystemConfig, logger?: Logger<ILogObj>);
27
+ authorize(authorization: Authorization, context: IMessageContext, evse?: EvseDto, connector?: ConnectorDto): Promise<AuthorizationStatusEnumType>;
28
+ }
@@ -0,0 +1,152 @@
1
+ // SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import { AuthorizationStatusEnum, AuthorizationWhitelistEnum, } from '@citrineos/base';
5
+ import { Logger } from 'tslog';
6
+ import { OidcTokenProvider } from '../authorization/index.js';
7
+ export class RealTimeAuthorizer {
8
+ _locationRepository;
9
+ _config;
10
+ _logger;
11
+ _oidcTokenProvider;
12
+ constructor(locationRepository, config, logger) {
13
+ this._locationRepository = locationRepository;
14
+ this._config = config;
15
+ this._logger = logger
16
+ ? logger.getSubLogger({ name: this.constructor.name })
17
+ : new Logger({ name: this.constructor.name });
18
+ if (config.oidcClient) {
19
+ this._oidcTokenProvider = new OidcTokenProvider(config.oidcClient, this._logger);
20
+ }
21
+ }
22
+ async authorize(authorization, context, evse, connector) {
23
+ if (!authorization.realTimeAuthUrl) {
24
+ this._logger.debug(`No Realtime Auth URL from authorization ${authorization.id}`);
25
+ return authorization.status;
26
+ }
27
+ else if (!authorization.realTimeAuth ||
28
+ authorization.realTimeAuth === AuthorizationWhitelistEnum.Allowed) {
29
+ this._logger.debug(`Realtime Auth whitelisted for authorization ${authorization.id}`);
30
+ return authorization.status;
31
+ }
32
+ else if (authorization.status !== AuthorizationStatusEnum.Accepted) {
33
+ this._logger.debug(`Skipping Realtime Auth for authorization ${authorization.id} with status ${authorization.status}`);
34
+ return authorization.status;
35
+ }
36
+ let evseId = undefined;
37
+ let connectorId = undefined;
38
+ let result = AuthorizationStatusEnum.Invalid;
39
+ try {
40
+ const chargingStation = await this._locationRepository.readChargingStationByStationId(context.tenantId, context.stationId);
41
+ // Determine evseId and connectorId
42
+ // Priority: provided evse and connector > provided evse with single connector > station with single evse and single connector
43
+ if (evse && connector) {
44
+ evseId = evse.id;
45
+ connectorId = connector.id;
46
+ }
47
+ else if (evse && !connector && evse.connectors?.length === 1) {
48
+ evseId = evse.id;
49
+ connectorId = evse.connectors[0].id;
50
+ }
51
+ else if (chargingStation &&
52
+ chargingStation.evses &&
53
+ chargingStation.evses.length === 1 &&
54
+ chargingStation.evses[0].connectors?.length === 1) {
55
+ evseId = chargingStation.evses[0].id;
56
+ connectorId = chargingStation.evses[0].connectors[0].id;
57
+ }
58
+ if (evseId === undefined || connectorId === undefined) {
59
+ this._logger.error(`Cannot determine evseId and connectorId for Realtime Auth of authorization ${authorization.id}`);
60
+ return authorization.status;
61
+ }
62
+ else if (authorization.realTimeAuthLastAttempt) {
63
+ const realTimeAuthLastAttempt = authorization.realTimeAuthLastAttempt;
64
+ // Check if last attempt was at the same station and connector within the timeout period
65
+ if (context.stationId === realTimeAuthLastAttempt.stationId &&
66
+ connectorId === realTimeAuthLastAttempt.connectorId) {
67
+ const lastAttempt = new Date(realTimeAuthLastAttempt.timestamp);
68
+ const timeout = authorization.realTimeAuthTimeout ?? this._config.realTimeAuthDefaultTimeoutSeconds;
69
+ const now = new Date();
70
+ const diffInSeconds = (now.getTime() - lastAttempt.getTime()) / 1000;
71
+ if (diffInSeconds < timeout) {
72
+ this._logger.debug(`Skipping Realtime Auth for authorization ${authorization.id} due to timeout (${diffInSeconds}s < ${timeout}s)`);
73
+ return realTimeAuthLastAttempt.result;
74
+ }
75
+ }
76
+ }
77
+ const payload = {
78
+ tenantPartnerId: authorization.tenantPartnerId, // Required if authorization has RealTimeAuth
79
+ idToken: authorization.idToken,
80
+ idTokenType: authorization.idTokenType,
81
+ locationId: chargingStation.locationId.toString(),
82
+ stationId: context.stationId,
83
+ evseId: evseId,
84
+ connectorId: connectorId,
85
+ };
86
+ this._logger.debug(`Sending Realtime Auth request for authorization ${authorization.id} to url: ${authorization.realTimeAuthUrl}`);
87
+ const headers = {
88
+ 'Content-Type': 'application/json',
89
+ };
90
+ if (this._oidcTokenProvider) {
91
+ try {
92
+ const token = await this._oidcTokenProvider.getToken();
93
+ headers['Authorization'] = `Bearer ${token}`;
94
+ }
95
+ catch (error) {
96
+ this._logger.error('Failed to get OIDC token:', error);
97
+ return AuthorizationStatusEnum.Invalid;
98
+ }
99
+ }
100
+ const response = await fetch(authorization.realTimeAuthUrl, {
101
+ method: 'POST',
102
+ headers,
103
+ body: JSON.stringify(payload),
104
+ });
105
+ const responseJson = await response.json();
106
+ const realTimeAuth = responseJson;
107
+ this._logger.debug(`Real time auth response: ${realTimeAuth.data.allowed}`);
108
+ if (realTimeAuth) {
109
+ switch (realTimeAuth.data.allowed) {
110
+ case 'ALLOWED':
111
+ result = AuthorizationStatusEnum.Accepted;
112
+ break;
113
+ case 'BLOCKED':
114
+ result = AuthorizationStatusEnum.Blocked;
115
+ break;
116
+ case 'EXPIRED':
117
+ result = AuthorizationStatusEnum.Expired;
118
+ break;
119
+ case 'NO_CREDIT':
120
+ result = AuthorizationStatusEnum.NoCredit;
121
+ break;
122
+ case 'NOT_ALLOWED':
123
+ result = AuthorizationStatusEnum.NotAtThisLocation;
124
+ break;
125
+ default:
126
+ result = AuthorizationStatusEnum.Unknown;
127
+ }
128
+ }
129
+ else {
130
+ result = AuthorizationStatusEnum.Unknown;
131
+ }
132
+ }
133
+ catch (error) {
134
+ this._logger.error(`Real-Time Auth failed: ${error}`);
135
+ if (authorization.realTimeAuth === 'AllowedOffline') {
136
+ result = AuthorizationStatusEnum.Accepted;
137
+ }
138
+ }
139
+ authorization.realTimeAuthLastAttempt = {
140
+ timestamp: new Date().toISOString(),
141
+ result,
142
+ stationId: context.stationId,
143
+ evseId: evseId,
144
+ connectorId: connectorId,
145
+ };
146
+ authorization.save().catch((error) => {
147
+ this._logger.error(`Failed to save realTimeAuthLastAttempt for authorization ${authorization.id}: ${error}`);
148
+ });
149
+ return result;
150
+ }
151
+ }
152
+ //# sourceMappingURL=RealTimeAuthorizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RealTimeAuthorizer.js","sourceRoot":"","sources":["../../src/authorizer/RealTimeAuthorizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export * from './RealTimeAuthorizer.js';
@@ -0,0 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ export * from './RealTimeAuthorizer.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/authorizer/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,sCAAsC;AACtC,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { ICache } from '@citrineos/base';
2
+ import type { ClassConstructor } from 'class-transformer';
3
+ /**
4
+ * Implementation of cache interface with memory storage
5
+ */
6
+ export declare class MemoryCache implements ICache {
7
+ private _cache;
8
+ private _keySubscriptionMap;
9
+ private _keySubscriptionPromiseMap;
10
+ private _timeoutMap;
11
+ constructor();
12
+ exists(key: string, namespace?: string): Promise<boolean>;
13
+ remove(key: string, namespace?: string): Promise<boolean>;
14
+ onChange<T>(key: string, waitSeconds: number, namespace?: string, classConstructor?: () => ClassConstructor<T>): Promise<T | null>;
15
+ get<T>(key: string, namespace?: string, classConstructor?: () => ClassConstructor<T>): Promise<T | null>;
16
+ set(key: string, value: string, namespace?: string, expireSeconds?: number): Promise<boolean>;
17
+ setIfNotExist(key: string, value: string, namespace?: string, expireSeconds?: number): Promise<boolean>;
18
+ private resolveOnChange;
19
+ }
@@ -0,0 +1,147 @@
1
+ // SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import { plainToInstance } from 'class-transformer';
5
+ /**
6
+ * Implementation of cache interface with memory storage
7
+ */
8
+ export class MemoryCache {
9
+ _cache;
10
+ _keySubscriptionMap;
11
+ _keySubscriptionPromiseMap;
12
+ _timeoutMap;
13
+ constructor() {
14
+ const keySubscriptionMap = new Map();
15
+ const subscriptionHandler = {
16
+ // Returns value on keySubscriptions when Map.set(key, value) is called
17
+ set(target, property, value) {
18
+ const setOutcome = Reflect.set(target, property, value);
19
+ if (typeof property === 'string' && keySubscriptionMap.has(property) && setOutcome) {
20
+ (keySubscriptionMap?.get(property))(value);
21
+ }
22
+ return setOutcome;
23
+ },
24
+ // Returns null on keySubscriptions when Map.delete(key) is called
25
+ deleteProperty(target, property) {
26
+ const deleteOutcome = Reflect.deleteProperty(target, property);
27
+ if (typeof property === 'string' && keySubscriptionMap.has(property) && deleteOutcome) {
28
+ (keySubscriptionMap?.get(property))(null);
29
+ }
30
+ return deleteOutcome;
31
+ },
32
+ // Here to support Map.get and Map.has, does not alter behavior
33
+ get(target, property) {
34
+ const value = Reflect.get(target, property);
35
+ if (typeof value === 'function') {
36
+ // Return a bound version of the method to the original target
37
+ return value.bind(target);
38
+ }
39
+ return value;
40
+ },
41
+ };
42
+ this._cache = new Proxy(new Map(), subscriptionHandler);
43
+ this._keySubscriptionMap = keySubscriptionMap;
44
+ this._keySubscriptionPromiseMap = new Map();
45
+ this._timeoutMap = new Map();
46
+ }
47
+ exists(key, namespace) {
48
+ namespace = namespace || 'default';
49
+ const namespaceKey = `${namespace}:${key}`;
50
+ return Promise.resolve(this._cache.has(namespaceKey));
51
+ }
52
+ async remove(key, namespace) {
53
+ namespace = namespace || 'default';
54
+ const namespaceKey = `${namespace}:${key}`;
55
+ return this._cache.delete(namespaceKey);
56
+ }
57
+ onChange(key, waitSeconds, namespace, classConstructor) {
58
+ namespace = namespace || 'default';
59
+ const namespaceKey = `${namespace}:${key}`;
60
+ // Either get existing promise awaiting change on this key or create a new one and store it.
61
+ // This way, any number of threads can wait for the same key at the same time.
62
+ // Type must include 'undefined' due to Map.get(key)'s return type, however in no case can it actually be undefined.
63
+ const onChangeValuePromise = this._keySubscriptionPromiseMap.has(namespaceKey)
64
+ ? this._keySubscriptionPromiseMap.get(namespaceKey)
65
+ : this._keySubscriptionPromiseMap
66
+ .set(namespaceKey, new Promise((resolve) => {
67
+ this._keySubscriptionMap.set(namespaceKey, (value) => {
68
+ resolve(value);
69
+ this._keySubscriptionMap.delete(namespaceKey);
70
+ this._keySubscriptionPromiseMap.delete(namespaceKey);
71
+ });
72
+ }))
73
+ .get(namespaceKey);
74
+ return Promise.race([
75
+ onChangeValuePromise?.then((value) => {
76
+ if (typeof value === 'string') {
77
+ if (classConstructor) {
78
+ return plainToInstance(classConstructor(), JSON.parse(value));
79
+ }
80
+ else {
81
+ return value;
82
+ }
83
+ }
84
+ else {
85
+ return value;
86
+ }
87
+ }),
88
+ new Promise((resolve) => {
89
+ setTimeout(() => {
90
+ resolve(this.get(key, namespace, classConstructor));
91
+ }, waitSeconds * 1000);
92
+ }),
93
+ ]);
94
+ }
95
+ async get(key, namespace, classConstructor) {
96
+ namespace = namespace || 'default';
97
+ const namespaceKey = `${namespace}:${key}`;
98
+ const result = this._cache.get(namespaceKey);
99
+ if (result) {
100
+ if (classConstructor) {
101
+ return plainToInstance(classConstructor(), JSON.parse(result));
102
+ }
103
+ return result;
104
+ }
105
+ return null;
106
+ }
107
+ async set(key, value, namespace, expireSeconds) {
108
+ namespace = namespace || 'default';
109
+ const namespaceKey = `${namespace}:${key}`;
110
+ this._cache.set(namespaceKey, value);
111
+ if (this._timeoutMap.has(namespaceKey)) {
112
+ clearTimeout(this._timeoutMap.get(namespaceKey));
113
+ }
114
+ if (expireSeconds) {
115
+ this._timeoutMap.set(namespaceKey, setTimeout(() => {
116
+ this._cache.delete(namespaceKey);
117
+ }, expireSeconds * 1000));
118
+ }
119
+ this.resolveOnChange(namespaceKey, value);
120
+ return true;
121
+ }
122
+ async setIfNotExist(key, value, namespace, expireSeconds) {
123
+ namespace = namespace || 'default';
124
+ const namespaceKey = `${namespace}:${key}`;
125
+ if (this._cache.has(namespaceKey)) {
126
+ return false;
127
+ }
128
+ this._cache.set(namespaceKey, value);
129
+ if (this._timeoutMap.has(namespaceKey)) {
130
+ clearTimeout(this._timeoutMap.get(namespaceKey));
131
+ }
132
+ if (expireSeconds) {
133
+ this._timeoutMap.set(namespaceKey, setTimeout(() => {
134
+ this._cache.delete(namespaceKey);
135
+ }, expireSeconds * 1000));
136
+ }
137
+ this.resolveOnChange(namespaceKey, value);
138
+ return true;
139
+ }
140
+ resolveOnChange(namespaceKey, value) {
141
+ const resolveOnChangeCallback = this._keySubscriptionMap.get(namespaceKey);
142
+ if (resolveOnChangeCallback) {
143
+ resolveOnChangeCallback(value);
144
+ }
145
+ }
146
+ }
147
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/cache/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { ICache } from '@citrineos/base';
2
+ import type { ClassConstructor } from 'class-transformer';
3
+ import type { RedisClientOptions } from 'redis';
4
+ /**
5
+ * Implementation of cache interface with redis storage
6
+ */
7
+ export declare class RedisCache implements ICache {
8
+ private _client;
9
+ constructor(clientOptions?: RedisClientOptions);
10
+ exists(key: string, namespace?: string): Promise<boolean>;
11
+ remove(key: string, namespace?: string | undefined): Promise<boolean>;
12
+ onChange<T>(key: string, waitSeconds: number, namespace?: string | undefined, classConstructor?: (() => ClassConstructor<T>) | undefined): Promise<T | null>;
13
+ get<T>(key: string, namespace?: string, classConstructor?: () => ClassConstructor<T>): Promise<T | null>;
14
+ set(key: string, value: string, namespace?: string, expireSeconds?: number): Promise<boolean>;
15
+ setIfNotExist(key: string, value: string, namespace?: string, expireSeconds?: number): Promise<boolean>;
16
+ }