From: Jérôme Benoit Date: Tue, 18 Nov 2025 18:20:29 +0000 (+0100) Subject: refactor(ocpp2): integrate InMemoryAuthCache into auth service X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=f42be9ff42807507590fe8cf9a01c4db3f73a967;p=e-mobility-charging-stations-simulator.git refactor(ocpp2): integrate InMemoryAuthCache into auth service Update factory, interfaces, and service to support cache integration: - AuthComponentFactory.createAuthCache() now instantiates InMemoryAuthCache - Add evictions field to CacheStats interface - Add rateLimit field to AuthStats interface (blockedRequests, rateLimitedIdentifiers, totalChecks) - Make OCPPAuthServiceImpl.getStats() async to fetch cache stats from strategies - Update factory test to expect cache instance Enables monitoring of cache evictions and rate limiting events. --- diff --git a/src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts b/src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts index 04399a00..9e137032 100644 --- a/src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts +++ b/src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts @@ -9,6 +9,7 @@ import type { import type { AuthConfiguration } from '../types/AuthTypes.js' import { OCPPVersion } from '../../../../types/ocpp/OCPPVersion.js' +import { InMemoryAuthCache } from '../cache/InMemoryAuthCache.js' import { AuthConfigValidator } from '../utils/ConfigValidator.js' /** @@ -62,14 +63,20 @@ export class AuthComponentFactory { } /** - * Create authorization cache (delegated to service implementation) + * Create authorization cache with rate limiting * @param config - Authentication configuration - * @returns undefined (cache creation delegated to service) + * @returns AuthCache instance with configured TTL and rate limiting */ - static createAuthCache (config: AuthConfiguration): undefined { - // Cache creation is delegated to OCPPAuthServiceImpl - // This method exists for API completeness - return undefined + static createAuthCache (config: AuthConfiguration): AuthCache { + return new InMemoryAuthCache({ + defaultTtl: config.authorizationCacheLifetime ?? 3600, + maxEntries: config.maxCacheEntries ?? 1000, + rateLimit: { + enabled: true, + maxRequests: 10, // 10 requests per minute per identifier + windowMs: 60000, // 1 minute window + }, + }) } /** diff --git a/src/charging-station/ocpp/auth/interfaces/OCPPAuthService.ts b/src/charging-station/ocpp/auth/interfaces/OCPPAuthService.ts index 939ba93b..b18cd6a2 100644 --- a/src/charging-station/ocpp/auth/interfaces/OCPPAuthService.ts +++ b/src/charging-station/ocpp/auth/interfaces/OCPPAuthService.ts @@ -88,6 +88,18 @@ export interface AuthStats { /** Local authorization usage rate */ localUsageRate: number + /** Rate limiting statistics */ + rateLimit?: { + /** Number of requests blocked by rate limiting */ + blockedRequests: number + + /** Number of identifiers currently rate-limited */ + rateLimitedIdentifiers: number + + /** Total rate limit checks performed */ + totalChecks: number + } + /** Remote authorization success rate */ remoteSuccessRate: number @@ -161,6 +173,9 @@ export interface AuthStrategy { } export interface CacheStats { + /** Number of entries evicted due to capacity limits */ + evictions: number + /** Expired entries count */ expiredEntries: number diff --git a/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts b/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts index 92d0976c..3e29665d 100644 --- a/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts +++ b/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts @@ -335,8 +335,9 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { /** * Get authentication statistics + * @returns Authentication statistics including cache and rate limiting metrics */ - public getStats (): Promise { + public async getStats (): Promise { const avgResponseTime = this.metrics.totalRequests > 0 ? this.metrics.totalResponseTime / this.metrics.totalRequests @@ -353,16 +354,36 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { ? this.metrics.successfulAuth / this.metrics.remoteAuthCount : 0 - return Promise.resolve({ + // Get rate limiting stats from cache via remote strategy + let rateLimitStats: + | undefined + | { blockedRequests: number; rateLimitedIdentifiers: number; totalChecks: number } + const remoteStrategy = this.strategies.get('remote') + if (remoteStrategy?.getStats) { + const strategyStats = await remoteStrategy.getStats() + if ('cache' in strategyStats) { + const cacheStats = strategyStats.cache as { + rateLimit?: { + blockedRequests: number + rateLimitedIdentifiers: number + totalChecks: number + } + } + rateLimitStats = cacheStats.rateLimit + } + } + + return { avgResponseTime: Math.round(avgResponseTime * 100) / 100, cacheHitRate: Math.round(cacheHitRate * 10000) / 100, failedAuth: this.metrics.failedAuth, lastUpdated: this.metrics.lastReset, localUsageRate: Math.round(localUsageRate * 10000) / 100, + rateLimit: rateLimitStats, remoteSuccessRate: Math.round(remoteSuccessRate * 10000) / 100, successfulAuth: this.metrics.successfulAuth, totalRequests: this.metrics.totalRequests, - }) + } } /** diff --git a/tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts b/tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts index 4f7a4759..8e3b47b5 100644 --- a/tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts +++ b/tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts @@ -61,7 +61,7 @@ await describe('AuthComponentFactory', async () => { }) await describe('createAuthCache', async () => { - await it('should return undefined (delegated to service)', () => { + await it('should create InMemoryAuthCache instance', () => { const config: AuthConfiguration = { allowOfflineTxForUnknownId: false, authorizationCacheEnabled: true, @@ -74,7 +74,11 @@ await describe('AuthComponentFactory', async () => { const result = AuthComponentFactory.createAuthCache(config) - expect(result).toBeUndefined() + expect(result).toBeDefined() + expect(result).toHaveProperty('get') + expect(result).toHaveProperty('set') + expect(result).toHaveProperty('clear') + expect(result).toHaveProperty('getStats') }) })