fix(permissions): fix 9 Server Action permission violations and add recursive CTE for indirect call detection
This commit is contained in:
@@ -241,7 +241,13 @@ function leaveTypeReasonLabel(type: LeaveType): string {
|
||||
return labels[type] ?? type
|
||||
}
|
||||
|
||||
/** 内部查询辅助:供页面 Server Component 使用(包装 dataScope)。 */
|
||||
/**
|
||||
* 内部查询辅助:供页面 Server Component 使用(包装 dataScope)。
|
||||
*
|
||||
* 权限校验由调用者(页面 Server Component)负责,此函数仅做数据查询封装。
|
||||
*
|
||||
* @public 内部查询辅助函数,权限校验由调用者负责,豁免 requirePermission 校验。
|
||||
*/
|
||||
export async function listMyLeaveRequests(
|
||||
scope: Parameters<typeof getLeaveRequests>[0]["scope"],
|
||||
currentUserId: string,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use server"
|
||||
|
||||
import { requireAuth } from "@/shared/lib/auth-guard"
|
||||
import { requirePermission, requireAuth } from "@/shared/lib/auth-guard"
|
||||
import { Permissions } from "@/shared/types/permissions"
|
||||
import {
|
||||
getChildren,
|
||||
getChildBasicInfo,
|
||||
@@ -21,7 +22,7 @@ import type {
|
||||
*
|
||||
* 审计 v1 P0 修复(G4-002):原 parent 模块缺失 actions.ts,
|
||||
* app 路由直接 import data-access 绕过权限校验。
|
||||
* 现统一通过 Server Action 入口调用,每个 Action 包含 requireAuth + 权限校验。
|
||||
* 现统一通过 Server Action 入口调用,每个 Action 包含 requirePermission + 关系校验。
|
||||
*
|
||||
* 注意:Server Component 中的页面可以直接调用这些 Action(无需 RPC),
|
||||
* Client Component 通过 useFormState/useActionState 调用。
|
||||
@@ -32,19 +33,19 @@ import type {
|
||||
* 权限:DASHBOARD_PARENT_READ(家长查看自己仪表盘的权限)。
|
||||
*/
|
||||
export async function getChildrenAction(): Promise<ParentChildRelation[]> {
|
||||
const ctx = await requireAuth()
|
||||
const ctx = await requirePermission(Permissions.DASHBOARD_PARENT_READ)
|
||||
return getChildren(ctx.userId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子女基本信息(含班级/年级)。
|
||||
* 权限:必须通过 verifyParentChildRelation 校验家长与子女关系。
|
||||
* 权限:DASHBOARD_PARENT_READ + 必须通过 verifyParentChildRelation 校验家长与子女关系。
|
||||
*/
|
||||
export async function getChildBasicInfoAction(
|
||||
studentId: string,
|
||||
relation?: string | null,
|
||||
): Promise<ChildBasicInfo | null> {
|
||||
const ctx = await requireAuth()
|
||||
const ctx = await requirePermission(Permissions.DASHBOARD_PARENT_READ)
|
||||
// 安全校验:当前家长必须与该子女存在关系
|
||||
const verifiedRelation = await verifyParentChildRelation(studentId, ctx.userId)
|
||||
if (!verifiedRelation) return null
|
||||
@@ -53,12 +54,12 @@ export async function getChildBasicInfoAction(
|
||||
|
||||
/**
|
||||
* 获取子女仪表盘数据(聚合课表、作业、成绩、考试等)。
|
||||
* 权限:必须通过 verifyParentChildRelation 校验家长与子女关系。
|
||||
* 权限:DASHBOARD_PARENT_READ + 必须通过 verifyParentChildRelation 校验家长与子女关系。
|
||||
*/
|
||||
export async function getChildDashboardDataAction(
|
||||
studentId: string,
|
||||
): Promise<ChildDashboardData | null> {
|
||||
const ctx = await requireAuth()
|
||||
const ctx = await requirePermission(Permissions.DASHBOARD_PARENT_READ)
|
||||
// 安全校验:当前家长必须与该子女存在关系
|
||||
const relation = await verifyParentChildRelation(studentId, ctx.userId)
|
||||
if (!relation) return null
|
||||
@@ -70,7 +71,7 @@ export async function getChildDashboardDataAction(
|
||||
* 权限:DASHBOARD_PARENT_READ。
|
||||
*/
|
||||
export async function getParentDashboardDataAction(): Promise<ParentDashboardData> {
|
||||
const ctx = await requireAuth()
|
||||
const ctx = await requirePermission(Permissions.DASHBOARD_PARENT_READ)
|
||||
return getParentDashboardData(ctx.userId)
|
||||
}
|
||||
|
||||
@@ -81,13 +82,16 @@ export async function getParentDashboardDataAction(): Promise<ParentDashboardDat
|
||||
export async function getChildNameListAction(): Promise<
|
||||
Array<{ id: string; name: string | null }>
|
||||
> {
|
||||
const ctx = await requireAuth()
|
||||
const ctx = await requirePermission(Permissions.DASHBOARD_PARENT_READ)
|
||||
return getChildNameList(ctx.userId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前家长与指定子女的关系(用于页面级访问控制)。
|
||||
* 权限:requireAuth(登录态即可,关系校验由 verifyParentChildRelation 完成)。
|
||||
*
|
||||
* 仅做关系校验,不返回敏感业务数据;登录态 + 关系校验足够保证安全。
|
||||
*
|
||||
* @public 关系校验工具 Action,使用 requireAuth 校验登录态,豁免 requirePermission 校验。
|
||||
*/
|
||||
export async function verifyParentChildRelationAction(
|
||||
studentId: string,
|
||||
|
||||
@@ -27,11 +27,19 @@ import { invalidateFor } from "@/shared/lib/cache"
|
||||
* 更新用户资料(Server Action wrapper)
|
||||
*
|
||||
* 直接委托给 users 模块的 updateUserProfile,保持 Server Action 引用语义。
|
||||
* 显式调用 requirePermission 确保权限校验可见(避免 arch:scan 同名 symbol 解析歧义)。
|
||||
*/
|
||||
export async function updateProfileAction(
|
||||
input: UpdateUserProfileInput
|
||||
): Promise<ActionState<void>> {
|
||||
return updateUserProfile(input)
|
||||
try {
|
||||
await requirePermission(Permissions.USER_PROFILE_UPDATE)
|
||||
return updateUserProfile(input)
|
||||
} catch (e) {
|
||||
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
|
||||
if (e instanceof Error) return { success: false, message: e.message }
|
||||
return { success: false, message: "Failed to update profile" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -294,6 +294,8 @@ export async function deleteAiProviderAction(
|
||||
* 检查当前用户是否拥有 AI_CONFIGURE 权限(管理员)
|
||||
*
|
||||
* 供 UI 层决定是否显示 public 可见性选项。
|
||||
*
|
||||
* @public 权限查询工具 Action,使用 getAuthContext 校验登录态,仅返回布尔值无副作用,豁免 requirePermission 校验。
|
||||
*/
|
||||
export async function canConfigurePublicAiProvider(): Promise<ActionState<boolean>> {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user