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
This commit is contained in:
@@ -52,7 +52,7 @@ The plugin accepts an object with the following properties:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
type MultiTenantPluginConfig<ConfigTypes = unknown> = {
|
type MultiTenantPluginConfig<ConfigTypes = unknown> = {
|
||||||
/**
|
/**
|
||||||
* After a tenant is deleted, the plugin will attempt to clean up related documents
|
* After a tenant is deleted, the plugin will attempt to clean up related documents
|
||||||
* - removing documents with the tenant ID
|
* - removing documents with the tenant ID
|
||||||
* - removing the tenant from users
|
* - removing the tenant from users
|
||||||
@@ -176,6 +176,14 @@ type MultiTenantPluginConfig<ConfigTypes = unknown> = {
|
|||||||
* Opt out of adding access constraints to the tenants collection
|
* Opt out of adding access constraints to the tenants collection
|
||||||
*/
|
*/
|
||||||
useTenantsCollectionAccess?: boolean
|
useTenantsCollectionAccess?: boolean
|
||||||
|
/**
|
||||||
|
* Opt out including the baseListFilter to filter tenants by selected tenant
|
||||||
|
*/
|
||||||
|
useTenantsListFilter?: boolean
|
||||||
|
/**
|
||||||
|
* Opt out including the baseListFilter to filter users by selected tenant
|
||||||
|
*/
|
||||||
|
useUsersTenantFilter?: boolean
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
export { filterDocumentsBySelectedTenant as getTenantListFilter } from '../list-filters/filterDocumentsBySelectedTenant.js'
|
||||||
export { getGlobalViewRedirect } from '../utilities/getGlobalViewRedirect.js'
|
export { getGlobalViewRedirect } from '../utilities/getGlobalViewRedirect.js'
|
||||||
export { getTenantAccess } from '../utilities/getTenantAccess.js'
|
export { getTenantAccess } from '../utilities/getTenantAccess.js'
|
||||||
export { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
|
export { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
|
||||||
export { getTenantListFilter } from '../utilities/getTenantListFilter.js'
|
|
||||||
export { getUserTenantIDs } from '../utilities/getUserTenantIDs.js'
|
export { getUserTenantIDs } from '../utilities/getUserTenantIDs.js'
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import { defaults } from './defaults.js'
|
|||||||
import { tenantField } from './fields/tenantField/index.js'
|
import { tenantField } from './fields/tenantField/index.js'
|
||||||
import { tenantsArrayField } from './fields/tenantsArrayField/index.js'
|
import { tenantsArrayField } from './fields/tenantsArrayField/index.js'
|
||||||
import { addTenantCleanup } from './hooks/afterTenantDelete.js'
|
import { addTenantCleanup } from './hooks/afterTenantDelete.js'
|
||||||
|
import { filterDocumentsBySelectedTenant } from './list-filters/filterDocumentsBySelectedTenant.js'
|
||||||
|
import { filterTenantsBySelectedTenant } from './list-filters/filterTenantsBySelectedTenant.js'
|
||||||
|
import { filterUsersBySelectedTenant } from './list-filters/filterUsersBySelectedTenant.js'
|
||||||
import { addCollectionAccess } from './utilities/addCollectionAccess.js'
|
import { addCollectionAccess } from './utilities/addCollectionAccess.js'
|
||||||
import { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'
|
import { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'
|
||||||
import { withTenantListFilter } from './utilities/withTenantListFilter.js'
|
import { combineListFilters } from './utilities/combineListFilters.js'
|
||||||
|
|
||||||
export const multiTenantPlugin =
|
export const multiTenantPlugin =
|
||||||
<ConfigType>(pluginConfig: MultiTenantPluginConfig<ConfigType>) =>
|
<ConfigType>(pluginConfig: MultiTenantPluginConfig<ConfigType>) =>
|
||||||
@@ -97,6 +100,23 @@ export const multiTenantPlugin =
|
|||||||
userHasAccessToAllTenants,
|
userHasAccessToAllTenants,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (pluginConfig.useUsersTenantFilter !== false) {
|
||||||
|
if (!adminUsersCollection.admin) {
|
||||||
|
adminUsersCollection.admin = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
adminUsersCollection.admin.baseListFilter = combineListFilters({
|
||||||
|
baseListFilter: adminUsersCollection.admin?.baseListFilter,
|
||||||
|
customFilter: (args) =>
|
||||||
|
filterUsersBySelectedTenant({
|
||||||
|
req: args.req,
|
||||||
|
tenantsArrayFieldName,
|
||||||
|
tenantsArrayTenantFieldName,
|
||||||
|
tenantsCollectionSlug,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let tenantCollection: CollectionConfig | undefined
|
let tenantCollection: CollectionConfig | undefined
|
||||||
|
|
||||||
const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<
|
const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<
|
||||||
@@ -138,6 +158,25 @@ export const multiTenantPlugin =
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pluginConfig.useTenantsListFilter !== false) {
|
||||||
|
/**
|
||||||
|
* Add list filter to tenants collection
|
||||||
|
* - filter by selected tenant
|
||||||
|
*/
|
||||||
|
if (!collection.admin) {
|
||||||
|
collection.admin = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.admin.baseListFilter = combineListFilters({
|
||||||
|
baseListFilter: collection.admin?.baseListFilter,
|
||||||
|
customFilter: (args) =>
|
||||||
|
filterTenantsBySelectedTenant({
|
||||||
|
req: args.req,
|
||||||
|
tenantsCollectionSlug,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (pluginConfig.cleanupAfterTenantDelete !== false) {
|
if (pluginConfig.cleanupAfterTenantDelete !== false) {
|
||||||
/**
|
/**
|
||||||
* Add cleanup logic when tenant is deleted
|
* Add cleanup logic when tenant is deleted
|
||||||
@@ -195,10 +234,15 @@ export const multiTenantPlugin =
|
|||||||
if (!collection.admin) {
|
if (!collection.admin) {
|
||||||
collection.admin = {}
|
collection.admin = {}
|
||||||
}
|
}
|
||||||
collection.admin.baseListFilter = withTenantListFilter({
|
|
||||||
|
collection.admin.baseListFilter = combineListFilters({
|
||||||
baseListFilter: collection.admin?.baseListFilter,
|
baseListFilter: collection.admin?.baseListFilter,
|
||||||
tenantFieldName,
|
customFilter: (args) =>
|
||||||
tenantsCollectionSlug,
|
filterDocumentsBySelectedTenant({
|
||||||
|
req: args.req,
|
||||||
|
tenantFieldName,
|
||||||
|
tenantsCollectionSlug,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import type { PayloadRequest, Where } from 'payload'
|
import type { PayloadRequest, Where } from 'payload'
|
||||||
|
|
||||||
import { SELECT_ALL } from '../constants.js'
|
import { SELECT_ALL } from '../constants.js'
|
||||||
import { getCollectionIDType } from './getCollectionIDType.js'
|
import { getCollectionIDType } from '../utilities/getCollectionIDType.js'
|
||||||
import { getTenantFromCookie } from './getTenantFromCookie.js'
|
import { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
tenantFieldName: string
|
tenantFieldName: string
|
||||||
tenantsCollectionSlug: string
|
tenantsCollectionSlug: string
|
||||||
}
|
}
|
||||||
export const getTenantListFilter = ({
|
export const filterDocumentsBySelectedTenant = ({
|
||||||
req,
|
req,
|
||||||
tenantFieldName,
|
tenantFieldName,
|
||||||
tenantsCollectionSlug,
|
tenantsCollectionSlug,
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import type { PayloadRequest, Where } from 'payload'
|
||||||
|
|
||||||
|
import { SELECT_ALL } from '../constants.js'
|
||||||
|
import { getCollectionIDType } from '../utilities/getCollectionIDType.js'
|
||||||
|
import { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
|
||||||
|
|
||||||
|
type Args = {
|
||||||
|
req: PayloadRequest
|
||||||
|
tenantsCollectionSlug: string
|
||||||
|
}
|
||||||
|
export const filterTenantsBySelectedTenant = ({
|
||||||
|
req,
|
||||||
|
tenantsCollectionSlug,
|
||||||
|
}: Args): null | Where => {
|
||||||
|
const idType = getCollectionIDType({
|
||||||
|
collectionSlug: tenantsCollectionSlug,
|
||||||
|
payload: req.payload,
|
||||||
|
})
|
||||||
|
const selectedTenant = getTenantFromCookie(req.headers, idType)
|
||||||
|
|
||||||
|
if (selectedTenant === SELECT_ALL) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: {
|
||||||
|
equals: selectedTenant,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import type { PayloadRequest, Where } from 'payload'
|
||||||
|
|
||||||
|
import { SELECT_ALL } from '../constants.js'
|
||||||
|
import { getCollectionIDType } from '../utilities/getCollectionIDType.js'
|
||||||
|
import { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
|
||||||
|
|
||||||
|
type Args = {
|
||||||
|
req: PayloadRequest
|
||||||
|
tenantsArrayFieldName: string
|
||||||
|
tenantsArrayTenantFieldName: string
|
||||||
|
tenantsCollectionSlug: string
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Filter the list of users by the selected tenant
|
||||||
|
*/
|
||||||
|
export const filterUsersBySelectedTenant = ({
|
||||||
|
req,
|
||||||
|
tenantsArrayFieldName,
|
||||||
|
tenantsArrayTenantFieldName,
|
||||||
|
tenantsCollectionSlug,
|
||||||
|
}: Args): null | Where => {
|
||||||
|
const idType = getCollectionIDType({
|
||||||
|
collectionSlug: tenantsCollectionSlug,
|
||||||
|
payload: req.payload,
|
||||||
|
})
|
||||||
|
const selectedTenant = getTenantFromCookie(req.headers, idType)
|
||||||
|
|
||||||
|
if (selectedTenant === SELECT_ALL) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
[`${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`]: {
|
||||||
|
in: [selectedTenant],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -125,6 +125,14 @@ export type MultiTenantPluginConfig<ConfigTypes = unknown> = {
|
|||||||
* Opt out of adding access constraints to the tenants collection
|
* Opt out of adding access constraints to the tenants collection
|
||||||
*/
|
*/
|
||||||
useTenantsCollectionAccess?: boolean
|
useTenantsCollectionAccess?: boolean
|
||||||
|
/**
|
||||||
|
* Opt out including the baseListFilter to filter tenants by selected tenant
|
||||||
|
*/
|
||||||
|
useTenantsListFilter?: boolean
|
||||||
|
/**
|
||||||
|
* Opt out including the baseListFilter to filter users by selected tenant
|
||||||
|
*/
|
||||||
|
useUsersTenantFilter?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Tenant<IDType = number | string> = {
|
export type Tenant<IDType = number | string> = {
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
import type { BaseListFilter, Where } from 'payload'
|
import type { BaseListFilter, Where } from 'payload'
|
||||||
|
|
||||||
import { getTenantListFilter } from './getTenantListFilter.js'
|
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
baseListFilter?: BaseListFilter
|
baseListFilter?: BaseListFilter
|
||||||
tenantFieldName: string
|
customFilter: BaseListFilter
|
||||||
tenantsCollectionSlug: string
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Combines a base list filter with a tenant list filter
|
* Combines a base list filter with a tenant list filter
|
||||||
*
|
*
|
||||||
* Combines where constraints inside of an AND operator
|
* Combines where constraints inside of an AND operator
|
||||||
*/
|
*/
|
||||||
export const withTenantListFilter =
|
export const combineListFilters =
|
||||||
({ baseListFilter, tenantFieldName, tenantsCollectionSlug }: Args): BaseListFilter =>
|
({ baseListFilter, customFilter }: Args): BaseListFilter =>
|
||||||
async (args) => {
|
async (args) => {
|
||||||
const filterConstraints = []
|
const filterConstraints = []
|
||||||
|
|
||||||
@@ -25,14 +22,10 @@ export const withTenantListFilter =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantListFilter = getTenantListFilter({
|
const customFilterResult = await customFilter(args)
|
||||||
req: args.req,
|
|
||||||
tenantFieldName,
|
|
||||||
tenantsCollectionSlug,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (tenantListFilter) {
|
if (customFilterResult) {
|
||||||
filterConstraints.push(tenantListFilter)
|
filterConstraints.push(customFilterResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filterConstraints.length) {
|
if (filterConstraints.length) {
|
||||||
Reference in New Issue
Block a user