JSPM

exguard-cached

1.2.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 21
  • Score
    100M100P100Q107728F
  • License MIT

ExGuard cached user data handling package for backend integration

Package Exports

  • exguard-cached
  • exguard-cached/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (exguard-cached) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

ExGuard Cached

NestJS backend package for caching user data and permissions in Redis. Provides fast, cache-first access to user roles, groups, and permissions using the guard: key prefix.

Installation

npm install exguard-cached

Required Environment Variables

Create a .env file in your NestJS backend root:

# Redis Connection
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password
REDIS_DB=0
REDIS_URL=redis://localhost:6379

# Cache Settings
REDIS_KEY_PREFIX=guard:
REDIS_TTL=300

CACHE_DEFAULT_TTL=300
CACHE_KEY_PREFIX=guard:
CACHE_ENABLE_LOGGING=false

# Permission Caching
PERMISSION_CACHE_ENABLED=true
PERMISSION_CACHE_TTL=300

Key Notes:

  • REDIS_KEY_PREFIX and CACHE_KEY_PREFIX must be guard: (default)
  • Cache keys follow format: guard:user:{cognitoSubId}
  • Permissions are stored as: module:resource:action (e.g., exGUARD:user_groups:update_group)

Files to Configure

1. app.module.ts - Main Application Module

import { Module } from '@nestjs/common';
import { ExGuardCacheModule } from 'exguard-cached';
import { RedisModule } from './redis.module'; // Your Redis module

@Module({
  imports: [
    RedisModule, // Your Redis client module
    ExGuardCacheModule.forRoot({
      redisClient: redisClient, // Inject your Redis client
      config: {
        cache: {
          defaultTtl: 300,
          keyPrefix: 'guard:',
          enableLogging: false,
        },
        permissions: {
          enableCaching: true,
          cacheTtl: 300,
        },
      },
    }),
  ],
})
export class AppModule {}

2. redis.module.ts - Redis Client Setup

import { Module, Global } from '@nestjs/common';
import { createClient } from 'redis';

@Global()
@Module({
  providers: [
    {
      provide: 'REDIS_CLIENT',
      useFactory: async () => {
        const client = createClient({
          socket: {
            host: process.env.REDIS_HOST || 'localhost',
            port: parseInt(process.env.REDIS_PORT || '6379'),
          },
          password: process.env.REDIS_PASSWORD,
          database: parseInt(process.env.REDIS_DB || '0'),
        });

        await client.connect();
        return client;
      },
    },
  ],
  exports: ['REDIS_CLIENT'],
})
export class RedisModule {}

3. Controller Implementation

import { Controller, Get, Post, Body, Param, Injectable } from '@nestjs/common';
import { 
  RequireCachedUserPermission, 
  CachedUser, 
  CachedUserData,
  CachedUserService 
} from 'exguard-cached';

@Controller('users')
export class UsersController {
  constructor(private cachedUserService: CachedUserService) {}

  // Example 1: Get current user data from cache
  @Get('me')
  @RequireCachedUserPermission('exGUARD', 'user_groups:view_group')
  async getMyProfile(@CachedUser() cachedUser: CachedUserData) {
    return {
      user: cachedUser.user,
      roles: cachedUser.roles,
      groups: cachedUser.groups,
      permissions: cachedUser.permissions,
      fieldOffice: cachedUser.user?.fieldOffice,
    };
  }

  // Example 2: Check specific permission
  @Get('check-permission/:module/:action')
  async checkPermission(
    @Param('module') module: string,
    @Param('action') action: string,
    @CachedUser() cachedUser: CachedUserData,
  ) {
    const hasPermission = cachedUser.permissions.some(p => 
      p.includes(module) && p.includes(action)
    );

    return { hasPermission, module, action };
  }

  // Example 3: Get user's field office
  @Get('field-office')
  async getFieldOffice(@CachedUser() cachedUser: CachedUserData) {
    return {
      fieldOffice: cachedUser.user?.fieldOffice || null,
    };
  }

