Контроль доступу в 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 дні.







