UNPKG

@restorecommerce/acs-client

Version:

Access Control Service Client

151 lines 5.32 kB
import { cfg } from '../config.js'; import logger from '../logger.js'; import * as crypto from 'crypto'; import { createClient } from 'redis'; let attempted = false; let redisInstance; let ttl; let cacheEnabled = true; let redisSubjectInstance; /** * Initialize ACS Cache */ export const initializeCache = async () => { if (attempted || !cacheEnabled) { return; } attempted = true; try { const redisConfig = cfg.get('authorization:cache'); const redisSubConfig = cfg.get('redis'); if (redisConfig) { redisConfig.database = cfg.get('authorization:cache:db-index'); redisInstance = createClient(redisConfig); redisInstance.on('error', (err) => logger?.error('Redis Client Error in ACS cache', { code: err.code, message: err.message, stack: err.stack })); await redisInstance.connect(); ttl = cfg.get('authorization:cache:ttl'); } if (redisSubConfig) { // init redis subject instance redisSubConfig.database = redisSubConfig['db-indexes']['db-subject']; redisSubjectInstance = createClient(redisSubConfig); redisSubjectInstance.on('error', (err) => logger?.error('Redis Client Error in ACS cache', { code: err.code, message: err.message, stack: err.stack })); await redisSubjectInstance.connect(); } } catch (e) { attempted = false; } }; /** * Find the object in cache. If not found, compute it using the filler function * * @param keyData The data to base the cache key on * @param filler The function to execute if key is not found in cache * @param prefix The prefix to apply to the object key in the cache */ export const getOrFill = async (keyData, filler, useCache, prefix) => { if (!redisInstance || !cacheEnabled) { return filler(keyData); } const inputHash = crypto.createHash('md5').update(JSON.stringify(keyData)).digest().toString('base64'); let redisKey = `${inputHash}`; if (prefix) { redisKey = `${prefix}:` + redisKey; } const redisKeyResponse = await redisInstance.get(redisKey); if (redisKeyResponse && useCache) { const response = JSON.parse(redisKeyResponse?.toString()); const evaluation_cacheable = response?.evaluation_cacheable || response?.policy_sets?.some(policy_set => policy_set?.policies?.some(policy => policy?.evaluation_cacheable !== false && policy.rules?.some(rule => rule?.evaluation_cacheable))); if (evaluation_cacheable) { logger?.debug('Found key in cache: ' + redisKey); return response; } } if (!useCache) { // when useCache is false, dont store in cache return await filler(keyData); } const acsResponse = await filler(keyData); logger?.debug(`Filling cache key: ${redisKey}`, acsResponse); if (acsResponse) { if (ttl) { await redisInstance.setEx(redisKey, ttl, JSON.stringify(acsResponse)); } else { await redisInstance.set(redisKey, JSON.stringify(acsResponse)); } } return acsResponse; }; /** * Find the object in cache. * * @param key The key to be looked up in cache * @param filler The function to execute if key is not found in cache * @param prefix The prefix to apply to the object key in the cache */ export const get = async (key) => { if (!redisSubjectInstance) { logger?.warn('No Redis Subject Instance!'); return; } const redisResponse = await redisSubjectInstance.get(key); if (!redisResponse) { logger?.info('Key does not exist', { key }); return; } if (redisResponse) { logger?.debug('Found key in cache: ' + key); return JSON.parse(redisResponse?.toString()); } }; /** * Flush the ACS cache * * @param prefix An optional prefix to flush instead of entire cache */ export const flushCache = async (prefix) => { if (!redisInstance || !cacheEnabled) { logger?.info('Redis client not initialized in acs-client'); return; } if (prefix != undefined) { const flushPattern = `acs:${prefix}:*`; logger?.debug(`Flushing cache with pattern ${flushPattern}`); let scanIterator; try { scanIterator = redisInstance.scanIterator({ MATCH: flushPattern, COUNT: 100 }); for await (const key of scanIterator) { await redisInstance.del(key); } logger?.debug(`Successfully flushed cache pattern ${flushPattern}`); return; } catch (err) { logger?.error('Error flushing ACS cache', { code: err.code, message: err.message, stack: err.stack }); return; } } logger?.debug('Flushing ACS cache'); const reply = await redisInstance.flushDb(); if (reply) { logger?.debug('Flushed ACS cache'); } }; /** * Enable / Disable ACS Caching * * @param enabled Whether to enable or disable the cache */ export const setCacheStatus = (enabled) => { cacheEnabled = enabled; if (enabled) { logger?.debug('ACS Cache Enabled'); initializeCache(); } else { logger?.debug('ACS Cache Disabled'); } }; //# sourceMappingURL=cache.js.map