roles-privileges-payload-plugin 1.1.2 → 1.2.0
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 +15 -0
- package/dist/collections/roles.d.ts +2 -2
- package/dist/collections/roles.js +27 -45
- package/dist/collections/roles.js.map +1 -1
- package/dist/components/PrivilegesSelect.d.ts +3 -3
- package/dist/components/PrivilegesSelect.js +89 -71
- package/dist/components/PrivilegesSelect.js.map +1 -1
- package/dist/exports/utilities.d.ts +5 -4
- package/dist/exports/utilities.js +10 -8
- package/dist/exports/utilities.js.map +1 -1
- package/dist/fields/slug/SlugComponent.d.ts +9 -0
- package/dist/fields/slug/SlugComponent.js +83 -0
- package/dist/fields/slug/SlugComponent.js.map +1 -0
- package/dist/fields/slug/formatSlug.d.ts +3 -0
- package/dist/fields/slug/formatSlug.js +15 -0
- package/dist/fields/slug/formatSlug.js.map +1 -0
- package/dist/fields/slug/index.d.ts +8 -0
- package/dist/fields/slug/index.js +47 -0
- package/dist/fields/slug/index.js.map +1 -0
- package/dist/fields/slug/index.scss +12 -0
- package/dist/index.d.ts +17 -7
- package/dist/index.js +59 -10
- package/dist/index.js.map +1 -1
- package/dist/roles-privileges-payload-plugin-1.2.0.tgz +0 -0
- package/dist/translations/index.js +4 -4
- package/dist/translations/index.js.map +1 -1
- package/dist/translations/languages/en.js +19 -18
- package/dist/translations/languages/en.js.map +1 -1
- package/dist/translations/languages/fr.js +19 -18
- package/dist/translations/languages/fr.js.map +1 -1
- package/dist/translations/types.d.ts +43 -42
- package/dist/translations/types.js.map +1 -1
- package/dist/utils/assignSuperAdminToFirstUser.d.ts +10 -0
- package/dist/utils/assignSuperAdminToFirstUser.js +70 -0
- package/dist/utils/assignSuperAdminToFirstUser.js.map +1 -0
- package/dist/utils/createCustomPrivilege.d.ts +9 -9
- package/dist/utils/createCustomPrivilege.js +4 -4
- package/dist/utils/createCustomPrivilege.js.map +1 -1
- package/dist/utils/generateGlobalPrivileges.d.ts +4 -4
- package/dist/utils/generateGlobalPrivileges.js +4 -4
- package/dist/utils/generateGlobalPrivileges.js.map +1 -1
- package/dist/utils/generatePrivileges.d.ts +6 -6
- package/dist/utils/generatePrivileges.js +6 -6
- package/dist/utils/generatePrivileges.js.map +1 -1
- package/dist/utils/seedSuperAdminRole.js +7 -7
- package/dist/utils/seedSuperAdminRole.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,17 @@ That's it! The plugin will:
|
|
|
52
52
|
4. Add a `roles` collection with a privilege selector UI
|
|
53
53
|
5. Wrap all collection and global access controls with privilege checks
|
|
54
54
|
6. Seed a Super Admin role with all privileges
|
|
55
|
+
7. **Automatically assign the Super Admin role to the first user created** 🎯
|
|
56
|
+
|
|
57
|
+
### First User Super Admin Assignment
|
|
58
|
+
|
|
59
|
+
By default, the plugin automatically assigns the Super Admin role to the first user created in your system. This ensures that:
|
|
60
|
+
|
|
61
|
+
- Your initial user has full access to configure the system
|
|
62
|
+
- You don't get locked out during initial setup
|
|
63
|
+
- The user collection is automatically detected from `config.admin.user` (defaults to `'users'`)
|
|
64
|
+
|
|
65
|
+
This feature can be disabled by setting `assignSuperAdminToFirstUser: false` in the plugin options.
|
|
55
66
|
|
|
56
67
|
## Configuration Options
|
|
57
68
|
|
|
@@ -78,6 +89,10 @@ rolesPrivilegesPayloadPlugin({
|
|
|
78
89
|
// Seed a Super Admin role with all privileges on init
|
|
79
90
|
seedSuperAdmin: true,
|
|
80
91
|
|
|
92
|
+
// Automatically assign Super Admin role to the first user created (defaults to true)
|
|
93
|
+
// Ensures the initial user has full system access
|
|
94
|
+
assignSuperAdminToFirstUser: true,
|
|
95
|
+
|
|
81
96
|
// Provide a custom roles collection configuration (optional)
|
|
82
97
|
// Use createRolesCollection() helper to create a base and customize it
|
|
83
98
|
customRolesCollection: undefined,
|
|
@@ -2,19 +2,19 @@ import type { CollectionBeforeChangeHook, CollectionBeforeDeleteHook, Collection
|
|
|
2
2
|
import type { GlobalPrivilege } from '../utils/generateGlobalPrivileges.js';
|
|
3
3
|
import type { Privilege } from '../utils/generatePrivileges.js';
|
|
4
4
|
export type CollectionData = {
|
|
5
|
-
collectionSlug: string;
|
|
6
5
|
collectionLabel: {
|
|
7
6
|
en: string;
|
|
8
7
|
fr: string;
|
|
9
8
|
};
|
|
9
|
+
collectionSlug: string;
|
|
10
10
|
privileges: Record<string, Privilege>;
|
|
11
11
|
};
|
|
12
12
|
export type GlobalData = {
|
|
13
|
-
globalSlug: string;
|
|
14
13
|
globalLabel: {
|
|
15
14
|
en: string;
|
|
16
15
|
fr: string;
|
|
17
16
|
};
|
|
17
|
+
globalSlug: string;
|
|
18
18
|
privileges: Record<string, GlobalPrivilege>;
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { APIError } from 'payload';
|
|
2
|
+
import { slugField } from 'src/fields/slug/index.js';
|
|
2
3
|
import { hasPrivilege } from '../utils/privilegesAccess.js';
|
|
3
4
|
/**
|
|
4
5
|
* Hook to ensure the Super Admin role cannot be deleted
|
|
5
|
-
*/ export const ensureSuperAdminDontGetDeleted = async ({
|
|
6
|
+
*/ export const ensureSuperAdminDontGetDeleted = async ({ id, req })=>{
|
|
6
7
|
const role = await req.payload.findByID({
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
id,
|
|
9
|
+
collection: 'roles'
|
|
9
10
|
});
|
|
10
11
|
if (role && role.slug === 'super-admin') {
|
|
11
12
|
throw new APIError(req.t('plugin-roles-privileges:error-cannot-delete-super-admin'), 400);
|
|
@@ -13,7 +14,7 @@ import { hasPrivilege } from '../utils/privilegesAccess.js';
|
|
|
13
14
|
};
|
|
14
15
|
/**
|
|
15
16
|
* Hook to ensure the Super Admin role slug cannot be changed
|
|
16
|
-
*/ export const ensureSuperAdminDontGetUpdated =
|
|
17
|
+
*/ export const ensureSuperAdminDontGetUpdated = ({ data, originalDoc, req })=>{
|
|
17
18
|
if (originalDoc && originalDoc.slug === 'super-admin') {
|
|
18
19
|
if (data.slug && data.slug !== 'super-admin') {
|
|
19
20
|
throw new APIError(req.t('plugin-roles-privileges:error-cannot-modify-super-admin-slug'), 400);
|
|
@@ -27,85 +28,62 @@ import { hasPrivilege } from '../utils/privilegesAccess.js';
|
|
|
27
28
|
*/ export const createRolesCollection = (collections, globals)=>{
|
|
28
29
|
return {
|
|
29
30
|
slug: 'roles',
|
|
30
|
-
labels: {
|
|
31
|
-
singular: ({ t })=>t('plugin-roles-privileges:roles-collection-label-singular'),
|
|
32
|
-
plural: ({ t })=>t('plugin-roles-privileges:roles-collection-label-plural')
|
|
33
|
-
},
|
|
34
31
|
access: {
|
|
35
32
|
// Allow authenticated users to read roles (needed for user role resolution)
|
|
36
33
|
read: ()=>true,
|
|
37
34
|
// Require specific privileges for other operations
|
|
38
35
|
create: hasPrivilege('roles-create'),
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
delete: hasPrivilege('roles-delete'),
|
|
37
|
+
update: hasPrivilege('roles-update')
|
|
41
38
|
},
|
|
42
39
|
admin: {
|
|
43
|
-
useAsTitle: 'title',
|
|
44
40
|
defaultColumns: [
|
|
45
41
|
'title',
|
|
46
42
|
'slug'
|
|
47
|
-
]
|
|
43
|
+
],
|
|
44
|
+
useAsTitle: 'title'
|
|
48
45
|
},
|
|
49
46
|
fields: [
|
|
50
47
|
{
|
|
51
48
|
name: 'title',
|
|
52
49
|
type: 'text',
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: 'slug',
|
|
58
|
-
type: 'text',
|
|
59
|
-
required: true,
|
|
60
|
-
unique: true,
|
|
61
|
-
label: ({ t })=>t('plugin-roles-privileges:roles-field-slug-label'),
|
|
62
|
-
admin: {
|
|
63
|
-
description: ({ t })=>t('plugin-roles-privileges:roles-field-slug-description')
|
|
64
|
-
},
|
|
65
|
-
hooks: {
|
|
66
|
-
beforeValidate: [
|
|
67
|
-
({ value })=>{
|
|
68
|
-
if (typeof value === 'string') {
|
|
69
|
-
return value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
|
|
70
|
-
}
|
|
71
|
-
return value;
|
|
72
|
-
}
|
|
73
|
-
]
|
|
74
|
-
}
|
|
50
|
+
label: ({ t })=>t('plugin-roles-privileges:roles-field-title-label'),
|
|
51
|
+
required: true
|
|
75
52
|
},
|
|
53
|
+
...slugField(),
|
|
76
54
|
{
|
|
77
55
|
name: 'privileges',
|
|
78
56
|
type: 'array',
|
|
79
|
-
label: ({ t })=>t('plugin-roles-privileges:roles-field-privileges-label'),
|
|
80
57
|
admin: {
|
|
81
|
-
description: ({ t })=>t('plugin-roles-privileges:roles-field-privileges-description'),
|
|
82
58
|
components: {
|
|
83
59
|
Field: {
|
|
84
|
-
path: 'roles-privileges-payload-plugin/client#PrivilegesSelect',
|
|
85
60
|
clientProps: {
|
|
86
61
|
collections: collections || [],
|
|
87
62
|
globals: globals || []
|
|
88
|
-
}
|
|
63
|
+
},
|
|
64
|
+
path: 'roles-privileges-payload-plugin/client#PrivilegesSelect'
|
|
89
65
|
}
|
|
90
|
-
}
|
|
66
|
+
},
|
|
67
|
+
description: ({ t })=>t('plugin-roles-privileges:roles-field-privileges-description')
|
|
91
68
|
},
|
|
92
|
-
minRows: 1,
|
|
93
69
|
fields: [
|
|
94
70
|
{
|
|
95
71
|
name: 'privilege',
|
|
96
72
|
type: 'text',
|
|
97
|
-
|
|
98
|
-
|
|
73
|
+
label: ({ t })=>t('plugin-roles-privileges:roles-field-privileges-label'),
|
|
74
|
+
required: true
|
|
99
75
|
}
|
|
100
|
-
]
|
|
76
|
+
],
|
|
77
|
+
label: ({ t })=>t('plugin-roles-privileges:roles-field-privileges-label'),
|
|
78
|
+
minRows: 1
|
|
101
79
|
},
|
|
102
80
|
{
|
|
103
81
|
name: 'description',
|
|
104
82
|
type: 'textarea',
|
|
105
|
-
label: ({ t })=>t('plugin-roles-privileges:roles-field-description-label'),
|
|
106
83
|
admin: {
|
|
107
84
|
description: ({ t })=>t('plugin-roles-privileges:roles-field-description-description')
|
|
108
|
-
}
|
|
85
|
+
},
|
|
86
|
+
label: ({ t })=>t('plugin-roles-privileges:roles-field-description-label')
|
|
109
87
|
}
|
|
110
88
|
],
|
|
111
89
|
hooks: {
|
|
@@ -115,6 +93,10 @@ import { hasPrivilege } from '../utils/privilegesAccess.js';
|
|
|
115
93
|
beforeDelete: [
|
|
116
94
|
ensureSuperAdminDontGetDeleted
|
|
117
95
|
]
|
|
96
|
+
},
|
|
97
|
+
labels: {
|
|
98
|
+
plural: ({ t })=>t('plugin-roles-privileges:roles-collection-label-plural'),
|
|
99
|
+
singular: ({ t })=>t('plugin-roles-privileges:roles-collection-label-singular')
|
|
118
100
|
}
|
|
119
101
|
};
|
|
120
102
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/roles.ts"],"sourcesContent":["import type {\n CollectionBeforeChangeHook,\n CollectionBeforeDeleteHook,\n CollectionConfig,\n} from 'payload'\nimport { APIError } from 'payload'\nimport type { GlobalPrivilege } from '../utils/generateGlobalPrivileges.js'\nimport type { Privilege } from '../utils/generatePrivileges.js'\nimport { hasPrivilege } from '../utils/privilegesAccess.js'\n\nexport type CollectionData = {\n
|
|
1
|
+
{"version":3,"sources":["../../src/collections/roles.ts"],"sourcesContent":["import type {\n CollectionBeforeChangeHook,\n CollectionBeforeDeleteHook,\n CollectionConfig,\n} from 'payload'\n\nimport { APIError } from 'payload'\nimport { slugField } from 'src/fields/slug/index.js'\n\nimport type { GlobalPrivilege } from '../utils/generateGlobalPrivileges.js'\nimport type { Privilege } from '../utils/generatePrivileges.js'\n\nimport { hasPrivilege } from '../utils/privilegesAccess.js'\n\nexport type CollectionData = {\n collectionLabel: { en: string; fr: string }\n collectionSlug: string\n privileges: Record<string, Privilege>\n}\n\nexport type GlobalData = {\n globalLabel: { en: string; fr: string }\n globalSlug: string\n privileges: Record<string, GlobalPrivilege>\n}\n\n/**\n * Hook to ensure the Super Admin role cannot be deleted\n */\nexport const ensureSuperAdminDontGetDeleted: CollectionBeforeDeleteHook = async ({ id, req }) => {\n const role = await req.payload.findByID({\n id,\n collection: 'roles',\n })\n\n if (role && role.slug === 'super-admin') {\n throw new APIError(\n (req.t as (key: string) => string)('plugin-roles-privileges:error-cannot-delete-super-admin'),\n 400,\n )\n }\n}\n\n/**\n * Hook to ensure the Super Admin role slug cannot be changed\n */\nexport const ensureSuperAdminDontGetUpdated: CollectionBeforeChangeHook = ({\n data,\n originalDoc,\n req,\n}) => {\n if (originalDoc && originalDoc.slug === 'super-admin') {\n if (data.slug && data.slug !== 'super-admin') {\n throw new APIError(\n (req.t as (key: string) => string)(\n 'plugin-roles-privileges:error-cannot-modify-super-admin-slug',\n ),\n 400,\n )\n }\n }\n return data\n}\n\n/**\n * Roles collection configuration\n * This collection manages user roles and their associated privileges\n */\nexport const createRolesCollection = (\n collections?: CollectionData[],\n globals?: GlobalData[],\n): CollectionConfig => {\n return {\n slug: 'roles',\n access: {\n // Allow authenticated users to read roles (needed for user role resolution)\n read: () => true,\n // Require specific privileges for other operations\n create: hasPrivilege('roles-create'),\n delete: hasPrivilege('roles-delete'),\n update: hasPrivilege('roles-update'),\n },\n admin: {\n defaultColumns: ['title', 'slug'],\n useAsTitle: 'title',\n },\n fields: [\n {\n name: 'title',\n type: 'text',\n label: ({ t }) =>\n (t as (key: string) => string)('plugin-roles-privileges:roles-field-title-label'),\n required: true,\n },\n ...slugField(),\n {\n name: 'privileges',\n type: 'array',\n admin: {\n components: {\n Field: {\n clientProps: {\n collections: collections || [],\n globals: globals || [],\n },\n path: 'roles-privileges-payload-plugin/client#PrivilegesSelect',\n },\n },\n description: ({ t }) =>\n (t as (key: string) => string)(\n 'plugin-roles-privileges:roles-field-privileges-description',\n ),\n },\n fields: [\n {\n name: 'privilege',\n type: 'text',\n label: ({ t }) =>\n (t as (key: string) => string)(\n 'plugin-roles-privileges:roles-field-privileges-label',\n ),\n required: true,\n },\n ],\n label: ({ t }) =>\n (t as (key: string) => string)('plugin-roles-privileges:roles-field-privileges-label'),\n minRows: 1,\n },\n {\n name: 'description',\n type: 'textarea',\n admin: {\n description: ({ t }) =>\n (t as (key: string) => string)(\n 'plugin-roles-privileges:roles-field-description-description',\n ),\n },\n label: ({ t }) =>\n (t as (key: string) => string)('plugin-roles-privileges:roles-field-description-label'),\n },\n ],\n hooks: {\n beforeChange: [ensureSuperAdminDontGetUpdated],\n beforeDelete: [ensureSuperAdminDontGetDeleted],\n },\n labels: {\n plural: ({ t }) =>\n (t as (key: string) => string)('plugin-roles-privileges:roles-collection-label-plural'),\n singular: ({ t }) =>\n (t as (key: string) => string)('plugin-roles-privileges:roles-collection-label-singular'),\n },\n }\n}\n"],"names":["APIError","slugField","hasPrivilege","ensureSuperAdminDontGetDeleted","id","req","role","payload","findByID","collection","slug","t","ensureSuperAdminDontGetUpdated","data","originalDoc","createRolesCollection","collections","globals","access","read","create","delete","update","admin","defaultColumns","useAsTitle","fields","name","type","label","required","components","Field","clientProps","path","description","minRows","hooks","beforeChange","beforeDelete","labels","plural","singular"],"mappings":"AAMA,SAASA,QAAQ,QAAQ,UAAS;AAClC,SAASC,SAAS,QAAQ,2BAA0B;AAKpD,SAASC,YAAY,QAAQ,+BAA8B;AAc3D;;CAEC,GACD,OAAO,MAAMC,iCAA6D,OAAO,EAAEC,EAAE,EAAEC,GAAG,EAAE;IAC1F,MAAMC,OAAO,MAAMD,IAAIE,OAAO,CAACC,QAAQ,CAAC;QACtCJ;QACAK,YAAY;IACd;IAEA,IAAIH,QAAQA,KAAKI,IAAI,KAAK,eAAe;QACvC,MAAM,IAAIV,SACR,AAACK,IAAIM,CAAC,CAA6B,4DACnC;IAEJ;AACF,EAAC;AAED;;CAEC,GACD,OAAO,MAAMC,iCAA6D,CAAC,EACzEC,IAAI,EACJC,WAAW,EACXT,GAAG,EACJ;IACC,IAAIS,eAAeA,YAAYJ,IAAI,KAAK,eAAe;QACrD,IAAIG,KAAKH,IAAI,IAAIG,KAAKH,IAAI,KAAK,eAAe;YAC5C,MAAM,IAAIV,SACR,AAACK,IAAIM,CAAC,CACJ,iEAEF;QAEJ;IACF;IACA,OAAOE;AACT,EAAC;AAED;;;CAGC,GACD,OAAO,MAAME,wBAAwB,CACnCC,aACAC;IAEA,OAAO;QACLP,MAAM;QACNQ,QAAQ;YACN,4EAA4E;YAC5EC,MAAM,IAAM;YACZ,mDAAmD;YACnDC,QAAQlB,aAAa;YACrBmB,QAAQnB,aAAa;YACrBoB,QAAQpB,aAAa;QACvB;QACAqB,OAAO;YACLC,gBAAgB;gBAAC;gBAAS;aAAO;YACjCC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAElB,CAAC,EAAE,GACX,AAACA,EAA8B;gBACjCmB,UAAU;YACZ;eACG7B;YACH;gBACE0B,MAAM;gBACNC,MAAM;gBACNL,OAAO;oBACLQ,YAAY;wBACVC,OAAO;4BACLC,aAAa;gCACXjB,aAAaA,eAAe,EAAE;gCAC9BC,SAASA,WAAW,EAAE;4BACxB;4BACAiB,MAAM;wBACR;oBACF;oBACAC,aAAa,CAAC,EAAExB,CAAC,EAAE,GACjB,AAACA,EACC;gBAEN;gBACAe,QAAQ;oBACN;wBACEC,MAAM;wBACNC,MAAM;wBACNC,OAAO,CAAC,EAAElB,CAAC,EAAE,GACX,AAACA,EACC;wBAEJmB,UAAU;oBACZ;iBACD;gBACDD,OAAO,CAAC,EAAElB,CAAC,EAAE,GACX,AAACA,EAA8B;gBACjCyB,SAAS;YACX;YACA;gBACET,MAAM;gBACNC,MAAM;gBACNL,OAAO;oBACLY,aAAa,CAAC,EAAExB,CAAC,EAAE,GACjB,AAACA,EACC;gBAEN;gBACAkB,OAAO,CAAC,EAAElB,CAAC,EAAE,GACX,AAACA,EAA8B;YACnC;SACD;QACD0B,OAAO;YACLC,cAAc;gBAAC1B;aAA+B;YAC9C2B,cAAc;gBAACpC;aAA+B;QAChD;QACAqC,QAAQ;YACNC,QAAQ,CAAC,EAAE9B,CAAC,EAAE,GACZ,AAACA,EAA8B;YACjC+B,UAAU,CAAC,EAAE/B,CAAC,EAAE,GACd,AAACA,EAA8B;QACnC;IACF;AACF,EAAC"}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import type { GlobalPrivilege } from '../utils/generateGlobalPrivileges.js';
|
|
2
2
|
import type { Privilege } from '../utils/generatePrivileges.js';
|
|
3
3
|
type CollectionPrivileges = {
|
|
4
|
-
collectionSlug: string;
|
|
5
4
|
collectionLabel: Record<string, string>;
|
|
5
|
+
collectionSlug: string;
|
|
6
6
|
privileges: Record<string, Privilege>;
|
|
7
7
|
};
|
|
8
8
|
type GlobalPrivileges = {
|
|
9
|
-
globalSlug: string;
|
|
10
9
|
globalLabel: Record<string, string>;
|
|
10
|
+
globalSlug: string;
|
|
11
11
|
privileges: Record<string, GlobalPrivilege>;
|
|
12
12
|
};
|
|
13
13
|
declare const _default_1: import("react").NamedExoticComponent<{
|
|
14
14
|
readonly validate?: import("payload").ArrayFieldValidation;
|
|
15
15
|
} & import("payload").FieldPaths & {
|
|
16
16
|
readonly field: Omit<import("payload").ArrayFieldClient, "type"> & Partial<Pick<import("payload").ArrayFieldClient, "type">>;
|
|
17
|
-
} & Omit<import("payload").ClientComponentProps, "
|
|
17
|
+
} & Omit<import("payload").ClientComponentProps, "customComponents" | "field">>;
|
|
18
18
|
export default _default_1;
|
|
19
19
|
export type { CollectionPrivileges, GlobalPrivileges };
|