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 помогут реализовать даже сложные сценарии доступа. Свяжитесь с нами для оценки.







