feat: 完善 RBAC 权限体系与权限管理页面

This commit is contained in:
2026-08-05 17:10:51 +08:00
parent 68270e7571
commit 644c35ce53
16 changed files with 1311 additions and 926 deletions

View File

@@ -1,5 +1,4 @@
import { Injectable } from '@nestjs/common';
import { ForbiddenException } from '@nestjs/common';
import { Injectable, ForbiddenException } from '@nestjs/common';
import { CaslAbilityFactory } from './casl-ability.factory';
import { AppAbility, AppSubject, AuthorizationRequest } from './interfaces';
import { CaslAction, permissionCodeSubject } from './casl.constants';
@@ -69,9 +68,7 @@ export class AuthorizationService {
*/
assertPermission(ability: AppAbility, permissionCode: string): void {
if (!this.canPermission(ability, permissionCode)) {
throw new ForbiddenException(
`权限不足:缺少权限码 ${permissionCode}`,
);
throw new ForbiddenException(`权限不足:缺少权限码 ${permissionCode}`);
}
}

View File

@@ -66,11 +66,6 @@ export function permissionCodeSubject(code: string): string {
return `PermissionCode:${code}`;
}
// ---------------------------------------------------------------------------
// Domain-level action mapping: permission code → CASL action
// Used ONLY for the domain layer — not for exact-code access checks.
// ---------------------------------------------------------------------------
function permissionToAction(permission: string): CaslAction | null {
const actionSegment = permission.split(':')[1] ?? permission;

View File

@@ -1,10 +1,6 @@
import { MongoAbility } from '@casl/ability';
import { CaslAction } from './casl.constants';
// ---------------------------------------------------------------------------
// Subject type union — all entity classes we protect with CASL.
// ---------------------------------------------------------------------------
// CASL expects the subject to be either the class constructor or a string.
// We use string subjects (SubjectName) for simplicity when no instance is
// available, and concrete instance types for per-resource checks.
@@ -12,10 +8,6 @@ export type AppSubject = string | Record<string, unknown>;
export type AppAbility = MongoAbility<[CaslAction, AppSubject]>;
// ---------------------------------------------------------------------------
// Authenticated user — what the JWT strategy places on `request.user`.
// ---------------------------------------------------------------------------
export interface AuthenticatedUser {
id: number;
username: string;
@@ -31,17 +23,16 @@ export interface AuthenticatedUser {
* Minimum authorization principal — the subset of AuthenticatedUser
* needed by CaslAbilityFactory and AuthorizationService.
*/
export type AuthPrincipal = { readonly permissions: readonly string[]; readonly isSuperAdmin: boolean };
export type AuthPrincipal = {
readonly permissions: readonly string[];
readonly isSuperAdmin: boolean;
};
/** Request-like carrier populated only by the trusted authentication layer. */
export interface AuthorizationRequest {
user?: AuthPrincipal;
}
// ---------------------------------------------------------------------------
// Policy handler types for @CheckPolicies()
// ---------------------------------------------------------------------------
/**
* Interface for class-based policy handlers.
*