mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-31 12:19:47 +02:00
Fix controlled document print
Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
@@ -987,6 +987,13 @@ export interface ClassCollaborators<T extends Doc> extends Doc {
|
||||
fields: (keyof T)[] // PersonId | Ref<Employee> | PersonId[] | Ref<Employee>[]
|
||||
provideSecurity?: boolean // If true, will provide security for collaborators
|
||||
provideAttachedSecurity?: boolean // If true, will provide security for collaborators of attached doc
|
||||
/**
|
||||
* If true, guest / read-only-guest `findAll` queries for this class are intersected with
|
||||
* document ids the account has as {@link Collaborator} (middleware), in addition to DB-level
|
||||
* checks when {@link provideSecurity} is enabled. Prefer this over overloading `provideSecurity`
|
||||
* when only meeting-minutes-style id filtering is needed.
|
||||
*/
|
||||
restrictGuestReadToCollaborators?: boolean
|
||||
}
|
||||
|
||||
export interface Collaborator extends AttachedDoc {
|
||||
|
||||
@@ -27,6 +27,7 @@ import core, {
|
||||
type FindResult,
|
||||
generateId,
|
||||
getClassCollaborators,
|
||||
mergeQueries,
|
||||
type LookupData,
|
||||
type MeasureContext,
|
||||
type ObjQueryType,
|
||||
@@ -613,19 +614,6 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar
|
||||
return domain === 'tx' ? 'objectSpace' : domain === 'space' ? '_id' : 'space'
|
||||
}
|
||||
|
||||
private mergeDocIdRestriction<T extends Doc>(query: DocumentQuery<T>, allowed: Ref<T>[]): DocumentQuery<T> {
|
||||
const allowedIds: DocumentQuery<T>['_id'] = { $in: allowed.length === 0 ? [] : allowed }
|
||||
const prevId = query._id
|
||||
if (prevId === undefined) {
|
||||
return { ...query, _id: allowedIds }
|
||||
}
|
||||
type WithAnd = DocumentQuery<T> & { $and?: DocumentQuery<T>[] }
|
||||
const { _id: _drop, $and, ...rest } = query as WithAnd
|
||||
const andParts: DocumentQuery<T>[] = [...($and ?? []), { _id: prevId }, { _id: allowedIds }]
|
||||
const merged: DocumentQuery<T> = { ...rest, $and: andParts }
|
||||
return merged
|
||||
}
|
||||
|
||||
private async applyGuestCollaboratorReadRestriction<T extends Doc>(
|
||||
ctx: MeasureContext<SessionData>,
|
||||
_class: Ref<Class<T>>,
|
||||
@@ -642,7 +630,7 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar
|
||||
}
|
||||
|
||||
const collabSec = getClassCollaborators(this.context.modelDb, this.context.hierarchy, _class)
|
||||
if (collabSec?.provideSecurity !== true) {
|
||||
if (collabSec?.restrictGuestReadToCollaborators !== true) {
|
||||
return query
|
||||
}
|
||||
|
||||
@@ -658,7 +646,9 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar
|
||||
{ projection: { attachedTo: 1 }, limit: 10_000 }
|
||||
)) as Collaborator[]
|
||||
const allowed = collabs.map((c) => c.attachedTo) as Ref<T>[]
|
||||
return this.mergeDocIdRestriction(query, allowed)
|
||||
const allowedIds: DocumentQuery<T>['_id'] = { $in: allowed.length === 0 ? [] : allowed }
|
||||
const restriction: DocumentQuery<T> = { _id: allowedIds }
|
||||
return mergeQueries(query, restriction)
|
||||
}
|
||||
|
||||
override async findAll<T extends Doc>(
|
||||
|
||||
@@ -60,7 +60,7 @@ function makeCtx (account: Account): MeasureContext<SessionData> {
|
||||
|
||||
function makeMiddleware (
|
||||
role: AccountRole,
|
||||
opts: { provideSecurity: boolean } = { provideSecurity: false }
|
||||
opts: { restrictGuestReadToCollaborators: boolean } = { restrictGuestReadToCollaborators: false }
|
||||
): { mw: SpaceSecurityMiddleware, account: Account, calls: Array<{ cls: Ref<Class<Doc>>, query: any }> } {
|
||||
const account = makeAccount(role)
|
||||
const calls: Array<{ cls: Ref<Class<Doc>>, query: any }> = []
|
||||
@@ -70,7 +70,8 @@ function makeMiddleware (
|
||||
space: core.space.Model,
|
||||
attachedTo: DOC_CLASS,
|
||||
fields: ['createdBy'],
|
||||
provideSecurity: opts.provideSecurity,
|
||||
provideSecurity: false,
|
||||
restrictGuestReadToCollaborators: opts.restrictGuestReadToCollaborators,
|
||||
modifiedOn: Date.now(),
|
||||
modifiedBy: core.account.System
|
||||
} as unknown as ClassCollaborators<Doc>
|
||||
@@ -144,8 +145,8 @@ function makeMiddleware (
|
||||
}
|
||||
|
||||
describe('SpaceSecurityMiddleware guest collaborator read restriction', () => {
|
||||
it('applies collaborator _id filter for guest when provideSecurity is enabled', async () => {
|
||||
const { mw, account, calls } = makeMiddleware(AccountRole.Guest, { provideSecurity: true })
|
||||
it('applies collaborator _id filter for guest when restrictGuestReadToCollaborators is enabled', async () => {
|
||||
const { mw, account, calls } = makeMiddleware(AccountRole.Guest, { restrictGuestReadToCollaborators: true })
|
||||
const ctx = makeCtx(account)
|
||||
|
||||
await mw.findAll(ctx, DOC_CLASS, { title: 'Meeting minutes' })
|
||||
@@ -160,7 +161,7 @@ describe('SpaceSecurityMiddleware guest collaborator read restriction', () => {
|
||||
})
|
||||
|
||||
it('keeps query unchanged for regular user', async () => {
|
||||
const { mw, account, calls } = makeMiddleware(AccountRole.User, { provideSecurity: true })
|
||||
const { mw, account, calls } = makeMiddleware(AccountRole.User, { restrictGuestReadToCollaborators: true })
|
||||
const ctx = makeCtx(account)
|
||||
|
||||
await mw.findAll(ctx, DOC_CLASS, { title: 'Meeting minutes' })
|
||||
|
||||
@@ -436,6 +436,7 @@ export class TClassCollaborators extends TDoc implements ClassCollaborators<Doc>
|
||||
fields!: (keyof Doc)[]
|
||||
provideSecurity?: boolean
|
||||
provideAttachedSecurity?: boolean
|
||||
restrictGuestReadToCollaborators?: boolean
|
||||
}
|
||||
|
||||
@Model(core.class.Collaborator, core.class.Doc, DOMAIN_COLLABORATOR)
|
||||
|
||||
@@ -644,7 +644,8 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc<ClassCollaborators<MeetingMinutes>>(core.class.ClassCollaborators, core.space.Model, {
|
||||
attachedTo: love.class.MeetingMinutes,
|
||||
fields: ['createdBy'],
|
||||
provideSecurity: true
|
||||
provideSecurity: true,
|
||||
restrictGuestReadToCollaborators: true
|
||||
})
|
||||
|
||||
builder.mixin(love.class.Room, core.class.Class, core.mixin.IndexConfiguration, {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
//
|
||||
|
||||
import contact from '@hcengineering/contact'
|
||||
import { TxOperations, type Ref, type Space } from '@hcengineering/core'
|
||||
import { DOMAIN_MODEL, TxOperations, type Ref, type Space } from '@hcengineering/core'
|
||||
import drive from '@hcengineering/drive'
|
||||
import {
|
||||
MeetingStatus,
|
||||
@@ -179,6 +179,21 @@ export const loveOperation: MigrateOperation = {
|
||||
func: async (client) => {
|
||||
await client.reindex(DOMAIN_MEETING_MINUTES, [love.class.MeetingMinutes])
|
||||
}
|
||||
},
|
||||
{
|
||||
state: 'meeting-minutes-restrict-guest-read-to-collaborators',
|
||||
mode: 'upgrade',
|
||||
func: async (client: MigrationClient) => {
|
||||
await client.update(
|
||||
DOMAIN_MODEL,
|
||||
{
|
||||
_class: core.class.ClassCollaborators,
|
||||
attachedTo: love.class.MeetingMinutes,
|
||||
restrictGuestReadToCollaborators: { $exists: false }
|
||||
},
|
||||
{ restrictGuestReadToCollaborators: true }
|
||||
)
|
||||
}
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user