payload-rbac-plugin 1.0.1 → 1.0.3-rc

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.
package/README.md CHANGED
@@ -103,6 +103,8 @@ export default buildConfig({
103
103
  | `permissionsFields` | `Field[]` | `[]` | Extra custom fields to inject into the Permissions collection. |
104
104
  | `hideRoles` | `boolean \| ((args: { user: any }) => boolean)` | `false` | Hide the Roles collection from the sidebar navigation. |
105
105
  | `hidePermissions` | `boolean \| ((args: { user: any }) => boolean)` | `false` | Hide the Permissions collection from the sidebar navigation. |
106
+ | `permissionsAccess` | `CollectionConfig['access']` | `{ ... }` | Access control functions for the generated Permissions collection (defaults to public). |
107
+ | `rolesAccess` | `CollectionConfig['access']` | `{ ... }` | Access control functions for the generated Roles collection (defaults to public). |
106
108
 
107
109
  ---
108
110
 
@@ -1,27 +1,120 @@
1
+ import { createBeforePermissionChangeHook } from '../hooks/beforePermissionChange';
1
2
  export const createPermissionsCollection = (options)=>{
2
3
  const slug = options.permissionsCollectionSlug || 'permissions';
3
4
  const customFields = options.permissionsFields || [];
4
5
  return {
5
6
  slug,
6
- admin: {
7
- useAsTitle: 'name',
8
- group: 'Access Control'
9
- },
10
7
  access: {
11
8
  read: ()=>true
12
9
  },
10
+ admin: {
11
+ group: 'Access Control',
12
+ hidden: options.hidePermissions ?? false,
13
+ useAsTitle: 'name'
14
+ },
13
15
  fields: [
16
+ {
17
+ name: 'type',
18
+ type: 'select',
19
+ admin: {
20
+ condition: (data)=>!data?.id && !data?._id && !data?.createdAt,
21
+ description: 'Choose whether to create a single custom permission or generate multiple CRUD permissions at once.',
22
+ disableListColumn: true,
23
+ disableListFilter: true
24
+ },
25
+ defaultValue: 'single',
26
+ options: [
27
+ {
28
+ label: 'Single Permission',
29
+ value: 'single'
30
+ },
31
+ {
32
+ label: 'Bulk CRUD Generator',
33
+ value: 'bulk'
34
+ }
35
+ ]
36
+ },
14
37
  {
15
38
  name: 'name',
16
39
  type: 'text',
17
- required: true,
40
+ admin: {
41
+ condition: (data)=>data?.type === 'single',
42
+ description: 'The unique name of the permission (e.g., "access:admin", "posts:create").'
43
+ },
18
44
  unique: true,
45
+ validate: (value, { data })=>{
46
+ if (data?.type === 'single' && !value) {
47
+ return 'Name is required for single permissions.';
48
+ }
49
+ return true;
50
+ }
51
+ },
52
+ {
53
+ name: 'collectionName',
54
+ type: 'text',
19
55
  admin: {
20
- description: 'The unique name of the permission (e.g., "create:users", "read:posts").'
56
+ condition: (data)=>data?.type === 'bulk',
57
+ description: 'Enter a collection slug (e.g., "posts") to auto-generate permissions.',
58
+ disableListColumn: true,
59
+ disableListFilter: true
21
60
  }
22
61
  },
62
+ {
63
+ type: 'row',
64
+ fields: [
65
+ {
66
+ name: 'create',
67
+ type: 'checkbox',
68
+ admin: {
69
+ condition: (data)=>data?.type === 'bulk',
70
+ disableListColumn: true,
71
+ disableListFilter: true,
72
+ width: '25%'
73
+ },
74
+ defaultValue: false
75
+ },
76
+ {
77
+ name: 'read',
78
+ type: 'checkbox',
79
+ admin: {
80
+ condition: (data)=>data?.type === 'bulk',
81
+ disableListColumn: true,
82
+ disableListFilter: true,
83
+ width: '25%'
84
+ },
85
+ defaultValue: false
86
+ },
87
+ {
88
+ name: 'update',
89
+ type: 'checkbox',
90
+ admin: {
91
+ condition: (data)=>data?.type === 'bulk',
92
+ disableListColumn: true,
93
+ disableListFilter: true,
94
+ width: '25%'
95
+ },
96
+ defaultValue: false
97
+ },
98
+ {
99
+ name: 'delete',
100
+ type: 'checkbox',
101
+ admin: {
102
+ condition: (data)=>data?.type === 'bulk',
103
+ disableListColumn: true,
104
+ disableListFilter: true,
105
+ width: '25%'
106
+ },
107
+ defaultValue: false
108
+ }
109
+ ]
110
+ },
23
111
  ...customFields
24
- ]
112
+ ],
113
+ hooks: {
114
+ beforeChange: [
115
+ createBeforePermissionChangeHook(slug)
116
+ ]
117
+ }
25
118
  };
26
119
  };
27
120
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collections/Permissions.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\nimport type { PluginOptions } from '../types.js'\n\nexport const createPermissionsCollection = (options: PluginOptions): CollectionConfig => {\n const slug = options.permissionsCollectionSlug || 'permissions'\n const customFields = options.permissionsFields || []\n\n return {\n slug,\n admin: {\n useAsTitle: 'name',\n group: 'Access Control',\n },\n access: {\n read: () => true, // Access logic can be customized or injected later\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The unique name of the permission (e.g., \"create:users\", \"read:posts\").',\n },\n },\n ...customFields,\n ],\n }\n}\n"],"names":["createPermissionsCollection","options","slug","permissionsCollectionSlug","customFields","permissionsFields","admin","useAsTitle","group","access","read","fields","name","type","required","unique","description"],"mappings":"AAGA,OAAO,MAAMA,8BAA8B,CAACC;IAC1C,MAAMC,OAAOD,QAAQE,yBAAyB,IAAI;IAClD,MAAMC,eAAeH,QAAQI,iBAAiB,IAAI,EAAE;IAEpD,OAAO;QACLH;QACAI,OAAO;YACLC,YAAY;YACZC,OAAO;QACT;QACAC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,UAAU;gBACVC,QAAQ;gBACRT,OAAO;oBACLU,aAAa;gBACf;YACF;eACGZ;SACJ;IACH;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/collections/Permissions.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { PluginOptions } from '../types'\n\nimport { createBeforePermissionChangeHook } from '../hooks/beforePermissionChange'\n\nexport const createPermissionsCollection = (options: PluginOptions): CollectionConfig => {\n const slug = options.permissionsCollectionSlug || 'permissions'\n const customFields = options.permissionsFields || []\n\n return {\n slug,\n access: {\n read: () => true,\n },\n admin: {\n group: 'Access Control',\n hidden: options.hidePermissions ?? false,\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'type',\n type: 'select',\n admin: {\n condition: (data) => !data?.id && !data?._id && !data?.createdAt,\n description:\n 'Choose whether to create a single custom permission or generate multiple CRUD permissions at once.',\n disableListColumn: true,\n disableListFilter: true,\n },\n defaultValue: 'single',\n options: [\n { label: 'Single Permission', value: 'single' },\n { label: 'Bulk CRUD Generator', value: 'bulk' },\n ],\n },\n {\n name: 'name',\n type: 'text',\n admin: {\n condition: (data) => data?.type === 'single',\n description: 'The unique name of the permission (e.g., \"access:admin\", \"posts:create\").',\n },\n unique: true,\n validate: (value: null | string | undefined, { data }: { data?: any }) => {\n if (data?.type === 'single' && !value) {\n return 'Name is required for single permissions.'\n }\n return true\n },\n },\n {\n name: 'collectionName',\n type: 'text',\n admin: {\n condition: (data) => data?.type === 'bulk',\n description: 'Enter a collection slug (e.g., \"posts\") to auto-generate permissions.',\n disableListColumn: true,\n disableListFilter: true,\n },\n },\n {\n type: 'row',\n fields: [\n {\n name: 'create',\n type: 'checkbox',\n admin: {\n condition: (data) => data?.type === 'bulk',\n disableListColumn: true,\n disableListFilter: true,\n width: '25%',\n },\n defaultValue: false,\n },\n {\n name: 'read',\n type: 'checkbox',\n admin: {\n condition: (data) => data?.type === 'bulk',\n disableListColumn: true,\n disableListFilter: true,\n width: '25%',\n },\n defaultValue: false,\n },\n {\n name: 'update',\n type: 'checkbox',\n admin: {\n condition: (data) => data?.type === 'bulk',\n disableListColumn: true,\n disableListFilter: true,\n width: '25%',\n },\n defaultValue: false,\n },\n {\n name: 'delete',\n type: 'checkbox',\n admin: {\n condition: (data) => data?.type === 'bulk',\n disableListColumn: true,\n disableListFilter: true,\n width: '25%',\n },\n defaultValue: false,\n },\n ],\n },\n ...customFields,\n ],\n hooks: {\n beforeChange: [createBeforePermissionChangeHook(slug)],\n },\n }\n}\n"],"names":["createBeforePermissionChangeHook","createPermissionsCollection","options","slug","permissionsCollectionSlug","customFields","permissionsFields","access","read","admin","group","hidden","hidePermissions","useAsTitle","fields","name","type","condition","data","id","_id","createdAt","description","disableListColumn","disableListFilter","defaultValue","label","value","unique","validate","width","hooks","beforeChange"],"mappings":"AAIA,SAASA,gCAAgC,QAAQ,kCAAiC;AAElF,OAAO,MAAMC,8BAA8B,CAACC;IAC1C,MAAMC,OAAOD,QAAQE,yBAAyB,IAAI;IAClD,MAAMC,eAAeH,QAAQI,iBAAiB,IAAI,EAAE;IAEpD,OAAO;QACLH;QACAI,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,OAAO;YACLC,OAAO;YACPC,QAAQT,QAAQU,eAAe,IAAI;YACnCC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLQ,WAAW,CAACC,OAAS,CAACA,MAAMC,MAAM,CAACD,MAAME,OAAO,CAACF,MAAMG;oBACvDC,aACE;oBACFC,mBAAmB;oBACnBC,mBAAmB;gBACrB;gBACAC,cAAc;gBACdvB,SAAS;oBACP;wBAAEwB,OAAO;wBAAqBC,OAAO;oBAAS;oBAC9C;wBAAED,OAAO;wBAAuBC,OAAO;oBAAO;iBAC/C;YACH;YACA;gBACEZ,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLQ,WAAW,CAACC,OAASA,MAAMF,SAAS;oBACpCM,aAAa;gBACf;gBACAM,QAAQ;gBACRC,UAAU,CAACF,OAAkC,EAAET,IAAI,EAAkB;oBACnE,IAAIA,MAAMF,SAAS,YAAY,CAACW,OAAO;wBACrC,OAAO;oBACT;oBACA,OAAO;gBACT;YACF;YACA;gBACEZ,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLQ,WAAW,CAACC,OAASA,MAAMF,SAAS;oBACpCM,aAAa;oBACbC,mBAAmB;oBACnBC,mBAAmB;gBACrB;YACF;YACA;gBACER,MAAM;gBACNF,QAAQ;oBACN;wBACEC,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLQ,WAAW,CAACC,OAASA,MAAMF,SAAS;4BACpCO,mBAAmB;4BACnBC,mBAAmB;4BACnBM,OAAO;wBACT;wBACAL,cAAc;oBAChB;oBACA;wBACEV,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLQ,WAAW,CAACC,OAASA,MAAMF,SAAS;4BACpCO,mBAAmB;4BACnBC,mBAAmB;4BACnBM,OAAO;wBACT;wBACAL,cAAc;oBAChB;oBACA;wBACEV,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLQ,WAAW,CAACC,OAASA,MAAMF,SAAS;4BACpCO,mBAAmB;4BACnBC,mBAAmB;4BACnBM,OAAO;wBACT;wBACAL,cAAc;oBAChB;oBACA;wBACEV,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLQ,WAAW,CAACC,OAASA,MAAMF,SAAS;4BACpCO,mBAAmB;4BACnBC,mBAAmB;4BACnBM,OAAO;wBACT;wBACAL,cAAc;oBAChB;iBACD;YACH;eACGpB;SACJ;QACD0B,OAAO;YACLC,cAAc;gBAAChC,iCAAiCG;aAAM;QACxD;IACF;AACF,EAAC"}
@@ -6,7 +6,8 @@ export const createRolesCollection = (options)=>{
6
6
  slug,
7
7
  admin: {
8
8
  useAsTitle: 'name',
9
- group: 'Access Control'
9
+ group: 'Access Control',
10
+ hidden: options.hideRoles ?? false
10
11
  },
11
12
  access: {
12
13
  read: ()=>true
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collections/Roles.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\nimport type { PluginOptions } from '../types.js'\n\nexport const createRolesCollection = (options: PluginOptions): CollectionConfig => {\n const slug = options.rolesCollectionSlug || 'roles'\n const permissionsSlug = options.permissionsCollectionSlug || 'permissions'\n const customFields = options.rolesFields || []\n\n return {\n slug,\n admin: {\n useAsTitle: 'name',\n group: 'Access Control',\n },\n access: {\n read: () => true,\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The unique name of the role (e.g., \"admin\", \"editor\").',\n },\n },\n {\n name: 'permissions',\n type: 'relationship',\n relationTo: permissionsSlug,\n hasMany: true,\n admin: {\n description: 'The permissions assigned to this role.',\n },\n },\n ...customFields,\n ],\n }\n}\n"],"names":["createRolesCollection","options","slug","rolesCollectionSlug","permissionsSlug","permissionsCollectionSlug","customFields","rolesFields","admin","useAsTitle","group","access","read","fields","name","type","required","unique","description","relationTo","hasMany"],"mappings":"AAGA,OAAO,MAAMA,wBAAwB,CAACC;IACpC,MAAMC,OAAOD,QAAQE,mBAAmB,IAAI;IAC5C,MAAMC,kBAAkBH,QAAQI,yBAAyB,IAAI;IAC7D,MAAMC,eAAeL,QAAQM,WAAW,IAAI,EAAE;IAE9C,OAAO;QACLL;QACAM,OAAO;YACLC,YAAY;YACZC,OAAO;QACT;QACAC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,UAAU;gBACVC,QAAQ;gBACRT,OAAO;oBACLU,aAAa;gBACf;YACF;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNI,YAAYf;gBACZgB,SAAS;gBACTZ,OAAO;oBACLU,aAAa;gBACf;YACF;eACGZ;SACJ;IACH;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/collections/Roles.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload';\nimport type { PluginOptions } from '../types';\n\nexport const createRolesCollection = (options: PluginOptions): CollectionConfig => {\n const slug = options.rolesCollectionSlug || 'roles'\n const permissionsSlug = options.permissionsCollectionSlug || 'permissions'\n const customFields = options.rolesFields || []\n\n return {\n slug,\n admin: {\n useAsTitle: 'name',\n group: 'Access Control',\n hidden: options.hideRoles ?? false,\n },\n access: {\n read: () => true,\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The unique name of the role (e.g., \"admin\", \"editor\").',\n },\n },\n {\n name: 'permissions',\n type: 'relationship',\n relationTo: permissionsSlug,\n hasMany: true,\n admin: {\n description: 'The permissions assigned to this role.',\n },\n },\n ...customFields,\n ],\n }\n}\n"],"names":["createRolesCollection","options","slug","rolesCollectionSlug","permissionsSlug","permissionsCollectionSlug","customFields","rolesFields","admin","useAsTitle","group","hidden","hideRoles","access","read","fields","name","type","required","unique","description","relationTo","hasMany"],"mappings":"AAGA,OAAO,MAAMA,wBAAwB,CAACC;IACpC,MAAMC,OAAOD,QAAQE,mBAAmB,IAAI;IAC5C,MAAMC,kBAAkBH,QAAQI,yBAAyB,IAAI;IAC7D,MAAMC,eAAeL,QAAQM,WAAW,IAAI,EAAE;IAE9C,OAAO;QACLL;QACAM,OAAO;YACLC,YAAY;YACZC,OAAO;YACPC,QAAQV,QAAQW,SAAS,IAAI;QAC/B;QACAC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,UAAU;gBACVC,QAAQ;gBACRX,OAAO;oBACLY,aAAa;gBACf;YACF;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNI,YAAYjB;gBACZkB,SAAS;gBACTd,OAAO;oBACLY,aAAa;gBACf;YACF;eACGd;SACJ;IACH;AACF,EAAC"}
@@ -1,3 +1,3 @@
1
- export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js';
1
+ export { BeforeDashboardClient } from '../components/BeforeDashboardClient';
2
2
 
3
3
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js'\n"],"names":["BeforeDashboardClient"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
1
+ {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { BeforeDashboardClient } from '../components/BeforeDashboardClient';\n\n"],"names":["BeforeDashboardClient"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,sCAAsC"}
@@ -1,3 +1,3 @@
1
- export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js';
1
+ export { BeforeDashboardServer } from '../components/BeforeDashboardServer';
2
2
 
3
3
  //# sourceMappingURL=rsc.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js'\n"],"names":["BeforeDashboardServer"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
1
+ {"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { BeforeDashboardServer } from '../components/BeforeDashboardServer';\n\n"],"names":["BeforeDashboardServer"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,sCAAsC"}
@@ -0,0 +1,58 @@
1
+ export const createBeforePermissionChangeHook = (slug)=>{
2
+ return async ({ data, req })=>{
3
+ // Only run generator logic on create/update if type is bulk
4
+ if (data.type === 'bulk' && data.collectionName) {
5
+ const actions = [
6
+ 'create',
7
+ 'read',
8
+ 'update',
9
+ 'delete'
10
+ ].filter((action)=>data[action] === true);
11
+ if (actions.length === 0) {
12
+ throw new Error('You must select at least one CRUD operation when using the Bulk Generator.');
13
+ }
14
+ // e.g. ["posts:create", "posts:read"]
15
+ const generatedNames = actions.map((action)=>`${data.collectionName}:${action}`);
16
+ // Filter out names that already exist
17
+ const uniqueNames = [];
18
+ for (const name of generatedNames){
19
+ const existing = await req.payload.find({
20
+ collection: slug,
21
+ where: {
22
+ name: {
23
+ equals: name
24
+ }
25
+ }
26
+ });
27
+ if (existing.docs.length === 0) {
28
+ uniqueNames.push(name);
29
+ }
30
+ }
31
+ if (uniqueNames.length === 0) {
32
+ throw new Error('All selected CRUD permissions already exist.');
33
+ }
34
+ // Mutate current document to save the first unique generated name
35
+ data.name = uniqueNames[0];
36
+ // Create the remaining unique permissions in the background
37
+ const remainingNames = uniqueNames.slice(1);
38
+ for (const name of remainingNames){
39
+ await req.payload.create({
40
+ collection: slug,
41
+ data: {
42
+ name
43
+ }
44
+ });
45
+ }
46
+ }
47
+ // Ensure UI-only fields are never saved to the database
48
+ delete data.type;
49
+ delete data.collectionName;
50
+ delete data.create;
51
+ delete data.read;
52
+ delete data.update;
53
+ delete data.delete;
54
+ return data;
55
+ };
56
+ };
57
+
58
+ //# sourceMappingURL=beforePermissionChange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/beforePermissionChange.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\nexport const createBeforePermissionChangeHook = (slug: string): CollectionBeforeChangeHook => {\n return async ({ data, req }) => {\n // Only run generator logic on create/update if type is bulk\n if (data.type === 'bulk' && data.collectionName) {\n const actions = ['create', 'read', 'update', 'delete'].filter(\n (action) => data[action] === true,\n )\n\n if (actions.length === 0) {\n throw new Error('You must select at least one CRUD operation when using the Bulk Generator.')\n }\n\n // e.g. [\"posts:create\", \"posts:read\"]\n const generatedNames = actions.map((action) => `${data.collectionName}:${action}`)\n\n // Filter out names that already exist\n const uniqueNames: string[] = []\n for (const name of generatedNames) {\n const existing = await req.payload.find({\n collection: slug,\n where: {\n name: { equals: name },\n },\n })\n if (existing.docs.length === 0) {\n uniqueNames.push(name)\n }\n }\n\n if (uniqueNames.length === 0) {\n throw new Error('All selected CRUD permissions already exist.')\n }\n\n // Mutate current document to save the first unique generated name\n data.name = uniqueNames[0]\n\n // Create the remaining unique permissions in the background\n const remainingNames = uniqueNames.slice(1)\n for (const name of remainingNames) {\n await req.payload.create({\n collection: slug,\n data: {\n name,\n },\n })\n }\n }\n\n // Ensure UI-only fields are never saved to the database\n delete data.type\n delete data.collectionName\n delete data.create\n delete data.read\n delete data.update\n delete data.delete\n\n return data\n }\n}\n"],"names":["createBeforePermissionChangeHook","slug","data","req","type","collectionName","actions","filter","action","length","Error","generatedNames","map","uniqueNames","name","existing","payload","find","collection","where","equals","docs","push","remainingNames","slice","create","read","update","delete"],"mappings":"AAEA,OAAO,MAAMA,mCAAmC,CAACC;IAC/C,OAAO,OAAO,EAAEC,IAAI,EAAEC,GAAG,EAAE;QACzB,4DAA4D;QAC5D,IAAID,KAAKE,IAAI,KAAK,UAAUF,KAAKG,cAAc,EAAE;YAC/C,MAAMC,UAAU;gBAAC;gBAAU;gBAAQ;gBAAU;aAAS,CAACC,MAAM,CAC3D,CAACC,SAAWN,IAAI,CAACM,OAAO,KAAK;YAG/B,IAAIF,QAAQG,MAAM,KAAK,GAAG;gBACxB,MAAM,IAAIC,MAAM;YAClB;YAEA,sCAAsC;YACtC,MAAMC,iBAAiBL,QAAQM,GAAG,CAAC,CAACJ,SAAW,GAAGN,KAAKG,cAAc,CAAC,CAAC,EAAEG,QAAQ;YAEjF,sCAAsC;YACtC,MAAMK,cAAwB,EAAE;YAChC,KAAK,MAAMC,QAAQH,eAAgB;gBACjC,MAAMI,WAAW,MAAMZ,IAAIa,OAAO,CAACC,IAAI,CAAC;oBACtCC,YAAYjB;oBACZkB,OAAO;wBACLL,MAAM;4BAAEM,QAAQN;wBAAK;oBACvB;gBACF;gBACA,IAAIC,SAASM,IAAI,CAACZ,MAAM,KAAK,GAAG;oBAC9BI,YAAYS,IAAI,CAACR;gBACnB;YACF;YAEA,IAAID,YAAYJ,MAAM,KAAK,GAAG;gBAC5B,MAAM,IAAIC,MAAM;YAClB;YAEA,kEAAkE;YAClER,KAAKY,IAAI,GAAGD,WAAW,CAAC,EAAE;YAE1B,4DAA4D;YAC5D,MAAMU,iBAAiBV,YAAYW,KAAK,CAAC;YACzC,KAAK,MAAMV,QAAQS,eAAgB;gBACjC,MAAMpB,IAAIa,OAAO,CAACS,MAAM,CAAC;oBACvBP,YAAYjB;oBACZC,MAAM;wBACJY;oBACF;gBACF;YACF;QACF;QAEA,wDAAwD;QACxD,OAAOZ,KAAKE,IAAI;QAChB,OAAOF,KAAKG,cAAc;QAC1B,OAAOH,KAAKuB,MAAM;QAClB,OAAOvB,KAAKwB,IAAI;QAChB,OAAOxB,KAAKyB,MAAM;QAClB,OAAOzB,KAAK0B,MAAM;QAElB,OAAO1B;IACT;AACF,EAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createPermissionsCollection } from './collections/Permissions.js';
2
- import { createRolesCollection } from './collections/Roles.js';
1
+ import { createPermissionsCollection } from './collections/Permissions';
2
+ import { createRolesCollection } from './collections/Roles';
3
3
  export const rbac = (pluginOptions)=>(config)=>{
4
4
  const options = pluginOptions || {};
5
5
  if (options.enabled === false) {
@@ -32,7 +32,7 @@ export const rbac = (pluginOptions)=>(config)=>{
32
32
  return config;
33
33
  };
34
34
  // Export utilities and types for consumers
35
- export * from './types.js';
36
- export * from './utilities/index.js';
35
+ export * from './types';
36
+ export * from './utilities/index';
37
37
 
38
38
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\nimport type { PluginOptions } from './types.js'\n\nimport { createPermissionsCollection } from './collections/Permissions.js'\nimport { createRolesCollection } from './collections/Roles.js'\n\nexport const rbac =\n (pluginOptions?: PluginOptions) =>\n (config: Config): Config => {\n const options = pluginOptions || {}\n \n if (options.enabled === false) {\n return config\n }\n\n if (!config.collections) {\n config.collections = []\n }\n\n // Add Roles and Permissions collections\n config.collections.push(createPermissionsCollection(options))\n config.collections.push(createRolesCollection(options))\n\n // Extend the target auth collection\n const authSlug = options.authCollectionSlug || 'users'\n const rolesSlug = options.rolesCollectionSlug || 'roles'\n\n const authCollection = config.collections.find(\n (collection) => collection.slug === authSlug,\n )\n\n if (authCollection) {\n authCollection.fields.push({\n name: 'roles',\n type: 'relationship',\n relationTo: rolesSlug,\n hasMany: true,\n saveToJWT: true,\n admin: {\n description: 'Roles assigned to this user.',\n },\n })\n } else {\n console.warn(`[rbac-plugin] Auth collection '${authSlug}' not found. Cannot inject roles field.`)\n }\n\n return config\n }\n\n// Export utilities and types for consumers\nexport * from './types.js'\nexport * from './utilities/index.js'\n"],"names":["createPermissionsCollection","createRolesCollection","rbac","pluginOptions","config","options","enabled","collections","push","authSlug","authCollectionSlug","rolesSlug","rolesCollectionSlug","authCollection","find","collection","slug","fields","name","type","relationTo","hasMany","saveToJWT","admin","description","console","warn"],"mappings":"AAGA,SAASA,2BAA2B,QAAQ,+BAA8B;AAC1E,SAASC,qBAAqB,QAAQ,yBAAwB;AAE9D,OAAO,MAAMC,OACX,CAACC,gBACD,CAACC;QACC,MAAMC,UAAUF,iBAAiB,CAAC;QAElC,IAAIE,QAAQC,OAAO,KAAK,OAAO;YAC7B,OAAOF;QACT;QAEA,IAAI,CAACA,OAAOG,WAAW,EAAE;YACvBH,OAAOG,WAAW,GAAG,EAAE;QACzB;QAEA,wCAAwC;QACxCH,OAAOG,WAAW,CAACC,IAAI,CAACR,4BAA4BK;QACpDD,OAAOG,WAAW,CAACC,IAAI,CAACP,sBAAsBI;QAE9C,oCAAoC;QACpC,MAAMI,WAAWJ,QAAQK,kBAAkB,IAAI;QAC/C,MAAMC,YAAYN,QAAQO,mBAAmB,IAAI;QAEjD,MAAMC,iBAAiBT,OAAOG,WAAW,CAACO,IAAI,CAC5C,CAACC,aAAeA,WAAWC,IAAI,KAAKP;QAGtC,IAAII,gBAAgB;YAClBA,eAAeI,MAAM,CAACT,IAAI,CAAC;gBACzBU,MAAM;gBACNC,MAAM;gBACNC,YAAYT;gBACZU,SAAS;gBACTC,WAAW;gBACXC,OAAO;oBACLC,aAAa;gBACf;YACF;QACF,OAAO;YACLC,QAAQC,IAAI,CAAC,CAAC,+BAA+B,EAAEjB,SAAS,uCAAuC,CAAC;QAClG;QAEA,OAAOL;IACT,EAAC;AAEH,2CAA2C;AAC3C,cAAc,aAAY;AAC1B,cAAc,uBAAsB"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config } from 'payload';\nimport type { PluginOptions } from './types';\n\nimport { createPermissionsCollection } from './collections/Permissions';\nimport { createRolesCollection } from './collections/Roles';\n\nexport const rbac =\n (pluginOptions?: PluginOptions) =>\n (config: Config): Config => {\n const options = pluginOptions || {}\n \n if (options.enabled === false) {\n return config\n }\n\n if (!config.collections) {\n config.collections = []\n }\n\n // Add Roles and Permissions collections\n config.collections.push(createPermissionsCollection(options))\n config.collections.push(createRolesCollection(options))\n\n // Extend the target auth collection\n const authSlug = options.authCollectionSlug || 'users'\n const rolesSlug = options.rolesCollectionSlug || 'roles'\n\n const authCollection = config.collections.find(\n (collection) => collection.slug === authSlug,\n )\n\n if (authCollection) {\n authCollection.fields.push({\n name: 'roles',\n type: 'relationship',\n relationTo: rolesSlug,\n hasMany: true,\n saveToJWT: true,\n admin: {\n description: 'Roles assigned to this user.',\n },\n })\n } else {\n console.warn(`[rbac-plugin] Auth collection '${authSlug}' not found. Cannot inject roles field.`)\n }\n\n return config\n }\n\n// Export utilities and types for consumers\nexport * from './types';\nexport * from './utilities/index';\n\n"],"names":["createPermissionsCollection","createRolesCollection","rbac","pluginOptions","config","options","enabled","collections","push","authSlug","authCollectionSlug","rolesSlug","rolesCollectionSlug","authCollection","find","collection","slug","fields","name","type","relationTo","hasMany","saveToJWT","admin","description","console","warn"],"mappings":"AAGA,SAASA,2BAA2B,QAAQ,4BAA4B;AACxE,SAASC,qBAAqB,QAAQ,sBAAsB;AAE5D,OAAO,MAAMC,OACX,CAACC,gBACD,CAACC;QACC,MAAMC,UAAUF,iBAAiB,CAAC;QAElC,IAAIE,QAAQC,OAAO,KAAK,OAAO;YAC7B,OAAOF;QACT;QAEA,IAAI,CAACA,OAAOG,WAAW,EAAE;YACvBH,OAAOG,WAAW,GAAG,EAAE;QACzB;QAEA,wCAAwC;QACxCH,OAAOG,WAAW,CAACC,IAAI,CAACR,4BAA4BK;QACpDD,OAAOG,WAAW,CAACC,IAAI,CAACP,sBAAsBI;QAE9C,oCAAoC;QACpC,MAAMI,WAAWJ,QAAQK,kBAAkB,IAAI;QAC/C,MAAMC,YAAYN,QAAQO,mBAAmB,IAAI;QAEjD,MAAMC,iBAAiBT,OAAOG,WAAW,CAACO,IAAI,CAC5C,CAACC,aAAeA,WAAWC,IAAI,KAAKP;QAGtC,IAAII,gBAAgB;YAClBA,eAAeI,MAAM,CAACT,IAAI,CAAC;gBACzBU,MAAM;gBACNC,MAAM;gBACNC,YAAYT;gBACZU,SAAS;gBACTC,WAAW;gBACXC,OAAO;oBACLC,aAAa;gBACf;YACF;QACF,OAAO;YACLC,QAAQC,IAAI,CAAC,CAAC,+BAA+B,EAAEjB,SAAS,uCAAuC,CAAC;QAClG;QAEA,OAAOL;IACT,EAAC;AAEH,2CAA2C;AAC3C,cAAc,UAAU;AACxB,cAAc,oBAAoB"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nexport interface PluginOptions {\n /**\n * Enable or disable plugin\n * @default false\n */\n enabled?: boolean\n\n /**\n * Override the default collection slug for Roles\n * @default 'roles'\n */\n rolesCollectionSlug?: string\n\n /**\n * Override the default collection slug for Permissions\n * @default 'permissions'\n */\n permissionsCollectionSlug?: string\n\n /**\n * The collection slug for the authentication collection (typically 'users')\n * @default 'users'\n */\n authCollectionSlug?: string\n\n /**\n * Inject additional custom fields into the generated Roles collection\n */\n rolesFields?: Field[]\n\n /**\n * Inject additional custom fields into the generated Permissions collection\n */\n permissionsFields?: Field[]\n}\n"],"names":[],"mappings":"AAEA,WAkCC"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nexport interface PluginOptions {\n /**\n * Enable or disable plugin\n * @default false\n */\n enabled?: boolean\n\n /**\n * Override the default collection slug for Roles\n * @default 'roles'\n */\n rolesCollectionSlug?: string\n\n /**\n * Override the default collection slug for Permissions\n * @default 'permissions'\n */\n permissionsCollectionSlug?: string\n\n /**\n * The collection slug for the authentication collection (typically 'users')\n * @default 'users'\n */\n authCollectionSlug?: string\n\n /**\n * Inject additional custom fields into the generated Roles collection\n */\n rolesFields?: Field[]\n\n /**\n * Inject additional custom fields into the generated Permissions collection\n */\n permissionsFields?: Field[]\n\n /**\n * Hide the Roles collection from the Admin panel sidebar/dashboard\n * Can be a boolean or a function based on the logged-in user\n * @default false\n */\n hideRoles?: boolean | ((args: { user: any }) => boolean)\n\n /**\n * Hide the Permissions collection from the Admin panel sidebar/dashboard\n * Can be a boolean or a function based on the logged-in user\n * @default false\n */\n hidePermissions?: boolean | ((args: { user: any }) => boolean)\n}\n"],"names":[],"mappings":"AAEA,WAgDC"}
@@ -1,4 +1,4 @@
1
- import { hasPermission } from './hasPermission.js';
1
+ import { hasPermission } from './hasPermission';
2
2
  /**
3
3
  * A Higher-Order Function to drop into Payload Collection access control fields.
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/checkPermission.ts"],"sourcesContent":["import type { Access } from 'payload'\nimport { hasPermission } from './hasPermission.js'\n\n/**\n * A Higher-Order Function to drop into Payload Collection access control fields.\n * \n * @param permissionName - The required permission name\n * @returns Access function\n */\nexport const checkPermission = (permissionName: string): Access => {\n return ({ req: { user } }) => {\n // We can just rely on the synchronous hasPermission check.\n // If relations are unpopulated, it will return false. Ensure your auth\n // collection is configured with adequate depth for saveToJWT or default depth.\n return hasPermission(user, permissionName)\n }\n}\n"],"names":["hasPermission","checkPermission","permissionName","req","user"],"mappings":"AACA,SAASA,aAAa,QAAQ,qBAAoB;AAElD;;;;;CAKC,GACD,OAAO,MAAMC,kBAAkB,CAACC;IAC9B,OAAO,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE;QACvB,2DAA2D;QAC3D,uEAAuE;QACvE,+EAA+E;QAC/E,OAAOJ,cAAcI,MAAMF;IAC7B;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/checkPermission.ts"],"sourcesContent":["import type { Access } from 'payload';\nimport { hasPermission } from './hasPermission';\n\n/**\n * A Higher-Order Function to drop into Payload Collection access control fields.\n * \n * @param permissionName - The required permission name\n * @returns Access function\n */\nexport const checkPermission = (permissionName: string): Access => {\n return ({ req: { user } }) => {\n // We can just rely on the synchronous hasPermission check.\n // If relations are unpopulated, it will return false. Ensure your auth\n // collection is configured with adequate depth for saveToJWT or default depth.\n return hasPermission(user, permissionName)\n }\n}\n"],"names":["hasPermission","checkPermission","permissionName","req","user"],"mappings":"AACA,SAASA,aAAa,QAAQ,kBAAkB;AAEhD;;;;;CAKC,GACD,OAAO,MAAMC,kBAAkB,CAACC;IAC9B,OAAO,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE;QACvB,2DAA2D;QAC3D,uEAAuE;QACvE,+EAA+E;QAC/E,OAAOJ,cAAcI,MAAMF;IAC7B;AACF,EAAC"}
@@ -19,16 +19,11 @@
19
19
  continue;
20
20
  }
21
21
  for (const permission of permissions){
22
- // If permission is an object (populated) and has a name
23
22
  if (typeof permission === 'object' && permission !== null) {
24
23
  if ('name' in permission && permission.name === requiredPermission) {
25
24
  return true;
26
25
  }
27
26
  }
28
- // Note: If permission is just an ID (unpopulated), we cannot synchronously
29
- // determine its name here. In a strictly synchronous check, we must return false
30
- // or ignore it. For a fully robust check with unpopulated data, an async version
31
- // requiring the payload instance would be necessary.
32
27
  }
33
28
  }
34
29
  return false;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/hasPermission.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\n/**\n * Checks if a user has a specific permission.\n * Gracefully handles populated and unpopulated relationship states as far as possible synchronously.\n * \n * @param user - The Payload user object from req.user\n * @param requiredPermission - The name of the permission to check for\n * @returns boolean\n */\nexport const hasPermission = (\n user: PayloadRequest['user'] | null | undefined,\n requiredPermission: string,\n): boolean => {\n if (!user || !user.roles || !Array.isArray(user.roles)) {\n return false\n }\n\n for (const role of user.roles) {\n // If role is just an ID (unpopulated), we can't check its permissions synchronously\n if (typeof role !== 'object' || role === null) {\n continue\n }\n\n const permissions = role.permissions\n if (!permissions || !Array.isArray(permissions)) {\n continue\n }\n\n for (const permission of permissions) {\n // If permission is an object (populated) and has a name\n if (typeof permission === 'object' && permission !== null) {\n if ('name' in permission && permission.name === requiredPermission) {\n return true\n }\n } \n // Note: If permission is just an ID (unpopulated), we cannot synchronously\n // determine its name here. In a strictly synchronous check, we must return false \n // or ignore it. For a fully robust check with unpopulated data, an async version \n // requiring the payload instance would be necessary.\n }\n }\n\n return false\n}\n"],"names":["hasPermission","user","requiredPermission","roles","Array","isArray","role","permissions","permission","name"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,MAAMA,gBAAgB,CAC3BC,MACAC;IAEA,IAAI,CAACD,QAAQ,CAACA,KAAKE,KAAK,IAAI,CAACC,MAAMC,OAAO,CAACJ,KAAKE,KAAK,GAAG;QACtD,OAAO;IACT;IAEA,KAAK,MAAMG,QAAQL,KAAKE,KAAK,CAAE;QAC7B,oFAAoF;QACpF,IAAI,OAAOG,SAAS,YAAYA,SAAS,MAAM;YAC7C;QACF;QAEA,MAAMC,cAAcD,KAAKC,WAAW;QACpC,IAAI,CAACA,eAAe,CAACH,MAAMC,OAAO,CAACE,cAAc;YAC/C;QACF;QAEA,KAAK,MAAMC,cAAcD,YAAa;YACpC,wDAAwD;YACxD,IAAI,OAAOC,eAAe,YAAYA,eAAe,MAAM;gBACzD,IAAI,UAAUA,cAAcA,WAAWC,IAAI,KAAKP,oBAAoB;oBAClE,OAAO;gBACT;YACF;QACA,2EAA2E;QAC3E,kFAAkF;QAClF,kFAAkF;QAClF,qDAAqD;QACvD;IACF;IAEA,OAAO;AACT,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/hasPermission.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\n/**\n * Checks if a user has a specific permission.\n * Gracefully handles populated and unpopulated relationship states as far as possible synchronously.\n * \n * @param user - The Payload user object from req.user\n * @param requiredPermission - The name of the permission to check for\n * @returns boolean\n */\nexport const hasPermission = (\n user: PayloadRequest['user'] | null | undefined,\n requiredPermission: string,\n): boolean => {\n if (!user || !user.roles || !Array.isArray(user.roles)) {\n return false\n }\n\n for (const role of user.roles) {\n // If role is just an ID (unpopulated), we can't check its permissions synchronously\n if (typeof role !== 'object' || role === null) {\n continue\n }\n\n const permissions = role.permissions\n if (!permissions || !Array.isArray(permissions)) {\n continue\n }\n\n for (const permission of permissions) {\n if (typeof permission === 'object' && permission !== null) {\n if ('name' in permission && permission.name === requiredPermission) {\n return true\n }\n } \n }\n }\n\n return false\n}\n"],"names":["hasPermission","user","requiredPermission","roles","Array","isArray","role","permissions","permission","name"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,MAAMA,gBAAgB,CAC3BC,MACAC;IAEA,IAAI,CAACD,QAAQ,CAACA,KAAKE,KAAK,IAAI,CAACC,MAAMC,OAAO,CAACJ,KAAKE,KAAK,GAAG;QACtD,OAAO;IACT;IAEA,KAAK,MAAMG,QAAQL,KAAKE,KAAK,CAAE;QAC7B,oFAAoF;QACpF,IAAI,OAAOG,SAAS,YAAYA,SAAS,MAAM;YAC7C;QACF;QAEA,MAAMC,cAAcD,KAAKC,WAAW;QACpC,IAAI,CAACA,eAAe,CAACH,MAAMC,OAAO,CAACE,cAAc;YAC/C;QACF;QAEA,KAAK,MAAMC,cAAcD,YAAa;YACpC,IAAI,OAAOC,eAAe,YAAYA,eAAe,MAAM;gBACzD,IAAI,UAAUA,cAAcA,WAAWC,IAAI,KAAKP,oBAAoB;oBAClE,OAAO;gBACT;YACF;QACF;IACF;IAEA,OAAO;AACT,EAAC"}
@@ -1,4 +1,5 @@
1
- import { hasPermission } from './hasPermission.js';
1
+ import { describe, expect, it } from 'vitest';
2
+ import { hasPermission } from './hasPermission';
2
3
  describe('hasPermission', ()=>{
3
4
  it('should return false if user is null', ()=>{
4
5
  expect(hasPermission(null, 'read:posts')).toBe(false);
@@ -112,6 +113,7 @@ describe('hasPermission', ()=>{
112
113
  ]
113
114
  };
114
115
  expect(hasPermission(user, 'delete:posts')).toBe(true);
116
+ expect(hasPermission(user, 'delete:posts')).toBe(true);
115
117
  expect(hasPermission(user, 'read:posts')).toBe(true);
116
118
  expect(hasPermission(user, 'create:users')).toBe(false);
117
119
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/hasPermission.spec.ts"],"sourcesContent":["import { hasPermission } from './hasPermission.js'\nimport type { PayloadRequest } from 'payload'\n\ndescribe('hasPermission', () => {\n it('should return false if user is null', () => {\n expect(hasPermission(null, 'read:posts')).toBe(false)\n })\n\n it('should return false if user has no roles array', () => {\n const user = { id: '1' } as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if user roles is unpopulated (array of strings)', () => {\n const user = {\n id: '1',\n roles: ['role-id-1'],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if role has no permissions', () => {\n const user = {\n id: '1',\n roles: [{ id: 'role-1', name: 'admin' }],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if role permissions are unpopulated (array of strings)', () => {\n const user = {\n id: '1',\n roles: [{ id: 'role-1', name: 'admin', permissions: ['perm-id-1'] }],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return true if permission is found in populated roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'admin',\n permissions: [\n { id: 'perm-1', name: 'read:posts' },\n { id: 'perm-2', name: 'write:posts' },\n ],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'write:posts')).toBe(true)\n })\n\n it('should return false if permission is not found in populated roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'editor',\n permissions: [{ id: 'perm-1', name: 'read:posts' }],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'delete:posts')).toBe(false)\n })\n\n it('should correctly search through multiple roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'editor',\n permissions: [{ id: 'perm-1', name: 'read:posts' }],\n },\n {\n id: 'role-2',\n name: 'manager',\n permissions: [{ id: 'perm-2', name: 'delete:posts' }],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'delete:posts')).toBe(true)\n expect(hasPermission(user, 'read:posts')).toBe(true)\n expect(hasPermission(user, 'create:users')).toBe(false)\n })\n})\n"],"names":["hasPermission","describe","it","expect","toBe","user","id","roles","name","permissions"],"mappings":"AAAA,SAASA,aAAa,QAAQ,qBAAoB;AAGlDC,SAAS,iBAAiB;IACxBC,GAAG,uCAAuC;QACxCC,OAAOH,cAAc,MAAM,eAAeI,IAAI,CAAC;IACjD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YAAEC,IAAI;QAAI;QACvBH,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,uEAAuE;QACxE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;aAAY;QACtB;QACAJ,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;oBAAED,IAAI;oBAAUE,MAAM;gBAAQ;aAAE;QAC1C;QACAL,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,8EAA8E;QAC/E,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;oBAAED,IAAI;oBAAUE,MAAM;oBAASC,aAAa;wBAAC;qBAAY;gBAAC;aAAE;QACtE;QACAN,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,gEAAgE;QACjE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBACX;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;wBACnC;4BAAEF,IAAI;4BAAUE,MAAM;wBAAc;qBACrC;gBACH;aACD;QACH;QACAL,OAAOH,cAAcK,MAAM,gBAAgBD,IAAI,CAAC;IAClD;IAEAF,GAAG,qEAAqE;QACtE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;qBAAE;gBACrD;aACD;QACH;QACAL,OAAOH,cAAcK,MAAM,iBAAiBD,IAAI,CAAC;IACnD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;qBAAE;gBACrD;gBACA;oBACEF,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAe;qBAAE;gBACvD;aACD;QACH;QACAL,OAAOH,cAAcK,MAAM,iBAAiBD,IAAI,CAAC;QACjDD,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;QAC/CD,OAAOH,cAAcK,MAAM,iBAAiBD,IAAI,CAAC;IACnD;AACF"}
1
+ {"version":3,"sources":["../../src/utilities/hasPermission.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload';\nimport { describe, expect, it } from 'vitest';\nimport { hasPermission } from './hasPermission';\n\ndescribe('hasPermission', () => {\n it('should return false if user is null', () => {\n expect(hasPermission(null, 'read:posts')).toBe(false)\n })\n\n it('should return false if user has no roles array', () => {\n const user = { id: '1' } as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if user roles is unpopulated (array of strings)', () => {\n const user = {\n id: '1',\n roles: ['role-id-1'],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if role has no permissions', () => {\n const user = {\n id: '1',\n roles: [{ id: 'role-1', name: 'admin' }],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if role permissions are unpopulated (array of strings)', () => {\n const user = {\n id: '1',\n roles: [{ id: 'role-1', name: 'admin', permissions: ['perm-id-1'] }],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return true if permission is found in populated roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'admin',\n permissions: [\n { id: 'perm-1', name: 'read:posts' },\n { id: 'perm-2', name: 'write:posts' },\n ],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'write:posts')).toBe(true)\n })\n\n it('should return false if permission is not found in populated roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'editor',\n permissions: [{ id: 'perm-1', name: 'read:posts' }],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'delete:posts')).toBe(false)\n })\n\n it('should correctly search through multiple roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'editor',\n permissions: [{ id: 'perm-1', name: 'read:posts' }],\n },\n {\n id: 'role-2',\n name: 'manager',\n permissions: [{ id: 'perm-2', name: 'delete:posts' }],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'delete:posts')).toBe(true)\n expect(hasPermission(user, 'delete:posts')).toBe(true)\n expect(hasPermission(user, 'read:posts')).toBe(true)\n expect(hasPermission(user, 'create:users')).toBe(false)\n })\n})\n"],"names":["describe","expect","it","hasPermission","toBe","user","id","roles","name","permissions"],"mappings":"AACA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAS;AAC9C,SAASC,aAAa,QAAQ,kBAAkB;AAEhDH,SAAS,iBAAiB;IACxBE,GAAG,uCAAuC;QACxCD,OAAOE,cAAc,MAAM,eAAeC,IAAI,CAAC;IACjD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YAAEC,IAAI;QAAI;QACvBL,OAAOE,cAAcE,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,uEAAuE;QACxE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;aAAY;QACtB;QACAN,OAAOE,cAAcE,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;oBAAED,IAAI;oBAAUE,MAAM;gBAAQ;aAAE;QAC1C;QACAP,OAAOE,cAAcE,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,8EAA8E;QAC/E,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;oBAAED,IAAI;oBAAUE,MAAM;oBAASC,aAAa;wBAAC;qBAAY;gBAAC;aAAE;QACtE;QACAR,OAAOE,cAAcE,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,gEAAgE;QACjE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBACX;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;wBACnC;4BAAEF,IAAI;4BAAUE,MAAM;wBAAc;qBACrC;gBACH;aACD;QACH;QACAP,OAAOE,cAAcE,MAAM,gBAAgBD,IAAI,CAAC;IAClD;IAEAF,GAAG,qEAAqE;QACtE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;qBAAE;gBACrD;aACD;QACH;QACAP,OAAOE,cAAcE,MAAM,iBAAiBD,IAAI,CAAC;IACnD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;qBAAE;gBACrD;gBACA;oBACEF,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAe;qBAAE;gBACvD;aACD;QACH;QACAP,OAAOE,cAAcE,MAAM,iBAAiBD,IAAI,CAAC;QACjDH,OAAOE,cAAcE,MAAM,iBAAiBD,IAAI,CAAC;QACjDH,OAAOE,cAAcE,MAAM,eAAeD,IAAI,CAAC;QAC/CH,OAAOE,cAAcE,MAAM,iBAAiBD,IAAI,CAAC;IACnD;AACF"}
@@ -1,4 +1,4 @@
1
- export { hasPermission } from './hasPermission.js';
2
- export { checkPermission } from './checkPermission.js';
1
+ export { checkPermission } from './checkPermission';
2
+ export { hasPermission } from './hasPermission';
3
3
 
4
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/index.ts"],"sourcesContent":["export { hasPermission } from './hasPermission.js'\nexport { checkPermission } from './checkPermission.js'\n"],"names":["hasPermission","checkPermission"],"mappings":"AAAA,SAASA,aAAa,QAAQ,qBAAoB;AAClD,SAASC,eAAe,QAAQ,uBAAsB"}
1
+ {"version":3,"sources":["../../src/utilities/index.ts"],"sourcesContent":["export { checkPermission } from './checkPermission';\nexport { hasPermission } from './hasPermission';\n\n"],"names":["checkPermission","hasPermission"],"mappings":"AAAA,SAASA,eAAe,QAAQ,oBAAoB;AACpD,SAASC,aAAa,QAAQ,kBAAkB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payload-rbac-plugin",
3
- "version": "1.0.1",
3
+ "version": "1.0.3rc",
4
4
  "description": "RBAC plugin for payloadcms",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,44 +30,26 @@
30
30
  "type": "module",
31
31
  "exports": {
32
32
  ".": {
33
- "import": "./src/index.ts",
34
- "types": "./src/index.ts",
35
- "default": "./src/index.ts"
33
+ "import": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.js"
36
36
  },
37
37
  "./client": {
38
- "import": "./src/exports/client.ts",
39
- "types": "./src/exports/client.ts",
40
- "default": "./src/exports/client.ts"
38
+ "import": "./dist/exports/client.js",
39
+ "types": "./dist/exports/client.d.ts",
40
+ "default": "./dist/exports/client.js"
41
41
  },
42
42
  "./rsc": {
43
- "import": "./src/exports/rsc.ts",
44
- "types": "./src/exports/rsc.ts",
45
- "default": "./src/exports/rsc.ts"
43
+ "import": "./dist/exports/rsc.js",
44
+ "types": "./dist/exports/rsc.d.ts",
45
+ "default": "./dist/exports/rsc.js"
46
46
  }
47
47
  },
48
- "main": "./src/index.ts",
49
- "types": "./src/index.ts",
48
+ "main": "./dist/index.js",
49
+ "types": "./dist/index.d.ts",
50
50
  "files": [
51
51
  "dist"
52
52
  ],
53
- "scripts": {
54
- "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
55
- "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
56
- "build:types": "tsc --outDir dist --rootDir ./src",
57
- "clean": "rimraf {dist,*.tsbuildinfo}",
58
- "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
59
- "dev": "next dev dev --turbo",
60
- "dev:generate-importmap": "pnpm dev:payload generate:importmap",
61
- "dev:generate-types": "pnpm dev:payload generate:types",
62
- "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
63
- "generate:importmap": "pnpm dev:generate-importmap",
64
- "generate:types": "pnpm dev:generate-types",
65
- "lint": "eslint",
66
- "lint:fix": "eslint ./src --fix",
67
- "test": "pnpm test:int && pnpm test:e2e",
68
- "test:e2e": "playwright test",
69
- "test:int": "vitest"
70
- },
71
53
  "devDependencies": {
72
54
  "@eslint/eslintrc": "^3.2.0",
73
55
  "@payloadcms/db-mongodb": "3.84.1",
@@ -110,33 +92,23 @@
110
92
  "node": "^18.20.2 || >=20.9.0",
111
93
  "pnpm": "^9 || ^10"
112
94
  },
113
- "publishConfig": {
114
- "exports": {
115
- ".": {
116
- "import": "./dist/index.js",
117
- "types": "./dist/index.d.ts",
118
- "default": "./dist/index.js"
119
- },
120
- "./client": {
121
- "import": "./dist/exports/client.js",
122
- "types": "./dist/exports/client.d.ts",
123
- "default": "./dist/exports/client.js"
124
- },
125
- "./rsc": {
126
- "import": "./dist/exports/rsc.js",
127
- "types": "./dist/exports/rsc.d.ts",
128
- "default": "./dist/exports/rsc.js"
129
- }
130
- },
131
- "main": "./dist/index.js",
132
- "types": "./dist/index.d.ts"
133
- },
134
- "pnpm": {
135
- "onlyBuiltDependencies": [
136
- "sharp",
137
- "esbuild",
138
- "unrs-resolver"
139
- ]
140
- },
141
- "registry": "https://registry.npmjs.org/"
142
- }
95
+ "registry": "https://registry.npmjs.org/",
96
+ "scripts": {
97
+ "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
98
+ "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
99
+ "build:types": "tsc --outDir dist --rootDir ./src",
100
+ "clean": "rimraf {dist,*.tsbuildinfo}",
101
+ "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
102
+ "dev": "next dev dev --turbo",
103
+ "dev:generate-importmap": "pnpm dev:payload generate:importmap",
104
+ "dev:generate-types": "pnpm dev:payload generate:types",
105
+ "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
106
+ "generate:importmap": "pnpm dev:generate-importmap",
107
+ "generate:types": "pnpm dev:generate-types",
108
+ "lint": "eslint",
109
+ "lint:fix": "eslint ./src --fix",
110
+ "test": "pnpm test:int && pnpm test:e2e",
111
+ "test:e2e": "playwright test",
112
+ "test:int": "vitest"
113
+ }
114
+ }