fix(plugin-multi-tenant): skip baseFilter if user has access to all tenants (#13633)

Fixes
https://github.com/payloadcms/payload/issues/13589#issuecomment-3234832478


### Problem
The baseFilter is being applied when a user has no tenant selected, is
assigned to tenants BUT also passes the `userHasAccessToAllTenants`
check.


### Fix
Do not apply the baseFilter when no tenant is selected and the user
passes the `userHasAccessToAllTenants` check.
This commit is contained in:
Jarrod Flesch
2025-09-02 09:33:35 -04:00
committed by GitHub
parent 917c66fe44
commit 65b3845d92
3 changed files with 51 additions and 14 deletions

View File

@@ -1,24 +1,30 @@
import type { PayloadRequest, Where } from 'payload'
import type { PayloadRequest, TypedUser, Where } from 'payload'
import type { MultiTenantPluginConfig } from '../types.js'
import { defaults } from '../defaults.js'
import { getCollectionIDType } from '../utilities/getCollectionIDType.js'
import { getTenantFromCookie } from '../utilities/getTenantFromCookie.js'
import { getUserTenantIDs } from '../utilities/getUserTenantIDs.js'
type Args = {
type Args<ConfigType = unknown> = {
filterFieldName: string
req: PayloadRequest
tenantsArrayFieldName?: string
tenantsArrayTenantFieldName?: string
tenantsCollectionSlug: string
userHasAccessToAllTenants: Required<
MultiTenantPluginConfig<ConfigType>
>['userHasAccessToAllTenants']
}
export const filterDocumentsByTenants = ({
export const filterDocumentsByTenants = <ConfigType = unknown>({
filterFieldName,
req,
tenantsArrayFieldName = defaults.tenantsArrayFieldName,
tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName,
tenantsCollectionSlug,
}: Args): null | Where => {
userHasAccessToAllTenants,
}: Args<ConfigType>): null | Where => {
const idType = getCollectionIDType({
collectionSlug: tenantsCollectionSlug,
payload: req.payload,
@@ -34,6 +40,15 @@ export const filterDocumentsByTenants = ({
}
}
if (
req.user &&
userHasAccessToAllTenants(
req?.user as ConfigType extends { user: unknown } ? ConfigType['user'] : TypedUser,
)
) {
return null
}
// scope to user assigned tenants
const userAssignedTenants = getUserTenantIDs(req.user, {
tenantsArrayFieldName,

View File

@@ -116,12 +116,13 @@ export const multiTenantPlugin =
adminUsersCollection.admin.baseFilter = combineFilters({
baseFilter,
customFilter: (args) =>
filterDocumentsByTenants({
filterDocumentsByTenants<ConfigType>({
filterFieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,
req: args.req,
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
}),
})
}
@@ -176,6 +177,7 @@ export const multiTenantPlugin =
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
})
if (pluginConfig.collections[foldersSlug]?.customTenantField !== true) {
@@ -207,12 +209,13 @@ export const multiTenantPlugin =
collection.admin.baseFilter = combineFilters({
baseFilter: collection.admin?.baseFilter ?? collection.admin?.baseListFilter,
customFilter: (args) =>
filterDocumentsByTenants({
filterDocumentsByTenants<ConfigType>({
filterFieldName: tenantFieldName,
req: args.req,
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
}),
})
}
@@ -279,6 +282,7 @@ export const multiTenantPlugin =
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
}),
})
}
@@ -317,7 +321,7 @@ export const multiTenantPlugin =
collection.endpoints = [
...(collection.endpoints || []),
getTenantOptionsEndpoint<ConfigType>({
getTenantOptionsEndpoint({
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
@@ -346,6 +350,7 @@ export const multiTenantPlugin =
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
})
if (pluginConfig.collections[collection.slug]?.customTenantField !== true) {
@@ -383,6 +388,7 @@ export const multiTenantPlugin =
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
}),
})
}

View File

@@ -1,9 +1,11 @@
import type { Block, Config, Field, RelationshipField, SanitizedConfig } from 'payload'
import type { Block, Config, Field, RelationshipField, SanitizedConfig, TypedUser } from 'payload'
import type { MultiTenantPluginConfig } from '../types.js'
import { defaults } from '../defaults.js'
import { filterDocumentsByTenants } from '../filters/filterDocumentsByTenants.js'
type AddFilterOptionsToFieldsArgs = {
type AddFilterOptionsToFieldsArgs<ConfigType = unknown> = {
blockReferencesWithFilters: string[]
config: Config | SanitizedConfig
fields: Field[]
@@ -13,9 +15,12 @@ type AddFilterOptionsToFieldsArgs = {
tenantsArrayFieldName: string
tenantsArrayTenantFieldName: string
tenantsCollectionSlug: string
userHasAccessToAllTenants: Required<
MultiTenantPluginConfig<ConfigType>
>['userHasAccessToAllTenants']
}
export function addFilterOptionsToFields({
export function addFilterOptionsToFields<ConfigType = unknown>({
blockReferencesWithFilters,
config,
fields,
@@ -25,7 +30,8 @@ export function addFilterOptionsToFields({
tenantsArrayFieldName = defaults.tenantsArrayFieldName,
tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName,
tenantsCollectionSlug,
}: AddFilterOptionsToFieldsArgs) {
userHasAccessToAllTenants,
}: AddFilterOptionsToFieldsArgs<ConfigType>) {
fields.forEach((field) => {
if (field.type === 'relationship') {
/**
@@ -46,6 +52,7 @@ export function addFilterOptionsToFields({
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
})
}
} else {
@@ -63,6 +70,7 @@ export function addFilterOptionsToFields({
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
})
}
})
@@ -85,6 +93,7 @@ export function addFilterOptionsToFields({
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
})
}
@@ -113,6 +122,7 @@ export function addFilterOptionsToFields({
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
})
}
})
@@ -130,28 +140,33 @@ export function addFilterOptionsToFields({
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
})
})
}
})
}
type AddFilterArgs = {
type AddFilterArgs<ConfigType = unknown> = {
field: RelationshipField
tenantEnabledCollectionSlugs: string[]
tenantFieldName: string
tenantsArrayFieldName: string
tenantsArrayTenantFieldName: string
tenantsCollectionSlug: string
userHasAccessToAllTenants: Required<
MultiTenantPluginConfig<ConfigType>
>['userHasAccessToAllTenants']
}
function addFilter({
function addFilter<ConfigType = unknown>({
field,
tenantEnabledCollectionSlugs,
tenantFieldName,
tenantsArrayFieldName = defaults.tenantsArrayFieldName,
tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName,
tenantsCollectionSlug,
}: AddFilterArgs) {
userHasAccessToAllTenants,
}: AddFilterArgs<ConfigType>) {
// User specified filter
const originalFilter = field.filterOptions
field.filterOptions = async (args) => {
@@ -175,6 +190,7 @@ function addFilter({
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
userHasAccessToAllTenants,
})
// If the tenant filter returns null, meaning no tenant filter, just use the original filter