diff --git a/packages/platform/src/platform.ts b/packages/platform/src/platform.ts index 1dee622126..a3204d91df 100644 --- a/packages/platform/src/platform.ts +++ b/packages/platform/src/platform.ts @@ -147,6 +147,7 @@ export default plugin(platformId, { InternalServerError: '' as StatusCode, MaintenanceWarning: '' as StatusCode<{ time: number }>, AccountNotFound: '' as StatusCode<{ account?: string }>, + AccountMismatch: '' as StatusCode<{ account?: string, requiredAccount?: string }>, AccountNotConfirmed: '' as StatusCode, WorkspaceNotFound: '' as StatusCode<{ workspaceUuid?: string, workspaceName?: string, workspaceUrl?: string }>, WorkspaceArchived: '' as StatusCode<{ workspaceUuid: string }>, diff --git a/server/middleware/src/identity.ts b/server/middleware/src/identity.ts new file mode 100644 index 0000000000..c947268a20 --- /dev/null +++ b/server/middleware/src/identity.ts @@ -0,0 +1,63 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import core, { MeasureContext, Tx, systemAccountUuid, type SessionData, type TxApplyIf } from '@hcengineering/core' +import platform, { PlatformError, Severity, Status } from '@hcengineering/platform' +import { BaseMiddleware, Middleware, TxMiddlewareResult, type PipelineContext } from '@hcengineering/server-core' + +/** + * @public + */ +export class IdentityMiddleware extends BaseMiddleware implements Middleware { + private constructor (context: PipelineContext, next?: Middleware) { + super(context, next) + } + + static async create ( + ctx: MeasureContext, + context: PipelineContext, + next: Middleware | undefined + ): Promise { + return new IdentityMiddleware(context, next) + } + + tx (ctx: MeasureContext, txes: Tx[]): Promise { + const account = ctx.contextData.account + if (account.uuid === systemAccountUuid) { + // TODO: We need to enhance allowed list in case of user service, on behalf of user activities. + + // We pass for system accounts and services. + return this.provideTx(ctx, txes) + } + function checkTx (tx: Tx): void { + const mxAccount = ctx.contextData.socialStringsToUsers.get(tx.modifiedBy) + if (mxAccount === undefined || mxAccount !== account.uuid) { + throw new PlatformError( + new Status(Severity.ERROR, platform.status.AccountMismatch, { + account: account.uuid, + requiredAccount: mxAccount + }) + ) + } + } + for (const tx of txes) { + checkTx(tx) + if (tx._class === core.class.TxApplyIf) { + const atx = tx as TxApplyIf + atx.txes.forEach(checkTx) + } + } + return this.provideTx(ctx, txes) + } +} diff --git a/server/middleware/src/index.ts b/server/middleware/src/index.ts index e4a4c71f3b..793e4b5509 100644 --- a/server/middleware/src/index.ts +++ b/server/middleware/src/index.ts @@ -36,3 +36,4 @@ export * from './spaceSecurity' export * from './triggers' export * from './txPush' export * from './queue' +export * from './identity' diff --git a/server/server-pipeline/src/pipeline.ts b/server/server-pipeline/src/pipeline.ts index b1f3082f95..34c6c43ee2 100644 --- a/server/server-pipeline/src/pipeline.ts +++ b/server/server-pipeline/src/pipeline.ts @@ -23,6 +23,7 @@ import { DomainFindMiddleware, DomainTxMiddleware, FullTextMiddleware, + IdentityMiddleware, LiveQueryMiddleware, LookupMiddleware, LowLevelMiddleware, @@ -116,6 +117,7 @@ export function createServerPipeline ( const middlewares: MiddlewareCreator[] = [ LookupMiddleware.create, + IdentityMiddleware.create, ModifiedMiddleware.create, PrivateMiddleware.create, NotificationsMiddleware.create,