Контроль доступа в Payload CMS
Payload реализует контроль доступа через функции, возвращающие true, false или объект-условие (MongoDB query / SQL WHERE). Нет YAML-конфигов или GUI — только TypeScript-функции, которые получают контекст запроса и возвращают решение.
Структура Access Control
// Функция доступа получает: req (с req.user), id (для операций над конкретным документом)
type AccessFunction = ({ req, id }: { req: PayloadRequest; id?: string | number }) =>
boolean | Where | Promise<boolean | Where>
Where — это объект-условие, который Payload передаёт в запрос к БД. Это значит, что пользователь получает только документы, удовлетворяющие условию — не проверку на каждый документ по отдельности.
Роли пользователей
// collections/Users.ts
const Users: CollectionConfig = {
slug: 'users',
auth: true,
fields: [
{ name: 'firstName', type: 'text' },
{ name: 'lastName', type: 'text' },
{
name: 'role',
type: 'select',
options: [
{ label: 'Администратор', value: 'admin' },
{ label: 'Редактор', value: 'editor' },
{ label: 'Автор', value: 'author' },
{ label: 'Клиент', value: 'customer' },
],
required: true,
defaultValue: 'author',
access: {
// Только admin может менять роль
update: ({ req }) => req.user?.role === 'admin',
},
},
],
}
Контроль доступа к коллекции
// collections/Posts.ts
const Posts: CollectionConfig = {
slug: 'posts',
access: {
// Публичное чтение только опубликованных
read: ({ req }) => {
if (req.user?.role === 'admin' || req.user?.role === 'editor') {
return true // Видят всё
}
return {
status: { equals: 'published' } // Остальные только published
}
},
// Создавать могут editor и author
create: ({ req }) =>
['admin', 'editor', 'author'].includes(req.user?.role || ''),
// Обновлять: admin/editor — всё, author — только своё
update: ({ req }) => {
if (!req.user) return false
if (['admin', 'editor'].includes(req.user.role)) return true
if (req.user.role === 'author') {
return { author: { equals: req.user.id } }
}
return false
},
// Удалять только admin
delete: ({ req }) => req.user?.role === 'admin',
},
}
Контроль доступа на уровне поля
fields: [
{
name: 'internalNotes',
type: 'textarea',
access: {
// Поле видно только admin и editor
read: ({ req }) => ['admin', 'editor'].includes(req.user?.role || ''),
// Обновлять может только admin
update: ({ req }) => req.user?.role === 'admin',
},
},
{
name: 'publishedAt',
type: 'date',
access: {
// Дату публикации устанавливает только editor или admin
update: ({ req }) => ['admin', 'editor'].includes(req.user?.role || ''),
},
},
]
Динамический доступ через организации
Для мультитенантных схем — доступ через связанную организацию:
// collections/Documents.ts
{
slug: 'documents',
access: {
read: ({ req }) => {
if (!req.user) return false
if (req.user.role === 'admin') return true
// Пользователь видит только документы своей организации
return {
organization: { equals: req.user.organization }
}
},
update: ({ req }) => {
if (!req.user) return false
if (req.user.role === 'admin') return true
return {
and: [
{ organization: { equals: req.user.organization } },
{ lockedBy: { not_equals: req.user.id } }, // не заблокирован другим
]
}
},
},
}
Защита API эндпоинтов
// Кастомный эндпоинт с проверкой доступа
{
path: '/export',
method: 'get',
handler: async (req: PayloadRequest, res: Response) => {
// Проверка аутентификации
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' })
}
// Проверка роли
if (!['admin', 'editor'].includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' })
}
// Аудит лог
await req.payload.create({
collection: 'audit-logs',
data: {
action: 'export',
user: req.user.id,
timestamp: new Date().toISOString(),
},
})
const data = await req.payload.find({ collection: 'documents', limit: 10000 })
return res.json(data)
},
}
Публичное API с ограничениями
// Для публичных запросов (без авторизации)
read: ({ req }) => {
if (!req.user) {
// Только активные, только часть полей
return {
and: [
{ status: { equals: 'active' } },
{ visibleToPublic: { equals: true } },
]
}
}
return true
}
Utility-хелперы для переиспользования
// utils/access.ts
export const isAdmin = ({ req }: AccessArgs) => req.user?.role === 'admin'
export const isEditorOrAbove = ({ req }: AccessArgs) =>
['admin', 'editor'].includes(req.user?.role || '')
export const isAuthenticated = ({ req }: AccessArgs) => Boolean(req.user)
export const isOwner = ({ req, id }: AccessArgs) => {
if (!req.user) return false
if (req.user.role === 'admin') return true
return { createdBy: { equals: req.user.id } }
}
// Использование:
access: {
read: () => true,
create: isAuthenticated,
update: isOwner,
delete: isAdmin,
}
Сроки
Настройка системы ролей и контроля доступа для проекта с 3–5 ролями и 5–10 коллекциями — 2–3 дня.







