@zealamic/payload-auth-rbac-plugin 1.0.0 → 1.0.1

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/dist/components/role-permission-matrix-client/default-data.js +2 -2
  2. package/dist/components/role-permission-matrix-client/default-data.js.map +1 -1
  3. package/dist/components/role-permission-matrix-client/index.js +2 -2
  4. package/dist/components/role-permission-matrix-client/index.js.map +1 -1
  5. package/dist/components/role-permission-matrix-client/matrix.module.scss +1 -4
  6. package/dist/components/role-permission-matrix-client/types.d.ts +4 -6
  7. package/dist/components/role-permission-matrix-client/types.js.map +1 -1
  8. package/docs/TRANSLATIONS.md +12 -5
  9. package/package.json +4 -27
  10. package/src/collections/permission-actions/default-data.ts +36 -0
  11. package/src/collections/permission-actions/index.ts +144 -0
  12. package/src/collections/permission-actions/types.ts +56 -0
  13. package/src/collections/permission-features/default-data.ts +30 -0
  14. package/src/collections/permission-features/index.ts +122 -0
  15. package/src/collections/permission-features/types.ts +47 -0
  16. package/src/collections/permissions/default-data.ts +38 -0
  17. package/src/collections/permissions/index.ts +160 -0
  18. package/src/collections/permissions/types.ts +57 -0
  19. package/src/collections/roles/default-data.ts +44 -0
  20. package/src/collections/roles/hooks/sync-permission-matrix-draft.ts +73 -0
  21. package/src/collections/roles/index.ts +178 -0
  22. package/src/collections/roles/types.ts +56 -0
  23. package/src/collections/roles-permissions/default-data.ts +28 -0
  24. package/src/collections/roles-permissions/index.ts +107 -0
  25. package/src/collections/roles-permissions/types.ts +42 -0
  26. package/src/collections/users/default-data.ts +19 -0
  27. package/src/collections/users/index.ts +148 -0
  28. package/src/collections/users/parent-path.ts +310 -0
  29. package/src/collections/users/types.ts +25 -0
  30. package/src/components/role-permission-matrix-client/default-data.ts +25 -0
  31. package/src/components/role-permission-matrix-client/index.tsx +369 -0
  32. package/src/components/role-permission-matrix-client/matrix.module.scss +66 -0
  33. package/src/components/role-permission-matrix-client/types.ts +16 -0
  34. package/src/endpoints/customEndpointHandler.ts +5 -0
  35. package/src/exports/client.ts +1 -0
  36. package/src/exports/rsc.ts +0 -0
  37. package/src/general-types.d.ts +5 -0
  38. package/src/index.ts +249 -0
  39. package/src/lib/constants/general.ts +1 -0
  40. package/src/lib/constants/index.ts +15 -0
  41. package/src/lib/constants/permission-action.ts +9 -0
  42. package/src/lib/constants/permission-feature.ts +4 -0
  43. package/src/lib/constants/permission.ts +4 -0
  44. package/src/lib/constants/role.ts +10 -0
  45. package/src/lib/constants/user.ts +1 -0
  46. package/src/lib/utils/access.ts +611 -0
  47. package/src/lib/utils/data.ts +7 -0
  48. package/src/lib/utils/fields.ts +62 -0
  49. package/src/lib/utils/index.ts +4 -0
  50. package/src/lib/utils/localization.ts +106 -0
  51. package/src/styles/variables.scss +1 -0
  52. package/src/types.ts +64 -0
