144 lines
4.1 KiB
TypeScript
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;
|
|
}
|