- 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
45 lines
2.1 KiB
TypeScript
45 lines
2.1 KiB
TypeScript
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;
|
||
}
|