Skip to main content
Glama
permission.service.ts16.3 kB
import { Injectable, ConflictException, NotFoundException, BadRequestException, Logger, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Permission, PermissionCategory, PermissionAction, SYSTEM_PERMISSIONS, } from '../../../database/entities/permission.entity'; import { Role } from '../../../database/entities/role.entity'; import { AuditAction, AuditLevel, AuditStatus } from '../../../database/entities/audit-log.entity'; import { CreatePermissionDto, UpdatePermissionDto, PermissionQueryDto, PaginatedResponseDto, } from '../dto/security.dto'; import { AuditService } from './audit.service'; export interface PermissionResponseDto { id: string; name: string; description?: string; category: PermissionCategory; action: PermissionAction; resource?: string; enabled: boolean; isSystem: boolean; conditions?: any; metadata?: any; createdAt: Date; updatedAt: Date; } @Injectable() export class PermissionService { private readonly logger = new Logger(PermissionService.name); constructor( @InjectRepository(Permission) private readonly permissionRepository: Repository<Permission>, @InjectRepository(Role) private readonly roleRepository: Repository<Role>, private readonly auditService: AuditService, ) {} /** * 创建权限 */ async createPermission( createPermissionDto: CreatePermissionDto, operatorId?: string, ipAddress?: string, ): Promise<PermissionResponseDto> { const { name } = createPermissionDto; // 检查权限名称是否已存在 const existingPermission = await this.permissionRepository.findOne({ where: { name }, }); if (existingPermission) { throw new ConflictException('权限名称已存在'); } // 创建权限 const permission = this.permissionRepository.create({ ...createPermissionDto, enabled: createPermissionDto.enabled !== false, isSystem: false, // 用户创建的权限不是系统权限 }); const savedPermission = await this.permissionRepository.save(permission); // 记录审计日志 await this.auditService.log({ action: AuditAction.PERMISSION_GRANTED, level: AuditLevel.INFO, status: AuditStatus.SUCCESS, userId: operatorId, resource: 'permissions', resourceId: savedPermission.id, ipAddress, details: { name: savedPermission.name, category: savedPermission.category, action: savedPermission.action, resource: savedPermission.resource, }, }); this.logger.log(`权限创建成功: ${savedPermission.name} (${savedPermission.id})`); return this.toResponseDto(savedPermission); } /** * 更新权限 */ async updatePermission( id: string, updatePermissionDto: UpdatePermissionDto, operatorId?: string, ipAddress?: string, ): Promise<PermissionResponseDto> { const permission = await this.findPermissionById(id); // 检查是否为系统权限 if (permission.isSystem) { throw new BadRequestException('不能修改系统权限'); } // 检查权限名称冲突 if (updatePermissionDto.name && updatePermissionDto.name !== permission.name) { const existingPermission = await this.permissionRepository.findOne({ where: { name: updatePermissionDto.name }, }); if (existingPermission) { throw new ConflictException('权限名称已存在'); } } // 更新权限数据 Object.assign(permission, updatePermissionDto); const updatedPermission = await this.permissionRepository.save(permission); // 记录审计日志 await this.auditService.log({ action: AuditAction.PERMISSION_GRANTED, level: AuditLevel.INFO, status: AuditStatus.SUCCESS, userId: operatorId, resource: 'permissions', resourceId: updatedPermission.id, ipAddress, details: { changes: updatePermissionDto, }, }); this.logger.log(`权限更新成功: ${updatedPermission.name} (${updatedPermission.id})`); return this.toResponseDto(updatedPermission); } /** * 删除权限 */ async deletePermission( id: string, operatorId?: string, ipAddress?: string, ): Promise<void> { const permission = await this.findPermissionById(id); // 检查是否为系统权限 if (permission.isSystem) { throw new BadRequestException('不能删除系统权限'); } // 检查是否有角色使用此权限 const roleCount = await this.roleRepository .createQueryBuilder('role') .innerJoin('role.permissions', 'permission') .where('permission.id = :permissionId', { permissionId: id }) .getCount(); if (roleCount > 0) { throw new BadRequestException(`无法删除权限,还有 ${roleCount} 个角色正在使用此权限`); } await this.permissionRepository.remove(permission); // 记录审计日志 await this.auditService.log({ action: AuditAction.PERMISSION_REVOKED, level: AuditLevel.WARNING, status: AuditStatus.SUCCESS, userId: operatorId, resource: 'permissions', resourceId: id, ipAddress, details: { name: permission.name, category: permission.category, action: permission.action, }, }); this.logger.warn(`权限删除成功: ${permission.name} (${id})`); } /** * 获取权限详情 */ async getPermissionById(id: string): Promise<PermissionResponseDto> { const permission = await this.findPermissionById(id); return this.toResponseDto(permission); } /** * 查询权限列表 */ async getPermissions( query: PermissionQueryDto, ): Promise<PaginatedResponseDto<PermissionResponseDto>> { const { search, category, action, resource, enabled, page = 1, limit = 20, } = query; const queryBuilder = this.permissionRepository.createQueryBuilder('permission'); // 搜索条件 if (search) { queryBuilder.andWhere( '(permission.name LIKE :search OR permission.description LIKE :search OR permission.resource LIKE :search)', { search: `%${search}%` }, ); } if (category) { queryBuilder.andWhere('permission.category = :category', { category }); } if (action) { queryBuilder.andWhere('permission.action = :action', { action }); } if (resource) { queryBuilder.andWhere('permission.resource = :resource', { resource }); } if (enabled !== undefined) { queryBuilder.andWhere('permission.enabled = :enabled', { enabled }); } // 排序 queryBuilder .orderBy('permission.category', 'ASC') .addOrderBy('permission.action', 'ASC') .addOrderBy('permission.name', 'ASC'); // 分页 const offset = (page - 1) * limit; queryBuilder.skip(offset).take(limit); const [permissions, total] = await queryBuilder.getManyAndCount(); const totalPages = Math.ceil(total / limit); return { data: permissions.map(permission => this.toResponseDto(permission)), total, page, limit, totalPages, hasNext: page < totalPages, hasPrev: page > 1, }; } /** * 获取权限分类列表 */ async getPermissionCategories(): Promise<Array<{ category: PermissionCategory; count: number }>> { const result = await this.permissionRepository .createQueryBuilder('permission') .select('permission.category', 'category') .addSelect('COUNT(*)', 'count') .where('permission.enabled = :enabled', { enabled: true }) .groupBy('permission.category') .getRawMany(); return result.map(item => ({ category: item.category, count: parseInt(item.count, 10), })); } /** * 获取权限操作列表 */ async getPermissionActions(): Promise<Array<{ action: PermissionAction; count: number }>> { const result = await this.permissionRepository .createQueryBuilder('permission') .select('permission.action', 'action') .addSelect('COUNT(*)', 'count') .where('permission.enabled = :enabled', { enabled: true }) .groupBy('permission.action') .getRawMany(); return result.map(item => ({ action: item.action, count: parseInt(item.count, 10), })); } /** * 获取资源列表 */ async getResources(): Promise<Array<{ resource: string; count: number }>> { const result = await this.permissionRepository .createQueryBuilder('permission') .select('permission.resource', 'resource') .addSelect('COUNT(*)', 'count') .where('permission.enabled = :enabled', { enabled: true }) .andWhere('permission.resource IS NOT NULL') .groupBy('permission.resource') .getRawMany(); return result.map(item => ({ resource: item.resource, count: parseInt(item.count, 10), })); } /** * 启用/禁用权限 */ async togglePermissionStatus( id: string, enabled: boolean, operatorId?: string, ipAddress?: string, ): Promise<PermissionResponseDto> { const permission = await this.findPermissionById(id); // 检查是否为系统权限 if (permission.isSystem) { throw new BadRequestException('不能禁用系统权限'); } permission.enabled = enabled; const updatedPermission = await this.permissionRepository.save(permission); // 记录审计日志 await this.auditService.log({ action: enabled ? AuditAction.PERMISSION_GRANTED : AuditAction.PERMISSION_REVOKED, level: AuditLevel.INFO, status: AuditStatus.SUCCESS, userId: operatorId, resource: 'permissions', resourceId: id, ipAddress, details: { permissionName: permission.name, enabled, }, }); this.logger.log(`权限${enabled ? '启用' : '禁用'}成功: ${permission.name}`); return this.toResponseDto(updatedPermission); } /** * 批量创建权限 */ async createPermissionsBatch( permissions: CreatePermissionDto[], operatorId?: string, ipAddress?: string, ): Promise<PermissionResponseDto[]> { const createdPermissions: Permission[] = []; for (const permissionDto of permissions) { // 检查权限名称是否已存在 const existingPermission = await this.permissionRepository.findOne({ where: { name: permissionDto.name }, }); if (!existingPermission) { const permission = this.permissionRepository.create({ ...permissionDto, enabled: permissionDto.enabled !== false, isSystem: false, }); createdPermissions.push(permission); } } if (createdPermissions.length > 0) { const savedPermissions = await this.permissionRepository.save(createdPermissions); // 记录审计日志 await this.auditService.log({ action: AuditAction.PERMISSION_GRANTED, level: AuditLevel.INFO, status: AuditStatus.SUCCESS, userId: operatorId, resource: 'permissions', ipAddress, details: { count: savedPermissions.length, permissions: savedPermissions.map(p => p.name), }, }); this.logger.log(`批量创建权限成功: ${savedPermissions.length} 个权限`); return savedPermissions.map(permission => this.toResponseDto(permission)); } return []; } /** * 检查权限是否存在 */ async checkPermissionExists(name: string): Promise<boolean> { const permission = await this.permissionRepository.findOne({ where: { name, enabled: true }, }); return !!permission; } /** * 根据名称获取权限 */ async getPermissionByName(name: string): Promise<Permission | null> { return this.permissionRepository.findOne({ where: { name, enabled: true }, }); } /** * 获取用户权限列表 */ async getUserPermissions(userId: string): Promise<string[]> { const result = await this.permissionRepository .createQueryBuilder('permission') .innerJoin('permission.roles', 'role') .innerJoin('role.users', 'user') .where('user.id = :userId', { userId }) .andWhere('permission.enabled = :enabled', { enabled: true }) .andWhere('role.enabled = :roleEnabled', { roleEnabled: true }) .select('permission.name') .distinct(true) .getRawMany(); return result.map(item => item.permission_name); } /** * 初始化系统权限 */ async initializeSystemPermissions(): Promise<void> { for (const permissionData of Object.values(SYSTEM_PERMISSIONS)) { const existingPermission = await this.permissionRepository.findOne({ where: { name: permissionData.name }, }); if (!existingPermission) { const permission = this.permissionRepository.create({ ...permissionData, enabled: true, isSystem: true, }); await this.permissionRepository.save(permission); this.logger.log(`系统权限初始化: ${permissionData.name}`); } } } /** * 为角色分配系统权限 */ async assignSystemPermissionsToRole(roleName: string): Promise<void> { const role = await this.roleRepository.findOne({ where: { name: roleName }, relations: ['permissions'], }); if (!role) { this.logger.warn(`角色不存在: ${roleName}`); return; } let permissionNames: string[] = []; // 根据角色分配不同的权限 switch (roleName) { case 'SUPER_ADMIN': // 超级管理员拥有所有权限 const allPermissions = await this.permissionRepository.find({ where: { enabled: true }, }); role.permissions = allPermissions; break; case 'ADMIN': permissionNames = [ 'user:create', 'user:read', 'user:update', 'user:delete', 'role:create', 'role:read', 'role:update', 'role:delete', 'permission:read', 'server:create', 'server:read', 'server:update', 'server:delete', 'api:create', 'api:read', 'api:update', 'api:delete', 'monitoring:read', 'config:read', 'config:update', 'audit:read', ]; break; case 'OPERATOR': permissionNames = [ 'user:read', 'role:read', 'server:create', 'server:read', 'server:update', 'api:read', 'api:update', 'monitoring:read', 'config:read', ]; break; case 'VIEWER': permissionNames = [ 'user:read', 'role:read', 'server:read', 'api:read', 'monitoring:read', 'config:read', ]; break; case 'GUEST': permissionNames = [ 'server:read', 'api:read', ]; break; } if (permissionNames.length > 0) { const permissions = await this.permissionRepository.find({ where: permissionNames.map(name => ({ name })), }); role.permissions = permissions; } await this.roleRepository.save(role); this.logger.log(`为角色 ${roleName} 分配了 ${role.permissions.length} 个权限`); } /** * 根据ID查找权限 */ private async findPermissionById(id: string): Promise<Permission> { const permission = await this.permissionRepository.findOne({ where: { id }, }); if (!permission) { throw new NotFoundException('权限不存在'); } return permission; } /** * 转换为响应DTO */ private toResponseDto(permission: Permission): PermissionResponseDto { return { id: permission.id, name: permission.name, description: permission.description, category: permission.category, action: permission.action, resource: permission.resource, enabled: permission.enabled, isSystem: permission.isSystem, conditions: permission.conditions, metadata: permission.metadata, createdAt: permission.createdAt, updatedAt: permission.updatedAt, }; } }

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