KeystoneJS: гранулярный контроль доступа и ролевая модель
При разработке headless CMS на KeystoneJS часто требуется разграничить доступ между редакторами, модераторами и администраторами, а также скрыть внутренние заметки от обычных пользователей. Типовое решение — многоуровневая система управления доступом: на уровне операций (CRUD), отдельных элементов и конкретных полей. Мы помогли более чем 30 проектам настроить такие модели «под ключ» — от аналитики до деплоя, с гарантией безопасности и производительности. В одном из проектов для издательского дома потребовалось 7 ролей с разным доступом к 15 спискам — ролевая модель через БД позволила менять права без перезапуска сервера, что сократило время на согласование изменений на 40%.
Почему KeystoneJS выигрывает у Strapi в гибкости доступа?
Strapi использует жёсткие роли с фиксированными разрешениями, а Directus предлагает только три уровня (public, readonly, full). KeystoneJS даёт четыре уровня — Operation, Filter, Item, Field — плюс возможность хранить роли в базе. Это позволяет реализовать любые бизнес-правила без хардкода. Например, мы настроили систему, где редактор может редактировать только свои черновики, модератор — публиковать чужие, а админ — удалять. Настройка заняла 3 дня, тогда как на Strapi пришлось бы писать кастомный middleware, что увеличило бы срок до 2 недель — это в 4.5 раза дольше.
Как настроить контроль доступа для нескольких ролей в KeystoneJS?
Разберём настройку на примере типового проекта. Сначала определите списки и роли. Для каждой роли создайте запись в списке Role с полями-флагами, как показано ниже. Затем в access-функциях каждого списка проверяйте эти флаги. Такой подход гибок и позволяет управлять правами через админку.
Уровни доступа KeystoneJS: от операций до полей
Система доступа KeystoneJS построена на четырёх уровнях. Каждый решает свою задачу.
| Уровень | Что контролирует | Когда применяется | Пример |
|---|---|---|---|
| Operation Access | CRUD-операции целиком | До выборки из БД | Только админ может удалять |
| Filter Access | Видимые записи через фильтр | Автоматически в запрос | Редактор видит только свои посты |
| Item Access | Конкретная запись после выборки | После загрузки из БД | Редактор может менять только черновики |
| Field Access | Конкретное поле | При чтении/записи | Зарплату видят только HR |
Эти уровни детально описаны в документации KeystoneJS Access Control Guide. Вот пример комбинированной настройки для списка Post:
access: { operation: { query: ({ session }) => !!session, create: ({ session }) => session?.data?.role?.canManagePosts, update: ({ session }) => ['editor', 'admin'].includes(session?.data?.role), delete: ({ session }) => session?.data?.role === 'admin', }, filter: { query: ({ session }) => { if (session?.data?.role === 'admin') return true; return { author: { id: { equals: session?.data?.id } } }; }, }, item: { update: async ({ session, item }) => { if (session?.data?.role === 'admin') return true; return item.status === 'draft' && item.authorId === session?.data?.id; }, }, fields: { internalNotes: text({ access: { read: ({ session }) => session?.data?.role === 'admin', create: ({ session }) => session?.data?.role === 'admin', update: ({ session }) => session?.data?.role === 'admin', }, }), salary: integer({ access: { read: ({ session }) => ['admin', 'hr'].includes(session?.data?.role), update: ({ session }) => session?.data?.role === 'admin', }, }), }, }, Как хранить роли в базе данных?
Вместо хардкода ролей в коде — хранение прав в БД. Это позволяет менять разрешения через админку без перезапуска.
Сравнение подходов:
| Подход | Гибкость | Возможность изменения без деплоя | Производительность |
|---|---|---|---|
| Хардкод в access-функциях | Низкая | Нет | Высокая |
| Хранение в БД (список Role) | Высокая | Да | Средняя (дополнительный запрос) |
// lists/Role.ts export const Role = list({ access: { operation: { query: allowAll, create: ({ session }) => session?.data?.role === 'admin', update: ({ session }) => session?.data?.role === 'admin', delete: ({ session }) => session?.data?.role === 'admin', }, }, fields: { name: text({ validation: { isRequired: true }, isIndexed: 'unique' }), canManagePosts: checkbox({ defaultValue: false }), canManageUsers: checkbox({ defaultValue: false }), canManageRoles: checkbox({ defaultValue: false }), canPublish: checkbox({ defaultValue: false }), users: relationship({ ref: 'User.role', many: true }), }, }); // auth.ts sessionData: 'id name email role { canManagePosts canManageUsers canPublish }', Использование в списке Post:
access: { operation: { create: ({ session }) => !!session?.data?.role?.canManagePosts, update: ({ session }) => !!session?.data?.role?.canManagePosts, delete: ({ session }) => !!session?.data?.role?.canManagePosts, }, }, Как мы настраиваем доступ: процесс и объём работ
- Анализ объектов и ролей — определяем списки, операции, поля. На выходе матрица прав.
- Проектирование ролевой модели — создаём список Role с чекбоксами для каждого разрешения.
- Реализация access-функций — пишем Operation, Filter, Item, Field для каждого списка.
- Тестирование сценариев — проверяем права для каждой роли (до 20 кейсов).
- Деплой и мониторинг — разворачиваем на сервере и следим за логами.
Что входит в работу
- Разработка ролевой модели (до 10 списков)
- Настройка CRUD-доступа для каждого списка
- Фильтрация видимости записей
- Скрытие/блокировка полей
- Интеграция с вашей системой аутентификации
- Тестирование сценариев доступа
- Документация по доступным ролям и правам
- Гарантия на корректную работу прав доступа
Частые ошибки при настройке доступа
Новички часто путают уровни — например, используют Filter Access, когда нужен Item Access. Filter Access работает автоматически на этапе запроса и не может проверить поля элемента, которые ещё не загружены. Item Access подходит для проверки статуса записи или авторства. Ещё одна типичная ошибка — забыть включить нужные поля роли в sessionData. Без этого access-функции не увидят права. Мы проверяем все сценарии, чтобы исключить такие ситуации.
Сроки и стоимость
Настройка типовой ролевой модели (3–4 роли, 5–10 списков) занимает 2–4 дня. Стоимость рассчитывается индивидуально — напишите нам, оценим ваш проект за 1 день. Получите консультацию инженера по вашему проекту.
Опытные инженеры с 5+ годами работы с KeystoneJS помогут реализовать даже сложные сценарии доступа. Свяжитесь с нами для оценки — это бесплатно.







