Права доступа API-ключей: скоупы read/write/full и выборочный доступ к формам и справочникам

- organization_api_keys.scopes (jsonb, NULL = полный доступ для legacy-ключей)
- MCP: фильтрация инструментов по режиму и проверка formIds в handler'ах
- PATCH /api/mcp-keys/:id, валидация скоупов в server/utils/api-key.ts
- UI Settings: диалог создания/редактирования ключа с режимом и мультивыбором форм/справочников
This commit is contained in:
2026-07-21 16:12:55 +03:00
parent 08b0979ca2
commit a7733b4a03
8 changed files with 660 additions and 103 deletions

View File

@@ -2,7 +2,7 @@
// Implementation is split into domain modules under server/storage/
import { users, organizations, userSessions, invitations, userCustomFields, userCustomValues, forms, formTabs, formFields, formStatuses, statusTransitions, tasks, taskFieldValues, fieldHistory, taskMessages, userNotifications, messageReads, bookmarkFolders, bookmarks, bots, botSubscriptions, botSessions, dataTables, dataTableRows, dataTablePermissions, externalServices, regularTableRows, fieldTemplates, customTabModules, taskTabValues, deviceTokens, userPresence, webPushSubscriptions, customPages, type User, type Organization, type UserSession, type Invitation, type InsertUser, type InsertOrganization, type InsertUserSession, type InsertInvitation, type UserWithOrganization, type UserCustomField, type UserCustomValue, type CreateUserCustomField, type UpdateUserCustomField, type UserProfileTab, type UserProfileField, type UserProfileFieldValue, type UserProfileAuditLog, type InsertUserProfileTab, type InsertUserProfileField, type InsertUserProfileFieldValue, type InsertUserProfileAuditLog, userProfileAuditLog, type Form, type FormTab, type FormField, type FormStatus, type StatusTransition, type Task, type TaskFieldValue, type FieldHistory, type TaskMessage, type TaskMessageWithAuthor, type UserNotification, type MessageRead, type InsertMessageRead, type BookmarkFolder, type Bookmark, type InsertForm, type InsertFormTab, type InsertFormField, type InsertFormStatus, type InsertStatusTransition, type InsertTask, type InsertTaskFieldValue, type InsertFieldHistory, type InsertTaskMessage, type InsertUserNotification, type InsertBookmarkFolder, type InsertBookmark, type Bot, type BotSubscription, type BotSession, type InsertBot, type InsertBotSubscription, type InsertBotSession, type BotWithSubscriptions, type DataTable, type DataTableRow, type DataTablePermission, type DataTableAccessRule, type InsertDataTableAccessRule, type InsertDataTable, type InsertDataTableRow, type InsertDataTablePermission, type DataTableWithRows, type DataTableFull, type ExternalService, type InsertExternalService, type RegularTableRow, type InsertRegularTableRow, type FieldTemplate, type InsertFieldTemplate, type CustomTabModule, type InsertCustomTabModule, type TaskTabValue, type InsertTaskTabValue, type DeviceToken, type InsertDeviceToken, type UserPresence, type InsertUserPresence, type WebPushSubscription, type CustomPage, type InsertCustomPage } from "@shared/schema";
import { organizationApiKeys, type OrganizationApiKey } from "@shared/schema";
import { organizationApiKeys, type OrganizationApiKey, type ApiKeyScopes } from "@shared/schema";
import { automations, type Automation, type InsertAutomation } from "@shared/schema";
import { taskRelations, type TaskRelation, type InsertTaskRelation } from "@shared/schema";
import { taskReminders, type TaskReminder, type InsertTaskReminder } from "@shared/schema";
@@ -364,7 +364,8 @@ export interface IStorage {
upsertTaskTabValues(taskId: number, tabId: number, values: Record<string, any>): Promise<TaskTabValue>;
// Organization API Keys (MCP)
createApiKey(organizationId: number, createdBy: number, label: string): Promise<{ key: string; record: OrganizationApiKey }>;
createApiKey(organizationId: number, createdBy: number, label: string, scopes?: ApiKeyScopes | null): Promise<{ key: string; record: OrganizationApiKey }>;
updateApiKey(id: number, organizationId: number, data: { label?: string; scopes?: ApiKeyScopes | null }): Promise<OrganizationApiKey | undefined>;
listApiKeys(organizationId: number): Promise<OrganizationApiKey[]>;
deleteApiKey(id: number, organizationId: number): Promise<void>;
getApiKeyByHash(rawKey: string): Promise<OrganizationApiKey | undefined>;