import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { PERMISSION_KEY } from '../decorators/require-permission.decorator'; @Injectable() export class PermissionsGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredPermission = this.reflector.getAllAndOverride( PERMISSION_KEY, [context.getHandler(), context.getClass()], ); if (!requiredPermission) return true; const { user } = context.switchToHttp().getRequest(); if (!user) return false; // Super admins bypass all permission checks if (user.is_super_admin) return true; // API keys are full programmatic access to their own license (always // tenant-scoped by license_id via @CurrentTenant). Granted here rather than // enumerating every permission. Future: scoped/read-only keys. if (user.is_api_key) return true; // Check permissions JSONB from role const permissions = user.permissions as Record | undefined; if (!permissions) return false; // Global wildcard — the Owner role (full control of its license) carries // {"*": true}, so new features never need to amend the role enumeration. if (permissions['*'] === true) return true; // Support wildcard: "server.*" matches "server.view", "server.console", etc. const parts = requiredPermission.split('.'); const wildcard = parts[0] + '.*'; return permissions[requiredPermission] === true || permissions[wildcard] === true; } }