Files
leaudit-platform-frontend/app/api/cross-checking/verify-document-access.ts
T

144 lines
4.1 KiB
TypeScript

/**
* 验证用户是否有权访问指定文档
*
* 权限验证逻辑:
* 1. 检查文档是否属于指定的任务
* 2. 检查用户是否是该任务的参与者(评审员或发起人)
* 3. 防止用户通过修改 URL 参数访问未授权的文档
*/
import { postgrestGet } from "../postgrest-client";
interface DocumentAccessCheckParams {
documentId: string | number;
taskId: string | number;
userId: number;
jwtToken?: string;
}
interface DocumentAccessCheckResult {
hasAccess: boolean;
reason?: string;
userRole?: 'assigner' | 'assignee' | 'none';
}
/**
* 验证文档访问权限
* @param params 验证参数
* @returns 验证结果
*/
export async function verifyDocumentAccess(
params: DocumentAccessCheckParams
): Promise<DocumentAccessCheckResult> {
const { documentId, taskId, userId, jwtToken } = params;
try {
// 1. 检查文档是否属于该任务(通过 cross_task_document_mapping 表)
const documentMappingResponse = await postgrestGet('/api/postgrest/proxy/cross_task_document_mapping', {
select: 'task_id,document_id',
filter: {
task_id: `eq.${taskId}`,
document_id: `eq.${documentId}`
},
token: jwtToken
});
if (documentMappingResponse.error) {
console.error('❌ [verifyDocumentAccess] 查询文档-任务映射失败:', documentMappingResponse.error);
return {
hasAccess: false,
reason: '查询文档-任务映射失败'
};
}
// 提取数据
const mappingData = Array.isArray(documentMappingResponse.data)
? documentMappingResponse.data
: [];
// 文档不属于该任务
if (mappingData.length === 0) {
console.warn(`⚠️ [verifyDocumentAccess] 文档 ${documentId} 不属于任务 ${taskId}`);
return {
hasAccess: false,
reason: '文档不属于该任务'
};
}
// 2. 检查用户是否是该任务的参与者
const taskResponse = await postgrestGet('/api/postgrest/proxy/cross_examination_tasks', {
select: 'assigner_id,assignee_ids',
filter: {
id: `eq.${taskId}`
},
token: jwtToken
});
if (taskResponse.error) {
console.error('❌ [verifyDocumentAccess] 查询任务信息失败:', taskResponse.error);
return {
hasAccess: false,
reason: '查询任务信息失败'
};
}
const taskData = Array.isArray(taskResponse.data) ? taskResponse.data[0] : null;
if (!taskData) {
console.warn(`⚠️ [verifyDocumentAccess] 任务 ${taskId} 不存在`);
return {
hasAccess: false,
reason: '任务不存在'
};
}
// 3. 验证用户身份
const isAssigner = taskData.assigner_id === userId;
const assigneeIds = Array.isArray(taskData.assignee_ids) ? taskData.assignee_ids : [];
const isAssignee = assigneeIds.includes(userId);
if (isAssigner) {
console.log(`✅ [verifyDocumentAccess] 用户 ${userId} 是任务 ${taskId} 的发起人,允许访问文档 ${documentId}`);
return {
hasAccess: true,
userRole: 'assigner'
};
}
if (isAssignee) {
console.log(`✅ [verifyDocumentAccess] 用户 ${userId} 是任务 ${taskId} 的评审员,允许访问文档 ${documentId}`);
return {
hasAccess: true,
userRole: 'assignee'
};
}
// 用户既不是发起人也不是评审员
console.warn(`⚠️ [verifyDocumentAccess] 用户 ${userId} 无权访问任务 ${taskId} 的文档 ${documentId}`);
return {
hasAccess: false,
reason: '您没有权限访问该文档',
userRole: 'none'
};
} catch (error) {
console.error('❌ [verifyDocumentAccess] 验证失败:', error);
return {
hasAccess: false,
reason: error instanceof Error ? error.message : '权限验证失败'
};
}
}
/**
* 快速验证文档访问权限(仅返回布尔值)
* @param params 验证参数
* @returns 是否有权限访问
*/
export async function canAccessDocument(
params: DocumentAccessCheckParams
): Promise<boolean> {
const result = await verifyDocumentAccess(params);
return result.hasAccess;
}