Skip to content

Commit 45cee23

Browse files
feat(plugin-multi-tenant): filter users list and tenants lists (#11417)
### What? - Adds `users` base list filtering when tenant is selected - Adds `tenants` base list filtering when tenant is selected
1 parent 67b7a73 commit 45cee23

File tree

8 files changed

+142
-22
lines changed

8 files changed

+142
-22
lines changed

docs/plugins/multi-tenant.mdx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ The plugin accepts an object with the following properties:
5252

5353
```ts
5454
type MultiTenantPluginConfig<ConfigTypes = unknown> = {
55-
/**
55+
/**
5656
* After a tenant is deleted, the plugin will attempt to clean up related documents
5757
* - removing documents with the tenant ID
5858
* - removing the tenant from users
@@ -176,6 +176,14 @@ type MultiTenantPluginConfig<ConfigTypes = unknown> = {
176176
* Opt out of adding access constraints to the tenants collection
177177
*/
178178
useTenantsCollectionAccess?: boolean
179+
/**
180+
* Opt out including the baseListFilter to filter tenants by selected tenant
181+
*/
182+
useTenantsListFilter?: boolean
183+
/**
184+
* Opt out including the baseListFilter to filter users by selected tenant
185+
*/
186+
useUsersTenantFilter?: boolean
179187
}
180188
```
181189
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
export { filterDocumentsBySelectedTenant as getTenantListFilter } from '../list-filters/filterDocumentsBySelectedTenant.js'
12
export { getGlobalViewRedirect } from '../utilities/getGlobalViewRedirect.js'
23
export { getTenantAccess } from '../utilities/getTenantAccess.js'
34
export { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
4-
export { getTenantListFilter } from '../utilities/getTenantListFilter.js'
55
export { getUserTenantIDs } from '../utilities/getUserTenantIDs.js'

packages/plugin-multi-tenant/src/index.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import { defaults } from './defaults.js'
66
import { tenantField } from './fields/tenantField/index.js'
77
import { tenantsArrayField } from './fields/tenantsArrayField/index.js'
88
import { addTenantCleanup } from './hooks/afterTenantDelete.js'
9+
import { filterDocumentsBySelectedTenant } from './list-filters/filterDocumentsBySelectedTenant.js'
10+
import { filterTenantsBySelectedTenant } from './list-filters/filterTenantsBySelectedTenant.js'
11+
import { filterUsersBySelectedTenant } from './list-filters/filterUsersBySelectedTenant.js'
912
import { addCollectionAccess } from './utilities/addCollectionAccess.js'
1013
import { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'
11-
import { withTenantListFilter } from './utilities/withTenantListFilter.js'
14+
import { combineListFilters } from './utilities/combineListFilters.js'
1215

1316
export const multiTenantPlugin =
1417
<ConfigType>(pluginConfig: MultiTenantPluginConfig<ConfigType>) =>
@@ -97,6 +100,23 @@ export const multiTenantPlugin =
97100
userHasAccessToAllTenants,
98101
})
99102

103+
if (pluginConfig.useUsersTenantFilter !== false) {
104+
if (!adminUsersCollection.admin) {
105+
adminUsersCollection.admin = {}
106+
}
107+
108+
adminUsersCollection.admin.baseListFilter = combineListFilters({
109+
baseListFilter: adminUsersCollection.admin?.baseListFilter,
110+
customFilter: (args) =>
111+
filterUsersBySelectedTenant({
112+
req: args.req,
113+
tenantsArrayFieldName,
114+
tenantsArrayTenantFieldName,
115+
tenantsCollectionSlug,
116+
}),
117+
})
118+
}
119+
100120
let tenantCollection: CollectionConfig | undefined
101121

102122
const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<
@@ -138,6 +158,25 @@ export const multiTenantPlugin =
138158
})
139159
}
140160

