From f446e036eeffa01a1fee0e4990469ae262df9282 Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 19 Feb 2026 21:34:28 +0400 Subject: [PATCH] Update space security and permissions (#10529) Signed-off-by: Kristina Fefelova --- .../packages/middleware/src/spacePermissions.ts | 13 ++++++++++++- .../packages/middleware/src/spaceSecurity.ts | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/foundations/server/packages/middleware/src/spacePermissions.ts b/foundations/server/packages/middleware/src/spacePermissions.ts index 18090c0673..6acd7f1634 100644 --- a/foundations/server/packages/middleware/src/spacePermissions.ts +++ b/foundations/server/packages/middleware/src/spacePermissions.ts @@ -37,7 +37,7 @@ import core, { } from '@hcengineering/core' import platform, { PlatformError, Severity, Status } from '@hcengineering/platform' import { type Middleware, type TxMiddlewareResult, type PipelineContext } from '@hcengineering/server-core' - +import contact from '@hcengineering/contact' import { BaseMiddleware } from '@hcengineering/server-core' /** @@ -446,10 +446,21 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle this.checkSpacePermissions(ctx, cudTx, cudTx.objectSpace) if (isSpace) { + this.checkSpaceTypePermissions(ctx, cudTx as TxCUD) this.checkSpacePermissions(ctx, cudTx, cudTx.objectId as Ref, true) } } + private checkSpaceTypePermissions (ctx: MeasureContext, cudTx: TxCUD): void { + const account = ctx.contextData.account + const h = this.context.hierarchy + if (account.primarySocialId === core.account.System) return + + if (h.isDerived(cudTx.objectClass, contact.class.PersonSpace)) { + this.throwForbidden() + } + } + private checkSpacePermissions ( ctx: MeasureContext, cudTx: TxCUD, diff --git a/foundations/server/packages/middleware/src/spaceSecurity.ts b/foundations/server/packages/middleware/src/spaceSecurity.ts index 28f3098e9b..4c8bd4223e 100644 --- a/foundations/server/packages/middleware/src/spaceSecurity.ts +++ b/foundations/server/packages/middleware/src/spaceSecurity.ts @@ -688,7 +688,7 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar findResult.lookupMap ) } - if (!isOwner(account, ctx) && account.role !== AccountRole.DocGuest) { + if (account.role !== AccountRole.DocGuest) { if (options?.lookup !== undefined) { for (const object of findResult) { if (object.$lookup !== undefined) { @@ -737,19 +737,29 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar if (Object.keys(lookup).length === 0) return const account = ctx.contextData.account if (isSystem(account, ctx)) return + const owner = isOwner(account, ctx) + const h = this.context.hierarchy const allowedSpaces = new Set(this.getAllAllowedSpaces(account, true, showArchived)) for (const key in lookup) { const val = lookup[key] if (Array.isArray(val)) { const arr: AttachedDoc[] = [] for (const value of val) { - if (allowedSpaces.has(value.space)) { + const isSpace = '_class' in value && h.isDerived(value._class, core.class.Space) + const availableForOwner = owner && isSpace + const availableSpace = isSpace && allowedSpaces.has(value._id) + const availableDoc = !isSpace && allowedSpaces.has(value.space) + if (availableForOwner || availableSpace || availableDoc) { arr.push(value) } } lookup[key] = arr as any } else if (val !== undefined) { - if (!allowedSpaces.has(val.space)) { + const isSpace = '_class' in val && h.isDerived(val._class, core.class.Space) + const availableForOwner = owner && isSpace + const availableSpace = isSpace && allowedSpaces.has(val._id as Ref) + const availableDoc = !isSpace && allowedSpaces.has(val.space) + if (!availableForOwner && !availableSpace && !availableDoc) { // allow attached lookups for guests when collaborator security is enabled // do not check if collaborator of the doc because it's being checked on the storage (DB) level // as otherwise there will be no doc here at all