@zealamic/payload-auth-rbac-plugin 1.0.0-beta.1 → 1.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +27 -31
  2. package/assets/cover-photo.jpg +0 -0
  3. package/dist/collections/permission-actions/default-data.d.ts +2 -0
  4. package/dist/collections/permission-actions/index.d.ts +3 -0
  5. package/dist/collections/permission-actions/types.d.ts +51 -0
  6. package/dist/collections/permission-features/default-data.d.ts +2 -0
  7. package/dist/collections/permission-features/index.d.ts +3 -0
  8. package/dist/collections/permission-features/types.d.ts +43 -0
  9. package/dist/collections/permissions/default-data.d.ts +2 -0
  10. package/dist/collections/permissions/index.d.ts +3 -0
  11. package/dist/collections/permissions/types.d.ts +54 -0
  12. package/dist/collections/roles/default-data.d.ts +2 -0
  13. package/dist/collections/roles/hooks/sync-permission-matrix-draft.d.ts +6 -0
  14. package/dist/collections/roles/index.d.ts +3 -0
  15. package/dist/collections/roles/types.d.ts +53 -0
  16. package/dist/collections/roles-permissions/default-data.d.ts +2 -0
  17. package/dist/collections/roles-permissions/index.d.ts +3 -0
  18. package/dist/collections/roles-permissions/types.d.ts +39 -0
  19. package/dist/collections/users/default-data.d.ts +2 -0
  20. package/dist/collections/users/index.d.ts +3 -0
  21. package/dist/collections/users/parent-path.d.ts +31 -0
  22. package/dist/collections/users/types.d.ts +23 -0
  23. package/dist/components/role-permission-matrix-client/default-data.d.ts +2 -0
  24. package/dist/components/role-permission-matrix-client/default-data.js +2 -1
  25. package/dist/components/role-permission-matrix-client/default-data.js.map +1 -1
  26. package/dist/components/role-permission-matrix-client/index.d.ts +1 -0
  27. package/dist/components/role-permission-matrix-client/index.js +18 -68
  28. package/dist/components/role-permission-matrix-client/index.js.map +1 -1
  29. package/dist/components/role-permission-matrix-client/matrix.module.scss +69 -0
  30. package/dist/components/role-permission-matrix-client/types.d.ts +18 -0
  31. package/dist/endpoints/customEndpointHandler.d.ts +2 -0
  32. package/dist/exports/client.d.ts +1 -0
  33. package/dist/exports/rsc.d.ts +1 -0
  34. package/dist/index.d.ts +5 -0
  35. package/dist/lib/constants/general.d.ts +1 -0
  36. package/dist/lib/constants/index.d.ts +14 -0
  37. package/dist/lib/constants/permission-action.d.ts +8 -0
  38. package/dist/lib/constants/permission-feature.d.ts +4 -0
  39. package/dist/lib/constants/permission.d.ts +4 -0
  40. package/dist/lib/constants/role.d.ts +9 -0
  41. package/dist/lib/constants/user.d.ts +1 -0
  42. package/dist/lib/utils/access.d.ts +76 -0
  43. package/dist/lib/utils/data.d.ts +6 -0
  44. package/dist/lib/utils/fields.d.ts +18 -0
  45. package/dist/lib/utils/index.d.ts +4 -0
  46. package/dist/lib/utils/localization.d.ts +27 -0
  47. package/dist/styles/variables.scss +1 -0
  48. package/dist/types.d.ts +38 -0
  49. package/docs/COLLECTIONS.md +440 -0
  50. package/docs/TRANSLATIONS.md +462 -0
  51. package/docs/UTILS.md +226 -0
  52. package/package.json +21 -4