161+
if (pluginConfig.useTenantsListFilter !== false) {
162+
/**
163+
* Add list filter to tenants collection
164+
* - filter by selected tenant
165+
*/
166+
if (!collection.admin) {
167+
collection.admin = {}
168+
}
169+
170+
collection.admin.baseListFilter = combineListFilters({
171+
baseListFilter: collection.admin?.baseListFilter,
172+
customFilter: (args) =>
173+
filterTenantsBySelectedTenant({
174+
req: args.req,
175+
tenantsCollectionSlug,
176+
}),
177+
})
178+
}
179+
141180
if (pluginConfig.cleanupAfterTenantDelete !== false) {
142181
/**
143182
* Add cleanup logic when tenant is deleted
@@ -195,10 +234,15 @@ export const multiTenantPlugin =
195234
if (!collection.admin) {
196235
collection.admin = {}
197236
}
198-
collection.admin.baseListFilter = withTenantListFilter({
237+
238+
collection.admin.baseListFilter = combineListFilters({
199239
baseListFilter: collection.admin?.baseListFilter,
200-
tenantFieldName,
201-
tenantsCollectionSlug,
240+
customFilter: (args) =>
241+
filterDocumentsBySelectedTenant({
242+
req: args.req,
243+
tenantFieldName,
244+
tenantsCollectionSlug,
245+
}),
202246
})
203247
}
204248

packages/plugin-multi-tenant/src/utilities/getTenantListFilter.ts renamed to packages/plugin-multi-tenant/src/list-filters/filterDocumentsBySelectedTenant.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import type { PayloadRequest, Where } from 'payload'
22

33
import { SELECT_ALL } from '../constants.js'
4-
import { getCollectionIDType } from './getCollectionIDType.js'
5-
import { getTenantFromCookie } from './getTenantFromCookie.js'
4+
import { getCollectionIDType } from '../utilities/getCollectionIDType.js'
5+
import { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
66

77
type Args = {
88
req: PayloadRequest
99
tenantFieldName: string
1010
tenantsCollectionSlug: string
1111
}
12-
export const getTenantListFilter = ({
12+
export const filterDocumentsBySelectedTenant = ({
1313
req,
1414
tenantFieldName,
1515
tenantsCollectionSlug,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { PayloadRequest, Where } from 'payload'
2+
3+
import { SELECT_ALL } from '../constants.js'
4+
import { getCollectionIDType } from '../utilities/getCollectionIDType.js'
5+
import { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
6+
7+
type Args = {
8+
req: PayloadRequest
9+
tenantsCollectionSlug: string
10+
}
11+
export const filterTenantsBySelectedTenant = ({
12+
req,
13+
tenantsCollectionSlug,
14+
}: Args): null | Where => {
15+
const idType = getCollectionIDType({
16+
collectionSlug: tenantsCollectionSlug,
17+
payload: req.payload,
18+
})
19+
const selectedTenant = getTenantFromCookie(req.headers, idType)
20+
21+
if (selectedTenant === SELECT_ALL) {
22+
return {}
23+
}
24+
25+
return {
26+
id: {
27+
equals: selectedTenant,
28+
},
29+
}
30+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { PayloadRequest, Where } from 'payload'
2+
3+
import { SELECT_ALL } from '../constants.js'
4+
import { getCollectionIDType } from '../utilities/getCollectionIDType.js'
5+
import { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
6+
7+
type Args = {
8+
req: PayloadRequest
9+
tenantsArrayFieldName: string
10+
tenantsArrayTenantFieldName: string
11+
tenantsCollectionSlug: string
12+
}
13+
/**
14+
* Filter the list of users by the selected tenant
15+
*/
16+
export const filterUsersBySelectedTenant = ({
17+
req,
18+
tenantsArrayFieldName,
19+
tenantsArrayTenantFieldName,
20+
tenantsCollectionSlug,
21+
}: Args): null | Where => {
22+
const idType = getCollectionIDType({
23+
collectionSlug: tenantsCollectionSlug,
24+
payload: req.payload,
25+
})
26+
const selectedTenant = getTenantFromCookie(req.headers, idType)
27+
28+
if (selectedTenant === SELECT_ALL) {
29+
return {}
30+
}
31+
32+
return {
33+
[`${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`]: {
34+
in: [selectedTenant],
35+
},
36+
}
37+
}

packages/plugin-multi-tenant/src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ export type MultiTenantPluginConfig<ConfigTypes = unknown> = {
125125
* Opt out of adding access constraints to the tenants collection
126126
*/
127127
useTenantsCollectionAccess?: boolean
128+
/**
129+
* Opt out including the baseListFilter to filter tenants by selected tenant
130+
*/
131+
useTenantsListFilter?: boolean
132+
/**
133+
* Opt out including the baseListFilter to filter users by selected tenant
134+
*/
135+
useUsersTenantFilter?: boolean
128136
}
129137

130138
export type Tenant<IDType = number | string> = {

packages/plugin-multi-tenant/src/utilities/withTenantListFilter.ts renamed to packages/plugin-multi-tenant/src/utilities/combineListFilters.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import type { BaseListFilter, Where } from 'payload'
22

3-
import { getTenantListFilter } from './getTenantListFilter.js'
4-
53
type Args = {
64
baseListFilter?: BaseListFilter
7-
tenantFieldName: string
8-
tenantsCollectionSlug: string
5+
customFilter: BaseListFilter
96
}
107
/**
118
* Combines a base list filter with a tenant list filter
129
*
1310
* Combines where constraints inside of an AND operator
1411
*/
15-
export const withTenantListFilter =
16-
({ baseListFilter, tenantFieldName, tenantsCollectionSlug }: Args): BaseListFilter =>
12+
export const combineListFilters =
13+
({ baseListFilter, customFilter }: Args): BaseListFilter =>
1714
async (args) => {
1815
const filterConstraints = []
1916

@@ -25,14 +22,10 @@ export const withTenantListFilter =
2522
}
2623
}
2724

28-
const tenantListFilter = getTenantListFilter({
29-
req: args.req,
30-
tenantFieldName,
31-
tenantsCollectionSlug,
32-
})
25+
const customFilterResult = await customFilter(args)
3326

34-
if (tenantListFilter) {
35-
filterConstraints.push(tenantListFilter)
27+
if (customFilterResult) {
28+
filterConstraints.push(customFilterResult)
3629
}
3730

3831
if (filterConstraints.length) {

0 commit comments

Comments
 (0)