From a0d1ad3abeb3875726b009794f40eb8046d61ec5 Mon Sep 17 00:00:00 2001 From: Lana Date: Mon, 5 Oct 2026 10:02:51 +0300 Subject: [PATCH] =?UTF-8?q?fix(access):=20=D1=83=D1=82=D0=BE=D1=87=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D1=82=D0=B8=D0=BF=20=D1=80=D0=BE=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D1=83=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=D0=B8=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D0=B2=D0=BD=D1=8B=D1=85=20=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: ZOL-15626 Co-Authored-By: Paperclip --- .../src/__tests__/company-user-directory-route.test.ts | 9 ++++++++- ui/src/api/access.ts | 1 + ui/src/lib/company-members.test.ts | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/server/src/__tests__/company-user-directory-route.test.ts b/server/src/__tests__/company-user-directory-route.test.ts index 5b1ab7d5aa4..783e6c63754 100644 --- a/server/src/__tests__/company-user-directory-route.test.ts +++ b/server/src/__tests__/company-user-directory-route.test.ts @@ -4,11 +4,13 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { accessRoutes } from "../routes/access.js"; import { errorHandler } from "../middleware/index.js"; +const { hasPermission } = vi.hoisted(() => ({ hasPermission: vi.fn() })); + vi.mock("../services/index.js", () => ({ accessService: () => ({ isInstanceAdmin: vi.fn(), canUser: vi.fn(), - hasPermission: vi.fn(), + hasPermission, }), agentService: () => ({ getById: vi.fn(), @@ -135,6 +137,7 @@ describe("GET /companies/:companyId/user-directory", () => { }); it("lets a company agent read roles without allowing membership management", async () => { + hasPermission.mockImplementation(async (_companyId, _principalType, _principalId, permissionKey) => permissionKey !== "users:manage_permissions"); const app = createApp({ type: "agent", agentId: "agent-1", companyId: "company-1", source: "agent_key" }); const res = await request(app).get("/api/companies/company-1/user-directory"); expect(res.status).toBe(200); @@ -144,6 +147,10 @@ describe("GET /companies/:companyId/user-directory", () => { ]); expect((await request(app).get("/api/companies/company-1/members")).status).toBe(403); expect((await request(app).patch("/api/companies/company-1/members/member-1").send({ membershipRole: "owner" })).status).toBe(403); + expect(hasPermission.mock.calls).toEqual([ + ["company-1", "agent", "agent-1", "users:manage_permissions"], + ["company-1", "agent", "agent-1", "users:manage_permissions"], + ]); }); it("rejects another company and unauthenticated callers", async () => { diff --git a/ui/src/api/access.ts b/ui/src/api/access.ts index 35c76898e0f..c77b212d3e1 100644 --- a/ui/src/api/access.ts +++ b/ui/src/api/access.ts @@ -149,6 +149,7 @@ export type CompanyMembersResponse = { export type CompanyUserDirectoryEntry = { principalId: string; status: "active"; + membershipRole: string | null; user: { id: string; email: string | null; name: string | null; image: string | null } | null; }; diff --git a/ui/src/lib/company-members.test.ts b/ui/src/lib/company-members.test.ts index c8b1e630ba6..f2231d491a6 100644 --- a/ui/src/lib/company-members.test.ts +++ b/ui/src/lib/company-members.test.ts @@ -130,6 +130,7 @@ describe("company-members helpers", () => { { principalId: "user-1", status: "active", + membershipRole: null, user: { id: "user-1", name: "Taylor", email: "taylor@example.com", image: null }, }, ];