@@ -0,0 +1,440 @@
1
+ # Collections guide
2
+
3
+ This plugin targets **Payload CMS 3.x** (`payload ^3.84.1`). It is **not compatible with Payload 2.x**.
4
+
5
+ It registers **five RBAC collections** and optionally **augments your app's users collection**.
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ ```
12
+ permission-features ──┐
13
+ permission-actions ──┼──► permissions ◄── roles-permissions ──► roles ◄── users.roles
14
+ ```
15
+
16
+ | Config key (camelCase) | Slug (API) | Purpose |
17
+ | ---------------------- | --------------------- | --------------------------------------------- |
18
+ | `permissionActions` | `permission-actions` | Action verbs (`create`, `read`, …) |
19
+ | `permissionFeatures` | `permission-features` | Resource areas (`users`, `posts`, …) |
20
+ | `permissions` | `permissions` | Feature + action pairs (enforceable units) |
21
+ | `roles` | `roles` | Role definitions + permission matrix UI |
22
+ | `rolesPermissions` | `roles-permissions` | Join table: role ↔ permission ↔ enabled |
23
+ | — | `users` (app) | Auth collection — plugin adds fields + access |
24
+
25
+ **Defaults:** only **super admins** can access the five RBAC collections. `roles-permissions` is **hidden** in Admin.
26
+
27
+ ---
28
+
29
+ ## Collection details
30
+
31
+ ### `permission-actions`
32
+
33
+ | Field | Description |
34
+ | ----------- | -------------------------------------------------------------- |
35
+ | `code` | Unique code — must match `actionCode` in `getPermissionAccess` |
36
+ | `type` | `main` (matrix column) or `sub` (sub-action row) |
37
+ | `sortOrder` | Display order |
38
+ | `status` | `active` / `inactive` |
39
+
40
+ ### `permission-features`
41
+
42
+ | Field | Description |
43
+ | ----------- | -------------------------------------------------------- |
44
+ | `code` | Unique code — must match `featureCode` in access helpers |
45
+ | `sortOrder` | Display order |
46
+ | `status` | `active` / `inactive` |
47
+
48
+ ### `permissions`
49
+
50
+ | Field | Description |
51
+ | ------------------- | -------------------------------------- |
52
+ | `name` | Human-readable label |
53
+ | `permissionFeature` | → `permission-features` |
54
+ | `permissionAction` | → `permission-actions` |
55
+ | `sortOrder` | Optional ordering |
56
+ | `status` | Only `active` permissions are enforced |
57
+
58
+ ### `roles`
59
+
60
+ | Field | Description |
61
+ | ----------------------- | -------------------------------------------------------------------------------------------------- |
62
+ | `code` | Unique machine identifier |
63
+ | `name` | Display name |
64
+ | `description` | Optional |
65
+ | `status` | `active` / `inactive` |
66
+ | `dataScope` | `own` / `hierarchy` / `all` — limits **which documents** a user can read/update/delete (see below) |
67
+ | `permissionMatrixDraft` | JSON field + custom matrix UI (update screen only) |
68
+
69
+ #### What is `dataScope`?
70
+
71
+ `dataScope` answers: _“After the user passes the permission check, which rows/documents can they see or change?”_
72
+
73
+ It works **together with** the permission matrix (`roles-permissions`). The matrix controls **whether** an action is allowed; `dataScope` controls **how far** that action reaches.
74
+
75
+ | Value | Who can access documents |
76
+ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------- |
77
+ | `own` | Only records the user created (`createdBy` = current user) |
78
+ | `hierarchy` | Records created by the user **or** their subordinates in the user tree (`users.parent` / `parentPath`) |
79
+ | `all` | Any record **within collections that use data-scope helpers** — still requires an enabled permission in the matrix (see note below) |
80
+
81
+ > **`all` vs `isSuperAdmin` — not the same thing**
82
+ >
83
+ > | | `dataScope: all` (on a role) | `isSuperAdmin: true` (on a user) |
84
+ > | ---------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------- |
85
+ > | **What it controls** | Row-level filter only — “see all documents in this feature” | Full bypass of RBAC permission + data-scope checks |
86
+ > | **Still needs matrix permission?** | **Yes** — user must have the feature/action enabled on a role | **No** — skips `roles-permissions` entirely |
87
+ > | **Typical use** | Operational roles: “read/update all **posts**”, “read all **orders**” | Break-glass / platform admins: manage roles, permissions, system config |
88
+ > | **Privilege level** | Lower than super admin | Highest |
89
+ >
90
+ > **Recommended split for developers:**
91
+ >
92
+ > - Use **`dataScope: all`** on **business roles** when someone should access all records of a _specific feature_ (e.g. all `posts`), but only for actions you grant in the matrix.
93
+ > - Reserve **`isSuperAdmin`** for a small set of accounts that manage **system-wide RBAC config**: `roles`, `permissions`, `permission-features`, `permission-actions`, and other global settings. Plugin RBAC collections default to `getSuperAdminAccess` for this reason.
94
+ > - Do **not** treat `all` as a substitute for super admin. A role with `all` on `posts` can read every post **only if** it has `posts` + `read` in the matrix — it cannot manage roles or permissions unless you explicitly grant those features too.
95
+ >
96
+ > Exact behavior depends on how you wire `access` on each collection (`getPermissionAccess` vs data-scope helpers). The table above reflects the **intended** separation.
97
+
98
+ **Multiple roles:** if a user has more than one role, the **widest** scope wins:
99
+
100
+ ```
101
+ all > hierarchy > own
102
+ ```
103
+
104
+ **Example:** a user with roles `Author` (`own`) and `Manager` (`hierarchy`) effectively gets `hierarchy`.
105
+
106
+ **Where it applies:** data-scope helpers such as `getPermissionAndDataScopeReadAccess` and `getPermissionAndDataScopeMutationAccess`. Plain `getPermissionAccess` checks permission only — no row filter.
107
+
108
+ **What you need in your app collections:**
109
+
110
+ 1. A field storing the creator (default: `createdBy` → relationship to `users`)
111
+ 2. Set it on create (hook or default value)
112
+ 3. Pass `options: { createdByField: "createdBy" }` to data-scope helpers
113
+
114
+ ```ts
115
+ // posts collection — read filtered by dataScope
116
+ read: getPermissionAndDataScopeReadAccess({
117
+ featureCode: "posts",
118
+ actionCode: "read",
119
+ options: { createdByField: "createdBy" },
120
+ }),
121
+ ```
122
+
123
+ **Users collection:** the plugin uses `createdByField: "id"` (each user document is “owned” by itself) and adds `parent` / `parentPath` for hierarchy. See [UTILS](https://github.com/zealamic/payload-auth-rbac-plugin/blob/main/docs/UTILS.md) for full helper reference.
124
+
125
+ **Super admin (`isSuperAdmin`):** bypasses permission checks **and** data scope on every helper. Use sparingly for platform administrators — not for everyday business roles. Set via seed/Local API only (`isSuperAdmin` is read-only in Admin).
126
+
127
+ **How the permission matrix works:**
128
+
129
+ 1. Admin toggles checkboxes → updates `permissionMatrixDraft` on the form
130
+ 2. **Save role** → `afterChange` hook syncs to `roles-permissions`
131
+ 3. Runtime RBAC reads **`roles-permissions`**, not the JSON draft
132
+
133
+ **Field name vs translation key:** the schema field is `permissionMatrixDraft`; the translation label key is `permissionMatrix`:
134
+
135
+ ```ts
136
+ translations: {
137
+ en: {
138
+ collections: {
139
+ roles: {
140
+ fields: {
141
+ permissionMatrix: { label: "Permission Matrix" }, // label for permissionMatrixDraft
142
+ },
143
+ },
144
+ },
145
+ },
146
+ }
147
+ ```
148
+
149
+ ### `roles-permissions`
150
+
151
+ | Field | Description |
152
+ | ------------ | ----------------------------- |
153
+ | `role` | → `roles` |
154
+ | `permission` | → `permissions` |
155
+ | `enabled` | Grant on/off (default `true`) |
156
+
157
+ Managed via the matrix on the role edit screen — you normally do not open this collection in Admin.
158
+
159
+ ---
160
+
161
+ ## Users collection
162
+
163
+ The plugin does **not** add a separate users collection. It **augments** the collection referenced by `config.admin.user` (default: `users`).
164
+
165
+ Toggle with `autoModifyUsersCollection` (default: `true`).
166
+
167
+ ### Fields the plugin adds
168
+
169
+ | Field | Description |
170
+ | -------------- | ---------------------------------------------------- |
171
+ | `isSuperAdmin` | Bypasses all checks; **read-only** in Admin |
172
+ | `roles` | hasMany relationship → `roles` |
173
+ | `parent` | Relationship to parent user (for `hierarchy` scope) |
174
+ | `parentPath` | Hidden, auto-maintained — used for hierarchy lookups |
175
+
176
+ ### Default access on users
177
+
178
+ `featureCode` = users collection slug (e.g. `"users"`).
179
+
180
+ | Operation | `actionCode` |
181
+ | -------------- | ----------------------- |
182
+ | `create` | `create` |
183
+ | `read` | `read` (+ data scope) |
184
+ | `update` | `update` (+ data scope) |
185
+ | `delete` | `delete` (+ data scope) |
186
+ | `readVersions` | `readVersions` |
187
+ | `unlock` | `unlock` |
188
+
189
+ Seed a `permission-features` record with `code` equal to the users slug, plus matching actions and permissions.
190
+
191
+ **Your access overrides the plugin:** spread order is `{ ...pluginDefaults, ...yourAccess }` — your handler wins for the same operation.
192
+
193
+ ### Bootstrap super admin
194
+
195
+ `isSuperAdmin` cannot be set in the Admin UI. Use a seed script or Local API:
196
+
197
+ ```ts
198
+ await payload.update({
199
+ collection: "users",
200
+ id: userId,
201
+ data: { isSuperAdmin: true },
202
+ overrideAccess: true,
203
+ });
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Customizing collections
209
+
210
+ Pass overrides under `collections` in plugin config:
211
+
212
+ ```ts
213
+ import {
214
+ getPermissionAccess,
215
+ payloadAuthRbacPlugin,
216
+ } from "@zealamic/payload-auth-rbac-plugin";
217
+
218
+ export default buildConfig({
219
+ plugins: [
220
+ payloadAuthRbacPlugin({
221
+ autoModifyUsersCollection: true,
222
+ collections: {
223
+ permissionActions: {
224
+ /* ... */
225
+ },
226
+ permissionFeatures: {
227
+ /* ... */
228
+ },
229
+ permissions: {
230
+ /* ... */
231
+ },
232
+ roles: {
233
+ /* ... */
234
+ },
235
+ rolesPermissions: {
236
+ /* ... */
237
+ },
238
+ },
239
+ translations: {
240
+ /* label i18n — see TRANSLATIONS.md */
241
+ },
242
+ }),
243
+ ],
244
+ });
245
+ ```
246
+
247
+ ### What you can override
248
+
249
+ | Property | Effect |
250
+ | -------- | --------------------------------------------------- |
251
+ | `fields` | Add fields or merge with defaults **by field name** |
252
+ | `access` | Override access handlers (spread after defaults) |
253
+ | `labels` | Collection singular/plural labels |
254
+ | `admin` | Group, columns, hidden, etc. |
255
+
256
+ ### Field merge rules
257
+
258
+ - **Same `name`** → shallow merge `{ ...pluginField, ...yourField }`
259
+ - **New name** → appended after defaults
260
+ - **Wrong name** → duplicate field — always match existing names exactly
261
+
262
+ `admin`, `hooks`, and `validate` on a field override **replace** the plugin field's values entirely (no deep merge).
263
+
264
+ ---
265
+
266
+ ## Customization examples
267
+
268
+ ### 1. Hide a field in Admin
269
+
270
+ ```ts
271
+ collections: {
272
+ permissionActions: {
273
+ fields: [
274
+ {
275
+ name: "sortOrder",
276
+ type: "number",
277
+ admin: { hidden: true },
278
+ },
279
+ ],
280
+ },
281
+ },
282
+ ```
283
+
284
+ ### 2. Let non–super-admins read/update permission-actions
285
+
286
+ ```ts
287
+ collections: {
288
+ permissionActions: {
289
+ access: {
290
+ read: getPermissionAccess({
291
+ featureCode: "permission-actions",
292
+ actionCode: "read",
293
+ }),
294
+ update: getPermissionAccess({
295
+ featureCode: "permission-actions",
296
+ actionCode: "update",
297
+ }),
298
+ },
299
+ },
300
+ },
301
+ ```
302
+
303
+ Requires matching `permission-features`, `permission-actions`, and `permissions` records in the database.
304
+
305
+ ### 3. Change role list columns
306
+
307
+ ```ts
308
+ collections: {
309
+ roles: {
310
+ admin: {
311
+ defaultColumns: ["code", "name", "dataScope", "status"],
312
+ },
313
+ },
314
+ },
315
+ ```
316
+
317
+ ### 4. Add a custom field to roles
318
+
319
+ ```ts
320
+ collections: {
321
+ roles: {
322
+ fields: [
323
+ {
324
+ name: "department",
325
+ type: "text",
326
+ admin: { position: "sidebar" },
327
+ },
328
+ ],
329
+ },
330
+ },
331
+ ```
332
+
333
+ ### 5. Expose `roles-permissions` for debugging
334
+
335
+ ```ts
336
+ collections: {
337
+ rolesPermissions: {
338
+ admin: { hidden: false },
339
+ },
340
+ },
341
+ ```
342
+
343
+ ### 6. Translate user field labels (not via `collections.users`)
344
+
345
+ ```ts
346
+ payloadAuthRbacPlugin({
347
+ translations: {
348
+ en: {
349
+ collections: {
350
+ users: {
351
+ fields: {
352
+ roles: { label: "Assigned Roles" },
353
+ isSuperAdmin: { label: "Super Admin" },
354
+ },
355
+ },
356
+ },
357
+ },
358
+ },
359
+ });
360
+ ```
361
+
362
+ ### 7. Custom users slug (`admin.user`)
363
+
364
+ ```ts
365
+ export default buildConfig({
366
+ admin: { user: "members" },
367
+ plugins: [payloadAuthRbacPlugin({ autoModifyUsersCollection: true })],
368
+ collections: [
369
+ {
370
+ slug: "members",
371
+ auth: true,
372
+ fields: [{ name: "email", type: "email", required: true }],
373
+ },
374
+ ],
375
+ });
376
+ ```
377
+
378
+ Seed `permission-features` with `code: "members"` to match the slug.
379
+
380
+ ### 8. Disable auto-modify users
381
+
382
+ ```ts
383
+ payloadAuthRbacPlugin({
384
+ autoModifyUsersCollection: false,
385
+ });
386
+ ```
387
+
388
+ You must add `roles`, `isSuperAdmin`, and wire `getPermissionAccess` on the users collection yourself.
389
+
390
+ ---
391
+
392
+ ## Quick setup checklist
393
+
394
+ 1. Register the plugin in `payload.config.ts`
395
+ 2. Seed **permission-features** (`users`, `posts`, …)
396
+ 3. Seed **permission-actions** (`create`, `read`, `update`, `delete`, …)
397
+ 4. Create **permissions** (one row per feature + action pair)
398
+ 5. Create **roles**, configure the matrix, and Save
399
+ 6. Assign **roles** to users
400
+ 7. Bootstrap a **super admin** (seed / DB)
401
+ 8. Apply access helpers on app collections — see [UTILS](https://github.com/zealamic/payload-auth-rbac-plugin/blob/main/docs/UTILS.md)
402
+
403
+ ---
404
+
405
+ ## Deletes and referential integrity
406
+
407
+ The plugin does **not** cascade deletes. Recommended:
408
+
409
+ - Prefer `status: inactive` over deleting features, actions, or permissions
410
+ - Before deleting a role: remove it from users and delete related `roles-permissions` rows
411
+
412
+ ```ts
413
+ // Example: clean join rows when a role is deleted
414
+ hooks: {
415
+ beforeDelete: [
416
+ async ({ id, req }) => {
417
+ await req.payload.delete({
418
+ collection: "roles-permissions",
419
+ where: { role: { equals: id } },
420
+ req,
421
+ });
422
+ },
423
+ ],
424
+ },
425
+ ```
426
+
427
+ ---
428
+
429
+ ## Quick reference
430
+
431
+ | Goal | Use |
432
+ | -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
433
+ | Translate labels | `translations` → [TRANSLATIONS](https://github.com/zealamic/payload-auth-rbac-plugin/blob/main/docs/TRANSLATIONS.md) |
434
+ | Hide / extend fields | `collections.<key>.fields` |
435
+ | Change who can CRUD | `collections.<key>.access` |
436
+ | Access helpers | [UTILS](https://github.com/zealamic/payload-auth-rbac-plugin/blob/main/docs/UTILS.md) |
437
+ | Setup & plugin config | [README](https://github.com/zealamic/payload-auth-rbac-plugin/blob/main/README.md) |
438
+ | Disable users modification | `autoModifyUsersCollection: false` |
439
+
440
+ Working demos: `dev/rbac.ts`, `dev/collections/posts.ts`.