import { AuthOrchestrationOperateScope, AuthOrchestrationReadScope, AuthRelayReadScope, AuthRelayWriteScope, WS_METHODS, WsRpcGroup,} from "@t3tools/contracts";import { describe, expect, it } from "@effect/vitest";import { RPC_REQUIRED_SCOPES, requiredScopeForRpcMethod } from "./RpcAuthorization.ts";describe("RPC authorization scopes", () => { it("declares exactly one scope for every RPC in the server group", () => { expect(new Set(Object.keys(RPC_REQUIRED_SCOPES))).toEqual(new Set(WsRpcGroup.requests.keys())); }); it("authorizes background policy reporting and observation deliberately", () => { expect(requiredScopeForRpcMethod(WS_METHODS.serverReportClientActivity)).toBe( AuthOrchestrationReadScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.serverReportHostPowerState)).toBe( AuthOrchestrationOperateScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.serverGetBackgroundPolicy)).toBe( AuthOrchestrationReadScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.subscribeBackgroundPolicy)).toBe( AuthOrchestrationReadScope, ); }); it("allows relay status reads without granting relay installation access", () => { expect(requiredScopeForRpcMethod(WS_METHODS.cloudGetRelayClientStatus)).toBe( AuthRelayReadScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.cloudInstallRelayClient)).toBe(AuthRelayWriteScope); }); it("requires permission to operate on a thread before uploading feedback", () => { expect(requiredScopeForRpcMethod(WS_METHODS.providerUploadFeedback)).toBe( AuthOrchestrationOperateScope, ); }); it("requires write access to import agent session history", () => { expect(requiredScopeForRpcMethod(WS_METHODS.agentSessionsScan)).toBe( AuthOrchestrationReadScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.agentSessionsImport)).toBe( AuthOrchestrationOperateScope, ); }); it("reads the reviewer menu under the same scope as the pull request it belongs to", () => { // The candidate list is a read like the detail beside it, and asking somebody for a review is // a write like every other pull request operation. expect(requiredScopeForRpcMethod(WS_METHODS.pullRequestsReviewerCandidates)).toBe( requiredScopeForRpcMethod(WS_METHODS.pullRequestsDetail), ); expect(requiredScopeForRpcMethod(WS_METHODS.pullRequestsRequestReviewers)).toBe( requiredScopeForRpcMethod(WS_METHODS.pullRequestsComment), ); }); it("rejects unknown RPC method names", () => { for (const method of ["server.notRegistered", "toString", "constructor"]) { expect(() => requiredScopeForRpcMethod(method)).toThrow( `RPC method ${method} has no declared authorization scope.`, ); } });});
import {
AuthOrchestrationOperateScope,
AuthOrchestrationReadScope,
AuthRelayReadScope,
AuthRelayWriteScope,
WS_METHODS,
WsRpcGroup,
} from "@t3tools/contracts";
import { describe, expect, it } from "@effect/vitest";
import { RPC_REQUIRED_SCOPES, requiredScopeForRpcMethod } from "./RpcAuthorization.ts";
describe("RPC authorization scopes", () => {
it("declares exactly one scope for every RPC in the server group", () => {
expect(new Set(Object.keys(RPC_REQUIRED_SCOPES))).toEqual(new Set(WsRpcGroup.requests.keys()));
});
it("authorizes background policy reporting and observation deliberately", () => {
expect(requiredScopeForRpcMethod(WS_METHODS.serverReportClientActivity)).toBe(
AuthOrchestrationReadScope,
);
expect(requiredScopeForRpcMethod(WS_METHODS.serverReportHostPowerState)).toBe(
AuthOrchestrationOperateScope,
);
expect(requiredScopeForRpcMethod(WS_METHODS.serverGetBackgroundPolicy)).toBe(
AuthOrchestrationReadScope,
);
expect(requiredScopeForRpcMethod(WS_METHODS.subscribeBackgroundPolicy)).toBe(
AuthOrchestrationReadScope,
);
});
it("allows relay status reads without granting relay installation access", () => {
expect(requiredScopeForRpcMethod(WS_METHODS.cloudGetRelayClientStatus)).toBe(
AuthRelayReadScope,
);
expect(requiredScopeForRpcMethod(WS_METHODS.cloudInstallRelayClient)).toBe(AuthRelayWriteScope);
});
it("requires permission to operate on a thread before uploading feedback", () => {
expect(requiredScopeForRpcMethod(WS_METHODS.providerUploadFeedback)).toBe(
AuthOrchestrationOperateScope,
);
});
it("requires write access to import agent session history", () => {
expect(requiredScopeForRpcMethod(WS_METHODS.agentSessionsScan)).toBe(
AuthOrchestrationReadScope,
);
expect(requiredScopeForRpcMethod(WS_METHODS.agentSessionsImport)).toBe(
AuthOrchestrationOperateScope,
);
});
it("reads the reviewer menu under the same scope as the pull request it belongs to", () => {
// The candidate list is a read like the detail beside it, and asking somebody for a review is
// a write like every other pull request operation.
expect(requiredScopeForRpcMethod(WS_METHODS.pullRequestsReviewerCandidates)).toBe(
requiredScopeForRpcMethod(WS_METHODS.pullRequestsDetail),
);
expect(requiredScopeForRpcMethod(WS_METHODS.pullRequestsRequestReviewers)).toBe(
requiredScopeForRpcMethod(WS_METHODS.pullRequestsComment),
);
});
it("rejects unknown RPC method names", () => {
for (const method of ["server.notRegistered", "toString", "constructor"]) {
expect(() => requiredScopeForRpcMethod(method)).toThrow(
`RPC method ${method} has no declared authorization scope.`,
);
}
});
});