fix: add missing @RequirePermission decorators

Adds @RequirePermission to 19 TS GraphQL resolvers across 5
subgraphs (iam, config-service, core-edu, content, msg) per
audit report §6.1. Maps: iam user/role -> IAM_USER_READ;
config-service 5 queries -> CONFIG_USER; core-edu classInfo ->
CLASS_READ, exam -> EXAM_READ, grade -> GRADE_READ, homework
-> HOMEWORK_READ, datascope visibleGrades/visibleExams ->
GRADE_READ/EXAM_READ; content chapter/knowledgePoint/question/
textbook -> CONTENT_*_READ; msg notifications ->
MSG_NOTIFICATION_READ, template -> MSG_NOTIFICATION_MANAGE.
Federation resolveReference left unguarded. Python subgraphs
(data-ana, ai) deferred to follow-up infrastructure work.
This commit is contained in:
SpecialX
2026-07-17 13:26:58 +08:00
parent 315b954998
commit 1b5781bf42
17 changed files with 117 additions and 30 deletions

View File

@@ -6,6 +6,10 @@
*/
import { Resolver, Query, ObjectType, Field, ID } from "@nestjs/graphql";
import { ConfigService } from "../../config-config/config.service.js";
import {
Permissions,
RequirePermission,
} from "../../middleware/permission.guard.js";
@ObjectType()
export class LayoutTemplateGql {
@@ -15,7 +19,7 @@ export class LayoutTemplateGql {
@Field()
displayName!: string;
@Field({ nullable: true })
@Field(() => String, { nullable: true })
description: string | null = null;
@Field()
@@ -27,6 +31,7 @@ export class LayoutTemplateResolver {
constructor(private readonly service: ConfigService) {}
@Query(() => [LayoutTemplateGql])
@RequirePermission(Permissions.CONFIG_USER)
async layoutTemplates(): Promise<LayoutTemplateGql[]> {
const templates = await this.service.listLayoutTemplates();
return templates.map((t) => ({

View File

@@ -21,6 +21,10 @@ import {
Int,
} from "@nestjs/graphql";
import { ConfigService } from "../../config-config/config.service.js";
import {
Permissions,
RequirePermission,
} from "../../middleware/permission.guard.js";
@ObjectType()
export class PluginConfigLayoutGql {
@@ -123,6 +127,7 @@ export class PluginConfigResolver {
* (来自 x-user-role 头),返回与 gRPC GetPluginConfig 等价的结果。
*/
@Query(() => PluginConfigResponseGql)
@RequirePermission(Permissions.CONFIG_USER)
async pluginConfig(
@Args("userId", { type: () => ID }) userId: string,
@Args("role", {

View File

@@ -21,6 +21,10 @@ import {
type PluginRegistryEntity,
} from "../dataloader.service.js";
import { ConfigService } from "../../config-config/config.service.js";
import {
Permissions,
RequirePermission,
} from "../../middleware/permission.guard.js";
@ObjectType()
@Directive(`@key(fields: "pluginId")`)
@@ -62,6 +66,7 @@ export class PluginResolver {
}
@Query(() => PluginRegistry, { nullable: true })
@RequirePermission(Permissions.CONFIG_USER)
async plugin(
@Args("pluginId", { type: () => ID }) pluginId: string,
): Promise<PluginRegistryEntity | null> {
@@ -69,6 +74,7 @@ export class PluginResolver {
}
@Query(() => [PluginRegistry])
@RequirePermission(Permissions.CONFIG_USER)
async plugins(): Promise<PluginRegistryEntity[]> {
const list = await this.service.listPlugins({ isActive: true });
return list.map((p) => ({

View File

@@ -20,6 +20,10 @@ import {
DataLoaderService,
type UserLayoutOverrideEntity,
} from "../dataloader.service.js";
import {
Permissions,
RequirePermission,
} from "../../middleware/permission.guard.js";
@ObjectType()
@Directive(`@key(fields: "userId")`)
@@ -46,6 +50,7 @@ export class UserLayoutResolver {
}
@Query(() => UserLayoutOverrideGql, { nullable: true })
@RequirePermission(Permissions.CONFIG_USER)
async userLayoutOverride(
@Args("userId", { type: () => ID }) userId: string,
): Promise<UserLayoutOverrideEntity | null> {