@payloadcms/plugin-multi-tenant 4.0.0-canary.35 → 4.0.0-canary.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/plugin-multi-tenant",
3
- "version": "4.0.0-canary.35",
3
+ "version": "4.0.0-canary.36",
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/translations": "4.0.0-canary.35",
84
- "payload": "4.0.0-canary.35",
85
- "@payloadcms/ui": "4.0.0-canary.35"
83
+ "payload": "4.0.0-canary.36",
84
+ "@payloadcms/ui": "4.0.0-canary.36",
85
+ "@payloadcms/translations": "4.0.0-canary.36"
86
86
  },
87
87
  "peerDependencies": {
88
- "@payloadcms/ui": "4.0.0-canary.35",
89
- "payload": "4.0.0-canary.35"
88
+ "@payloadcms/ui": "4.0.0-canary.36",
89
+ "payload": "4.0.0-canary.36"
90
90
  },
91
91
  "homepage:": "https://payloadcms.com",
92
92
  "scripts": {
@@ -95,7 +95,7 @@
95
95
  "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
96
96
  "build:types": "tsc --emitDeclarationOnly --outDir dist",
97
97
  "clean": "rimraf {dist,*.tsbuildinfo}",
98
- "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
98
+ "copyfiles": "copyfiles -u 1 -e \"**/*.spec.*\" \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
99
99
  "lint": "eslint .",
100
100
  "lint:fix": "eslint . --fix",
101
101
  "pack:plugin": "pnpm build && pnpm pack"
@@ -1,54 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,70 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { multiTenantPlugin } from './index.js';
3
- describe('multiTenantPlugin', ()=>{
4
- it('applies tenant assignment access defaults', async ()=>{
5
- let hasAllTenantAccess = false;
6
- const plugin = multiTenantPlugin({
7
- collections: {},
8
- userHasAccessToAllTenants: ()=>hasAllTenantAccess
9
- });
10
- const config = await plugin({
11
- collections: [
12
- {
13
- slug: 'users',
14
- auth: true,
15
- fields: []
16
- },
17
- {
18
- slug: 'tenants',
19
- fields: []
20
- }
21
- ]
22
- });
23
- const usersCollection = config.collections?.find(({ slug })=>slug === 'users');
24
- const tenantsField = usersCollection?.fields.find((field)=>'name' in field && field.name === 'tenants');
25
- const accessArgs = {
26
- req: {
27
- user: {
28
- id: 'test-user'
29
- }
30
- }
31
- };
32
- expect(await tenantsField?.access?.create?.(accessArgs)).toBe(false);
33
- expect(await tenantsField?.access?.update?.(accessArgs)).toBe(false);
34
- hasAllTenantAccess = true;
35
- expect(await tenantsField?.access?.create?.(accessArgs)).toBe(true);
36
- expect(await tenantsField?.access?.update?.(accessArgs)).toBe(true);
37
- });
38
- it('retains configured tenant assignment access callbacks', async ()=>{
39
- const createAccess = ()=>false;
40
- const updateAccess = ()=>true;
41
- const plugin = multiTenantPlugin({
42
- collections: {},
43
- tenantsArrayField: {
44
- arrayFieldAccess: {
45
- create: createAccess,
46
- update: updateAccess
47
- }
48
- }
49
- });
50
- const config = await plugin({
51
- collections: [
52
- {
53
- slug: 'users',
54
- auth: true,
55
- fields: []
56
- },
57
- {
58
- slug: 'tenants',
59
- fields: []
60
- }
61
- ]
62
- });
63
- const usersCollection = config.collections?.find(({ slug })=>slug === 'users');
64
- const tenantsField = usersCollection?.fields.find((field)=>'name' in field && field.name === 'tenants');
65
- expect(tenantsField?.access?.create).toBe(createAccess);
66
- expect(tenantsField?.access?.update).toBe(updateAccess);
67
- });
68
- });
69
-
70
- //# sourceMappingURL=index.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.spec.ts"],"sourcesContent":["import type { ArrayField, Config } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { multiTenantPlugin } from './index.js'\n\ndescribe('multiTenantPlugin', () => {\n it('applies tenant assignment access defaults', async () => {\n let hasAllTenantAccess = false\n const plugin = multiTenantPlugin({\n collections: {},\n userHasAccessToAllTenants: () => hasAllTenantAccess,\n })\n const config = await plugin({\n collections: [\n { slug: 'users', auth: true, fields: [] },\n { slug: 'tenants', fields: [] },\n ],\n } as Config)\n const usersCollection = config.collections?.find(({ slug }) => slug === 'users')\n const tenantsField = usersCollection?.fields.find(\n (field): field is ArrayField => 'name' in field && field.name === 'tenants',\n )\n const accessArgs = { req: { user: { id: 'test-user' } } } as never\n\n expect(await tenantsField?.access?.create?.(accessArgs)).toBe(false)\n expect(await tenantsField?.access?.update?.(accessArgs)).toBe(false)\n\n hasAllTenantAccess = true\n\n expect(await tenantsField?.access?.create?.(accessArgs)).toBe(true)\n expect(await tenantsField?.access?.update?.(accessArgs)).toBe(true)\n })\n\n it('retains configured tenant assignment access callbacks', async () => {\n const createAccess = () => false\n const updateAccess = () => true\n const plugin = multiTenantPlugin({\n collections: {},\n tenantsArrayField: {\n arrayFieldAccess: {\n create: createAccess,\n update: updateAccess,\n },\n },\n })\n const config = await plugin({\n collections: [\n { slug: 'users', auth: true, fields: [] },\n { slug: 'tenants', fields: [] },\n ],\n } as Config)\n const usersCollection = config.collections?.find(({ slug }) => slug === 'users')\n const tenantsField = usersCollection?.fields.find(\n (field): field is ArrayField => 'name' in field && field.name === 'tenants',\n )\n\n expect(tenantsField?.access?.create).toBe(createAccess)\n expect(tenantsField?.access?.update).toBe(updateAccess)\n })\n})\n"],"names":["describe","expect","it","multiTenantPlugin","hasAllTenantAccess","plugin","collections","userHasAccessToAllTenants","config","slug","auth","fields","usersCollection","find","tenantsField","field","name","accessArgs","req","user","id","access","create","toBe","update","createAccess","updateAccess","tenantsArrayField","arrayFieldAccess"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,iBAAiB,QAAQ,aAAY;AAE9CH,SAAS,qBAAqB;IAC5BE,GAAG,6CAA6C;QAC9C,IAAIE,qBAAqB;QACzB,MAAMC,SAASF,kBAAkB;YAC/BG,aAAa,CAAC;YACdC,2BAA2B,IAAMH;QACnC;QACA,MAAMI,SAAS,MAAMH,OAAO;YAC1BC,aAAa;gBACX;oBAAEG,MAAM;oBAASC,MAAM;oBAAMC,QAAQ,EAAE;gBAAC;gBACxC;oBAAEF,MAAM;oBAAWE,QAAQ,EAAE;gBAAC;aAC/B;QACH;QACA,MAAMC,kBAAkBJ,OAAOF,WAAW,EAAEO,KAAK,CAAC,EAAEJ,IAAI,EAAE,GAAKA,SAAS;QACxE,MAAMK,eAAeF,iBAAiBD,OAAOE,KAC3C,CAACE,QAA+B,UAAUA,SAASA,MAAMC,IAAI,KAAK;QAEpE,MAAMC,aAAa;YAAEC,KAAK;gBAAEC,MAAM;oBAAEC,IAAI;gBAAY;YAAE;QAAE;QAExDnB,OAAO,MAAMa,cAAcO,QAAQC,SAASL,aAAaM,IAAI,CAAC;QAC9DtB,OAAO,MAAMa,cAAcO,QAAQG,SAASP,aAAaM,IAAI,CAAC;QAE9DnB,qBAAqB;QAErBH,OAAO,MAAMa,cAAcO,QAAQC,SAASL,aAAaM,IAAI,CAAC;QAC9DtB,OAAO,MAAMa,cAAcO,QAAQG,SAASP,aAAaM,IAAI,CAAC;IAChE;IAEArB,GAAG,yDAAyD;QAC1D,MAAMuB,eAAe,IAAM;QAC3B,MAAMC,eAAe,IAAM;QAC3B,MAAMrB,SAASF,kBAAkB;YAC/BG,aAAa,CAAC;YACdqB,mBAAmB;gBACjBC,kBAAkB;oBAChBN,QAAQG;oBACRD,QAAQE;gBACV;YACF;QACF;QACA,MAAMlB,SAAS,MAAMH,OAAO;YAC1BC,aAAa;gBACX;oBAAEG,MAAM;oBAASC,MAAM;oBAAMC,QAAQ,EAAE;gBAAC;gBACxC;oBAAEF,MAAM;oBAAWE,QAAQ,EAAE;gBAAC;aAC/B;QACH;QACA,MAAMC,kBAAkBJ,OAAOF,WAAW,EAAEO,KAAK,CAAC,EAAEJ,IAAI,EAAE,GAAKA,SAAS;QACxE,MAAMK,eAAeF,iBAAiBD,OAAOE,KAC3C,CAACE,QAA+B,UAAUA,SAASA,MAAMC,IAAI,KAAK;QAGpEf,OAAOa,cAAcO,QAAQC,QAAQC,IAAI,CAACE;QAC1CxB,OAAOa,cAAcO,QAAQG,QAAQD,IAAI,CAACG;IAC5C;AACF"}
@@ -1,134 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { addCollectionAccess } from './addCollectionAccess.js';
3
- const createScope = (collection, accessResultCallback)=>({
4
- accessResultCallback,
5
- adminUsersSlug: 'users',
6
- collection,
7
- fieldName: 'tenant',
8
- userHasAccessToAllTenants: ()=>false
9
- });
10
- const createArgs = (slug = 'posts')=>({
11
- req: {
12
- user: {
13
- collection: 'users',
14
- id: 'user-1',
15
- tenants: [
16
- {
17
- tenant: 'tenant-1'
18
- }
19
- ]
20
- }
21
- },
22
- slug
23
- });
24
- describe('addCollectionAccess', ()=>{
25
- it('adds tenant constraints through base access without replacing collection access', async ()=>{
26
- const documentRead = vi.fn(()=>({
27
- published: {
28
- equals: true
29
- }
30
- }));
31
- const collection = {
32
- slug: 'posts',
33
- access: {
34
- read: documentRead
35
- },
36
- fields: []
37
- };
38
- const config = {
39
- baseAccess: {
40
- collections: {
41
- read: ()=>({
42
- archived: {
43
- not_equals: true
44
- }
45
- })
46
- }
47
- }
48
- };
49
- addCollectionAccess({
50
- config,
51
- scopes: [
52
- createScope(collection)
53
- ]
54
- });
55
- expect(collection.access?.read).toBe(documentRead);
56
- await expect(config.baseAccess?.collections?.read?.(createArgs())).resolves.toEqual({
57
- and: [
58
- {
59
- archived: {
60
- not_equals: true
61
- }
62
- },
63
- {
64
- tenant: {
65
- in: [
66
- 'tenant-1'
67
- ]
68
- }
69
- }
70
- ]
71
- });
72
- await expect(config.baseAccess?.collections?.read?.(createArgs('pages'))).resolves.toEqual({
73
- archived: {
74
- not_equals: true
75
- }
76
- });
77
- expect(documentRead).not.toHaveBeenCalled();
78
- });
79
- it('returns a boolean tenant result for collection create access', async ()=>{
80
- const collection = {
81
- slug: 'posts',
82
- fields: []
83
- };
84
- const config = {};
85
- addCollectionAccess({
86
- config,
87
- scopes: [
88
- createScope(collection)
89
- ]
90
- });
91
- await expect(config.baseAccess?.collections?.create?.(createArgs())).resolves.toBe(true);
92
- });
93
- it('keeps callback wrapping when an access result override is configured', async ()=>{
94
- const documentResult = {
95
- published: {
96
- equals: true
97
- }
98
- };
99
- const documentRead = vi.fn(()=>documentResult);
100
- const accessResultCallback = vi.fn(({ accessResult })=>accessResult);
101
- const collection = {
102
- slug: 'posts',
103
- access: {
104
- read: documentRead
105
- },
106
- fields: []
107
- };
108
- const config = {};
109
- addCollectionAccess({
110
- config,
111
- scopes: [
112
- createScope(collection, accessResultCallback)
113
- ]
114
- });
115
- expect(config.baseAccess).toBeUndefined();
116
- await expect(collection.access?.read?.(createArgs())).resolves.toEqual({
117
- and: [
118
- documentResult,
119
- {
120
- tenant: {
121
- in: [
122
- 'tenant-1'
123
- ]
124
- }
125
- }
126
- ]
127
- });
128
- expect(accessResultCallback).toHaveBeenCalledWith(expect.objectContaining({
129
- accessKey: 'read'
130
- }));
131
- });
132
- });
133
-
134
- //# sourceMappingURL=addCollectionAccess.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/addCollectionAccess.spec.ts"],"sourcesContent":["import type { AccessArgs, CollectionConfig, Config } from 'payload'\n\nimport { describe, expect, it, vi } from 'vitest'\n\nimport type { TenantAccessConfig } from './addCollectionAccess.js'\n\nimport { addCollectionAccess } from './addCollectionAccess.js'\n\nconst createScope = (\n collection: CollectionConfig,\n accessResultCallback?: TenantAccessConfig['accessResultCallback'],\n): TenantAccessConfig => ({\n accessResultCallback,\n adminUsersSlug: 'users',\n collection,\n fieldName: 'tenant',\n userHasAccessToAllTenants: () => false,\n})\n\nconst createArgs = (slug = 'posts'): AccessArgs => ({\n req: {\n user: {\n collection: 'users',\n id: 'user-1',\n tenants: [{ tenant: 'tenant-1' }],\n },\n } as unknown as AccessArgs['req'],\n slug,\n})\n\ndescribe('addCollectionAccess', () => {\n it('adds tenant constraints through base access without replacing collection access', async () => {\n const documentRead = vi.fn(() => ({ published: { equals: true } }))\n const collection: CollectionConfig = {\n slug: 'posts',\n access: { read: documentRead },\n fields: [],\n }\n const config = {\n baseAccess: {\n collections: {\n read: () => ({ archived: { not_equals: true } }),\n },\n },\n } as Config\n\n addCollectionAccess({ config, scopes: [createScope(collection)] })\n\n expect(collection.access?.read).toBe(documentRead)\n await expect(config.baseAccess?.collections?.read?.(createArgs())).resolves.toEqual({\n and: [{ archived: { not_equals: true } }, { tenant: { in: ['tenant-1'] } }],\n })\n await expect(config.baseAccess?.collections?.read?.(createArgs('pages'))).resolves.toEqual({\n archived: { not_equals: true },\n })\n expect(documentRead).not.toHaveBeenCalled()\n })\n\n it('returns a boolean tenant result for collection create access', async () => {\n const collection: CollectionConfig = { slug: 'posts', fields: [] }\n const config = {} as Config\n\n addCollectionAccess({ config, scopes: [createScope(collection)] })\n\n await expect(config.baseAccess?.collections?.create?.(createArgs())).resolves.toBe(true)\n })\n\n it('keeps callback wrapping when an access result override is configured', async () => {\n const documentResult = { published: { equals: true } }\n const documentRead = vi.fn(() => documentResult)\n const accessResultCallback = vi.fn(({ accessResult }) => accessResult)\n const collection: CollectionConfig = {\n slug: 'posts',\n access: { read: documentRead },\n fields: [],\n }\n const config = {} as Config\n\n addCollectionAccess({\n config,\n scopes: [createScope(collection, accessResultCallback)],\n })\n\n expect(config.baseAccess).toBeUndefined()\n await expect(collection.access?.read?.(createArgs())).resolves.toEqual({\n and: [documentResult, { tenant: { in: ['tenant-1'] } }],\n })\n expect(accessResultCallback).toHaveBeenCalledWith(\n expect.objectContaining({ accessKey: 'read' }),\n )\n })\n})\n"],"names":["describe","expect","it","vi","addCollectionAccess","createScope","collection","accessResultCallback","adminUsersSlug","fieldName","userHasAccessToAllTenants","createArgs","slug","req","user","id","tenants","tenant","documentRead","fn","published","equals","access","read","fields","config","baseAccess","collections","archived","not_equals","scopes","toBe","resolves","toEqual","and","in","not","toHaveBeenCalled","create","documentResult","accessResult","toBeUndefined","toHaveBeenCalledWith","objectContaining","accessKey"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAIjD,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,MAAMC,cAAc,CAClBC,YACAC,uBACwB,CAAA;QACxBA;QACAC,gBAAgB;QAChBF;QACAG,WAAW;QACXC,2BAA2B,IAAM;IACnC,CAAA;AAEA,MAAMC,aAAa,CAACC,OAAO,OAAO,GAAkB,CAAA;QAClDC,KAAK;YACHC,MAAM;gBACJR,YAAY;gBACZS,IAAI;gBACJC,SAAS;oBAAC;wBAAEC,QAAQ;oBAAW;iBAAE;YACnC;QACF;QACAL;IACF,CAAA;AAEAZ,SAAS,uBAAuB;IAC9BE,GAAG,mFAAmF;QACpF,MAAMgB,eAAef,GAAGgB,EAAE,CAAC,IAAO,CAAA;gBAAEC,WAAW;oBAAEC,QAAQ;gBAAK;YAAE,CAAA;QAChE,MAAMf,aAA+B;YACnCM,MAAM;YACNU,QAAQ;gBAAEC,MAAML;YAAa;YAC7BM,QAAQ,EAAE;QACZ;QACA,MAAMC,SAAS;YACbC,YAAY;gBACVC,aAAa;oBACXJ,MAAM,IAAO,CAAA;4BAAEK,UAAU;gCAAEC,YAAY;4BAAK;wBAAE,CAAA;gBAChD;YACF;QACF;QAEAzB,oBAAoB;YAAEqB;YAAQK,QAAQ;gBAACzB,YAAYC;aAAY;QAAC;QAEhEL,OAAOK,WAAWgB,MAAM,EAAEC,MAAMQ,IAAI,CAACb;QACrC,MAAMjB,OAAOwB,OAAOC,UAAU,EAAEC,aAAaJ,OAAOZ,eAAeqB,QAAQ,CAACC,OAAO,CAAC;YAClFC,KAAK;gBAAC;oBAAEN,UAAU;wBAAEC,YAAY;oBAAK;gBAAE;gBAAG;oBAAEZ,QAAQ;wBAAEkB,IAAI;4BAAC;yBAAW;oBAAC;gBAAE;aAAE;QAC7E;QACA,MAAMlC,OAAOwB,OAAOC,UAAU,EAAEC,aAAaJ,OAAOZ,WAAW,WAAWqB,QAAQ,CAACC,OAAO,CAAC;YACzFL,UAAU;gBAAEC,YAAY;YAAK;QAC/B;QACA5B,OAAOiB,cAAckB,GAAG,CAACC,gBAAgB;IAC3C;IAEAnC,GAAG,gEAAgE;QACjE,MAAMI,aAA+B;YAAEM,MAAM;YAASY,QAAQ,EAAE;QAAC;QACjE,MAAMC,SAAS,CAAC;QAEhBrB,oBAAoB;YAAEqB;YAAQK,QAAQ;gBAACzB,YAAYC;aAAY;QAAC;QAEhE,MAAML,OAAOwB,OAAOC,UAAU,EAAEC,aAAaW,SAAS3B,eAAeqB,QAAQ,CAACD,IAAI,CAAC;IACrF;IAEA7B,GAAG,wEAAwE;QACzE,MAAMqC,iBAAiB;YAAEnB,WAAW;gBAAEC,QAAQ;YAAK;QAAE;QACrD,MAAMH,eAAef,GAAGgB,EAAE,CAAC,IAAMoB;QACjC,MAAMhC,uBAAuBJ,GAAGgB,EAAE,CAAC,CAAC,EAAEqB,YAAY,EAAE,GAAKA;QACzD,MAAMlC,aAA+B;YACnCM,MAAM;YACNU,QAAQ;gBAAEC,MAAML;YAAa;YAC7BM,QAAQ,EAAE;QACZ;QACA,MAAMC,SAAS,CAAC;QAEhBrB,oBAAoB;YAClBqB;YACAK,QAAQ;gBAACzB,YAAYC,YAAYC;aAAsB;QACzD;QAEAN,OAAOwB,OAAOC,UAAU,EAAEe,aAAa;QACvC,MAAMxC,OAAOK,WAAWgB,MAAM,EAAEC,OAAOZ,eAAeqB,QAAQ,CAACC,OAAO,CAAC;YACrEC,KAAK;gBAACK;gBAAgB;oBAAEtB,QAAQ;wBAAEkB,IAAI;4BAAC;yBAAW;oBAAC;gBAAE;aAAE;QACzD;QACAlC,OAAOM,sBAAsBmC,oBAAoB,CAC/CzC,OAAO0C,gBAAgB,CAAC;YAAEC,WAAW;QAAO;IAEhD;AACF"}
@@ -1,116 +0,0 @@
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
@@ -1 +0,0 @@
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"}