Skip to main content
Glama
permissions.guard.ts4.24 kB
import { Injectable, CanActivate, ExecutionContext, ForbiddenException, Logger, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { Request } from 'express'; import { PERMISSIONS_KEY } from '../decorators/permissions.decorator'; import { PUBLIC_KEY } from '../decorators/permissions.decorator'; import { AuthService } from '../services/auth.service'; import { User } from '../../../database/entities/user.entity'; import { AuditAction, AuditLevel, AuditStatus } from '../../../database/entities/audit-log.entity'; @Injectable() export class PermissionsGuard implements CanActivate { private readonly logger = new Logger(PermissionsGuard.name); constructor( private readonly reflector: Reflector, private readonly authService: AuthService, ) {} async canActivate(context: ExecutionContext): Promise<boolean> { // 检查是否为公共端点 const isPublic = this.reflector.getAllAndOverride<boolean>(PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); if (isPublic) { return true; } // 获取所需权限 const requiredPermissions = this.reflector.getAllAndOverride<string[]>( PERMISSIONS_KEY, [context.getHandler(), context.getClass()], ); // 如果没有设置权限要求,则允许访问 if (!requiredPermissions || requiredPermissions.length === 0) { return true; } const request = context.switchToHttp().getRequest<Request>(); const user = request.user as User; if (!user) { throw new ForbiddenException('用户信息无效'); } try { // 检查用户是否具有所需权限 const hasPermission = await this.checkUserPermissions( user, requiredPermissions, ); if (!hasPermission) { // 记录权限检查失败 await this.logPermissionDenied( request, user.id, requiredPermissions, ); throw new ForbiddenException( `权限不足,需要以下权限之一: ${requiredPermissions.join(', ')}`, ); } return true; } catch (error) { if (error instanceof ForbiddenException) { throw error; } this.logger.error('权限检查过程中发生错误', { userId: user.id, requiredPermissions, error: error.message, }); throw new ForbiddenException('权限检查失败'); } } /** * 检查用户权限 */ private async checkUserPermissions( user: User, requiredPermissions: string[], ): Promise<boolean> { // 超级管理员拥有所有权限 if (user.hasRole('SUPER_ADMIN')) { return true; } // 检查用户是否具有任一所需权限 for (const permission of requiredPermissions) { const hasPermission = await this.authService.checkPermission( user.id, permission, ); if (hasPermission) { return true; } } return false; } /** * 记录权限拒绝日志 */ private async logPermissionDenied( request: Request, userId: string, requiredPermissions: string[], ): Promise<void> { try { const ipAddress = this.getClientIp(request); const userAgent = request.get('User-Agent') || ''; const path = request.path; const method = request.method; // 记录权限拒绝日志 await this.authService['auditService']?.log({ action: AuditAction.PERMISSION_REVOKED, level: AuditLevel.WARNING, status: AuditStatus.FAILED, userId, resource: 'security', ipAddress, userAgent, details: { path, method, requiredPermissions, reason: '权限不足', }, }); } catch (error) { this.logger.error('记录权限拒绝日志失败', error); } } /** * 获取客户端IP地址 */ private getClientIp(request: Request): string { return ( (request.headers['x-forwarded-for'] as string)?.split(',')[0] || (request.headers['x-real-ip'] as string) || request.connection.remoteAddress || request.socket.remoteAddress || '' ); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zaizaizhao/mcp-swagger-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server