diff --git a/server/core/src/types.ts b/server/core/src/types.ts index afb621593b..628f989f98 100644 --- a/server/core/src/types.ts +++ b/server/core/src/types.ts @@ -62,6 +62,9 @@ export interface ServerFindOptions extends FindOptions { domain: Domain } + // using for join query security + allowedSpaces?: Ref[] + // Optional measure context, for server side operations ctx?: MeasureContext } diff --git a/server/middleware/src/queryJoin.ts b/server/middleware/src/queryJoin.ts index 891de6008c..35fa53f3cc 100644 --- a/server/middleware/src/queryJoin.ts +++ b/server/middleware/src/queryJoin.ts @@ -22,7 +22,7 @@ import { type MeasureContext, Ref } from '@hcengineering/core' -import { BaseMiddleware, Middleware, type PipelineContext } from '@hcengineering/server-core' +import { BaseMiddleware, Middleware, ServerFindOptions, type PipelineContext } from '@hcengineering/server-core' import { deepEqual } from 'fast-equals' interface Query { @@ -45,7 +45,7 @@ export class QueryJoiner { ctx: MeasureContext, _class: Ref>, query: DocumentQuery, - options?: FindOptions + options?: ServerFindOptions ): Promise> { // Will find a query or add + 1 to callbacks const q = this.findQuery(_class, query, options) ?? this.createQuery(_class, query, options) @@ -131,7 +131,7 @@ export class QueryJoinMiddleware extends BaseMiddleware implements Middleware { ctx: MeasureContext, _class: Ref>, query: DocumentQuery, - options?: FindOptions + options?: ServerFindOptions ): Promise> { // Will find a query or add + 1 to callbacks return this.joiner.findAll(ctx, _class, query, options) diff --git a/server/middleware/src/spaceSecurity.ts b/server/middleware/src/spaceSecurity.ts index 2ce09da102..6c792db300 100644 --- a/server/middleware/src/spaceSecurity.ts +++ b/server/middleware/src/spaceSecurity.ts @@ -21,7 +21,6 @@ import core, { Doc, DocumentQuery, Domain, - FindOptions, FindResult, LookupData, MeasureContext, @@ -47,7 +46,13 @@ import core, { type SessionData } from '@hcengineering/core' import platform, { PlatformError, Severity, Status } from '@hcengineering/platform' -import { BaseMiddleware, Middleware, TxMiddlewareResult, type PipelineContext } from '@hcengineering/server-core' +import { + BaseMiddleware, + Middleware, + ServerFindOptions, + TxMiddlewareResult, + type PipelineContext +} from '@hcengineering/server-core' import { isOwner, isSystem } from './utils' type SpaceWithMembers = Pick @@ -416,6 +421,14 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar } } + ctx.contextData.broadcast.targets.spaceSec = (tx) => { + const space = this.spacesMap.get(tx.objectSpace) + if (space === undefined) return undefined + if (this.systemSpaces.has(space._id) || this.mainSpaces.includes(space._id)) return undefined + + return space.members.length === 0 ? undefined : this.getTargets(space?.members) + } + await this.next?.handleBroadcast(ctx) } @@ -497,7 +510,7 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar ctx: MeasureContext, _class: Ref>, query: DocumentQuery, - options?: FindOptions + options?: ServerFindOptions ): Promise> { await this.init(ctx) @@ -509,12 +522,7 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar let clientFilterSpaces: Set> | undefined - if ( - !this.skipFindCheck && - !isSystem(account, ctx) && - account.role !== AccountRole.DocGuest && - domain !== DOMAIN_MODEL - ) { + if (!isSystem(account, ctx) && account.role !== AccountRole.DocGuest && domain !== DOMAIN_MODEL) { if (!isOwner(account, ctx) || !isSpace) { if (query[field] !== undefined) { const res = await this.mergeQuery(ctx, account, query[field], domain, isSpace) @@ -536,6 +544,11 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar delete (newQuery as any)[field] } else if (spaces.result.length === 1) { ;(newQuery as any)[field] = spaces.result[0] + if (options !== undefined) { + options.allowedSpaces = spaces.result + } else { + options = { allowedSpaces: spaces.result } + } } else { // Check if spaces > 85% of all domain spaces, in this case return all and filter on client. if (spaces.result.length / spaces.domainSpaces.size > 0.85 && options?.limit === undefined) { @@ -543,17 +556,22 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar delete newQuery.space } else { ;(newQuery as any)[field] = { $in: spaces.result } + if (options !== undefined) { + options.allowedSpaces = spaces.result + } else { + options = { allowedSpaces: spaces.result } + } } } } } } - let findResult = await this.provideFindAll(ctx, _class, newQuery, options) + let findResult = await this.provideFindAll(ctx, _class, !this.skipFindCheck ? newQuery : query, options) if (clientFilterSpaces !== undefined) { const cfs = clientFilterSpaces findResult = toFindResult( - findResult.filter((it) => cfs.has(it.space)), + findResult.filter((it) => cfs.has((it as any)[field])), findResult.total, findResult.lookupMap )