xypriss-security
Version:
XyPriss Security is an advanced JavaScript security library designed for enterprise applications. It provides military-grade encryption, secure data structures, quantum-resistant cryptography, and comprehensive security utilities for modern web applicatio
810 lines (807 loc) • 28.3 kB
JavaScript
import { SecureCacheAdapter } from '../../src/cache/SecureCacheAdapter.js';
import 'xypriss-security';
/**
* SecureCacheClient - Secure caching solution
*
* A high-performance, secure cache client that supports multiple backend strategies
* including memory-only, Redis-only, and hybrid (memory + Redis) configurations.
* Features military-grade AES-256-GCM encryption, intelligent compression, and
* comprehensive monitoring capabilities.
*
* ## Features
* - **Multi-Strategy Support**: Memory, Redis, or Hybrid caching
* - **Military-Grade Security**: AES-256-GCM encryption with key rotation
* - **High Availability**: Redis Cluster and Sentinel support
* - **Performance Optimized**: Intelligent compression and hot data promotion
* - **Production Ready**: Comprehensive monitoring and health checks
* - **Type Safe**: Full TypeScript support with detailed interfaces
*
* ## Supported Cache Strategies
* - `memory`: Ultra-fast in-memory caching with LRU eviction
* - `redis`: Distributed Redis caching with clustering support
* - `hybrid`: Memory-first with Redis backup for optimal performance
*
* ## Security Features
* - AES-256-GCM encryption for all cached data
* - Automatic key rotation and tamper detection
* - Secure serialization with integrity verification
* - Access pattern monitoring and anomaly detection
*
* @example Basic Redis Configuration
* ```typescript
* import { SecureCacheClient } from "xypriss-security";
*
* const cache = new SecureCacheClient({
* strategy: "redis",
* redis: {
* host: "localhost",
* port: 6379,
* password: "your-secure-password"
* }
* });
*
* await cache.connect();
* await cache.set("user:123", { name: "John", role: "admin" }, { ttl: 3600 });
* const user = await cache.get("user:123");
* ```
*
* @example Hybrid Strategy with Encryption
* ```typescript
* const cache = new SecureCacheClient({
* strategy: "hybrid",
* memory: {
* maxSize: 100, // 100MB
* maxEntries: 10000
* },
* redis: {
* host: "redis-cluster.example.com",
* port: 6379,
* cluster: {
* enabled: true,
* nodes: [
* { host: "redis-1", port: 6379 },
* { host: "redis-2", port: 6379 }
* ]
* }
* },
* security: {
* encryption: true,
* keyRotation: true
* }
* });
* ```
*
* @example Advanced Usage with Tags and Monitoring
* ```typescript
* // Store data with tags for bulk invalidation
* await cache.set("product:123", productData, {
* ttl: 1800,
* tags: ["products", "category:electronics"]
* });
*
* // Batch operations for better performance
* await cache.mset({
* "user:1": userData1,
* "user:2": userData2
* }, { ttl: 3600 });
*
* // Invalidate by tags
* await cache.invalidateByTags(["products"]);
*
* // Monitor cache health
* const health = cache.getHealth();
* if (health.status !== "healthy") {
* console.warn("Cache issues:", health.details);
* }
*
* // Get performance statistics
* const stats = await cache.getStats();
* console.log(`Hit rate: ${stats.memory.hitRate * 100}%`);
* ```
*
* @since 4.2.3
* @version 4.2.3
* @author NEHONIX
* @see {@link ICacheAdapter} for the complete interface definition
* @see {@link https://lab.nehonix.space/nehonix_viewer/_doc/Nehonix%20XyPrissSecurity} for detailed documentation
*/
class SecureCacheClient {
/**
* Creates a new SecureCacheClient instance
*
* @param config - Cache configuration object
* @param config.strategy - Cache strategy: "memory", "redis", or "hybrid"
* @param config.redis - Redis configuration (required for "redis" and "hybrid" strategies)
* @param config.redis.host - Redis server hostname
* @param config.redis.port - Redis server port
* @param config.redis.password - Redis authentication password
* @param config.redis.cluster - Redis cluster configuration
* @param config.memory - Memory cache configuration (for "memory" and "hybrid" strategies)
* @param config.memory.maxSize - Maximum memory cache size in MB
* @param config.memory.maxEntries - Maximum number of cache entries
* @param config.security - Security configuration
* @param config.security.encryption - Enable AES-256-GCM encryption
* @param config.security.keyRotation - Enable automatic key rotation
* @param config.monitoring - Monitoring and health check configuration
*
* @example
* ```typescript
* const cache = new SecureCacheClient({
* strategy: "hybrid",
* redis: { host: "localhost", port: 6379 },
* memory: { maxSize: 100, maxEntries: 10000 },
* security: { encryption: true }
* });
* ```
*/
constructor(config) {
this.adapter = null;
this.config = config;
// Adapter will be created lazily on first use
this.adapter = new SecureCacheAdapter(this.config);
}
/**
* Ensures the cache adapter is initialized
* @private
* @returns Promise resolving to the initialized adapter
*/
async ensureAdapter() {
if (!this.adapter) {
// Use dynamic import for production compatibility
const { SecureCacheAdapter } = await import('../../src/cache/index.js');
this.adapter = new SecureCacheAdapter(this.config);
throw new Error("SecureCacheAdapter integration not available in this context");
}
return this.adapter; // Non-null assertion since we just created it
}
/**
* Retrieves a value from the cache
*
* @param key - The cache key to retrieve
* @returns Promise resolving to the cached value, or null if not found
*
* @example
* ```typescript
* const user = await cache.read<User>("user:123");
* if (user) {
* console.log("Found user:", user.name);
* }
* ```
*/
async read(key) {
const adapter = await this.ensureAdapter();
return adapter.get(key);
}
/**
* Retrieves a value from the cache (alias for get method)
*
* @param key - The cache key to retrieve
* @returns Promise resolving to the cached value, or null if not found
*
* @example
* ```typescript
* const user = await cache.read<User>("user:123");
* if (user) {
* console.log("Found user:", user.name);
* }
* ```
*/
/**
* Stores a value in the cache with optional TTL and tags
*
* @param key - The cache key to store the value under
* @param value - The value to cache (will be automatically serialized)
* @param options - Optional caching options
* @param options.ttl - Time to live in seconds (default: configured TTL)
* @param options.tags - Array of tags for bulk invalidation
* @returns Promise resolving to true if successful, false otherwise
*
* @example
* ```typescript
* // Basic usage
* await cache.write("user:123", { name: "John", role: "admin" });
*
* // With TTL (1 hour)
* await cache.write("session:abc", sessionData, { ttl: 3600 });
*
* // With tags for bulk invalidation
* await cache.write("product:456", productData, {
* ttl: 1800,
* tags: ["products", "category:electronics"]
* });
* ```
*/
async write(key, value, options) {
const adapter = await this.ensureAdapter();
return adapter.set(key, value, options);
}
/**
* Stores a value in the cache (alias for set method)
*
* @param key - The cache key to store the value under
* @param value - The value to cache (will be automatically serialized)
* @param options - Optional caching options
* @param options.ttl - Time to live in seconds (default: configured TTL)
* @param options.tags - Array of tags for bulk invalidation
* @returns Promise resolving to true if successful, false otherwise
*
* @example
* ```typescript
* // Basic usage
* await cache.write("user:123", { name: "John", role: "admin" });
*
* // With TTL (1 hour)
* await cache.write("session:abc", sessionData, { ttl: 3600 });
*
* // With tags for bulk invalidation
* await cache.write("product:456", productData, {
* ttl: 1800,
* tags: ["products", "category:electronics"]
* });
* ```
*/
/**
* Store encrypted session data with expiration (SETEX for sessions)
*
* This method provides secure session storage with automatic encryption,
* expiration, and session-specific security features. Perfect for storing
* user sessions, authentication tokens, and other sensitive temporary data.
*
* @param sessionKey - The session key (will be prefixed with 'session:')
* @param sessionData - The session data to encrypt and store
* @param ttlSeconds - Time to live in seconds (Redis SETEX style)
* @param options - Optional session-specific options
* @param options.userId - User ID for session tracking and validation
* @param options.ipAddress - IP address for session validation
* @param options.userAgent - User agent for session validation
* @param options.encryptionKey - Custom encryption key (generates secure key if not provided)
* @param options.tags - Array of tags for bulk invalidation
* @returns Promise resolving to true if successful, false otherwise
*
* @example Basic Session Storage
* ```typescript
* interface UserSession {
* userId: string;
* username: string;
* roles: string[];
* loginTime: number;
* lastActivity: number;
* }
*
* const sessionData: UserSession = {
* userId: "user123",
* username: "john_doe",
* roles: ["user", "admin"],
* loginTime: Date.now(),
* lastActivity: Date.now()
* };
*
* // Store encrypted session for 1 hour (3600 seconds)
* const success = await cache.setex("session_abc123def456", sessionData, 3600);
* ```
*
* @example Advanced Session Storage with Validation
* ```typescript
* // Store session with additional security context
* const success = await cache.setex("session_xyz789", sessionData, 7200, {
* userId: "user123",
* ipAddress: "192.168.1.100",
* userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
* tags: ["sessions", "user123", "admin-session"]
* });
*
* if (success) {
* console.log("Session stored securely with encryption");
* }
* ```
*
* @example Custom Encryption Key
* ```typescript
* import { XyPrissSecurity } from "xypriss-security";
*
* // Generate a custom encryption key for this session
* const customKey = XyPrissSecurity.generateSecureToken({ length: 32 });
*
* const success = await cache.setex("session_custom", sessionData, 1800, {
* encryptionKey: customKey,
* userId: "user456",
* tags: ["premium-sessions"]
* });
* ```
*
* @since 4.2.3
* @see {@link read} for retrieving session data
* @see {@link delete} for removing sessions
* @see {@link invalidateByTags} for bulk session invalidation
*/
async setex(sessionKey, sessionData, ttlSeconds, options = {}) {
const adapter = await this.ensureAdapter();
return adapter.setex(sessionKey, sessionData, ttlSeconds, options);
}
/**
* Retrieve encrypted session data with automatic decryption and validation
*
* Retrieves and decrypts session data stored with the setex method.
* Validates session integrity, expiration, and optional security context.
*
* @param sessionKey - The session key used when storing
* @param encryptionKey - The encryption key used when storing (required for decryption)
* @param options - Optional validation options for enhanced security
* @param options.validateUserId - User ID to validate against stored session
* @param options.validateIpAddress - IP address to validate against stored session
* @param options.validateUserAgent - User agent to validate against stored session
* @returns Promise resolving to decrypted session data, or null if not found/invalid
*
* @example Basic Session Retrieval
* ```typescript
* interface UserSession {
* userId: string;
* username: string;
* roles: string[];
* loginTime: number;
* }
*
* // Retrieve session data (encryption key must match the one used in setex)
* const sessionData = await cache.getSession<UserSession>(
* "session_abc123def456",
* encryptionKey
* );
*
* if (sessionData) {
* console.log("Welcome back,", sessionData.username);
* console.log("Your roles:", sessionData.roles);
* } else {
* console.log("Session not found or expired");
* }
* ```
*
* @example Session Retrieval with Security Validation
* ```typescript
* // Retrieve session with additional security checks
* const sessionData = await cache.getSession<UserSession>(
* "session_xyz789",
* encryptionKey,
* {
* validateUserId: "user123", // Must match stored user ID
* validateIpAddress: "192.168.1.100", // Must match stored IP
* validateUserAgent: req.headers['user-agent'] // Must match stored user agent
* }
* );
*
* if (sessionData) {
* // Session is valid and security context matches
* console.log("Secure session validated for:", sessionData.username);
* } else {
* // Session not found, expired, or security validation failed
* console.log("Session validation failed - possible security issue");
* }
* ```
*
* @example Express.js Middleware Usage
* ```typescript
* import { Request, Response, NextFunction } from 'express';
*
* async function sessionMiddleware(req: Request, res: Response, next: NextFunction) {
* const sessionId = req.cookies.sessionId;
* const encryptionKey = process.env.SESSION_ENCRYPTION_KEY;
*
* if (!sessionId || !encryptionKey) {
* return res.status(401).json({ error: 'No session' });
* }
*
* const sessionData = await cache.getSession(sessionId, encryptionKey, {
* validateIpAddress: req.ip,
* validateUserAgent: req.headers['user-agent']
* });
*
* if (!sessionData) {
* return res.status(401).json({ error: 'Invalid session' });
* }
*
* req.user = sessionData;
* next();
* }
* ```
*
* @since 4.2.3
* @see {@link setex} for storing encrypted session data
* @see {@link delete} for removing sessions
*/
async getSession(sessionKey, encryptionKey, options = {}) {
const adapter = await this.ensureAdapter();
return adapter.getSession(sessionKey, encryptionKey, options);
}
/**
* Deletes a value from the cache
*
* @param key - The cache key to delete
* @returns Promise resolving to true if the key was deleted, false if not found
*
* @example
* ```typescript
* const deleted = await cache.delete("user:123");
* if (deleted) {
* console.log("User cache cleared");
* }
* ```
*/
async delete(key) {
const adapter = await this.ensureAdapter();
return adapter.delete(key);
}
/**
* Checks if a key exists in the cache
*
* @param key - The cache key to check
* @returns Promise resolving to true if the key exists, false otherwise
*
* @example
* ```typescript
* if (await cache.exists("user:123")) {
* console.log("User is cached");
* }
* ```
*/
async exists(key) {
const adapter = await this.ensureAdapter();
return adapter.exists(key);
}
/**
* Clears all cached data
*
* ⚠️ **Warning**: This operation is irreversible and will remove all cached data
*
* @returns Promise that resolves when the cache is cleared
*
* @example
* ```typescript
* await cache.clear();
* console.log("All cache data cleared");
* ```
*/
async clear() {
const adapter = await this.ensureAdapter();
return adapter.clear();
}
/**
* Establishes connection to the cache backend
*
* Must be called before using the cache. For Redis strategies, this establishes
* the connection to the Redis server(s). For memory-only strategy, this initializes
* the in-memory cache.
*
* @returns Promise that resolves when the connection is established
* @throws {Error} If connection fails
*
* @example
* ```typescript
* try {
* await cache.connect();
* console.log("Cache connected successfully");
* } catch (error) {
* console.error("Failed to connect to cache:", error);
* }
* ```
*/
async connect() {
const adapter = await this.ensureAdapter();
return adapter.connect();
}
/**
* Closes the connection to the cache backend
*
* Gracefully closes all connections and cleans up resources. Should be called
* when shutting down the application.
*
* @returns Promise that resolves when the connection is closed
*
* @example
* ```typescript
* process.on('SIGTERM', async () => {
* await cache.disconnect();
* console.log("Cache disconnected");
* });
* ```
*/
async disconnect() {
const adapter = await this.ensureAdapter();
return adapter.disconnect();
}
/**
* Retrieves comprehensive cache performance statistics
*
* @returns Promise resolving to detailed statistics including hit rates, memory usage, and performance metrics
*
* @example
* ```typescript
* const stats = await cache.getStats();
* console.log(`Memory hit rate: ${stats.memory.hitRate * 100}%`);
* console.log(`Redis hit rate: ${stats.redis?.hitRate * 100}%`);
* console.log(`Total operations: ${stats.operations.total}`);
* console.log(`Average response time: ${stats.performance.avgResponseTime}ms`);
* ```
*/
async getStats() {
const adapter = await this.ensureAdapter();
const stats = await adapter.getStats();
// Transform the adapter stats to match our interface
return {
memory: {
hitRate: stats.memory?.hitRate || 0,
missRate: stats.memory?.missRate || 0,
size: stats.memory?.size || 0,
entries: stats.memory?.entries || 0,
maxSize: stats.memory?.maxSize || 0,
maxEntries: stats.memory?.maxEntries || 0,
},
redis: stats.redis
? {
hitRate: stats.redis.hitRate || 0,
missRate: stats.redis.missRate || 0,
connected: stats.redis.connected || false,
memoryUsage: stats.redis.memoryUsage || 0,
keyCount: stats.redis.keyCount || 0,
}
: undefined,
operations: {
total: stats.operations?.total || stats.total || 0,
gets: stats.operations?.gets || stats.gets || 0,
sets: stats.operations?.sets || stats.sets || 0,
deletes: stats.operations?.deletes || stats.deletes || 0,
errors: stats.operations?.errors || stats.errors || 0,
},
performance: {
avgResponseTime: stats.performance?.avgResponseTime ||
stats.avgResponseTime ||
0,
p95ResponseTime: stats.performance?.p95ResponseTime ||
stats.p95ResponseTime ||
0,
p99ResponseTime: stats.performance?.p99ResponseTime ||
stats.p99ResponseTime ||
0,
},
};
}
/**
* Retrieves multiple values from the cache in a single operation
*
* @param keys - Array of cache keys to retrieve
* @returns Promise resolving to an object with key-value pairs (missing keys are omitted)
*
* @example
* ```typescript
* const users = await cache.mread<User>(["user:1", "user:2", "user:3"]);
* console.log(users); // { "user:1": {...}, "user:2": {...} }
* ```
*/
async mread(keys) {
const adapter = await this.ensureAdapter();
return adapter.mget(keys);
}
/**
* Stores multiple key-value pairs in a single operation
*
* @param entries - Object with key-value pairs or array of [key, value] tuples
* @param options - Optional caching options applied to all entries
* @param options.ttl - Time to live in seconds for all entries
* @param options.tags - Array of tags applied to all entries
* @returns Promise resolving to true if successful, false otherwise
*
* @example
* ```typescript
* // Using object notation
* await cache.mwrite({
* "user:1": { name: "Alice" },
* "user:2": { name: "Bob" }
* }, { ttl: 3600 });
*
* // Using array notation
* await cache.mwrite([
* ["session:abc", sessionData1],
* ["session:def", sessionData2]
* ], { ttl: 1800, tags: ["sessions"] });
* ```
*/
async mwrite(entries, options) {
const adapter = await this.ensureAdapter();
return adapter.mset(entries, options);
}
/**
* Invalidates all cache entries that have any of the specified tags
*
* @param tags - Array of tags to invalidate
* @returns Promise resolving to the number of entries invalidated
*
* @example
* ```typescript
* // Invalidate all product-related cache entries
* const count = await cache.invalidateByTags(["products", "inventory"]);
* console.log(`Invalidated ${count} cache entries`);
* ```
*/
async invalidateByTags(tags) {
const adapter = await this.ensureAdapter();
return adapter.invalidateByTags(tags);
}
/**
* Gets the remaining time-to-live for a cache key
*
* @param key - The cache key to check
* @returns Promise resolving to TTL in seconds, or -1 if key doesn't exist, -2 if no TTL set
*
* @example
* ```typescript
* const ttl = await cache.getTTL("user:123");
* if (ttl > 0) {
* console.log(`Key expires in ${ttl} seconds`);
* }
* ```
*/
async getTTL(key) {
const adapter = await this.ensureAdapter();
return adapter.getTTL(key);
}
/**
* Sets or updates the expiration time for a cache key
*
* @param key - The cache key to set expiration for
* @param ttl - Time to live in seconds
* @returns Promise resolving to true if successful, false if key doesn't exist
*
* @example
* ```typescript
* // Extend expiration to 1 hour
* await cache.expire("user:123", 3600);
*
* // Set short expiration for temporary data
* await cache.expire("temp:data", 60);
* ```
*/
async expire(key, ttl) {
const adapter = await this.ensureAdapter();
return adapter.expire(key, ttl);
}
/**
* Retrieves all cache keys matching an optional pattern
*
* ⚠️ **Warning**: Use with caution in production as this can be expensive for large caches
*
* @param pattern - Optional glob-style pattern to filter keys (Redis syntax)
* @returns Promise resolving to array of matching keys
*
* @example
* ```typescript
* // Get all keys
* const allKeys = await cache.keys();
*
* // Get user-related keys
* const userKeys = await cache.keys("user:*");
*
* // Get session keys with pattern
* const sessionKeys = await cache.keys("session:*:active");
* ```
*/
async keys(pattern) {
const adapter = await this.ensureAdapter();
return adapter.keys(pattern);
}
/**
* Gets the current health status of the cache system
*
* @returns Health status object with overall status and detailed information
*
* @example
* ```typescript
* const health = cache.getHealth();
*
* switch (health.status) {
* case "healthy":
* console.log("Cache is operating normally");
* break;
* case "degraded":
* console.warn("Cache has issues but is functional:", health.details);
* break;
* case "unhealthy":
* console.error("Cache is not functional:", health.details);
* break;
* }
*
* // Check specific metrics
* if (health.details.redis?.connected === false) {
* console.error("Redis connection lost");
* }
* ```
*/
getHealth() {
if (!this.adapter) {
return {
status: "unhealthy",
details: {
errors: ["Cache adapter not initialized"],
lastCheck: new Date(),
},
};
}
return this.adapter.getHealth();
}
/**
* Memoizes a function with intelligent caching
*
* This method implements memoization - caching function results based on their inputs.
* It simplifies the common pattern of:
* 1. Generate a cache key from function parameters
* 2. Check if result exists in cache
* 3. If not, execute the function and cache the result
* 4. Return the cached or computed result
*
* @param keyGenerator - Function that generates a cache key from the parameters
* @param computeFunction - Function to execute if cache miss occurs
* @param options - Optional caching options
* @returns A memoized version of the function
*
* @example
* ```typescript
* import { Hash } from "xypriss-security";
*
* // Simple memoization with automatic key generation
* const memoizedSum = cache.memoize(
* (a: number, b: number) => Hash.create(String(a + b)).toString("hex"),
* (a: number, b: number) => a + b,
* { ttl: 3600 }
* );
*
* const result = await memoizedSum(1, 2); // Computes and caches
* const cached = await memoizedSum(1, 2); // Returns from cache
*
* // Advanced usage with async function
* const fetchUser = cache.memoize(
* (userId: string) => `user:${userId}`,
* async (userId: string) => {
* const response = await fetch(`/api/users/${userId}`);
* return response.json();
* },
* { ttl: 1800, tags: ["users"] }
* );
*
* const user = await fetchUser("123");
* ```
*/
memoize(keyGenerator, computeFunction, options) {
return async (...args) => {
// Ensure cache is connected
await this.ensureAdapter();
// Generate cache key
const cacheKey = keyGenerator(...args);
// Try to get from cache first
const cachedResult = await this.read(cacheKey);
if (cachedResult !== null && cachedResult !== undefined) {
return cachedResult;
}
// Cache miss - compute the result
const result = await computeFunction(...args);
// Store in cache
await this.write(cacheKey, result, options);
return result;
};
}
/**
* Deletes a value from the cache - alias for delete
*
* @param key - The cache key to delete
* @returns Promise resolving to true if the key was deleted, false if not found
*
* @example
* ```typescript
* const deleted = await cache.delete("user:123");
* if (deleted) {
* console.log("User cache cleared");
* }
* ```
*/
async del(key) {
return this.delete(key);
}
}
export { SecureCacheClient };
//# sourceMappingURL=SCC.js.map