Files
iistwin/server/utils/api-key-actor.ts
Ильяс Султанов 8cfd49fd9f Атрибуция от бота в MCP/REST + REST API по ключу (этап 2)
- MCP: getActor (владелец ключа + бот), аудит changedByName=бот/label ключа, botId, metadata.source='mcp'; аудит для update_task/update_task_status
- sendTaskMessage: botId (messageType 'bot'), починен путь isBotToken без req.user
- authenticateTokenOrApiKey: JWT или X-Api-Key с белым списком endpoint'ов (/api/upload, messages, field-values, create task), проверки скоупов, аудит source='api'
- Лимиты файлов через env: UPLOAD_IMAGE_MAX_MB=25, UPLOAD_DOC_MAX_MB=100, UPLOAD_MAX_MB=100
- MCP upload-инструменты: параметр fileUrl для привязки уже загруженного файла; новый инструмент get_api_guide
2026-07-22 15:18:04 +03:00

45 lines
2.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { storage } from '../storage';
import type { AuthenticatedRequest } from '../middleware/auth.middleware';
import { isFormAllowedByScopes } from './api-key';
// Актор изменений для REST-хендлеров, поддерживающих API-ключ:
// JWT — текущий пользователь; ключ бота — бот (changedBy=null); ключ без бота — label ключа.
export interface RestActor {
changedBy: number | null;
changedByName: string;
botId: number | null;
source: 'api' | null; // 'api' при авторизации по ключу (идёт в metadata аудит-записей)
}
export async function resolveRestActor(req: AuthenticatedRequest, organizationId: number): Promise<RestActor> {
if (!req.apiKey) {
const name = req.user
? (`${req.user.firstName || ''} ${req.user.middleName || ''} ${req.user.lastName || ''}`.trim() || req.user.email)
: 'API';
return { changedBy: req.user?.id ?? null, changedByName: name, botId: null, source: null };
}
const bot = req.apiKey.botId
? await storage.getBot(req.apiKey.botId, organizationId).catch(() => null)
: null;
return {
changedBy: bot ? null : req.apiKey.createdBy,
changedByName: bot?.name ?? `Ключ «${req.apiKey.label}»`,
botId: bot?.id ?? null,
source: 'api',
};
}
// Проверки авторизации по API-ключу для write-endpoint'а (изменение данных формы/задачи).
// Возвращает текст ошибки для 403 или null, если доступ разрешён.
// Для JWT-запросов всегда null (их проверки — в хендлерах, как раньше).
export function checkApiKeyWriteAccess(req: AuthenticatedRequest, formId: number): string | null {
if (!req.apiKey) return null;
if (req.apiKey.scopes.mode === 'read') {
return 'API-ключ в режиме read не может изменять данные';
}
if (!isFormAllowedByScopes(req.apiKey.scopes, formId)) {
return 'Доступ к этой форме запрещён правами API-ключа';
}
return null;
}