469 lines · 17.6 KB · TypeScript
RawDownload
import { randomBytes } from "node:crypto"; import { expect } from "@effect/vitest";import { Effect } from "effect";import { composePluginApi } from "@executor-js/api/server";import { openApiHttpPlugin } from "@executor-js/plugin-openapi/api";import {  AuthTemplateSlug,  ConnectionName,  IntegrationSlug,  OAuthClientSlug,} from "@executor-js/sdk/shared";import { serveOAuthTestServer } from "@executor-js/sdk/testing"; import type { Identity, Target as TargetShape } from "./target";import { Api } from "./services"; const api = composePluginApi([openApiHttpPlugin()] as const);const TEMPLATE = AuthTemplateSlug.make("apiKey"); const unique = (prefix: string): string => `${prefix}-${randomBytes(4).toString("hex")}`; const specPayload = (slug: IntegrationSlug) => ({  spec: {    kind: "blob" as const,    value: JSON.stringify({      openapi: "3.0.3",      info: { title: `Permissions ${slug}`, version: "1.0.0" },      paths: {        "/ping": {          get: {            operationId: "ping",            tags: ["ping"],            responses: { "200": { description: "pong" } },          },        },      },    }),  },  slug,  baseUrl: "http://127.0.0.1:59999",  authenticationTemplate: [    {      slug: TEMPLATE,      type: "apiKey" as const,      headers: { authorization: ["Bearer ", { type: "variable" as const, name: "token" }] },    },  ],}); const request = (  target: TargetShape,  identity: Identity,  method: string,  path: string,  body?: unknown,): Effect.Effect<Response> =>  Effect.promise(() =>    fetch(new URL(path, target.baseUrl), {      method,      headers: {        ...(identity.headers ?? {}),        origin: new URL(target.baseUrl).origin,        ...(body === undefined ? {} : { "content-type": "application/json" }),      },      ...(body === undefined ? {} : { body: JSON.stringify(body) }),    }),  ); const expectForbidden = (  target: TargetShape,  member: Identity,  label: string,  method: string,  path: string,  body?: unknown,): Effect.Effect<void> =>  request(target, member, method, path, body).pipe(    Effect.map((response) => {      expect(response.status, `${label} is denied at the HTTP boundary`).toBe(403);    }),  ); /** Exercise the complete workspace-write authorization matrix for one host boot. */export const workspaceWritePermissions = (target: TargetShape, admin: Identity, member: Identity) =>  Effect.scoped(    Effect.gen(function* () {      const { client: makeClient } = yield* Api;      const oauth = yield* serveOAuthTestServer();      const adminClient = yield* makeClient(api, admin);      const memberClient = yield* makeClient(api, member);      const prefix = unique("write-matrix");       const seedIntegration = IntegrationSlug.make(`${prefix}-seed`);      const deniedIntegration = IntegrationSlug.make(`${prefix}-denied`);      const adminIntegration = IntegrationSlug.make(`${prefix}-admin`);      const connectionSuffix = randomBytes(4).toString("hex");      const seedConnection = ConnectionName.make(`seed${connectionSuffix}`);      const deniedConnection = ConnectionName.make(`denied${connectionSuffix}`);      const personalConnection = ConnectionName.make(`personal${connectionSuffix}`);      const adminConnection = ConnectionName.make(`admin${connectionSuffix}`);      const seedClient = OAuthClientSlug.make(`${prefix}-seed`);      const deniedManualClient = OAuthClientSlug.make(`${prefix}-denied-manual`);      const deniedDcrClient = OAuthClientSlug.make(`${prefix}-denied-dcr`);      const adminManualClient = OAuthClientSlug.make(`${prefix}-admin-manual`);      const adminDcrClient = OAuthClientSlug.make(`${prefix}-admin-dcr`);      const policyPrefix = `executor.${prefix}`;      let registeredDynamicClient: OAuthClientSlug | undefined;       yield* adminClient.openapi.addSpec({ payload: specPayload(seedIntegration) });      yield* adminClient.connections.create({        payload: {          owner: "org",          name: seedConnection,          integration: seedIntegration,          template: TEMPLATE,          value: `${prefix}-seed-token`,        },      });      yield* adminClient.oauth.createClient({        payload: {          owner: "org",          slug: seedClient,          grant: "authorization_code",          authorizationUrl: oauth.authorizationEndpoint,          tokenUrl: oauth.tokenEndpoint,          clientId: `${prefix}-seed-id`,          clientSecret: `${prefix}-seed-secret`,        },      });      const seedPolicy = yield* adminClient.policies.create({        payload: { owner: "org", pattern: `${policyPrefix}.seed`, action: "approve" },      });      const seedIntegrationBefore = yield* adminClient.integrations.get({        params: { slug: seedIntegration },      });       const cleanup = Effect.gen(function* () {        const policies = yield* adminClient.policies.list();        yield* Effect.forEach(          policies.filter((policy) => policy.pattern.startsWith(policyPrefix)),          (policy) =>            adminClient.policies              .remove({ params: { policyId: policy.id }, payload: { owner: "org" } })              .pipe(Effect.ignore({ log: false })),          { discard: true },        );        const clients = yield* adminClient.oauth.listClients();        yield* Effect.forEach(          clients.filter((client) => String(client.slug).startsWith(prefix)),          (client) =>            adminClient.oauth              .removeClient({ params: { slug: client.slug }, payload: { owner: client.owner } })              .pipe(Effect.ignore({ log: false })),          { discard: true },        );        if (registeredDynamicClient !== undefined) {          yield* adminClient.oauth            .removeClient({              params: { slug: registeredDynamicClient },              payload: { owner: "org" },            })            .pipe(Effect.ignore({ log: false }));        }        for (const name of [seedConnection, adminConnection]) {          yield* adminClient.connections            .remove({ params: { owner: "org", integration: seedIntegration, name } })            .pipe(Effect.ignore({ log: false }));        }        yield* memberClient.connections          .remove({            params: { owner: "user", integration: seedIntegration, name: personalConnection },          })          .pipe(Effect.ignore({ log: false }));        for (const slug of [seedIntegration, deniedIntegration, adminIntegration]) {          yield* adminClient.openapi            .removeSpec({ params: { slug } })            .pipe(Effect.ignore({ log: false }));        }      }).pipe(Effect.ignore({ log: false }));       yield* Effect.ensuring(        Effect.gen(function* () {          const connectionPath = `/api/connections/org/${seedIntegration}/${seedConnection}`;          yield* expectForbidden(            target,            member,            "member org connection create",            "POST",            "/api/connections",            {              owner: "org",              name: deniedConnection,              integration: seedIntegration,              template: TEMPLATE,              value: `${prefix}-denied-token`,            },          );          yield* expectForbidden(            target,            member,            "member org connection update",            "PATCH",            connectionPath,            {              description: "denied",            },          );          yield* expectForbidden(            target,            member,            "member org connection remove",            "DELETE",            connectionPath,          );          yield* expectForbidden(            target,            member,            "member org connection refresh",            "POST",            `${connectionPath}/refresh`,          );          const connectionsAfterDenial = yield* adminClient.connections.list({            query: { owner: "org", integration: seedIntegration },          });          expect(            connectionsAfterDenial.some((connection) => connection.name === deniedConnection),            "the denied connection create persisted nothing",          ).toBe(false);          expect(            connectionsAfterDenial.find((connection) => connection.name === seedConnection),            "the denied update/remove left the seeded connection unchanged",          ).toMatchObject({ description: null, owner: "org" });           yield* expectForbidden(            target,            member,            "member integration create",            "POST",            "/api/openapi/specs",            specPayload(deniedIntegration),          );          yield* expectForbidden(            target,            member,            "member integration update",            "PATCH",            `/api/integrations/${seedIntegration}`,            { description: "denied" },          );          yield* expectForbidden(            target,            member,            "member integration remove",            "DELETE",            `/api/openapi/integrations/${seedIntegration}`,          );          yield* expectForbidden(            target,            member,            "member integration health-check update",            "PUT",            `/api/integrations/${seedIntegration}/health-check`,            { spec: { operation: "ping.ping" } },          );          const integrationsAfterDenial = yield* adminClient.integrations.list();          expect(            integrationsAfterDenial.some((integration) => integration.slug === deniedIntegration),            "the denied integration create persisted nothing",          ).toBe(false);          expect(            yield* adminClient.integrations.get({ params: { slug: seedIntegration } }),            "the denied integration mutations left the seeded integration unchanged",          ).toEqual(seedIntegrationBefore);           const manualPayload = {            owner: "org" as const,            slug: deniedManualClient,            grant: "authorization_code" as const,            authorizationUrl: oauth.authorizationEndpoint,            tokenUrl: oauth.tokenEndpoint,            clientId: `${prefix}-denied-id`,            clientSecret: `${prefix}-denied-secret`,          };          yield* expectForbidden(            target,            member,            "member manual OAuth client create",            "POST",            "/api/oauth/clients",            manualPayload,          );          yield* expectForbidden(            target,            member,            "member dynamic OAuth client create",            "POST",            "/api/oauth/clients/register-dynamic",            {              owner: "org",              slug: deniedDcrClient,              registrationEndpoint: oauth.registrationEndpoint,              authorizationUrl: oauth.authorizationEndpoint,              tokenUrl: oauth.tokenEndpoint,              scopes: ["read"],            },          );          const clientsAfterDenial = yield* adminClient.oauth.listClients();          expect(            clientsAfterDenial.some(              (client) => client.slug === deniedManualClient || client.slug === deniedDcrClient,            ),            "the denied OAuth client creates persisted nothing",          ).toBe(false);          expect(            clientsAfterDenial.some((client) => client.slug === seedClient),            "the seeded OAuth client survived denied creates",          ).toBe(true);           yield* expectForbidden(            target,            member,            "member org policy create",            "POST",            "/api/policies",            { owner: "org", pattern: `${policyPrefix}.denied`, action: "approve" },          );          yield* expectForbidden(            target,            member,            "member org policy update",            "PATCH",            `/api/policies/${seedPolicy.id}`,            { owner: "org", action: "block" },          );          yield* expectForbidden(            target,            member,            "member org policy remove",            "DELETE",            `/api/policies/${seedPolicy.id}`,            { owner: "org" },          );          const policiesAfterDenial = yield* adminClient.policies.list();          expect(            policiesAfterDenial.some((policy) => policy.pattern === `${policyPrefix}.denied`),            "the denied policy create persisted nothing",          ).toBe(false);          expect(            policiesAfterDenial.find((policy) => policy.id === seedPolicy.id),            "the denied policy update/remove left the seeded policy unchanged",          ).toMatchObject({ action: "approve", pattern: `${policyPrefix}.seed` });           const personal = yield* memberClient.connections.create({            payload: {              owner: "user",              name: personalConnection,              integration: seedIntegration,              template: TEMPLATE,              value: `${prefix}-personal-token`,            },          });          expect(personal.owner, "a member may create a Personal connection").toBe("user");          const personalUpdated = yield* memberClient.connections.update({            params: { owner: "user", integration: seedIntegration, name: personalConnection },            payload: { identityLabel: "Personal updated" },          });          expect(            personalUpdated.identityLabel,            "a member may update their Personal connection",          ).toBe("Personal updated");          const personalRemoved = yield* memberClient.connections.remove({            params: { owner: "user", integration: seedIntegration, name: personalConnection },          });          expect(personalRemoved.removed, "a member may remove their Personal connection").toBe(            true,          );           const adminCreated = yield* adminClient.connections.create({            payload: {              owner: "org",              name: adminConnection,              integration: seedIntegration,              template: TEMPLATE,              value: `${prefix}-admin-token`,            },          });          expect(adminCreated.owner, "an admin may create a Workspace connection").toBe("org");          const adminUpdated = yield* adminClient.connections.update({            params: { owner: "org", integration: seedIntegration, name: adminConnection },            payload: { description: "Admin updated" },          });          expect(adminUpdated.description, "an admin may PATCH a Workspace connection").toBe(            "Admin updated",          );          const refreshed = yield* adminClient.connections.refresh({            params: { owner: "org", integration: seedIntegration, name: adminConnection },          });          expect(refreshed.length, "an admin may refresh a Workspace connection").toBeGreaterThan(            0,          );          const adminRemoved = yield* adminClient.connections.remove({            params: { owner: "org", integration: seedIntegration, name: adminConnection },          });          expect(adminRemoved.removed, "an admin may remove a Workspace connection").toBe(true);           yield* adminClient.openapi.addSpec({ payload: specPayload(adminIntegration) });          const integrationUpdated = yield* adminClient.integrations.update({            params: { slug: adminIntegration },            payload: { description: "Admin integration updated" },          });          expect(integrationUpdated.description, "an admin may update an integration").toBe(            "Admin integration updated",          );          const healthSet = yield* adminClient.integrations.healthCheckSet({            params: { slug: adminIntegration },            payload: { spec: { operation: "ping.ping" } },          });          expect(healthSet.ok, "an admin may set an integration health check").toBe(true);          yield* adminClient.openapi.removeSpec({ params: { slug: adminIntegration } });           yield* adminClient.oauth.createClient({            payload: {              ...manualPayload,              slug: adminManualClient,              clientId: `${prefix}-admin-id`,              clientSecret: `${prefix}-admin-secret`,            },          });          const dynamic = yield* adminClient.oauth.registerDynamic({            payload: {              owner: "org",              slug: adminDcrClient,              registrationEndpoint: oauth.registrationEndpoint,              authorizationUrl: oauth.authorizationEndpoint,              tokenUrl: oauth.tokenEndpoint,              scopes: ["read"],            },          });          registeredDynamicClient = dynamic.client;          const dynamicRow = (yield* adminClient.oauth.listClients()).find(            (client) => client.slug === dynamic.client,          );          expect(            dynamicRow,            "an admin may dynamically register a persisted Workspace OAuth client",          ).toMatchObject({            owner: "org",            origin: { kind: "dynamic_client_registration" },          });           const adminPolicy = yield* adminClient.policies.create({            payload: { owner: "org", pattern: `${policyPrefix}.admin`, action: "approve" },          });          const policyUpdated = yield* adminClient.policies.update({            params: { policyId: adminPolicy.id },            payload: { owner: "org", action: "block" },          });          expect(policyUpdated.action, "an admin may update a Workspace tool policy").toBe("block");          const policyRemoved = yield* adminClient.policies.remove({            params: { policyId: adminPolicy.id },            payload: { owner: "org" },          });          expect(policyRemoved.removed, "an admin may remove a Workspace tool policy").toBe(true);        }),        cleanup,      );    }),  );