  // Example 4: Admin endpoint with strict permission
  @Get('admin/all')
  @RequireCachedUserPermission('exGUARD', 'user_roles:view_all')
  async getAllUsers(@CachedUser() cachedUser: CachedUserData) {
    // Only accessible if user has exGUARD:user_roles:view_all permission
    return {
      message: 'Admin access granted',
      userRoles: cachedUser.roles,
      userModules: cachedUser.modules,
    };
  }
}

4. Service Usage (Optional)

import { Injectable } from '@nestjs/common';
import { CachedUserService } from 'exguard-cached';

@Injectable()
export class MyService {
  constructor(private cachedUserService: CachedUserService) {}

  async getUserFromCache(cognitoSubId: string) {
    const cachedUser = await this.cachedUserService.getCachedUser(cognitoSubId);
    
    if (!cachedUser) {
      // Cache miss - fetch from database and cache it
      return null;
    }

    return cachedUser;
  }

  async setUserCache(cognitoSubId: string, userData: any) {
    await this.cachedUserService.setCachedUser(cognitoSubId, userData, 300);
  }

  async hasPermission(cognitoSubId: string, module: string, action: string) {
    return await this.cachedUserService.hasPermission(cognitoSubId, module, action);
  }

  async invalidateUser(cognitoSubId: string) {
    await this.cachedUserService.invalidateCachedUser(cognitoSubId);
  }
}

Cache Key Format

guard:user:{cognitoSubId}

Example: guard:user:09eaa52c-50d1-7054-5769-8819ac43eeed

Permission Format in Cache

Permissions are stored as strings in the format: module:resource:action

Examples from cached data:

  • exGUARD:user_groups:update_group
  • exGUARD:user_roles:view_all
  • exGUARD:modules:create

To check permission: Use hasPermission(cognitoSubId, 'exGUARD', 'update_group') which searches across all modules.

CachedUserData Structure

interface CachedUserData {
  user: {
    id: string;
    cognitoSubId: string;
    username: string;
    email: string;
    givenName: string;
    familyName: string;
    employeeNumber: string;
    fieldOffice: {
      id: string;
      code: string;
      name: string;
    } | null;
  } | null;
  groups: string[];        // ['group1', 'group2']
  roles: string[];         // ['role1', 'role2']
  modules: Array<{
    key: string;
    name: string;
    permissions: string[];
  }>;
  fieldOffices: string[];
  permissions: string[];   // ['exGUARD:user_groups:update_group', ...]
  cacheInfo: {
    userId: string;
    cognitoSubId: string;
    cacheKeys: {
      access: string;
      permissions: string;
    };
  };
}

API Reference

Decorators

@RequireCachedUserPermission(module, action)

Protects endpoints by checking cached permissions.

@Get()
@RequireCachedUserPermission('exGUARD', 'user_groups:view_group')
async myMethod() { ... }

@CachedUser()

Injects cached user data into the method parameter.

async myMethod(@CachedUser() user: CachedUserData) {
  console.log(user.permissions);
}

CachedUserService Methods

Method Description
getCachedUser(cognitoSubId) Get complete cached user data
setCachedUser(cognitoSubId, data, ttl?) Cache user data
invalidateCachedUser(cognitoSubId) Remove user from cache
hasPermission(cognitoSubId, module, action) Check if user has permission
getUserPermissions(cognitoSubId) Get all user permissions
getUserRoles(cognitoSubId) Get user roles
getUserGroups(cognitoSubId) Get user groups
getUserModules(cognitoSubId) Get user modules
getUserFieldOffice(cognitoSubId) Get user's field office

How It Works

  1. User logs in → Backend fetches user data from database
  2. Data is cached → Stored in Redis with key guard:user:{cognitoSubId}
  3. Subsequent requests → Check Redis first (fast), fallback to database
  4. Permission checks → Read from cached permissions array
  5. Cache invalidation → Triggered when user roles/groups change

Version History

  • 1.1.0 - Current version with Redis caching and NestJS integration