Download src/gateway/server-methods/artifacts-session-resolution.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 8.25 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/gateway/server-methods/artifacts-session-resolution.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/gateway/server-methods/artifacts-session-resolution.test.ts
-
curl -L -o artifacts-session-resolution.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/gateway/server-methods/artifacts-session-resolution.test.ts
8.25 kB
| import { beforeEach, describe, expect, it, vi } from "vitest"; | |
| import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; | |
| import { ensureProfileForEmail } from "../../state/user-profiles.js"; | |
| import { withOpenClawTestState } from "../../test-utils/openclaw-test-state.js"; | |
| import { | |
| roleClient, | |
| rolePolicyConfig, | |
| sharingPolicyClient, | |
| } from "../session-sharing.test-utils.js"; | |
| import { | |
| ArtifactSessionResolutionError, | |
| resolveAuthorizedArtifactSession, | |
| } from "./artifacts-session-resolution.js"; | |
| import type { GatewayClient } from "./types.js"; | |
| const mocks = vi.hoisted(() => ({ | |
| getTaskSession: vi.fn(), | |
| resolveRunSession: vi.fn(), | |
| })); | |
| vi.mock("../../tasks/task-status-access.js", () => ({ | |
| getTaskSessionLookupByIdForStatus: mocks.getTaskSession, | |
| })); | |
| vi.mock("../server-session-key.js", () => ({ | |
| resolveSessionKeyForRun: mocks.resolveRunSession, | |
| })); | |
| function identifiedClient(scopes: string[], profileId = "viewer@example.com"): GatewayClient { | |
| return { | |
| connect: { | |
| minProtocol: 1, | |
| maxProtocol: 1, | |
| client: { id: "openclaw-control-ui", version: "test", platform: "test", mode: "webchat" }, | |
| role: "operator", | |
| scopes, | |
| }, | |
| authenticatedUserId: "viewer@example.com", | |
| authenticatedUserProfile: { | |
| profileId, | |
| displayName: null, | |
| hasAvatar: false, | |
| updatedAt: 1, | |
| }, | |
| }; | |
| } | |
| describe("artifact session authorization", () => { | |
| beforeEach(() => vi.clearAllMocks()); | |
| it("denies direct and indirect incognito selectors while preserving admin access", async () => { | |
| await withOpenClawTestState({ scenario: "minimal" }, async () => { | |
| const sessionKey = "agent:main:dashboard:incognito-artifacts"; | |
| const cfg = { agents: { list: [{ id: "main", default: true }] } }; | |
| await upsertSessionEntryCore( | |
| { agentId: "main", sessionKey }, | |
| { | |
| sessionId: "session-incognito-artifacts", | |
| updatedAt: 1, | |
| incognito: true, | |
| visibility: "shared", | |
| }, | |
| ); | |
| mocks.getTaskSession.mockReturnValue({ | |
| requesterSessionKey: sessionKey, | |
| requesterAgentId: "main", | |
| ownerKey: sessionKey, | |
| }); | |
| mocks.resolveRunSession.mockReturnValue(sessionKey); | |
| const viewer = identifiedClient(["operator.read"]); | |
| expect(() => | |
| resolveAuthorizedArtifactSession( | |
| { sessionKey: "dashboard:incognito-artifacts", agentId: "main" }, | |
| cfg, | |
| viewer, | |
| ), | |
| ).toThrow('Incognito session "dashboard:incognito-artifacts" was not found.'); | |
| for (const query of [{ taskId: "task-private" }, { runId: "run-private" }]) { | |
| try { | |
| resolveAuthorizedArtifactSession(query, cfg, viewer); | |
| throw new Error("expected incognito artifact selector to be denied"); | |
| } catch (error) { | |
| expect(error).toBeInstanceOf(ArtifactSessionResolutionError); | |
| expect((error as ArtifactSessionResolutionError).shape).toMatchObject({ | |
| message: "no session found for artifact query", | |
| details: { type: "artifact_scope_not_found" }, | |
| }); | |
| } | |
| } | |
| expect( | |
| resolveAuthorizedArtifactSession( | |
| { sessionKey: "dashboard:incognito-artifacts", agentId: "main" }, | |
| cfg, | |
| identifiedClient(["operator.admin"]), | |
| ), | |
| ).toMatchObject({ sessionKey }); | |
| }); | |
| }); | |
| it.each([ | |
| { name: "draft without roles", visibility: "draft", cfg: {}, role: undefined }, | |
| { name: "draft without runtime config", visibility: "draft", cfg: undefined, role: undefined }, | |
| { | |
| name: "shared with a none role", | |
| visibility: "shared", | |
| cfg: rolePolicyConfig(), | |
| role: "none", | |
| }, | |
| { | |
| name: "draft with a write role", | |
| visibility: "draft", | |
| cfg: rolePolicyConfig(), | |
| role: "write", | |
| }, | |
| ] as const)( | |
| "hides $name artifacts behind direct, run, and task selectors", | |
| async ({ visibility, cfg, role }) => { | |
| await withOpenClawTestState({ scenario: "minimal" }, async () => { | |
| const viewerProfile = ensureProfileForEmail("viewer@example.com"); | |
| const ownerProfile = ensureProfileForEmail("owner@example.com"); | |
| const sessionKey = "agent:main:foreign-artifacts"; | |
| await upsertSessionEntryCore( | |
| { agentId: "main", sessionKey }, | |
| { | |
| sessionId: "session-foreign-artifacts", | |
| updatedAt: 1, | |
| createdActor: { type: "human", source: "profile", id: ownerProfile.id }, | |
| visibility, | |
| }, | |
| ); | |
| mocks.getTaskSession.mockImplementation((taskId: string) => | |
| taskId === "task-run" | |
| ? { runId: "run-foreign", agentId: "main" } | |
| : { | |
| requesterSessionKey: sessionKey, | |
| requesterAgentId: "main", | |
| ownerKey: sessionKey, | |
| }, | |
| ); | |
| mocks.resolveRunSession.mockReturnValue(sessionKey); | |
| const viewer = role | |
| ? roleClient(role, "artifact-viewer") | |
| : identifiedClient(["operator.read"], viewerProfile.id); | |
| for (const query of [ | |
| { sessionKey }, | |
| { runId: "run-foreign" }, | |
| { taskId: "task-foreign" }, | |
| { taskId: "task-run" }, | |
| ]) { | |
| expect(() => resolveAuthorizedArtifactSession(query, cfg, viewer)).toThrowError( | |
| expect.objectContaining({ | |
| shape: { | |
| code: "INVALID_REQUEST", | |
| message: "no session found for artifact query", | |
| details: { type: "artifact_scope_not_found" }, | |
| }, | |
| }), | |
| ); | |
| } | |
| expect( | |
| resolveAuthorizedArtifactSession( | |
| { sessionKey }, | |
| cfg, | |
| identifiedClient(["operator.read"], ownerProfile.id), | |
| ), | |
| ).toMatchObject({ sessionKey }); | |
| expect( | |
| resolveAuthorizedArtifactSession( | |
| { runId: "run-foreign" }, | |
| cfg, | |
| identifiedClient(["operator.admin"], viewerProfile.id), | |
| ), | |
| ).toMatchObject({ sessionKey }); | |
| }); | |
| }, | |
| ); | |
| it.each(["shared", "read-only", "suggest"] as const)( | |
| "allows reading foreign %s artifacts without participation", | |
| async (visibility) => { | |
| await withOpenClawTestState({ scenario: "minimal" }, async () => { | |
| const owner = ensureProfileForEmail("artifact-owner@example.test"); | |
| const viewer = roleClient("view", "artifact-reader"); | |
| const sessionKey = "agent:main:readable-artifacts"; | |
| await upsertSessionEntryCore( | |
| { agentId: "main", sessionKey }, | |
| { | |
| sessionId: "session-readable-artifacts", | |
| updatedAt: 1, | |
| createdActor: { type: "human", source: "profile", id: owner.id }, | |
| visibility, | |
| }, | |
| ); | |
| for (const cfg of [undefined, {}, rolePolicyConfig()]) { | |
| expect(resolveAuthorizedArtifactSession({ sessionKey }, cfg, viewer)).toMatchObject({ | |
| sessionKey, | |
| }); | |
| } | |
| }); | |
| }, | |
| ); | |
| it("preserves solo and system reads while denying identityless reads with roles", async () => { | |
| await withOpenClawTestState({ scenario: "minimal" }, async () => { | |
| const sessionKey = "agent:main:solo-artifacts"; | |
| await upsertSessionEntryCore( | |
| { agentId: "main", sessionKey }, | |
| { sessionId: "session-solo-artifacts", updatedAt: 1, visibility: "draft" }, | |
| ); | |
| const identityless = sharingPolicyClient({}); | |
| for (const client of [null, identityless, sharingPolicyClient({ user: "gateway-owner" })]) { | |
| expect(resolveAuthorizedArtifactSession({ sessionKey }, {}, client)).toMatchObject({ | |
| sessionKey, | |
| }); | |
| } | |
| const cfg = rolePolicyConfig(); | |
| expect(() => resolveAuthorizedArtifactSession({ sessionKey }, cfg, identityless)).toThrow( | |
| "no session found for artifact query", | |
| ); | |
| const system: GatewayClient = { | |
| ...identityless, | |
| internal: { operatorRoleActor: { kind: "system" } }, | |
| }; | |
| expect(resolveAuthorizedArtifactSession({ sessionKey }, cfg, system)).toMatchObject({ | |
| sessionKey, | |
| }); | |
| }); | |
| }); | |
| }); | |