@payloadcms/plugin-multi-tenant 4.0.0-canary.33 → 4.0.0-canary.34

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.
@@ -1,6 +1,7 @@
1
1
  import type { SingleRelationshipField } from 'payload';
2
- import type { RootTenantFieldConfigOverrides } from '../../types.js';
2
+ import type { MultiTenantPluginConfig, RootTenantFieldConfigOverrides } from '../../types.js';
3
3
  type Args = {
4
+ adminUsersSlug?: string;
4
5
  debug?: boolean;
5
6
  isAutosaveEnabled?: boolean;
6
7
  name: string;
@@ -9,7 +10,8 @@ type Args = {
9
10
  tenantsArrayTenantFieldName: string;
10
11
  tenantsCollectionSlug: string;
11
12
  unique: boolean;
13
+ userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants'];
12
14
  };
13
- export declare const tenantField: ({ name, debug, isAutosaveEnabled, overrides: _overrides, tenantsArrayFieldName, tenantsArrayTenantFieldName, tenantsCollectionSlug, unique, }: Args) => SingleRelationshipField;
15
+ export declare const tenantField: ({ name, adminUsersSlug, debug, isAutosaveEnabled, overrides: _overrides, tenantsArrayFieldName, tenantsArrayTenantFieldName, tenantsCollectionSlug, unique, userHasAccessToAllTenants, }: Args) => SingleRelationshipField;
14
16
  export {};
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fields/tenantField/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+B,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAEnF,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAA;AA8BpE,KAAK,IAAI,GAAG;IACV,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,8BAA8B,CAAA;IAC1C,qBAAqB,EAAE,MAAM,CAAA;IAC7B,2BAA2B,EAAE,MAAM,CAAA;IACnC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AACD,eAAO,MAAM,WAAW,kJASrB,IAAI,KAAG,uBAiGT,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fields/tenantField/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+B,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAEnF,OAAO,KAAK,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAA;AAsD7F,KAAK,IAAI,GAAG;IACV,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,8BAA8B,CAAA;IAC1C,qBAAqB,EAAE,MAAM,CAAA;IAC7B,2BAA2B,EAAE,MAAM,CAAA;IACnC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,MAAM,EAAE,OAAO,CAAA;IACf,yBAAyB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;CACjF,CAAA;AACD,eAAO,MAAM,WAAW,6LAWrB,IAAI,KAAG,uBAmHT,CAAA"}
@@ -2,9 +2,10 @@ import { defaults } from '../../defaults.js';
2
2
  import { getCollectionIDType } from '../../utilities/getCollectionIDType.js';
3
3
  import { getTenantFromCookie } from '../../utilities/getTenantFromCookie.js';
4
4
  import { getUserTenantIDs } from '../../utilities/getUserTenantIDs.js';
5
- const fieldValidation = (validateFunction)=>(value, options)=>{
5
+ import { isValidTenantAssignment } from '../../utilities/isValidTenantAssignment.js';
6
+ const fieldValidation = ({ tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, validateFunction })=>async (value, options)=>{
6
7
  if (validateFunction) {
7
- const result = validateFunction(value, options);
8
+ const result = await validateFunction(value, options);
8
9
  if (result !== true) {
9
10
  return result;
10
11
  }
@@ -18,15 +19,31 @@ const fieldValidation = (validateFunction)=>(value, options)=>{
18
19
  return options.req.t('validation:required');
19
20
  }
20
21
  }
21
- return true;
22
+ if (isValidTenantAssignment({
23
+ previousValue: options.previousValue,
24
+ req: options.req,
25
+ tenantsArrayFieldName,
26
+ tenantsArrayTenantFieldName,
27
+ userHasAccessToAllTenants,
28
+ value
29
+ })) {
30
+ return true;
31
+ }
32
+ return options.req.t('validation:invalidSelection');
22
33
  };
23
- export const tenantField = ({ name = defaults.tenantFieldName, debug, isAutosaveEnabled, overrides: _overrides = {}, tenantsArrayFieldName = defaults.tenantsArrayFieldName, tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName, tenantsCollectionSlug = defaults.tenantCollectionSlug, unique })=>{
34
+ export const tenantField = ({ name = defaults.tenantFieldName, adminUsersSlug, debug, isAutosaveEnabled, overrides: _overrides = {}, tenantsArrayFieldName = defaults.tenantsArrayFieldName, tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName, tenantsCollectionSlug = defaults.tenantCollectionSlug, unique, userHasAccessToAllTenants })=>{
24
35
  const { hasMany = false, validate, ...overrides } = _overrides || {};
25
36
  return {
26
37
  ...overrides || {},
27
38
  name,
28
39
  type: 'relationship',
29
- access: overrides.access || {},
40
+ access: {
41
+ ...adminUsersSlug ? {
42
+ create: ({ req })=>Boolean(req.user && req.user.collection === adminUsersSlug),
43
+ update: ({ req })=>Boolean(req.user && req.user.collection === adminUsersSlug)
44
+ } : {},
45
+ ...overrides.access || {}
46
+ },
30
47
  admin: {
31
48
  allowCreate: false,
32
49
  allowEdit: false,
@@ -57,7 +74,7 @@ export const tenantField = ({ name = defaults.tenantFieldName, debug, isAutosave
57
74
  });
58
75
  const tenantFromCookie = getTenantFromCookie(req.headers, idType);
59
76
  if (tenantFromCookie) {
60
- const isValidTenant = await req.payload.count({
77
+ const { totalDocs } = await req.payload.count({
61
78
  collection: tenantsCollectionSlug,
62
79
  overrideAccess: false,
63
80
  req,
@@ -70,7 +87,7 @@ export const tenantField = ({ name = defaults.tenantFieldName, debug, isAutosave
70
87
  }
71
88
  }
72
89
  });
73
- return isValidTenant ? tenantFromCookie : null;
90
+ return totalDocs > 0 ? tenantFromCookie : null;
74
91
  }
75
92
  if (req.user && isAutosaveEnabled) {
76
93
  const userTenants = getUserTenantIDs(req.user, {
@@ -103,11 +120,21 @@ export const tenantField = ({ name = defaults.tenantFieldName, debug, isAutosave
103
120
  ...hasMany ? {
104
121
  hasMany: true,
105
122
  // TODO: V4 - replace validation with required: true
106
- validate: fieldValidation(validate)
123
+ validate: fieldValidation({
124
+ tenantsArrayFieldName,
125
+ tenantsArrayTenantFieldName,
126
+ userHasAccessToAllTenants,
127
+ validateFunction: validate
128
+ })
107
129
  } : {
108
130
  hasMany: false,
109
131
  // TODO: V4 - replace validation with required: true
110
- validate: fieldValidation(validate)
132
+ validate: fieldValidation({
133
+ tenantsArrayFieldName,
134
+ tenantsArrayTenantFieldName,
135
+ userHasAccessToAllTenants,
136
+ validateFunction: validate
137
+ })
111
138
  },
112
139
  // @ts-expect-error translations are not typed for this plugin
113
140
  label: overrides.label || (({ t })=>t('plugin-multi-tenant:field-assignedTenant-label'))
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/fields/tenantField/index.ts"],"sourcesContent":["import type { RelationshipFieldValidation, SingleRelationshipField } from 'payload'\n\nimport type { RootTenantFieldConfigOverrides } from '../../types.js'\n\nimport { defaults } from '../../defaults.js'\nimport { getCollectionIDType } from '../../utilities/getCollectionIDType.js'\nimport { getTenantFromCookie } from '../../utilities/getTenantFromCookie.js'\nimport { getUserTenantIDs } from '../../utilities/getUserTenantIDs.js'\n\nconst fieldValidation =\n (validateFunction?: RelationshipFieldValidation): RelationshipFieldValidation =>\n (value, options) => {\n if (validateFunction) {\n const result = validateFunction(value, options)\n if (result !== true) {\n return result\n }\n }\n\n if (options.hasMany) {\n if (!value || (Array.isArray(value) && value.length === 0)) {\n return options.req.t('validation:required')\n }\n } else {\n if (!value) {\n return options.req.t('validation:required')\n }\n }\n\n return true\n }\n\ntype Args = {\n debug?: boolean\n isAutosaveEnabled?: boolean\n name: string\n overrides?: RootTenantFieldConfigOverrides\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n tenantsCollectionSlug: string\n unique: boolean\n}\nexport const tenantField = ({\n name = defaults.tenantFieldName,\n debug,\n isAutosaveEnabled,\n overrides: _overrides = {},\n tenantsArrayFieldName = defaults.tenantsArrayFieldName,\n tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName,\n tenantsCollectionSlug = defaults.tenantCollectionSlug,\n unique,\n}: Args): SingleRelationshipField => {\n const { hasMany = false, validate, ...overrides } = _overrides || {}\n return {\n ...(overrides || {}),\n name,\n type: 'relationship',\n access: overrides.access || {},\n admin: {\n allowCreate: false,\n allowEdit: false,\n disabled: { column: true, filter: true, groupBy: true },\n position: 'sidebar',\n ...(overrides.admin || {}),\n components: {\n ...(overrides.admin?.components || {}),\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#TenantField',\n ...(typeof overrides.admin?.components?.Field !== 'string'\n ? overrides.admin?.components?.Field || {}\n : {}),\n clientProps: {\n ...(typeof overrides.admin?.components?.Field !== 'string'\n ? (overrides.admin?.components?.Field || {})?.clientProps\n : {}),\n debug,\n unique,\n },\n },\n },\n },\n defaultValue:\n overrides.defaultValue ||\n (async ({ req }) => {\n const idType = getCollectionIDType({\n collectionSlug: tenantsCollectionSlug,\n payload: req.payload,\n })\n const tenantFromCookie = getTenantFromCookie(req.headers, idType)\n if (tenantFromCookie) {\n const isValidTenant = await req.payload.count({\n collection: tenantsCollectionSlug,\n overrideAccess: false,\n req,\n user: req.user,\n where: {\n id: {\n in: [tenantFromCookie],\n },\n },\n })\n return isValidTenant ? tenantFromCookie : null\n }\n if (req.user && isAutosaveEnabled) {\n const userTenants = getUserTenantIDs(req.user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n if (userTenants.length > 0) {\n return userTenants[0]\n }\n }\n return null\n }),\n filterOptions:\n overrides.filterOptions ||\n (({ req }) => {\n const userAssignedTenants = getUserTenantIDs(req.user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n if (userAssignedTenants.length > 0) {\n return {\n id: {\n in: userAssignedTenants,\n },\n }\n }\n\n return true\n }),\n index: true,\n relationTo: tenantsCollectionSlug,\n unique,\n ...(hasMany\n ? {\n hasMany: true,\n // TODO: V4 - replace validation with required: true\n validate: fieldValidation(validate as RelationshipFieldValidation),\n }\n : {\n hasMany: false,\n // TODO: V4 - replace validation with required: true\n validate: fieldValidation(validate as RelationshipFieldValidation),\n }),\n // @ts-expect-error translations are not typed for this plugin\n label: overrides.label || (({ t }) => t('plugin-multi-tenant:field-assignedTenant-label')),\n }\n}\n"],"names":["defaults","getCollectionIDType","getTenantFromCookie","getUserTenantIDs","fieldValidation","validateFunction","value","options","result","hasMany","Array","isArray","length","req","t","tenantField","name","tenantFieldName","debug","isAutosaveEnabled","overrides","_overrides","tenantsArrayFieldName","tenantsArrayTenantFieldName","tenantsCollectionSlug","tenantCollectionSlug","unique","validate","type","access","admin","allowCreate","allowEdit","disabled","column","filter","groupBy","position","components","Field","path","clientProps","defaultValue","idType","collectionSlug","payload","tenantFromCookie","headers","isValidTenant","count","collection","overrideAccess","user","where","id","in","userTenants","filterOptions","userAssignedTenants","index","relationTo","label"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/fields/tenantField/index.ts"],"sourcesContent":["import type { RelationshipFieldValidation, SingleRelationshipField } from 'payload'\n\nimport type { MultiTenantPluginConfig, RootTenantFieldConfigOverrides } from '../../types.js'\n\nimport { defaults } from '../../defaults.js'\nimport { getCollectionIDType } from '../../utilities/getCollectionIDType.js'\nimport { getTenantFromCookie } from '../../utilities/getTenantFromCookie.js'\nimport { getUserTenantIDs } from '../../utilities/getUserTenantIDs.js'\nimport { isValidTenantAssignment } from '../../utilities/isValidTenantAssignment.js'\n\nconst fieldValidation =\n ({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n validateFunction,\n }: {\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants']\n validateFunction?: RelationshipFieldValidation\n }): RelationshipFieldValidation =>\n async (value, options) => {\n if (validateFunction) {\n const result = await validateFunction(value, options)\n if (result !== true) {\n return result\n }\n }\n\n if (options.hasMany) {\n if (!value || (Array.isArray(value) && value.length === 0)) {\n return options.req.t('validation:required')\n }\n } else {\n if (!value) {\n return options.req.t('validation:required')\n }\n }\n\n if (\n isValidTenantAssignment({\n previousValue: options.previousValue,\n req: options.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n value,\n })\n ) {\n return true\n }\n\n return options.req.t('validation:invalidSelection')\n }\n\ntype Args = {\n adminUsersSlug?: string\n debug?: boolean\n isAutosaveEnabled?: boolean\n name: string\n overrides?: RootTenantFieldConfigOverrides\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n tenantsCollectionSlug: string\n unique: boolean\n userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants']\n}\nexport const tenantField = ({\n name = defaults.tenantFieldName,\n adminUsersSlug,\n debug,\n isAutosaveEnabled,\n overrides: _overrides = {},\n tenantsArrayFieldName = defaults.tenantsArrayFieldName,\n tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName,\n tenantsCollectionSlug = defaults.tenantCollectionSlug,\n unique,\n userHasAccessToAllTenants,\n}: Args): SingleRelationshipField => {\n const { hasMany = false, validate, ...overrides } = _overrides || {}\n return {\n ...(overrides || {}),\n name,\n type: 'relationship',\n access: {\n ...(adminUsersSlug\n ? {\n create: ({ req }) => Boolean(req.user && req.user.collection === adminUsersSlug),\n update: ({ req }) => Boolean(req.user && req.user.collection === adminUsersSlug),\n }\n : {}),\n ...(overrides.access || {}),\n },\n admin: {\n allowCreate: false,\n allowEdit: false,\n disabled: { column: true, filter: true, groupBy: true },\n position: 'sidebar',\n ...(overrides.admin || {}),\n components: {\n ...(overrides.admin?.components || {}),\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#TenantField',\n ...(typeof overrides.admin?.components?.Field !== 'string'\n ? overrides.admin?.components?.Field || {}\n : {}),\n clientProps: {\n ...(typeof overrides.admin?.components?.Field !== 'string'\n ? (overrides.admin?.components?.Field || {})?.clientProps\n : {}),\n debug,\n unique,\n },\n },\n },\n },\n defaultValue:\n overrides.defaultValue ||\n (async ({ req }) => {\n const idType = getCollectionIDType({\n collectionSlug: tenantsCollectionSlug,\n payload: req.payload,\n })\n const tenantFromCookie = getTenantFromCookie(req.headers, idType)\n if (tenantFromCookie) {\n const { totalDocs } = await req.payload.count({\n collection: tenantsCollectionSlug,\n overrideAccess: false,\n req,\n user: req.user,\n where: {\n id: {\n in: [tenantFromCookie],\n },\n },\n })\n return totalDocs > 0 ? tenantFromCookie : null\n }\n if (req.user && isAutosaveEnabled) {\n const userTenants = getUserTenantIDs(req.user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n if (userTenants.length > 0) {\n return userTenants[0]\n }\n }\n return null\n }),\n filterOptions:\n overrides.filterOptions ||\n (({ req }) => {\n const userAssignedTenants = getUserTenantIDs(req.user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n if (userAssignedTenants.length > 0) {\n return {\n id: {\n in: userAssignedTenants,\n },\n }\n }\n\n return true\n }),\n index: true,\n relationTo: tenantsCollectionSlug,\n unique,\n ...(hasMany\n ? {\n hasMany: true,\n // TODO: V4 - replace validation with required: true\n validate: fieldValidation({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n validateFunction: validate as RelationshipFieldValidation,\n }),\n }\n : {\n hasMany: false,\n // TODO: V4 - replace validation with required: true\n validate: fieldValidation({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n validateFunction: validate as RelationshipFieldValidation,\n }),\n }),\n // @ts-expect-error translations are not typed for this plugin\n label: overrides.label || (({ t }) => t('plugin-multi-tenant:field-assignedTenant-label')),\n }\n}\n"],"names":["defaults","getCollectionIDType","getTenantFromCookie","getUserTenantIDs","isValidTenantAssignment","fieldValidation","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","validateFunction","value","options","result","hasMany","Array","isArray","length","req","t","previousValue","tenantField","name","tenantFieldName","adminUsersSlug","debug","isAutosaveEnabled","overrides","_overrides","tenantsCollectionSlug","tenantCollectionSlug","unique","validate","type","access","create","Boolean","user","collection","update","admin","allowCreate","allowEdit","disabled","column","filter","groupBy","position","components","Field","path","clientProps","defaultValue","idType","collectionSlug","payload","tenantFromCookie","headers","totalDocs","count","overrideAccess","where","id","in","userTenants","filterOptions","userAssignedTenants","index","relationTo","label"],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { tenantField } from './index.js';
3
+ const baseArgs = {
4
+ adminUsersSlug: 'users',
5
+ name: 'tenant',
6
+ tenantsArrayFieldName: 'tenants',
7
+ tenantsArrayTenantFieldName: 'tenant',
8
+ tenantsCollectionSlug: 'tenants',
9
+ unique: false
10
+ };
11
+ describe('tenantField access', ()=>{
12
+ it('should let the admin users collection write the tenant field', ()=>{
13
+ const field = tenantField(baseArgs);
14
+ const req = {
15
+ user: {
16
+ collection: 'users'
17
+ }
18
+ };
19
+ expect(field.access.create({
20
+ req
21
+ })).toBe(true);
22
+ expect(field.access.update({
23
+ req
24
+ })).toBe(true);
25
+ });
26
+ it('should block other auth collections from writing the tenant field', ()=>{
27
+ const field = tenantField(baseArgs);
28
+ const req = {
29
+ user: {
30
+ collection: 'customers'
31
+ }
32
+ };
33
+ expect(field.access.create({
34
+ req
35
+ })).toBe(false);
36
+ expect(field.access.update({
37
+ req
38
+ })).toBe(false);
39
+ });
40
+ it('should block an unauthenticated request from writing the tenant field', ()=>{
41
+ const field = tenantField(baseArgs);
42
+ const req = {
43
+ user: null
44
+ };
45
+ expect(field.access.create({
46
+ req
47
+ })).toBe(false);
48
+ expect(field.access.update({
49
+ req
50
+ })).toBe(false);
51
+ });
52
+ });
53
+
54
+ //# sourceMappingURL=index.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/fields/tenantField/index.spec.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest'\n\nimport { tenantField } from './index.js'\n\nconst baseArgs = {\n adminUsersSlug: 'users',\n name: 'tenant',\n tenantsArrayFieldName: 'tenants',\n tenantsArrayTenantFieldName: 'tenant',\n tenantsCollectionSlug: 'tenants',\n unique: false,\n}\n\ndescribe('tenantField access', () => {\n it('should let the admin users collection write the tenant field', () => {\n const field = tenantField(baseArgs)\n const req = { user: { collection: 'users' } }\n\n expect(field.access!.create!({ req } as never)).toBe(true)\n expect(field.access!.update!({ req } as never)).toBe(true)\n })\n\n it('should block other auth collections from writing the tenant field', () => {\n const field = tenantField(baseArgs)\n const req = { user: { collection: 'customers' } }\n\n expect(field.access!.create!({ req } as never)).toBe(false)\n expect(field.access!.update!({ req } as never)).toBe(false)\n })\n\n it('should block an unauthenticated request from writing the tenant field', () => {\n const field = tenantField(baseArgs)\n const req = { user: null }\n\n expect(field.access!.create!({ req } as never)).toBe(false)\n expect(field.access!.update!({ req } as never)).toBe(false)\n })\n})\n"],"names":["describe","expect","it","tenantField","baseArgs","adminUsersSlug","name","tenantsArrayFieldName","tenantsArrayTenantFieldName","tenantsCollectionSlug","unique","field","req","user","collection","access","create","toBe","update"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,WAAW,QAAQ,aAAY;AAExC,MAAMC,WAAW;IACfC,gBAAgB;IAChBC,MAAM;IACNC,uBAAuB;IACvBC,6BAA6B;IAC7BC,uBAAuB;IACvBC,QAAQ;AACV;AAEAV,SAAS,sBAAsB;IAC7BE,GAAG,gEAAgE;QACjE,MAAMS,QAAQR,YAAYC;QAC1B,MAAMQ,MAAM;YAAEC,MAAM;gBAAEC,YAAY;YAAQ;QAAE;QAE5Cb,OAAOU,MAAMI,MAAM,CAAEC,MAAM,CAAE;YAAEJ;QAAI,IAAaK,IAAI,CAAC;QACrDhB,OAAOU,MAAMI,MAAM,CAAEG,MAAM,CAAE;YAAEN;QAAI,IAAaK,IAAI,CAAC;IACvD;IAEAf,GAAG,qEAAqE;QACtE,MAAMS,QAAQR,YAAYC;QAC1B,MAAMQ,MAAM;YAAEC,MAAM;gBAAEC,YAAY;YAAY;QAAE;QAEhDb,OAAOU,MAAMI,MAAM,CAAEC,MAAM,CAAE;YAAEJ;QAAI,IAAaK,IAAI,CAAC;QACrDhB,OAAOU,MAAMI,MAAM,CAAEG,MAAM,CAAE;YAAEN;QAAI,IAAaK,IAAI,CAAC;IACvD;IAEAf,GAAG,yEAAyE;QAC1E,MAAMS,QAAQR,YAAYC;QAC1B,MAAMQ,MAAM;YAAEC,MAAM;QAAK;QAEzBZ,OAAOU,MAAMI,MAAM,CAAEC,MAAM,CAAE;YAAEJ;QAAI,IAAaK,IAAI,CAAC;QACrDhB,OAAOU,MAAMI,MAAM,CAAEG,MAAM,CAAE;YAAEN;QAAI,IAAaK,IAAI,CAAC;IACvD;AACF"}
@@ -0,0 +1,15 @@
1
+ import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload';
2
+ import type { MultiTenantPluginConfig } from '../types.js';
3
+ type Args = {
4
+ collection: CollectionConfig;
5
+ tenantFieldName: string;
6
+ tenantsArrayFieldName: string;
7
+ tenantsArrayTenantFieldName: string;
8
+ userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'];
9
+ };
10
+ export declare const addCollectionBeforeValidateHook: ({ collection, tenantFieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, }: Args) => void;
11
+ export declare const enforceTenantMembership: ({ collectionSlug, tenantFieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, }: {
12
+ collectionSlug: string;
13
+ } & Omit<Args, 'collection'>) => CollectionBeforeValidateHook;
14
+ export {};
15
+ //# sourceMappingURL=enforceTenantMembership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforceTenantMembership.d.ts","sourceRoot":"","sources":["../../src/hooks/enforceTenantMembership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI7E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAI1D,KAAK,IAAI,GAAG;IACV,UAAU,EAAE,gBAAgB,CAAA;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,2BAA2B,EAAE,MAAM,CAAA;IACnC,yBAAyB,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,2BAA2B,CAAC,CAAA;CAC1F,CAAA;AAED,eAAO,MAAM,+BAA+B,oHAMzC,IAAI,SAYN,CAAA;AAED,eAAO,MAAM,uBAAuB,wHAO/B;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,KAAG,4BA0B1D,CAAA"}
@@ -0,0 +1,39 @@
1
+ import { ValidationError } from 'payload';
2
+ import { isValidTenantAssignment } from '../utilities/isValidTenantAssignment.js';
3
+ export const addCollectionBeforeValidateHook = ({ collection, tenantFieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>{
4
+ collection.hooks ??= {};
5
+ collection.hooks.beforeValidate ??= [];
6
+ collection.hooks.beforeValidate.push(enforceTenantMembership({
7
+ collectionSlug: collection.slug,
8
+ tenantFieldName,
9
+ tenantsArrayFieldName,
10
+ tenantsArrayTenantFieldName,
11
+ userHasAccessToAllTenants
12
+ }));
13
+ };
14
+ export const enforceTenantMembership = ({ collectionSlug, tenantFieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>({ data, originalDoc, req })=>{
15
+ if (!data || !(tenantFieldName in data)) {
16
+ return data;
17
+ }
18
+ if (!isValidTenantAssignment({
19
+ previousValue: originalDoc?.[tenantFieldName],
20
+ req,
21
+ tenantsArrayFieldName,
22
+ tenantsArrayTenantFieldName,
23
+ userHasAccessToAllTenants,
24
+ value: data[tenantFieldName]
25
+ })) {
26
+ throw new ValidationError({
27
+ collection: collectionSlug,
28
+ errors: [
29
+ {
30
+ message: req.t('validation:invalidSelection'),
31
+ path: tenantFieldName
32
+ }
33
+ ]
34
+ }, req.t);
35
+ }
36
+ return data;
37
+ };
38
+
39
+ //# sourceMappingURL=enforceTenantMembership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/enforceTenantMembership.ts"],"sourcesContent":["import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload'\n\nimport { ValidationError } from 'payload'\n\nimport type { MultiTenantPluginConfig } from '../types.js'\n\nimport { isValidTenantAssignment } from '../utilities/isValidTenantAssignment.js'\n\ntype Args = {\n collection: CollectionConfig\n tenantFieldName: string\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']\n}\n\nexport const addCollectionBeforeValidateHook = ({\n collection,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n}: Args) => {\n collection.hooks ??= {}\n collection.hooks.beforeValidate ??= []\n collection.hooks.beforeValidate.push(\n enforceTenantMembership({\n collectionSlug: collection.slug,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n }),\n )\n}\n\nexport const enforceTenantMembership =\n ({\n collectionSlug,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n }: { collectionSlug: string } & Omit<Args, 'collection'>): CollectionBeforeValidateHook =>\n ({ data, originalDoc, req }) => {\n if (!data || !(tenantFieldName in data)) {\n return data\n }\n\n if (\n !isValidTenantAssignment({\n previousValue: originalDoc?.[tenantFieldName],\n req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n value: (data as Record<string, unknown>)[tenantFieldName],\n })\n ) {\n throw new ValidationError(\n {\n collection: collectionSlug,\n errors: [{ message: req.t('validation:invalidSelection'), path: tenantFieldName }],\n },\n req.t,\n )\n }\n\n return data\n }\n"],"names":["ValidationError","isValidTenantAssignment","addCollectionBeforeValidateHook","collection","tenantFieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","hooks","beforeValidate","push","enforceTenantMembership","collectionSlug","slug","data","originalDoc","req","previousValue","value","errors","message","t","path"],"mappings":"AAEA,SAASA,eAAe,QAAQ,UAAS;AAIzC,SAASC,uBAAuB,QAAQ,0CAAyC;AAUjF,OAAO,MAAMC,kCAAkC,CAAC,EAC9CC,UAAU,EACVC,eAAe,EACfC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EACpB;IACLJ,WAAWK,KAAK,KAAK,CAAC;IACtBL,WAAWK,KAAK,CAACC,cAAc,KAAK,EAAE;IACtCN,WAAWK,KAAK,CAACC,cAAc,CAACC,IAAI,CAClCC,wBAAwB;QACtBC,gBAAgBT,WAAWU,IAAI;QAC/BT;QACAC;QACAC;QACAC;IACF;AAEJ,EAAC;AAED,OAAO,MAAMI,0BACX,CAAC,EACCC,cAAc,EACdR,eAAe,EACfC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EAC6B,GACxD,CAAC,EAAEO,IAAI,EAAEC,WAAW,EAAEC,GAAG,EAAE;QACzB,IAAI,CAACF,QAAQ,CAAEV,CAAAA,mBAAmBU,IAAG,GAAI;YACvC,OAAOA;QACT;QAEA,IACE,CAACb,wBAAwB;YACvBgB,eAAeF,aAAa,CAACX,gBAAgB;YAC7CY;YACAX;YACAC;YACAC;YACAW,OAAO,AAACJ,IAAgC,CAACV,gBAAgB;QAC3D,IACA;YACA,MAAM,IAAIJ,gBACR;gBACEG,YAAYS;gBACZO,QAAQ;oBAAC;wBAAEC,SAASJ,IAAIK,CAAC,CAAC;wBAAgCC,MAAMlB;oBAAgB;iBAAE;YACpF,GACAY,IAAIK,CAAC;QAET;QAEA,OAAOP;IACT,EAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAczD,eAAO,MAAM,iBAAiB,gEA+a5B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAezD,eAAO,MAAM,iBAAiB,gEA6b5B,CAAA"}
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { tenantField } from './fields/tenantField/index.js';
6
6
  import { tenantsArrayField } from './fields/tenantsArrayField/index.js';
7
7
  import { filterDocumentsByTenants } from './filters/filterDocumentsByTenants.js';
8
8
  import { addTenantCleanup } from './hooks/afterTenantDelete.js';
9
+ import { enforceTenantMembership } from './hooks/enforceTenantMembership.js';
9
10
  import { translations } from './translations/index.js';
10
11
  import { addCollectionAccess } from './utilities/addCollectionAccess.js';
11
12
  import { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js';
@@ -245,15 +246,26 @@ export const multiTenantPlugin = definePlugin({
245
246
  * Add tenant field to enabled collections
246
247
  */ collection.fields.unshift(tenantField({
247
248
  name: tenantFieldName,
249
+ adminUsersSlug: adminUsersCollection.slug,
248
250
  debug: pluginConfig.debug,
249
251
  isAutosaveEnabled: hasAutosaveEnabled(collection),
250
252
  overrides: pluginConfig.collections[collection.slug]?.tenantFieldOverrides ? pluginConfig.collections[collection.slug]?.tenantFieldOverrides : pluginConfig.tenantField || {},
251
253
  tenantsArrayFieldName,
252
254
  tenantsArrayTenantFieldName,
253
255
  tenantsCollectionSlug,
254
- unique: isGlobal
256
+ unique: isGlobal,
257
+ userHasAccessToAllTenants
255
258
  }));
256
259
  }
260
+ collection.hooks ??= {};
261
+ collection.hooks.beforeValidate ??= [];
262
+ collection.hooks.beforeValidate.push(enforceTenantMembership({
263
+ collectionSlug: collection.slug,
264
+ tenantFieldName,
265
+ tenantsArrayFieldName,
266
+ tenantsArrayTenantFieldName,
267
+ userHasAccessToAllTenants
268
+ }));
257
269
  const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {};
258
270
  if (useBaseFilter ?? useBaseListFilter ?? true) {
259
271
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { AcceptedLanguages } from '@payloadcms/translations'\nimport type { CollectionConfig } from 'payload'\n\nimport { definePlugin } from 'payload'\nimport { hasAutosaveEnabled } from 'payload/shared'\n\nimport type { PluginDefaultTranslationsObject } from './translations/types.js'\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { defaults } from './defaults.js'\nimport { getTenantOptionsEndpoint } from './endpoints/getTenantOptionsEndpoint.js'\nimport { tenantField } from './fields/tenantField/index.js'\nimport { tenantsArrayField } from './fields/tenantsArrayField/index.js'\nimport { filterDocumentsByTenants } from './filters/filterDocumentsByTenants.js'\nimport { addTenantCleanup } from './hooks/afterTenantDelete.js'\nimport { translations } from './translations/index.js'\nimport { addCollectionAccess, type TenantAccessConfig } from './utilities/addCollectionAccess.js'\nimport { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'\nimport { combineFilters } from './utilities/combineFilters.js'\nimport { miniChalk } from './utilities/miniChalk.js'\n\nexport const multiTenantPlugin = definePlugin<MultiTenantPluginConfig>({\n slug: '@payloadcms/plugin-multi-tenant',\n plugin: ({ config: incomingConfig, options: pluginConfig }) => {\n if (pluginConfig.enabled === false) {\n return incomingConfig\n }\n\n /**\n * Set defaults\n */\n const userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'] =\n typeof pluginConfig.userHasAccessToAllTenants === 'function'\n ? pluginConfig.userHasAccessToAllTenants\n : () => false\n const tenantsCollectionSlug = (pluginConfig.tenantsSlug =\n pluginConfig.tenantsSlug || defaults.tenantCollectionSlug)\n const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName\n const tenantsArrayFieldName =\n pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n const tenantAccessScopes: TenantAccessConfig[] = []\n\n /**\n * Add defaults for admin properties\n */\n if (!incomingConfig.admin) {\n incomingConfig.admin = {}\n }\n if (!incomingConfig.admin?.components) {\n incomingConfig.admin.components = {\n actions: [],\n beforeNav: [],\n providers: [],\n }\n }\n if (!incomingConfig.admin.components?.providers) {\n incomingConfig.admin.components.providers = []\n }\n if (!incomingConfig.admin.components?.actions) {\n incomingConfig.admin.components.actions = []\n }\n if (!incomingConfig.admin.components?.beforeNav) {\n incomingConfig.admin.components.beforeNav = []\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n /**\n * Add tenants array field to users collection\n */\n const adminUsersCollection = incomingConfig.collections.find(({ slug, auth }) => {\n if (incomingConfig.admin?.user) {\n return slug === incomingConfig.admin.user\n } else if (auth) {\n return true\n }\n })\n\n if (!adminUsersCollection) {\n throw Error('An auth enabled collection was not found')\n }\n\n /**\n * Add tenants array field to users collection\n */\n if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {\n adminUsersCollection.fields.push(\n tenantsArrayField({\n ...(pluginConfig?.tenantsArrayField || {}),\n arrayFieldAccess: {\n create: ({ req }) => Boolean(req.user && userHasAccessToAllTenants(req.user)),\n update: ({ req }) => Boolean(req.user && userHasAccessToAllTenants(req.user)),\n ...(pluginConfig?.tenantsArrayField?.arrayFieldAccess || {}),\n },\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n }),\n )\n }\n\n tenantAccessScopes.push({\n accessResultCallback: pluginConfig.usersAccessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection: adminUsersCollection,\n fieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.useUsersTenantFilter !== false) {\n if (!adminUsersCollection.admin) {\n adminUsersCollection.admin = {}\n }\n\n const baseFilter =\n adminUsersCollection.admin?.baseFilter ?? adminUsersCollection.admin?.baseListFilter\n adminUsersCollection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n let tenantCollection: CollectionConfig | undefined\n\n const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<\n [string[], string[]]\n >(\n (acc, slug) => {\n if (slug === adminUsersCollection.slug) {\n return acc\n }\n if (pluginConfig?.collections?.[slug]?.isGlobal) {\n acc[1].push(slug)\n } else {\n acc[0].push(slug)\n }\n\n return acc\n },\n [[], []],\n )\n\n // used to track and not duplicate filterOptions on referenced blocks\n const blockReferencesWithFilters: string[] = []\n\n // used to validate enabled collection slugs\n const multiTenantCollectionsFound: string[] = []\n\n /**\n * Modify collections\n */\n incomingConfig.collections.forEach((collection) => {\n /**\n * Modify tenants collection\n */\n if (collection.slug === tenantsCollectionSlug) {\n tenantCollection = collection\n\n if (pluginConfig.useTenantsCollectionAccess !== false) {\n /**\n * Add access control constraint to tenants collection\n * - constrains access a users assigned tenants\n */\n tenantAccessScopes.push({\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: 'id',\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n\n if (pluginConfig.useTenantsListFilter !== false) {\n /**\n * Add list filter to tenants collection\n * - filter by selected tenant\n */\n if (!collection.admin) {\n collection.admin = {}\n }\n\n const baseFilter = collection.admin?.baseFilter ?? collection.admin?.baseListFilter\n collection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: 'id',\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.cleanupAfterTenantDelete !== false) {\n /**\n * Add cleanup logic when tenant is deleted\n * - delete documents related to tenant\n * - remove tenant from users\n */\n addTenantCleanup({\n collection,\n enabledSlugs: [...collectionSlugs, ...globalCollectionSlugs],\n tenantFieldName,\n tenantsCollectionSlug,\n usersSlug: adminUsersCollection.slug,\n usersTenantsArrayFieldName: tenantsArrayFieldName,\n usersTenantsArrayTenantFieldName: tenantsArrayTenantFieldName,\n })\n }\n\n /**\n * Add custom tenant field that watches and dispatches updates to the selector\n */\n collection.fields.push({\n name: '_watchTenant',\n type: 'ui',\n admin: {\n components: {\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#WatchTenantCollection',\n },\n },\n disabled: { bulkEdit: true, column: true },\n },\n })\n\n collection.endpoints = [\n ...(collection.endpoints || []),\n getTenantOptionsEndpoint({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n }),\n ]\n } else if (pluginConfig.collections?.[collection.slug]) {\n if (collection.slug === adminUsersCollection.slug) {\n // eslint-disable-next-line no-console\n console.warn(\n `[plugin-multi-tenant] The admin users collection \"${collection.slug}\" should not be listed in pluginConfig.collections — it is already handled by the plugin. Skipping tenant-field processing for this collection to avoid double access control and validation errors.`,\n )\n return\n }\n multiTenantCollectionsFound.push(collection.slug)\n const isGlobal = Boolean(pluginConfig.collections[collection.slug]?.isGlobal)\n\n if (isGlobal) {\n collection.disableDuplicate = true\n }\n\n if (!pluginConfig.debug && !isGlobal) {\n collection.admin ??= {}\n collection.admin.components ??= {}\n collection.admin.components.edit ??= {}\n collection.admin.components.edit.editMenuItems ??= []\n collection.admin.components.edit.editMenuItems.push({\n path: '@payloadcms/plugin-multi-tenant/client#AssignTenantFieldTrigger',\n })\n }\n\n /**\n * Add filter options to all relationship fields\n */\n collection.fields = addFilterOptionsToFields({\n blockReferencesWithFilters,\n config: incomingConfig,\n fields: collection.fields,\n tenantEnabledCollectionSlugs: collectionSlugs,\n tenantEnabledGlobalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.collections[collection.slug]?.customTenantField !== true) {\n /**\n * Add tenant field to enabled collections\n */\n collection.fields.unshift(\n tenantField({\n name: tenantFieldName,\n debug: pluginConfig.debug,\n isAutosaveEnabled: hasAutosaveEnabled(collection),\n overrides: pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n ? pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n : pluginConfig.tenantField || {},\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n unique: isGlobal,\n }),\n )\n }\n\n const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {}\n if (useBaseFilter ?? useBaseListFilter ?? true) {\n /**\n * Add list filter to enabled collections\n * - filters results by selected tenant\n */\n collection.admin = collection.admin || {}\n collection.admin.baseFilter = combineFilters({\n baseFilter: collection.admin?.baseFilter ?? collection.admin?.baseListFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: tenantFieldName,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {\n /**\n * Add access control constraint to tenant enabled collection\n */\n tenantAccessScopes.push({\n accessResultCallback: pluginConfig.collections[collection.slug]?.accessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n }\n })\n\n if (!tenantCollection) {\n throw new Error(`Tenants collection not found with slug: ${tenantsCollectionSlug}`)\n }\n\n if (\n multiTenantCollectionsFound.length !==\n collectionSlugs.length + globalCollectionSlugs.length\n ) {\n const missingSlugs = [...collectionSlugs, ...globalCollectionSlugs].filter(\n (slug) => !multiTenantCollectionsFound.includes(slug),\n )\n // eslint-disable-next-line no-console\n console.error(\n miniChalk.yellowBold('WARNING (plugin-multi-tenant)'),\n 'missing collections',\n missingSlugs,\n 'try placing the multi-tenant plugin after other plugins.',\n )\n }\n\n addCollectionAccess({ config: incomingConfig, scopes: tenantAccessScopes })\n\n /**\n * Add TenantSelectionProvider to admin providers\n */\n incomingConfig.admin.components.providers.push({\n clientProps: {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug: tenantCollection.slug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider',\n })\n\n /**\n * Add global redirect action\n */\n if (globalCollectionSlugs.length) {\n incomingConfig.admin.components.actions.push({\n path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',\n serverProps: {\n globalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n })\n }\n\n /**\n * Add tenant selector to admin UI\n */\n incomingConfig.admin.components.beforeNav.push({\n clientProps: {\n enabledSlugs: [\n ...collectionSlugs,\n ...globalCollectionSlugs,\n adminUsersCollection.slug,\n tenantCollection.slug,\n ],\n label: pluginConfig.tenantSelectorLabel || undefined,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelector',\n })\n\n /**\n * Merge plugin translations\n */\n if (!incomingConfig.i18n) {\n incomingConfig.i18n = {}\n }\n Object.entries(translations).forEach(([locale, pluginI18nObject]) => {\n const typedLocale = locale as AcceptedLanguages\n if (!incomingConfig.i18n!.translations) {\n incomingConfig.i18n!.translations = {}\n }\n if (!(typedLocale in incomingConfig.i18n!.translations)) {\n incomingConfig.i18n!.translations[typedLocale] = {}\n }\n if (!('plugin-multi-tenant' in incomingConfig.i18n!.translations[typedLocale]!)) {\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {} as PluginDefaultTranslationsObject['plugin-multi-tenant']\n }\n\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {\n ...pluginI18nObject.translations['plugin-multi-tenant'],\n ...(pluginConfig.i18n?.translations?.[typedLocale] || {}),\n }\n })\n\n return incomingConfig\n },\n})\n"],"names":["definePlugin","hasAutosaveEnabled","defaults","getTenantOptionsEndpoint","tenantField","tenantsArrayField","filterDocumentsByTenants","addTenantCleanup","translations","addCollectionAccess","addFilterOptionsToFields","combineFilters","miniChalk","multiTenantPlugin","slug","plugin","config","incomingConfig","options","pluginConfig","enabled","userHasAccessToAllTenants","tenantsCollectionSlug","tenantsSlug","tenantCollectionSlug","tenantFieldName","name","tenantsArrayFieldName","arrayFieldName","tenantsArrayTenantFieldName","arrayTenantFieldName","tenantAccessScopes","admin","components","actions","beforeNav","providers","collections","adminUsersCollection","find","auth","user","Error","includeDefaultField","fields","push","arrayFieldAccess","create","req","Boolean","update","accessResultCallback","usersAccessResultOverride","adminUsersSlug","collection","fieldName","useUsersTenantFilter","baseFilter","baseListFilter","customFilter","args","filterFieldName","tenantCollection","collectionSlugs","globalCollectionSlugs","Object","keys","reduce","acc","isGlobal","blockReferencesWithFilters","multiTenantCollectionsFound","forEach","useTenantsCollectionAccess","useTenantsListFilter","cleanupAfterTenantDelete","enabledSlugs","usersSlug","usersTenantsArrayFieldName","usersTenantsArrayTenantFieldName","type","Field","path","disabled","bulkEdit","column","endpoints","useAsTitle","console","warn","disableDuplicate","debug","edit","editMenuItems","tenantEnabledCollectionSlugs","tenantEnabledGlobalSlugs","customTenantField","unshift","isAutosaveEnabled","overrides","tenantFieldOverrides","unique","useBaseFilter","useBaseListFilter","useTenantAccess","accessResultOverride","length","missingSlugs","filter","includes","error","yellowBold","scopes","clientProps","serverProps","globalSlugs","label","tenantSelectorLabel","undefined","i18n","entries","locale","pluginI18nObject","typedLocale"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { AcceptedLanguages } from '@payloadcms/translations'\nimport type { CollectionConfig } from 'payload'\n\nimport { definePlugin } from 'payload'\nimport { hasAutosaveEnabled } from 'payload/shared'\n\nimport type { PluginDefaultTranslationsObject } from './translations/types.js'\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { defaults } from './defaults.js'\nimport { getTenantOptionsEndpoint } from './endpoints/getTenantOptionsEndpoint.js'\nimport { tenantField } from './fields/tenantField/index.js'\nimport { tenantsArrayField } from './fields/tenantsArrayField/index.js'\nimport { filterDocumentsByTenants } from './filters/filterDocumentsByTenants.js'\nimport { addTenantCleanup } from './hooks/afterTenantDelete.js'\nimport { enforceTenantMembership } from './hooks/enforceTenantMembership.js'\nimport { translations } from './translations/index.js'\nimport { addCollectionAccess, type TenantAccessConfig } from './utilities/addCollectionAccess.js'\nimport { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'\nimport { combineFilters } from './utilities/combineFilters.js'\nimport { miniChalk } from './utilities/miniChalk.js'\n\nexport const multiTenantPlugin = definePlugin<MultiTenantPluginConfig>({\n slug: '@payloadcms/plugin-multi-tenant',\n plugin: ({ config: incomingConfig, options: pluginConfig }) => {\n if (pluginConfig.enabled === false) {\n return incomingConfig\n }\n\n /**\n * Set defaults\n */\n const userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'] =\n typeof pluginConfig.userHasAccessToAllTenants === 'function'\n ? pluginConfig.userHasAccessToAllTenants\n : () => false\n const tenantsCollectionSlug = (pluginConfig.tenantsSlug =\n pluginConfig.tenantsSlug || defaults.tenantCollectionSlug)\n const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName\n const tenantsArrayFieldName =\n pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n const tenantAccessScopes: TenantAccessConfig[] = []\n\n /**\n * Add defaults for admin properties\n */\n if (!incomingConfig.admin) {\n incomingConfig.admin = {}\n }\n if (!incomingConfig.admin?.components) {\n incomingConfig.admin.components = {\n actions: [],\n beforeNav: [],\n providers: [],\n }\n }\n if (!incomingConfig.admin.components?.providers) {\n incomingConfig.admin.components.providers = []\n }\n if (!incomingConfig.admin.components?.actions) {\n incomingConfig.admin.components.actions = []\n }\n if (!incomingConfig.admin.components?.beforeNav) {\n incomingConfig.admin.components.beforeNav = []\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n /**\n * Add tenants array field to users collection\n */\n const adminUsersCollection = incomingConfig.collections.find(({ slug, auth }) => {\n if (incomingConfig.admin?.user) {\n return slug === incomingConfig.admin.user\n } else if (auth) {\n return true\n }\n })\n\n if (!adminUsersCollection) {\n throw Error('An auth enabled collection was not found')\n }\n\n /**\n * Add tenants array field to users collection\n */\n if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {\n adminUsersCollection.fields.push(\n tenantsArrayField({\n ...(pluginConfig?.tenantsArrayField || {}),\n arrayFieldAccess: {\n create: ({ req }) => Boolean(req.user && userHasAccessToAllTenants(req.user)),\n update: ({ req }) => Boolean(req.user && userHasAccessToAllTenants(req.user)),\n ...(pluginConfig?.tenantsArrayField?.arrayFieldAccess || {}),\n },\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n }),\n )\n }\n\n tenantAccessScopes.push({\n accessResultCallback: pluginConfig.usersAccessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection: adminUsersCollection,\n fieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.useUsersTenantFilter !== false) {\n if (!adminUsersCollection.admin) {\n adminUsersCollection.admin = {}\n }\n\n const baseFilter =\n adminUsersCollection.admin?.baseFilter ?? adminUsersCollection.admin?.baseListFilter\n adminUsersCollection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n let tenantCollection: CollectionConfig | undefined\n\n const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<\n [string[], string[]]\n >(\n (acc, slug) => {\n if (slug === adminUsersCollection.slug) {\n return acc\n }\n if (pluginConfig?.collections?.[slug]?.isGlobal) {\n acc[1].push(slug)\n } else {\n acc[0].push(slug)\n }\n\n return acc\n },\n [[], []],\n )\n\n // used to track and not duplicate filterOptions on referenced blocks\n const blockReferencesWithFilters: string[] = []\n\n // used to validate enabled collection slugs\n const multiTenantCollectionsFound: string[] = []\n\n /**\n * Modify collections\n */\n incomingConfig.collections.forEach((collection) => {\n /**\n * Modify tenants collection\n */\n if (collection.slug === tenantsCollectionSlug) {\n tenantCollection = collection\n\n if (pluginConfig.useTenantsCollectionAccess !== false) {\n /**\n * Add access control constraint to tenants collection\n * - constrains access a users assigned tenants\n */\n tenantAccessScopes.push({\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: 'id',\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n\n if (pluginConfig.useTenantsListFilter !== false) {\n /**\n * Add list filter to tenants collection\n * - filter by selected tenant\n */\n if (!collection.admin) {\n collection.admin = {}\n }\n\n const baseFilter = collection.admin?.baseFilter ?? collection.admin?.baseListFilter\n collection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: 'id',\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.cleanupAfterTenantDelete !== false) {\n /**\n * Add cleanup logic when tenant is deleted\n * - delete documents related to tenant\n * - remove tenant from users\n */\n addTenantCleanup({\n collection,\n enabledSlugs: [...collectionSlugs, ...globalCollectionSlugs],\n tenantFieldName,\n tenantsCollectionSlug,\n usersSlug: adminUsersCollection.slug,\n usersTenantsArrayFieldName: tenantsArrayFieldName,\n usersTenantsArrayTenantFieldName: tenantsArrayTenantFieldName,\n })\n }\n\n /**\n * Add custom tenant field that watches and dispatches updates to the selector\n */\n collection.fields.push({\n name: '_watchTenant',\n type: 'ui',\n admin: {\n components: {\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#WatchTenantCollection',\n },\n },\n disabled: { bulkEdit: true, column: true },\n },\n })\n\n collection.endpoints = [\n ...(collection.endpoints || []),\n getTenantOptionsEndpoint({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n }),\n ]\n } else if (pluginConfig.collections?.[collection.slug]) {\n if (collection.slug === adminUsersCollection.slug) {\n // eslint-disable-next-line no-console\n console.warn(\n `[plugin-multi-tenant] The admin users collection \"${collection.slug}\" should not be listed in pluginConfig.collections — it is already handled by the plugin. Skipping tenant-field processing for this collection to avoid double access control and validation errors.`,\n )\n return\n }\n multiTenantCollectionsFound.push(collection.slug)\n const isGlobal = Boolean(pluginConfig.collections[collection.slug]?.isGlobal)\n\n if (isGlobal) {\n collection.disableDuplicate = true\n }\n\n if (!pluginConfig.debug && !isGlobal) {\n collection.admin ??= {}\n collection.admin.components ??= {}\n collection.admin.components.edit ??= {}\n collection.admin.components.edit.editMenuItems ??= []\n collection.admin.components.edit.editMenuItems.push({\n path: '@payloadcms/plugin-multi-tenant/client#AssignTenantFieldTrigger',\n })\n }\n\n /**\n * Add filter options to all relationship fields\n */\n collection.fields = addFilterOptionsToFields({\n blockReferencesWithFilters,\n config: incomingConfig,\n fields: collection.fields,\n tenantEnabledCollectionSlugs: collectionSlugs,\n tenantEnabledGlobalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.collections[collection.slug]?.customTenantField !== true) {\n /**\n * Add tenant field to enabled collections\n */\n collection.fields.unshift(\n tenantField({\n name: tenantFieldName,\n adminUsersSlug: adminUsersCollection.slug,\n debug: pluginConfig.debug,\n isAutosaveEnabled: hasAutosaveEnabled(collection),\n overrides: pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n ? pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n : pluginConfig.tenantField || {},\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n unique: isGlobal,\n userHasAccessToAllTenants,\n }),\n )\n }\n\n collection.hooks ??= {}\n collection.hooks.beforeValidate ??= []\n collection.hooks.beforeValidate.push(\n enforceTenantMembership({\n collectionSlug: collection.slug,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n }),\n )\n\n const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {}\n if (useBaseFilter ?? useBaseListFilter ?? true) {\n /**\n * Add list filter to enabled collections\n * - filters results by selected tenant\n */\n collection.admin = collection.admin || {}\n collection.admin.baseFilter = combineFilters({\n baseFilter: collection.admin?.baseFilter ?? collection.admin?.baseListFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: tenantFieldName,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {\n /**\n * Add access control constraint to tenant enabled collection\n */\n tenantAccessScopes.push({\n accessResultCallback: pluginConfig.collections[collection.slug]?.accessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n }\n })\n\n if (!tenantCollection) {\n throw new Error(`Tenants collection not found with slug: ${tenantsCollectionSlug}`)\n }\n\n if (\n multiTenantCollectionsFound.length !==\n collectionSlugs.length + globalCollectionSlugs.length\n ) {\n const missingSlugs = [...collectionSlugs, ...globalCollectionSlugs].filter(\n (slug) => !multiTenantCollectionsFound.includes(slug),\n )\n // eslint-disable-next-line no-console\n console.error(\n miniChalk.yellowBold('WARNING (plugin-multi-tenant)'),\n 'missing collections',\n missingSlugs,\n 'try placing the multi-tenant plugin after other plugins.',\n )\n }\n\n addCollectionAccess({ config: incomingConfig, scopes: tenantAccessScopes })\n\n /**\n * Add TenantSelectionProvider to admin providers\n */\n incomingConfig.admin.components.providers.push({\n clientProps: {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug: tenantCollection.slug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider',\n })\n\n /**\n * Add global redirect action\n */\n if (globalCollectionSlugs.length) {\n incomingConfig.admin.components.actions.push({\n path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',\n serverProps: {\n globalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n })\n }\n\n /**\n * Add tenant selector to admin UI\n */\n incomingConfig.admin.components.beforeNav.push({\n clientProps: {\n enabledSlugs: [\n ...collectionSlugs,\n ...globalCollectionSlugs,\n adminUsersCollection.slug,\n tenantCollection.slug,\n ],\n label: pluginConfig.tenantSelectorLabel || undefined,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelector',\n })\n\n /**\n * Merge plugin translations\n */\n if (!incomingConfig.i18n) {\n incomingConfig.i18n = {}\n }\n Object.entries(translations).forEach(([locale, pluginI18nObject]) => {\n const typedLocale = locale as AcceptedLanguages\n if (!incomingConfig.i18n!.translations) {\n incomingConfig.i18n!.translations = {}\n }\n if (!(typedLocale in incomingConfig.i18n!.translations)) {\n incomingConfig.i18n!.translations[typedLocale] = {}\n }\n if (!('plugin-multi-tenant' in incomingConfig.i18n!.translations[typedLocale]!)) {\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {} as PluginDefaultTranslationsObject['plugin-multi-tenant']\n }\n\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {\n ...pluginI18nObject.translations['plugin-multi-tenant'],\n ...(pluginConfig.i18n?.translations?.[typedLocale] || {}),\n }\n })\n\n return incomingConfig\n },\n})\n"],"names":["definePlugin","hasAutosaveEnabled","defaults","getTenantOptionsEndpoint","tenantField","tenantsArrayField","filterDocumentsByTenants","addTenantCleanup","enforceTenantMembership","translations","addCollectionAccess","addFilterOptionsToFields","combineFilters","miniChalk","multiTenantPlugin","slug","plugin","config","incomingConfig","options","pluginConfig","enabled","userHasAccessToAllTenants","tenantsCollectionSlug","tenantsSlug","tenantCollectionSlug","tenantFieldName","name","tenantsArrayFieldName","arrayFieldName","tenantsArrayTenantFieldName","arrayTenantFieldName","tenantAccessScopes","admin","components","actions","beforeNav","providers","collections","adminUsersCollection","find","auth","user","Error","includeDefaultField","fields","push","arrayFieldAccess","create","req","Boolean","update","accessResultCallback","usersAccessResultOverride","adminUsersSlug","collection","fieldName","useUsersTenantFilter","baseFilter","baseListFilter","customFilter","args","filterFieldName","tenantCollection","collectionSlugs","globalCollectionSlugs","Object","keys","reduce","acc","isGlobal","blockReferencesWithFilters","multiTenantCollectionsFound","forEach","useTenantsCollectionAccess","useTenantsListFilter","cleanupAfterTenantDelete","enabledSlugs","usersSlug","usersTenantsArrayFieldName","usersTenantsArrayTenantFieldName","type","Field","path","disabled","bulkEdit","column","endpoints","useAsTitle","console","warn","disableDuplicate","debug","edit","editMenuItems","tenantEnabledCollectionSlugs","tenantEnabledGlobalSlugs","customTenantField","unshift","isAutosaveEnabled","overrides","tenantFieldOverrides","unique","hooks","beforeValidate","collectionSlug","useBaseFilter","useBaseListFilter","useTenantAccess","accessResultOverride","length","missingSlugs","filter","includes","error","yellowBold","scopes","clientProps","serverProps","globalSlugs","label","tenantSelectorLabel","undefined","i18n","entries","locale","pluginI18nObject","typedLocale"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const getTenantIDsFromValue: (value: unknown) => string[];
2
+ //# sourceMappingURL=getTenantIDsFromValue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getTenantIDsFromValue.d.ts","sourceRoot":"","sources":["../../src/utilities/getTenantIDsFromValue.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,UAAW,OAAO,KAAG,MAAM,EAU5D,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { extractID } from './extractID.js';
2
+ export const getTenantIDsFromValue = (value)=>{
3
+ if (value === undefined || value === null) {
4
+ return [];
5
+ }
6
+ return (Array.isArray(value) ? value : [
7
+ value
8
+ ]).map((entry)=>{
9
+ const id = entry === null || entry === undefined ? undefined : extractID(entry);
10
+ return typeof id === 'number' || typeof id === 'string' ? String(id) : '';
11
+ });
12
+ };
13
+
14
+ //# sourceMappingURL=getTenantIDsFromValue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/getTenantIDsFromValue.ts"],"sourcesContent":["import type { Tenant } from '../types.js'\n\nimport { extractID } from './extractID.js'\n\nexport const getTenantIDsFromValue = (value: unknown): string[] => {\n if (value === undefined || value === null) {\n return []\n }\n\n return (Array.isArray(value) ? value : [value]).map((entry) => {\n const id = entry === null || entry === undefined ? undefined : extractID(entry as Tenant)\n\n return typeof id === 'number' || typeof id === 'string' ? String(id) : ''\n })\n}\n"],"names":["extractID","getTenantIDsFromValue","value","undefined","Array","isArray","map","entry","id","String"],"mappings":"AAEA,SAASA,SAAS,QAAQ,iBAAgB;AAE1C,OAAO,MAAMC,wBAAwB,CAACC;IACpC,IAAIA,UAAUC,aAAaD,UAAU,MAAM;QACzC,OAAO,EAAE;IACX;IAEA,OAAO,AAACE,CAAAA,MAAMC,OAAO,CAACH,SAASA,QAAQ;QAACA;KAAM,AAAD,EAAGI,GAAG,CAAC,CAACC;QACnD,MAAMC,KAAKD,UAAU,QAAQA,UAAUJ,YAAYA,YAAYH,UAAUO;QAEzE,OAAO,OAAOC,OAAO,YAAY,OAAOA,OAAO,WAAWC,OAAOD,MAAM;IACzE;AACF,EAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"getUserTenantIDs.d.ts","sourceRoot":"","sources":["../../src/utilities/getUserTenantIDs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAK/D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,MAAM,GAAG,MAAM,QACvD,IAAI,GAAG,oBAAoB,YACvB;IACR,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC,KACA,MAAM,EAoBR,CAAA"}
1
+ {"version":3,"file":"getUserTenantIDs.d.ts","sourceRoot":"","sources":["../../src/utilities/getUserTenantIDs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAK/D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,MAAM,GAAG,MAAM,QACvD,IAAI,GAAG,oBAAoB,YACvB;IACR,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC,KACA,MAAM,EAqBR,CAAA"}
@@ -12,8 +12,9 @@ import { extractID } from './extractID.js';
12
12
  const tenantsArrayTenantFieldName = options?.tenantsArrayTenantFieldName || defaults.tenantsArrayTenantFieldName;
13
13
  const tenantsArray = user[tenantsArrayFieldName];
14
14
  return (Array.isArray(tenantsArray) ? tenantsArray : [])?.reduce((acc, row)=>{
15
- if (row[tenantsArrayTenantFieldName]) {
16
- acc.push(extractID(row[tenantsArrayTenantFieldName]));
15
+ const tenantRow = row[tenantsArrayTenantFieldName];
16
+ if (tenantRow !== undefined && tenantRow !== null) {
17
+ acc.push(extractID(tenantRow));
17
18
  }
18
19
  return acc;
19
20
  }, []) || [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/getUserTenantIDs.ts"],"sourcesContent":["import type { Tenant, UserWithTenantsField } from '../types.js'\n\nimport { defaults } from '../defaults.js'\nimport { extractID } from './extractID.js'\n\n/**\n * Returns array of all tenant IDs assigned to a user\n *\n * @param user - User object with tenants field\n */\nexport const getUserTenantIDs = <IDType extends number | string>(\n user: null | UserWithTenantsField,\n options?: {\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n },\n): IDType[] => {\n if (!user) {\n return []\n }\n\n const tenantsArrayFieldName = options?.tenantsArrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n options?.tenantsArrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n\n const tenantsArray = (user as Record<string, unknown>)[tenantsArrayFieldName]\n\n return (\n (Array.isArray(tenantsArray) ? tenantsArray : [])?.reduce<IDType[]>((acc, row) => {\n if (row[tenantsArrayTenantFieldName]) {\n acc.push(extractID<IDType>(row[tenantsArrayTenantFieldName] as Tenant<IDType>))\n }\n\n return acc\n }, []) || []\n )\n}\n"],"names":["defaults","extractID","getUserTenantIDs","user","options","tenantsArrayFieldName","tenantsArrayTenantFieldName","tenantsArray","Array","isArray","reduce","acc","row","push"],"mappings":"AAEA,SAASA,QAAQ,QAAQ,iBAAgB;AACzC,SAASC,SAAS,QAAQ,iBAAgB;AAE1C;;;;CAIC,GACD,OAAO,MAAMC,mBAAmB,CAC9BC,MACAC;IAKA,IAAI,CAACD,MAAM;QACT,OAAO,EAAE;IACX;IAEA,MAAME,wBAAwBD,SAASC,yBAAyBL,SAASK,qBAAqB;IAC9F,MAAMC,8BACJF,SAASE,+BAA+BN,SAASM,2BAA2B;IAE9E,MAAMC,eAAe,AAACJ,IAAgC,CAACE,sBAAsB;IAE7E,OACE,AAACG,CAAAA,MAAMC,OAAO,CAACF,gBAAgBA,eAAe,EAAE,AAAD,GAAIG,OAAiB,CAACC,KAAKC;QACxE,IAAIA,GAAG,CAACN,4BAA4B,EAAE;YACpCK,IAAIE,IAAI,CAACZ,UAAkBW,GAAG,CAACN,4BAA4B;QAC7D;QAEA,OAAOK;IACT,GAAG,EAAE,KAAK,EAAE;AAEhB,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/getUserTenantIDs.ts"],"sourcesContent":["import type { Tenant, UserWithTenantsField } from '../types.js'\n\nimport { defaults } from '../defaults.js'\nimport { extractID } from './extractID.js'\n\n/**\n * Returns array of all tenant IDs assigned to a user\n *\n * @param user - User object with tenants field\n */\nexport const getUserTenantIDs = <IDType extends number | string>(\n user: null | UserWithTenantsField,\n options?: {\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n },\n): IDType[] => {\n if (!user) {\n return []\n }\n\n const tenantsArrayFieldName = options?.tenantsArrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n options?.tenantsArrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n\n const tenantsArray = (user as Record<string, unknown>)[tenantsArrayFieldName]\n\n return (\n (Array.isArray(tenantsArray) ? tenantsArray : [])?.reduce<IDType[]>((acc, row) => {\n const tenantRow = row[tenantsArrayTenantFieldName] as Tenant<IDType>\n if (tenantRow !== undefined && tenantRow !== null) {\n acc.push(extractID(tenantRow))\n }\n\n return acc\n }, []) || []\n )\n}\n"],"names":["defaults","extractID","getUserTenantIDs","user","options","tenantsArrayFieldName","tenantsArrayTenantFieldName","tenantsArray","Array","isArray","reduce","acc","row","tenantRow","undefined","push"],"mappings":"AAEA,SAASA,QAAQ,QAAQ,iBAAgB;AACzC,SAASC,SAAS,QAAQ,iBAAgB;AAE1C;;;;CAIC,GACD,OAAO,MAAMC,mBAAmB,CAC9BC,MACAC;IAKA,IAAI,CAACD,MAAM;QACT,OAAO,EAAE;IACX;IAEA,MAAME,wBAAwBD,SAASC,yBAAyBL,SAASK,qBAAqB;IAC9F,MAAMC,8BACJF,SAASE,+BAA+BN,SAASM,2BAA2B;IAE9E,MAAMC,eAAe,AAACJ,IAAgC,CAACE,sBAAsB;IAE7E,OACE,AAACG,CAAAA,MAAMC,OAAO,CAACF,gBAAgBA,eAAe,EAAE,AAAD,GAAIG,OAAiB,CAACC,KAAKC;QACxE,MAAMC,YAAYD,GAAG,CAACN,4BAA4B;QAClD,IAAIO,cAAcC,aAAaD,cAAc,MAAM;YACjDF,IAAII,IAAI,CAACd,UAAUY;QACrB;QAEA,OAAOF;IACT,GAAG,EAAE,KAAK,EAAE;AAEhB,EAAC"}
@@ -0,0 +1,13 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { MultiTenantPluginConfig } from '../types.js';
3
+ type Args = {
4
+ previousValue?: unknown;
5
+ req: PayloadRequest;
6
+ tenantsArrayFieldName: string;
7
+ tenantsArrayTenantFieldName: string;
8
+ userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants'];
9
+ value: unknown;
10
+ };
11
+ export declare const isValidTenantAssignment: ({ previousValue, req, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, value, }: Args) => boolean;
12
+ export {};
13
+ //# sourceMappingURL=isValidTenantAssignment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isValidTenantAssignment.d.ts","sourceRoot":"","sources":["../../src/utilities/isValidTenantAssignment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAK1D,KAAK,IAAI,GAAG;IACV,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,GAAG,EAAE,cAAc,CAAA;IACnB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,2BAA2B,EAAE,MAAM,CAAA;IACnC,yBAAyB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAChF,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,eAAO,MAAM,uBAAuB,kHAOjC,IAAI,KAAG,OAgCT,CAAA"}
@@ -0,0 +1,28 @@
1
+ import { getTenantIDsFromValue } from './getTenantIDsFromValue.js';
2
+ import { getUserTenantIDs } from './getUserTenantIDs.js';
3
+ export const isValidTenantAssignment = ({ previousValue, req, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, value })=>{
4
+ const { user } = req;
5
+ if (user) {
6
+ if (userHasAccessToAllTenants?.(user)) {
7
+ return true;
8
+ }
9
+ } else if (req.payloadAPI === 'local') {
10
+ return true;
11
+ }
12
+ const submittedDocumentTenantIDs = getTenantIDsFromValue(value);
13
+ const storedDocumentTenantIDs = getTenantIDsFromValue(previousValue);
14
+ if (!hasTenantValueChanged(submittedDocumentTenantIDs, storedDocumentTenantIDs)) {
15
+ return true;
16
+ }
17
+ if (submittedDocumentTenantIDs.length === 0) {
18
+ return false;
19
+ }
20
+ const userAssignedTenantIDs = getUserTenantIDs(user, {
21
+ tenantsArrayFieldName,
22
+ tenantsArrayTenantFieldName
23
+ });
24
+ return submittedDocumentTenantIDs.every((tenantID)=>userAssignedTenantIDs.some((userAssignedTenantID)=>String(userAssignedTenantID) === String(tenantID)));
25
+ };
26
+ const hasTenantValueChanged = (submittedTenantIDs, storedTenantIDs)=>submittedTenantIDs.length !== storedTenantIDs.length || submittedTenantIDs.some((tenantID)=>!storedTenantIDs.includes(tenantID));
27
+
28
+ //# sourceMappingURL=isValidTenantAssignment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/isValidTenantAssignment.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport type { MultiTenantPluginConfig } from '../types.js'\n\nimport { getTenantIDsFromValue } from './getTenantIDsFromValue.js'\nimport { getUserTenantIDs } from './getUserTenantIDs.js'\n\ntype Args = {\n previousValue?: unknown\n req: PayloadRequest\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants']\n value: unknown\n}\n\nexport const isValidTenantAssignment = ({\n previousValue,\n req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n value,\n}: Args): boolean => {\n const { user } = req\n\n if (user) {\n if (userHasAccessToAllTenants?.(user)) {\n return true\n }\n } else if (req.payloadAPI === 'local') {\n return true\n }\n\n const submittedDocumentTenantIDs = getTenantIDsFromValue(value)\n const storedDocumentTenantIDs = getTenantIDsFromValue(previousValue)\n\n if (!hasTenantValueChanged(submittedDocumentTenantIDs, storedDocumentTenantIDs)) {\n return true\n }\n\n if (submittedDocumentTenantIDs.length === 0) {\n return false\n }\n\n const userAssignedTenantIDs = getUserTenantIDs(user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n\n return submittedDocumentTenantIDs.every((tenantID) =>\n userAssignedTenantIDs.some(\n (userAssignedTenantID) => String(userAssignedTenantID) === String(tenantID),\n ),\n )\n}\n\nconst hasTenantValueChanged = (\n submittedTenantIDs: (number | string)[],\n storedTenantIDs: (number | string)[],\n): boolean =>\n submittedTenantIDs.length !== storedTenantIDs.length ||\n submittedTenantIDs.some((tenantID) => !storedTenantIDs.includes(tenantID))\n"],"names":["getTenantIDsFromValue","getUserTenantIDs","isValidTenantAssignment","previousValue","req","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","value","user","payloadAPI","submittedDocumentTenantIDs","storedDocumentTenantIDs","hasTenantValueChanged","length","userAssignedTenantIDs","every","tenantID","some","userAssignedTenantID","String","submittedTenantIDs","storedTenantIDs","includes"],"mappings":"AAIA,SAASA,qBAAqB,QAAQ,6BAA4B;AAClE,SAASC,gBAAgB,QAAQ,wBAAuB;AAWxD,OAAO,MAAMC,0BAA0B,CAAC,EACtCC,aAAa,EACbC,GAAG,EACHC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EACzBC,KAAK,EACA;IACL,MAAM,EAAEC,IAAI,EAAE,GAAGL;IAEjB,IAAIK,MAAM;QACR,IAAIF,4BAA4BE,OAAO;YACrC,OAAO;QACT;IACF,OAAO,IAAIL,IAAIM,UAAU,KAAK,SAAS;QACrC,OAAO;IACT;IAEA,MAAMC,6BAA6BX,sBAAsBQ;IACzD,MAAMI,0BAA0BZ,sBAAsBG;IAEtD,IAAI,CAACU,sBAAsBF,4BAA4BC,0BAA0B;QAC/E,OAAO;IACT;IAEA,IAAID,2BAA2BG,MAAM,KAAK,GAAG;QAC3C,OAAO;IACT;IAEA,MAAMC,wBAAwBd,iBAAiBQ,MAAM;QACnDJ;QACAC;IACF;IAEA,OAAOK,2BAA2BK,KAAK,CAAC,CAACC,WACvCF,sBAAsBG,IAAI,CACxB,CAACC,uBAAyBC,OAAOD,0BAA0BC,OAAOH;AAGxE,EAAC;AAED,MAAMJ,wBAAwB,CAC5BQ,oBACAC,kBAEAD,mBAAmBP,MAAM,KAAKQ,gBAAgBR,MAAM,IACpDO,mBAAmBH,IAAI,CAAC,CAACD,WAAa,CAACK,gBAAgBC,QAAQ,CAACN"}
@@ -0,0 +1,116 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { isValidTenantAssignment } from './isValidTenantAssignment.js';
3
+ const tenantsArrayFieldName = 'tenants';
4
+ const tenantsArrayTenantFieldName = 'tenant';
5
+ const memberOfTenantA = {
6
+ id: 'user-1',
7
+ collection: 'users',
8
+ tenants: [
9
+ {
10
+ tenant: 'tenant-a'
11
+ }
12
+ ]
13
+ };
14
+ const check = ({ payloadAPI = 'REST', previousValue, user = memberOfTenantA, userHasAccessToAllTenants, value })=>isValidTenantAssignment({
15
+ previousValue,
16
+ req: {
17
+ payloadAPI,
18
+ user
19
+ },
20
+ tenantsArrayFieldName,
21
+ tenantsArrayTenantFieldName,
22
+ userHasAccessToAllTenants,
23
+ value
24
+ });
25
+ describe('isValidTenantAssignment', ()=>{
26
+ it('should allow a tenant the user is assigned to', ()=>{
27
+ expect(check({
28
+ value: 'tenant-a'
29
+ })).toBe(true);
30
+ });
31
+ it('should reject a tenant the user is not assigned to', ()=>{
32
+ expect(check({
33
+ value: 'tenant-b'
34
+ })).toBe(false);
35
+ });
36
+ it('should allow an unchanged tenant even when it is outside the user list', ()=>{
37
+ expect(check({
38
+ previousValue: 'tenant-b',
39
+ value: 'tenant-b'
40
+ })).toBe(true);
41
+ });
42
+ it('should reject clearing an existing tenant', ()=>{
43
+ expect(check({
44
+ previousValue: 'tenant-a',
45
+ value: null
46
+ })).toBe(false);
47
+ });
48
+ it('should allow a userless Local API write', ()=>{
49
+ expect(check({
50
+ payloadAPI: 'local',
51
+ user: null,
52
+ value: 'tenant-b'
53
+ })).toBe(true);
54
+ });
55
+ it('should reject a userless request that names a tenant', ()=>{
56
+ expect(check({
57
+ user: null,
58
+ value: 'tenant-b'
59
+ })).toBe(false);
60
+ });
61
+ it('should allow any tenant for a user with access to all tenants', ()=>{
62
+ expect(check({
63
+ userHasAccessToAllTenants: ()=>true,
64
+ value: 'tenant-b'
65
+ })).toBe(true);
66
+ });
67
+ it('should not call userHasAccessToAllTenants with a null user', ()=>{
68
+ const predicate = (user)=>user.roles.includes('a');
69
+ expect(()=>check({
70
+ payloadAPI: 'local',
71
+ user: null,
72
+ userHasAccessToAllTenants: predicate,
73
+ value: null
74
+ })).not.toThrow();
75
+ });
76
+ it('should reject a hasMany value that mixes assigned and unassigned tenants', ()=>{
77
+ expect(check({
78
+ value: [
79
+ 'tenant-a',
80
+ 'tenant-b'
81
+ ]
82
+ })).toBe(false);
83
+ });
84
+ it('should treat a numeric ID and its string form as the same tenant', ()=>{
85
+ const user = {
86
+ id: 1,
87
+ collection: 'users',
88
+ tenants: [
89
+ {
90
+ tenant: 1
91
+ }
92
+ ]
93
+ };
94
+ expect(check({
95
+ user,
96
+ value: '1'
97
+ })).toBe(true);
98
+ expect(check({
99
+ user,
100
+ value: 1
101
+ })).toBe(true);
102
+ expect(check({
103
+ user,
104
+ value: '2'
105
+ })).toBe(false);
106
+ });
107
+ it('should reject a tenant value it cannot read rather than throw', ()=>{
108
+ expect(check({
109
+ value: [
110
+ null
111
+ ]
112
+ })).toBe(false);
113
+ });
114
+ });
115
+
116
+ //# sourceMappingURL=isValidTenantAssignment.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/isValidTenantAssignment.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { isValidTenantAssignment } from './isValidTenantAssignment.js'\n\nconst tenantsArrayFieldName = 'tenants'\nconst tenantsArrayTenantFieldName = 'tenant'\n\nconst memberOfTenantA = {\n id: 'user-1',\n collection: 'users',\n tenants: [{ tenant: 'tenant-a' }],\n}\n\ntype CheckArgs = {\n payloadAPI?: string\n previousValue?: unknown\n user?: unknown\n userHasAccessToAllTenants?: (user: unknown) => boolean\n value: unknown\n}\n\nconst check = ({\n payloadAPI = 'REST',\n previousValue,\n user = memberOfTenantA,\n userHasAccessToAllTenants,\n value,\n}: CheckArgs): boolean =>\n isValidTenantAssignment({\n previousValue,\n req: { payloadAPI, user } as unknown as PayloadRequest,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n value,\n })\n\ndescribe('isValidTenantAssignment', () => {\n it('should allow a tenant the user is assigned to', () => {\n expect(check({ value: 'tenant-a' })).toBe(true)\n })\n\n it('should reject a tenant the user is not assigned to', () => {\n expect(check({ value: 'tenant-b' })).toBe(false)\n })\n\n it('should allow an unchanged tenant even when it is outside the user list', () => {\n expect(check({ previousValue: 'tenant-b', value: 'tenant-b' })).toBe(true)\n })\n\n it('should reject clearing an existing tenant', () => {\n expect(check({ previousValue: 'tenant-a', value: null })).toBe(false)\n })\n\n it('should allow a userless Local API write', () => {\n expect(check({ payloadAPI: 'local', user: null, value: 'tenant-b' })).toBe(true)\n })\n\n it('should reject a userless request that names a tenant', () => {\n expect(check({ user: null, value: 'tenant-b' })).toBe(false)\n })\n\n it('should allow any tenant for a user with access to all tenants', () => {\n expect(check({ userHasAccessToAllTenants: () => true, value: 'tenant-b' })).toBe(true)\n })\n\n it('should not call userHasAccessToAllTenants with a null user', () => {\n const predicate = (user: unknown): boolean => (user as { roles: string[] }).roles.includes('a')\n\n expect(() =>\n check({ payloadAPI: 'local', user: null, userHasAccessToAllTenants: predicate, value: null }),\n ).not.toThrow()\n })\n\n it('should reject a hasMany value that mixes assigned and unassigned tenants', () => {\n expect(check({ value: ['tenant-a', 'tenant-b'] })).toBe(false)\n })\n\n it('should treat a numeric ID and its string form as the same tenant', () => {\n const user = { id: 1, collection: 'users', tenants: [{ tenant: 1 }] }\n\n expect(check({ user, value: '1' })).toBe(true)\n expect(check({ user, value: 1 })).toBe(true)\n expect(check({ user, value: '2' })).toBe(false)\n })\n\n it('should reject a tenant value it cannot read rather than throw', () => {\n expect(check({ value: [null] })).toBe(false)\n })\n})\n"],"names":["describe","expect","it","isValidTenantAssignment","tenantsArrayFieldName","tenantsArrayTenantFieldName","memberOfTenantA","id","collection","tenants","tenant","check","payloadAPI","previousValue","user","userHasAccessToAllTenants","value","req","toBe","predicate","roles","includes","not","toThrow"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,uBAAuB,QAAQ,+BAA8B;AAEtE,MAAMC,wBAAwB;AAC9B,MAAMC,8BAA8B;AAEpC,MAAMC,kBAAkB;IACtBC,IAAI;IACJC,YAAY;IACZC,SAAS;QAAC;YAAEC,QAAQ;QAAW;KAAE;AACnC;AAUA,MAAMC,QAAQ,CAAC,EACbC,aAAa,MAAM,EACnBC,aAAa,EACbC,OAAOR,eAAe,EACtBS,yBAAyB,EACzBC,KAAK,EACK,GACVb,wBAAwB;QACtBU;QACAI,KAAK;YAAEL;YAAYE;QAAK;QACxBV;QACAC;QACAU;QACAC;IACF;AAEFhB,SAAS,2BAA2B;IAClCE,GAAG,iDAAiD;QAClDD,OAAOU,MAAM;YAAEK,OAAO;QAAW,IAAIE,IAAI,CAAC;IAC5C;IAEAhB,GAAG,sDAAsD;QACvDD,OAAOU,MAAM;YAAEK,OAAO;QAAW,IAAIE,IAAI,CAAC;IAC5C;IAEAhB,GAAG,0EAA0E;QAC3ED,OAAOU,MAAM;YAAEE,eAAe;YAAYG,OAAO;QAAW,IAAIE,IAAI,CAAC;IACvE;IAEAhB,GAAG,6CAA6C;QAC9CD,OAAOU,MAAM;YAAEE,eAAe;YAAYG,OAAO;QAAK,IAAIE,IAAI,CAAC;IACjE;IAEAhB,GAAG,2CAA2C;QAC5CD,OAAOU,MAAM;YAAEC,YAAY;YAASE,MAAM;YAAME,OAAO;QAAW,IAAIE,IAAI,CAAC;IAC7E;IAEAhB,GAAG,wDAAwD;QACzDD,OAAOU,MAAM;YAAEG,MAAM;YAAME,OAAO;QAAW,IAAIE,IAAI,CAAC;IACxD;IAEAhB,GAAG,iEAAiE;QAClED,OAAOU,MAAM;YAAEI,2BAA2B,IAAM;YAAMC,OAAO;QAAW,IAAIE,IAAI,CAAC;IACnF;IAEAhB,GAAG,8DAA8D;QAC/D,MAAMiB,YAAY,CAACL,OAA2B,AAACA,KAA6BM,KAAK,CAACC,QAAQ,CAAC;QAE3FpB,OAAO,IACLU,MAAM;gBAAEC,YAAY;gBAASE,MAAM;gBAAMC,2BAA2BI;gBAAWH,OAAO;YAAK,IAC3FM,GAAG,CAACC,OAAO;IACf;IAEArB,GAAG,4EAA4E;QAC7ED,OAAOU,MAAM;YAAEK,OAAO;gBAAC;gBAAY;aAAW;QAAC,IAAIE,IAAI,CAAC;IAC1D;IAEAhB,GAAG,oEAAoE;QACrE,MAAMY,OAAO;YAAEP,IAAI;YAAGC,YAAY;YAASC,SAAS;gBAAC;oBAAEC,QAAQ;gBAAE;aAAE;QAAC;QAEpET,OAAOU,MAAM;YAAEG;YAAME,OAAO;QAAI,IAAIE,IAAI,CAAC;QACzCjB,OAAOU,MAAM;YAAEG;YAAME,OAAO;QAAE,IAAIE,IAAI,CAAC;QACvCjB,OAAOU,MAAM;YAAEG;YAAME,OAAO;QAAI,IAAIE,IAAI,CAAC;IAC3C;IAEAhB,GAAG,iEAAiE;QAClED,OAAOU,MAAM;YAAEK,OAAO;gBAAC;aAAK;QAAC,IAAIE,IAAI,CAAC;IACxC;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/plugin-multi-tenant",
3
- "version": "4.0.0-canary.33",
3
+ "version": "4.0.0-canary.34",
4
4
  "description": "Multi Tenant plugin for Payload",
5
5
  "keywords": [
6
6
  "payload",
@@ -80,13 +80,13 @@
80
80
  ],
81
81
  "devDependencies": {
82
82
  "@payloadcms/eslint-config": "3.28.0",
83
- "@payloadcms/ui": "4.0.0-canary.33",
84
- "@payloadcms/translations": "4.0.0-canary.33",
85
- "payload": "4.0.0-canary.33"
83
+ "@payloadcms/ui": "4.0.0-canary.34",
84
+ "payload": "4.0.0-canary.34",
85
+ "@payloadcms/translations": "4.0.0-canary.34"
86
86
  },
87
87
  "peerDependencies": {
88
- "@payloadcms/ui": "4.0.0-canary.33",
89
- "payload": "4.0.0-canary.33"
88
+ "@payloadcms/ui": "4.0.0-canary.34",
89
+ "payload": "4.0.0-canary.34"
90
90
  },
91
91
  "homepage:": "https://payloadcms.com",
92
92
  "scripts": {