From 58fd7951e09d2617ccc8236b485a6cdc9ace65c6 Mon Sep 17 00:00:00 2001 From: andz-bb Date: Tue, 16 Jun 2026 15:06:53 +0100 Subject: [PATCH] tighten tenant user update checks --- .../src/api/controllers/global/users.ts | 4 ++ .../src/api/routes/global/tests/users.spec.ts | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 847bc575ce..ec9b6ecfd1 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -109,6 +109,10 @@ export const save = async (ctx: UserCtx) => { export const changeTenantOwnerEmail = async ( ctx: Ctx ) => { + if (!ctx.internal) { + ctx.throw(403, "Unauthorized") + } + const { newAccountEmail, originalEmail, tenantIds } = ctx.request.body try { for (const tenantId of tenantIds) { diff --git a/packages/worker/src/api/routes/global/tests/users.spec.ts b/packages/worker/src/api/routes/global/tests/users.spec.ts index 9fddfa12b2..a676e8d86f 100644 --- a/packages/worker/src/api/routes/global/tests/users.spec.ts +++ b/packages/worker/src/api/routes/global/tests/users.spec.ts @@ -1528,5 +1528,43 @@ describe("/api/global/users", () => { .set(config.defaultHeaders()) .expect(403) }) + + it("should reject authenticated users on self-hosted", async () => { + const originalEmail = `original-${structures.uuid()}@example.com` + const newEmail = `new-${structures.uuid()}@example.com` + const tenantId = config.getTenantId() + + const tenantUser = await config.doInTenant(async () => { + return await userSdk.db.save( + structures.users.user({ + email: originalEmail, + tenantId, + roles: {}, + }), + { requirePassword: false, isAccountHolder: true } + ) + }) + + config.selfHosted() + try { + await config.request + .put(`/api/global/users/tenant/owner`) + .send({ + newAccountEmail: newEmail, + originalEmail, + tenantIds: [tenantId], + }) + .set(config.defaultHeaders()) + .expect(403) + } finally { + config.cloudHosted() + } + + const unchangedUser = await config.doInTenant(async () => { + return await userSdk.db.getUser(tenantUser._id!) + }) + + expect(unchangedUser!.email).toBe(originalEmail) + }) }) })