@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 +7 -7
- package/dist/fields/tenantField/index.spec.js +0 -54
- package/dist/fields/tenantField/index.spec.js.map +0 -1
- package/dist/index.spec.js +0 -70
- package/dist/index.spec.js.map +0 -1
- package/dist/utilities/addCollectionAccess.spec.js +0 -134
- package/dist/utilities/addCollectionAccess.spec.js.map +0 -1
- package/dist/utilities/isValidTenantAssignment.spec.js +0 -116
- package/dist/utilities/isValidTenantAssignment.spec.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/plugin-multi-tenant",
|
|
3
|
-
"version": "4.0.0-canary.
|
|
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
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"@payloadcms/
|
|
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.
|
|
89
|
-
"payload": "4.0.0-canary.
|
|
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"}
|
package/dist/index.spec.js
DELETED
|
@@ -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
|
package/dist/index.spec.js.map
DELETED
|
@@ -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"}
|