בקרת גישה גרעינית והרשאות מבוססות תפקידים ב-KeystoneJS
כאשר בונים CMS ללא ראש על KeystoneJS, לעיתים קרובות יש צורך לפצל גישה בין עורכים, מנחים ומנהלים, ולהסתיר הערות פנימיות ממשתמשים רגילים. פתרון טיפוסי הוא מערכת בקרת גישה רב-רמות: ברמת הפעולה (CRUD), ברמת הפריט וברמת השדה. סי�נו ליותר מ-30 פרויקטים להקים מודלים כאלה במפתח מלא—מניתוח ועד פריסה, עם ערבויות אבטחה וביצועים. בפרויקט אחד עבור בית הוצאה לאור, נדרשו 7 תפקידים עם גישה שונה ל-15 רשימות. מודל התפקידים דרך מסד הנתונים אפשר לשנות הרשאות ללא הפעלה מחדש של השרת, וקיצר את זמן אישור השינויים ב-40%.
למה KeystoneJS מנצח את Strapi בגמישות גישה
Strapi משתמש בתפקידים נוקשים עם הרשאות קבועות, ו-Directus מציעה רק שלוש רמות (ציבורי, קריאה בלבד, מלא). KeystoneJS מספק ארבע רמות—פעולה, סינון, פריט, שדה—בנוסף ליכולת לאחסן תפקידים במסד הנתונים. זה מאפשר ליישם כל חוק עסקי ללא קידוד קשיח. לדוגמה, הקמנו מערכת שבה עורך יכול לערוך רק טיוטות שלו, מנחה יכול לפרסם של אחרים, ומנהל יכול למחוק. ההתקנה ארכה 3 ימים, בעוד שב-Strapi היינו צריכים לכתוב middleware מותאם אישית, מה שמאריך את לוח הזמנים לשבועיים—פי 4.5 יותר.
כיצד להגדיר בקרת גישה למספר תפקידים ב-KeystoneJS
בואו נפרק את ההתקנה באמצעות דוגמה טיפוסית לפרויקט. ראשית, הגדירו את הרשימות והתפקידים. עבור כל תפקיד, צרו רשומה ברשימת Role עם שדות דגל כפי שמוצג למטה. לאחר מכן, בפונקציות הגישה של כל רשימה, בדקו את הדגלים הללו. גישה זו גמישה ומאפשרת ניהול הרשאות דרך לוח הניהול.
רמות גישה ב-KeystoneJS: מפעולות ועד שדות
בקרת הגישה של KeystoneJS מבוססת על ארבע רמות. כל אחת פותרת משימה ספציפית.
| רמה | מה היא שולטת | מתי היא חלה | דוגמה |
|---|---|---|---|
| גישת פעולה | כל פעולות ה-CRUD | לפני שאילתת מסד נתונים | רק מנהל יכול למחוק |
| גישת סינון | רשומות גלויות דרך סינון | אוטומטית בשאילתה | עורך רואה רק פוסטים שלו |
| גישת פריט | רשומה ספציפית לאחר שליפה | לאחר טעינה ממסד הנתונים | עורך יכול לשנות רק טיוטות |
| גישת שדה | שדה ספציפי | בקריאה/כתיבה | משכורת גלויה רק למשאבי אנוש |
רמות אלה מתוארות בפירוט במדריך בקרת הגישה של KeystoneJS. הנה דוגמה להגדרה משולבת עבור רשימת 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',
},
}),
},
} כיצד לאחסן תפקידים במסד הנתונים
במקום לקודד תפקידים בקוד, אחסנו הרשאות במסד הנתונים. זה מאפשר לשנות הרשאות דרך לוח הניהול ללא הפעלה מחדש.
השוואת גישות:
| גישה | גמישות | שינוי ללא פריסה | ביצועים |
|---|---|---|---|
| קידוד קשיח בפונקציות גישה | נמוכה | לא | גבוהים |
| אחסון במסד הנתונים (רשימת 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 }',
שימוש ברשימת 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: {
operation: {
create: ({ session }) => !!session?.data?.role?.canManagePosts,
update: ({ session }) => !!session?.data?.role?.canManagePosts,
delete: ({ session }) => !!session?.data?.role?.canManagePosts,
},
}, כיצד הקמנו גישה: תהליך והיקף עבודה
- ניתוח אובייקטים ותפקידים — הגדרת רשימות, פעולות, שדות. פלט: מטריצת הרשאות.
- עיצוב מודל תפקידים — יצירת רשימת Role עם תיבות סימון לכל הרשאה.
- יישום פונקציות גישה — כתיבת פעולה, סינון, פריט, שדה עבור כל רשימה.
- בדיקת תרחישים — אימות הרשאות לכל תפקיד (עד 20 מקרים).
- פריסה וניטור — פריסה לשרת וניטור לוגים.
מה כלול בעבודה
- פיתוח מודל תפקידים (עד 10 רשימות)
- הגדרת גישת CRUD לכל רשימה
- סינון נראות רשומות
- הסתרה / נעילת שדות
- אינטגרציה עם מערכת האימות שלך
- בדיקת תרחישי גישה
- תיעוד תפקידים והרשאות זמינים
- ערבות לפעולה תקינה של זכויות גישה
טעויות נפוצות בהגדרת גישה
מתחילים לעיתים קרובות מבלבלים בין רמות—לדוגמה, שימוש בגישת סינון כאשר נדרשת גישת פריט. גישת סינון פועלת אוטומטית בשלב השאילתה ואינה יכולה לבדוק שדות פריט שעדיין לא נטענו. גישת פריט מתאימה לבדיקת סטטוס או בעלות. טעות טיפוסית נוספת היא שכחת הכללת שדות התפקיד הנדרשים ב-// 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 }', . ללא זה, פונקציות הגישה לא יראו את ההרשאות. אנו בודקים את כל התרחישים כדי למנוע בעיות כאלה.
לוחות זמנים ועלות
הקמת מודל תפקידים טיפוסי (3–4 תפקידים, 5–10 רשימות) אורכת 2–4 ימים. העלות מחושבת באופן אישי—צרו קשר, נעריך את הפרויקט שלכם תוך יום אחד. קבלו ייעוץ מהנדס לפרויקט שלכם.
מהנדסים מנוסים עם 5+ שנות ניסיון בעבודה עם KeystoneJS יסייעו ליישם אפילו תרחישי גישה מורכבים. צרו קשר להערכה.