@@ -0,0 +1,122 @@
1
+ import type { CollectionConfig } from "payload";
2
+ import { STATUS } from "../../lib/constants/permission-feature.js";
3
+ import {
4
+ getArrayOfMergedFieldAffectingData,
5
+ getSuperAdminAccess,
6
+ toLocaleRecord,
7
+ toSelectPlaceholder,
8
+ } from "../../lib/utils/index.js";
9
+ import type { PermissionFeaturesCollectionParams } from "./types.js";
10
+
11
+ export const getPermissionFeaturesCollection = (
12
+ params: PermissionFeaturesCollectionParams,
13
+ ) => {
14
+ const {
15
+ translations = {},
16
+ access = {},
17
+ fields = [],
18
+ labels = {},
19
+ admin = {},
20
+ } = params || {};
21
+ const arrTranslationsKeys = Object.keys(translations);
22
+ const permissionFeatures: CollectionConfig = {
23
+ slug: "permission-features",
24
+ labels: {
25
+ singular: toLocaleRecord(
26
+ arrTranslationsKeys,
27
+ (locale) => translations[locale]?.labels?.singular,
28
+ ),
29
+ plural: toLocaleRecord(
30
+ arrTranslationsKeys,
31
+ (locale) => translations[locale]?.labels?.plural,
32
+ ),
33
+ ...labels,
34
+ },
35
+ admin: {
36
+ group: toLocaleRecord(
37
+ arrTranslationsKeys,
38
+ (locale) => translations[locale]?.admin?.group,
39
+ ),
40
+ useAsTitle: "code",
41
+ defaultColumns: ["code", "status", "updatedAt"],
42
+ ...admin,
43
+ },
44
+ access: {
45
+ create: getSuperAdminAccess,
46
+ update: getSuperAdminAccess,
47
+ delete: getSuperAdminAccess,
48
+ read: getSuperAdminAccess,
49
+ readVersions: getSuperAdminAccess,
50
+ unlock: getSuperAdminAccess,
51
+ admin: ({ req }) => {
52
+ return getSuperAdminAccess({ req });
53
+ },
54
+ ...access,
55
+ },
56
+ fields: getArrayOfMergedFieldAffectingData({
57
+ fields,
58
+ defaultFields: [
59
+ {
60
+ name: "code",
61
+ type: "text",
62
+ required: true,
63
+ unique: true,
64
+ index: true,
65
+ label: toLocaleRecord(
66
+ arrTranslationsKeys,
67
+ (locale) => translations[locale]?.fields?.code?.label,
68
+ ),
69
+ admin: {
70
+ placeholder: toLocaleRecord(
71
+ arrTranslationsKeys,
72
+ (locale) => translations[locale]?.fields?.code?.placeholder,
73
+ ),
74
+ },
75
+ },
76
+ {
77
+ name: "sortOrder",
78
+ type: "number",
79
+ required: false,
80
+ defaultValue: 0,
81
+ label: toLocaleRecord(
82
+ arrTranslationsKeys,
83
+ (locale) => translations[locale]?.fields?.sortOrder?.label,
84
+ ),
85
+ admin: {
86
+ placeholder: toLocaleRecord(
87
+ arrTranslationsKeys,
88
+ (locale) => translations[locale]?.fields?.sortOrder?.placeholder,
89
+ ),
90
+ },
91
+ },
92
+ {
93
+ name: "status",
94
+ type: "select",
95
+ required: true,
96
+ label: toLocaleRecord(
97
+ arrTranslationsKeys,
98
+ (locale) => translations[locale]?.fields?.status?.label,
99
+ ),
100
+ defaultValue: STATUS.ACTIVE,
101
+ options: Object.values(STATUS).map((status) => ({
102
+ label: toLocaleRecord(
103
+ arrTranslationsKeys,
104
+ (locale) =>
105
+ translations[locale]?.fields?.status?.[`${status}Label`],
106
+ ),
107
+ value: status,
108
+ })),
109
+ admin: {
110
+ placeholder: toSelectPlaceholder(
111
+ arrTranslationsKeys,
112
+ (locale) => translations[locale]?.fields?.status?.placeholder,
113
+ ),
114
+ },
115
+ },
116
+ ],
117
+ }),
118
+ timestamps: true,
119
+ };
120
+
121
+ return permissionFeatures;
122
+ };
@@ -0,0 +1,47 @@
1
+ import type { CollectionConfig, Field } from "payload";
2
+ import { STATUS } from "../../lib/constants/permission-feature.js";
3
+
4
+ export type PermissionFeaturesCollectionTranslations = {
5
+ [locale: string]: {
6
+ labels?: {
7
+ singular?: string;
8
+ plural?: string;
9
+ };
10
+ admin?: {
11
+ group?: string;
12
+ };
13
+ fields?: {
14
+ code?: {
15
+ label?: string;
16
+ placeholder?: string;
17
+ };
18
+ sortOrder?: {
19
+ label?: string;
20
+ placeholder?: string;
21
+ };
22
+ status?: {
23
+ label?: string;
24
+ placeholder?: string;
25
+ activeLabel?: string;
26
+ inactiveLabel?: string;
27
+ };
28
+ };
29
+ };
30
+ };
31
+
32
+ export type PermissionFeaturesCollectionParams = {
33
+ translations?: PermissionFeaturesCollectionTranslations;
34
+ fields?: Field[];
35
+ access?: CollectionConfig["access"];
36
+ labels?: CollectionConfig["labels"];
37
+ admin?: CollectionConfig["admin"];
38
+ };
39
+
40
+ export type PermissionFeatureStatus = (typeof STATUS)[keyof typeof STATUS];
41
+
42
+ export type PermissionFeature = {
43
+ id: string | number;
44
+ code?: string | null;
45
+ sortOrder?: number | null;
46
+ status?: PermissionFeatureStatus | null;
47
+ };
@@ -0,0 +1,38 @@
1
+ import type { PermissionsCollectionTranslations } from "./types.js";
2
+
3
+ export const permissionsDefaultTranslations: PermissionsCollectionTranslations =
4
+ {
5
+ en: {
6
+ labels: {
7
+ singular: "Permission",
8
+ plural: "Permissions",
9
+ },
10
+ admin: {
11
+ group: "System",
12
+ },
13
+ fields: {
14
+ name: {
15
+ label: "Name",
16
+ placeholder: "Enter name",
17
+ },
18
+ permissionFeature: {
19
+ label: "Permission Feature",
20
+ placeholder: "Select permission feature",
21
+ },
22
+ permissionAction: {
23
+ label: "Permission Action",
24
+ placeholder: "Select permission action",
25
+ },
26
+ sortOrder: {
27
+ label: "Sort Order",
28
+ placeholder: "Enter sort order",
29
+ },
30
+ status: {
31
+ label: "Status",
32
+ placeholder: "Select status",
33
+ activeLabel: "Active",
34
+ inactiveLabel: "Inactive",
35
+ },
36
+ },
37
+ },
38
+ };
@@ -0,0 +1,160 @@
1
+ import type { CollectionConfig } from "payload";
2
+ import { STATUS } from "../../lib/constants/permission.js";
3
+ import {
4
+ getArrayOfMergedFieldAffectingData,
5
+ getSuperAdminAccess,
6
+ toLocaleRecord,
7
+ toSelectPlaceholder,
8
+ } from "../../lib/utils/index.js";
9
+ import type { PermissionsCollectionParams } from "./types.js";
10
+
11
+ export const getPermissionsCollection = (
12
+ params: PermissionsCollectionParams,
13
+ ) => {
14
+ const {
15
+ translations = {},
16
+ access = {},
17
+ fields = [],
18
+ labels = {},
19
+ admin = {},
20
+ } = params || {};
21
+ const arrTranslationsKeys = Object.keys(translations);
22
+ const permissions: CollectionConfig = {
23
+ slug: "permissions",
24
+ labels: {
25
+ singular: toLocaleRecord(
26
+ arrTranslationsKeys,
27
+ (locale) => translations[locale]?.labels?.singular,
28
+ ),
29
+ plural: toLocaleRecord(
30
+ arrTranslationsKeys,
31
+ (locale) => translations[locale]?.labels?.plural,
32
+ ),
33
+ ...labels,
34
+ },
35
+ admin: {
36
+ group: toLocaleRecord(
37
+ arrTranslationsKeys,
38
+ (locale) => translations[locale]?.admin?.group,
39
+ ),
40
+ useAsTitle: "name",
41
+ defaultColumns: [
42
+ "name",
43
+ "permissionFeature",
44
+ "permissionAction",
45
+ "status",
46
+ "updatedAt",
47
+ ],
48
+ ...admin,
49
+ },
50
+ access: {
51
+ create: getSuperAdminAccess,
52
+ update: getSuperAdminAccess,
53
+ delete: getSuperAdminAccess,
54
+ read: getSuperAdminAccess,
55
+ readVersions: getSuperAdminAccess,
56
+ unlock: getSuperAdminAccess,
57
+ admin: ({ req }) => {
58
+ return getSuperAdminAccess({ req });
59
+ },
60
+ ...access,
61
+ },
62
+ fields: getArrayOfMergedFieldAffectingData({
63
+ fields,
64
+ defaultFields: [
65
+ {
66
+ name: "name",
67
+ type: "text",
68
+ required: true,
69
+ label: toLocaleRecord(
70
+ arrTranslationsKeys,
71
+ (locale) => translations[locale]?.fields?.name?.label,
72
+ ),
73
+ admin: {
74
+ placeholder: toLocaleRecord(
75
+ arrTranslationsKeys,
76
+ (locale) => translations[locale]?.fields?.name?.placeholder,
77
+ ),
78
+ },
79
+ },
80
+ {
81
+ name: "permissionFeature",
82
+ type: "relationship",
83
+ required: true,
84
+ relationTo: "permission-features",
85
+ label: toLocaleRecord(
86
+ arrTranslationsKeys,
87
+ (locale) => translations[locale]?.fields?.permissionFeature?.label,
88
+ ),
89
+ admin: {
90
+ placeholder: toSelectPlaceholder(
91
+ arrTranslationsKeys,
92
+ (locale) =>
93
+ translations[locale]?.fields?.permissionFeature?.placeholder,
94
+ ),
95
+ },
96
+ },
97
+ {
98
+ name: "permissionAction",
99
+ type: "relationship",
100
+ required: true,
101
+ relationTo: "permission-actions",
102
+ label: toLocaleRecord(
103
+ arrTranslationsKeys,
104
+ (locale) => translations[locale]?.fields?.permissionAction?.label,
105
+ ),
106
+ admin: {
107
+ placeholder: toSelectPlaceholder(
108
+ arrTranslationsKeys,
109
+ (locale) =>
110
+ translations[locale]?.fields?.permissionAction?.placeholder,
111
+ ),
112
+ },
113
+ },
114
+ {
115
+ name: "sortOrder",
116
+ type: "number",
117
+ required: false,
118
+ defaultValue: 0,
119
+ label: toLocaleRecord(
120
+ arrTranslationsKeys,
121
+ (locale) => translations[locale]?.fields?.sortOrder?.label,
122
+ ),
123
+ admin: {
124
+ placeholder: toLocaleRecord(
125
+ arrTranslationsKeys,
126
+ (locale) => translations[locale]?.fields?.sortOrder?.placeholder,
127
+ ),
128
+ },
129
+ },
130
+ {
131
+ name: "status",
132
+ type: "select",
133
+ required: true,
134
+ label: toLocaleRecord(
135
+ arrTranslationsKeys,
136
+ (locale) => translations[locale]?.fields?.status?.label,
137
+ ),
138
+ defaultValue: STATUS.ACTIVE,
139
+ options: Object.values(STATUS).map((status) => ({
140
+ label: toLocaleRecord(
141
+ arrTranslationsKeys,
142
+ (locale) =>
143
+ translations[locale]?.fields?.status?.[`${status}Label`],
144
+ ),
145
+ value: status,
146
+ })),
147
+ admin: {
148
+ placeholder: toSelectPlaceholder(
149
+ arrTranslationsKeys,
150
+ (locale) => translations[locale]?.fields?.status?.placeholder,
151
+ ),
152
+ },
153
+ },
154
+ ],
155
+ }),
156
+ timestamps: true,
157
+ };
158
+
159
+ return permissions;
160
+ };
@@ -0,0 +1,57 @@
1
+ import type { CollectionConfig, Field } from "payload";
2
+ import { STATUS } from "src/lib/constants/permission.js";
3
+
4
+ export type PermissionsCollectionTranslations = {
5
+ [locale: string]: {
6
+ labels?: {
7
+ singular?: string;
8
+ plural?: string;
9
+ };
10
+ admin?: {
11
+ group?: string;
12
+ };
13
+ fields?: {
14
+ name?: {
15
+ label?: string;
16
+ placeholder?: string;
17
+ };
18
+ permissionFeature?: {
19
+ label?: string;
20
+ placeholder?: string;
21
+ };
22
+ permissionAction?: {
23
+ label?: string;
24
+ placeholder?: string;
25
+ };
26
+ sortOrder?: {
27
+ label?: string;
28
+ placeholder?: string;
29
+ };
30
+ status?: {
31
+ label?: string;
32
+ placeholder?: string;
33
+ activeLabel?: string;
34
+ inactiveLabel?: string;
35
+ };
36
+ };
37
+ };
38
+ };
39
+
40
+ export type PermissionsCollectionParams = {
41
+ translations?: PermissionsCollectionTranslations;
42
+ fields?: Field[];
43
+ access?: CollectionConfig["access"];
44
+ labels?: CollectionConfig["labels"];
45
+ admin?: CollectionConfig["admin"];
46
+ };
47
+
48
+ export type PermissionItemRef = string | number | { id?: string | number };
49
+
50
+ export type PermissionStatus = (typeof STATUS)[keyof typeof STATUS];
51
+
52
+ export type Permission = {
53
+ id: string | number;
54
+ permissionFeature?: PermissionItemRef;
55
+ permissionAction?: PermissionItemRef;
56
+ status?: PermissionStatus | null;
57
+ };
@@ -0,0 +1,44 @@
1
+ import type { RolesCollectionTranslations } from "./types.js"
2
+
3
+ export const rolesDefaultTranslations: RolesCollectionTranslations = {
4
+ en: {
5
+ labels: {
6
+ singular: "Role",
7
+ plural: "Roles",
8
+ },
9
+ admin: {
10
+ group: "System",
11
+ },
12
+ fields: {
13
+ code: {
14
+ label: "Code",
15
+ placeholder: "Enter code",
16
+ },
17
+ name: {
18
+ label: "Name",
19
+ placeholder: "Enter name",
20
+ },
21
+ description: {
22
+ label: "Description",
23
+ placeholder: "Enter description",
24
+ },
25
+ status: {
26
+ label: "Status",
27
+ placeholder: "Select status",
28
+ activeLabel: "Active",
29
+ inactiveLabel: "Inactive",
30
+ },
31
+ dataScope: {
32
+ label: "Data Scope",
33
+ placeholder: "Select data scope",
34
+ ownLabel: "Own",
35
+ allLabel: "All",
36
+ hierarchyLabel: "Hierarchy",
37
+ },
38
+ permissionMatrix: {
39
+ label: "Permission Matrix",
40
+ placeholder: "Select permission matrix",
41
+ },
42
+ },
43
+ },
44
+ }
@@ -0,0 +1,73 @@
1
+ import type { CollectionAfterChangeHook } from "payload";
2
+
3
+ type PermissionMatrixDraft = Record<string, boolean>;
4
+
5
+ const isPermissionMatrixDraft = (
6
+ value: unknown,
7
+ ): value is PermissionMatrixDraft => {
8
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
9
+ return false;
10
+ }
11
+
12
+ return Object.values(value).every((entry) => typeof entry === "boolean");
13
+ };
14
+
15
+ /**
16
+ * Persists `permissionMatrixDraft` on the role document into `roles-permissions` rows.
17
+ * RBAC checks use `roles-permissions`, not the JSON draft field.
18
+ */
19
+ export const syncPermissionMatrixDraftAfterChange: CollectionAfterChangeHook =
20
+ async ({ doc, req }) => {
21
+ if (!doc.id || !isPermissionMatrixDraft(doc.permissionMatrixDraft)) {
22
+ return;
23
+ }
24
+
25
+ const roleID = doc.id;
26
+
27
+ for (const [permissionID, enabled] of Object.entries(
28
+ doc.permissionMatrixDraft,
29
+ )) {
30
+ if (!permissionID) {
31
+ continue;
32
+ }
33
+
34
+ const existing = await req.payload.find({
35
+ collection: "roles-permissions",
36
+ depth: 0,
37
+ limit: 1,
38
+ req,
39
+ where: {
40
+ and: [
41
+ { role: { equals: roleID } },
42
+ { permission: { equals: permissionID } },
43
+ ],
44
+ },
45
+ });
46
+
47
+ const row = existing.docs[0];
48
+
49
+ if (row?.id) {
50
+ if (row.enabled === enabled) {
51
+ continue;
52
+ }
53
+
54
+ await req.payload.update({
55
+ id: row.id,
56
+ collection: "roles-permissions",
57
+ data: { enabled },
58
+ req,
59
+ });
60
+ continue;
61
+ }
62
+
63
+ await req.payload.create({
64
+ collection: "roles-permissions",
65
+ data: {
66
+ role: roleID,
67
+ permission: permissionID,
68
+ enabled,
69
+ },
70
+ req,
71
+ });
72
+ }
73
+ };