Skip to content

Commit

Permalink
Space security via lookup
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
  • Loading branch information
haiodo committed Jul 26, 2024
1 parent cd211f8 commit 98e53c7
Showing 1 changed file with 61 additions and 11 deletions.
72 changes: 61 additions & 11 deletions server/middleware/src/spaceSecurity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,23 @@ import core, {
systemAccountEmail
} from '@hcengineering/core'
import platform, { PlatformError, Severity, Status } from '@hcengineering/platform'
import { Middleware, SessionContext, TxMiddlewareResult, type ServerStorage } from '@hcengineering/server-core'
import {
Middleware,
SessionContext,
TxMiddlewareResult,
type ServerFindOptions,
type ServerStorage
} from '@hcengineering/server-core'
import { BaseMiddleware } from './base'
import { getUser, isOwner, isSystem } from './utils'

enum SpaceSecurityKind {
regular = 'regular',
lookup = 'lookup'
}

const SERVER_SPACE_SECURITY = process.env.SERVER_SPACE_SECURITY ?? SpaceSecurityKind.regular

type SpaceWithMembers = Pick<Space, '_id' | 'members' | 'private' | '_class'>

/**
Expand Down Expand Up @@ -493,12 +506,9 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar
}

private async mergeQuery<T extends Doc>(
account: Account,
query: ObjQueryType<T['space']>,
domain: Domain,
isSpace: boolean
spaces: Ref<Space>[],
query: ObjQueryType<T['space']>
): Promise<ObjQueryType<T['space']>> {
const spaces = await this.filterByDomain(domain, this.getAllAllowedSpaces(account, !isSpace))
if (query == null) {
return { $in: spaces }
}
Expand Down Expand Up @@ -528,22 +538,57 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar

const domain = this.storage.hierarchy.getDomain(_class)
const newQuery = { ...query }
const newOptions: ServerFindOptions<T> = { ...options }
const account = await getUser(this.storage, ctx)
const isSpace = this.storage.hierarchy.isDerived(_class, core.class.Space)
const field = this.getKey(domain)

let clearExtra = false
if (!isSystem(account) && account.role !== AccountRole.DocGuest) {
if (!isOwner(account, ctx) || !isSpace) {
const spaces = await this.filterByDomain(domain, this.getAllAllowedSpaces(account, !isSpace))
if (
SERVER_SPACE_SECURITY === SpaceSecurityKind.lookup &&
!isSpace &&
(spaces.length > 5 || options?.lookup !== undefined)
) {
clearExtra = true
newOptions.extraPipeline = [
{
$lookup: {
from: 'space',
localField: 'space',
foreignField: '_id',
as: '__account_space',
pipeline: [
{
$match: {
$or: [{ members: account._id }, { _class: core.class.SystemSpace }]
}
},
{
$project: { _id: 1 }
}
]
}
},
{
$match: {
$or: {
'__account_space.0': { $exists: true },
space: { $in: [account._id, ...this.mainSpaces] }
}
}
}
]
} else if (!isOwner(account, ctx)) {
if (query[field] !== undefined) {
const res = await this.mergeQuery(account, query[field], domain, isSpace)
const res = await this.mergeQuery(spaces, query[field])
;(newQuery as any)[field] = res
if (typeof res === 'object') {
if (Array.isArray(res.$in) && res.$in.length === 1 && Object.keys(res).length === 1) {
;(newQuery as any)[field] = res.$in[0]
}
}
} else {
const spaces = await this.filterByDomain(domain, this.getAllAllowedSpaces(account, !isSpace))
if (spaces.length === 1) {
;(newQuery as any)[field] = spaces[0]
} else {
Expand All @@ -552,7 +597,12 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar
}
}
}
const findResult = await this.provideFindAll(ctx, _class, newQuery, options)
const findResult = await this.provideFindAll(ctx, _class, newQuery, newOptions)
if (clearExtra) {
for (const d of findResult) {
delete (d as any).__account_space
}
}
if (!isOwner(account, ctx) && account.role !== AccountRole.DocGuest) {
if (options?.lookup !== undefined) {
for (const object of findResult) {
Expand Down

0 comments on commit 98e53c7

Please sign in to comment.