import * as NodeCryptoLayer from "@effect/platform-node/NodeCrypto"; import { describe, expect, it } from "@effect/vitest"; import { PgDialect, QueryBuilder } from "drizzle-orm/pg-core"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as RelayDb from "../db.ts"; import { relayEnvironmentCredentials } from "../persistence/schema.ts"; import * as EnvironmentCredentials from "./EnvironmentCredentials.ts"; describe("EnvironmentCredentials", () => { it.effect("reports the credential creation persistence stage and preserves its cause", () => { const cause = new Error("database unavailable"); const fakeDb = { insert: (table: unknown) => { expect(table).toBe(relayEnvironmentCredentials); return { values: () => Effect.void, }; }, update: (table: unknown) => { expect(table).toBe(relayEnvironmentCredentials); return { set: () => ({ where: () => Effect.fail(cause), }), }; }, } as unknown as RelayDb.RelayDb["Service"]; return Effect.gen(function* () { const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; const error = yield* Effect.flip( credentials.create({ environmentId: "env_test", environmentPublicKey: "sensitive-public-key-material", }), ); expect(error).toMatchObject({ _tag: "EnvironmentCredentialCreatePersistenceError", stage: "revoke-previous-credentials", environmentId: "env_test", }); expect(error.credentialId).toMatch(/^[0-9a-f]{64}$/); expect(error.cause).toBe(cause); expect(error).not.toHaveProperty("environmentPublicKey"); }).pipe( Effect.provide( EnvironmentCredentials.layer.pipe( Layer.provide(NodeCryptoLayer.layer), Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), ), ), ); }); it.effect("does not retain credential tokens when lookup persistence fails", () => { const cause = new Error("database unavailable"); const token = "t3env_sensitive-credential-token"; const fakeDb = { select: () => ({ from: (table: unknown) => { expect(table).toBe(relayEnvironmentCredentials); return { where: () => ({ limit: () => Effect.fail(cause), }), }; }, }), } as unknown as RelayDb.RelayDb["Service"]; return Effect.gen(function* () { const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; const error = yield* Effect.flip(credentials.authenticate(token)); expect(error).toMatchObject({ _tag: "EnvironmentCredentialAuthenticatePersistenceError", stage: "lookup-credential", }); expect(error.cause).toBe(cause); expect(error).not.toHaveProperty("token"); }).pipe( Effect.provide( EnvironmentCredentials.layer.pipe( Layer.provide(NodeCryptoLayer.layer), Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), ), ), ); }); it.effect( "creates opaque credentials and revokes only older credentials for the same key", () => { const insertedValues: Array<{ readonly credentialId: string; readonly environmentId: string; readonly environmentPublicKey: string; readonly credentialHash: string; readonly revokedAt: null; readonly createdAt: string; readonly updatedAt: string; }> = []; const staleCredentialRevocations: Array<{ readonly values: Record; readonly condition: unknown; }> = []; const fakeDb = { insert: (table: unknown) => { expect(table).toBe(relayEnvironmentCredentials); return { values: (values: (typeof insertedValues)[number]) => { insertedValues.push(values); return Effect.void; }, }; }, update: (table: unknown) => { expect(table).toBe(relayEnvironmentCredentials); return { set: (values: Record) => ({ where: (condition: unknown) => { staleCredentialRevocations.push({ values, condition }); return Effect.void; }, }), }; }, } as unknown as RelayDb.RelayDb["Service"]; return Effect.gen(function* () { const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; const token = yield* credentials.create({ environmentId: "env_test", environmentPublicKey: "environment-public-key", }); const [, credentialId, secret] = token.split("_"); expect(token).toMatch(/^t3env_[0-9a-f]{64}_[0-9a-f]{96}$/); expect(credentialId).toHaveLength(64); expect(secret).toHaveLength(96); expect(insertedValues).toHaveLength(1); expect(insertedValues[0]).toMatchObject({ credentialId, environmentId: "env_test", environmentPublicKey: "environment-public-key", revokedAt: null, }); expect(insertedValues[0]?.credentialHash).toMatch(/^[A-Za-z0-9_-]{43}$/); expect(insertedValues[0]?.credentialHash).not.toContain(token); expect(insertedValues[0]?.createdAt).toBe(insertedValues[0]?.updatedAt); expect(staleCredentialRevocations).toHaveLength(1); expect(staleCredentialRevocations[0]?.values.revokedAt).toEqual( staleCredentialRevocations[0]?.values.updatedAt, ); const query = new PgDialect().sqlToQuery(staleCredentialRevocations[0]?.condition as never); expect(query.sql).toContain('"relay_environment_credentials"."environment_id" = $1'); expect(query.sql).toContain( '"relay_environment_credentials"."environment_public_key" = $2', ); expect(query.sql).toContain('"relay_environment_credentials"."credential_id" <> $3'); expect(query.sql).toContain('"relay_environment_credentials"."revoked_at" is null'); expect(query.params).toEqual(["env_test", "environment-public-key", credentialId]); }).pipe( Effect.provide( EnvironmentCredentials.layer.pipe( Layer.provide(NodeCryptoLayer.layer), Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), ), ), ); }, ); it.effect("revokes active credentials for an environment public key", () => { const updateValues: Array> = []; const whereConditions: Array = []; const fakeDb = { select: (fields: Parameters[0]) => new QueryBuilder().select(fields), update: (table: unknown) => { expect(table).toBe(relayEnvironmentCredentials); return { set: (values: Record) => { updateValues.push(values); return { where: (condition: unknown) => { whereConditions.push(condition); return { returning: (selection: unknown) => { expect(selection).toBeDefined(); return Effect.succeed([{ credentialId: "credential-1" }]); }, }; }, }; }, }; }, } as unknown as RelayDb.RelayDb["Service"]; return Effect.gen(function* () { const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; const revoked = yield* credentials.revokeForEnvironmentPublicKey({ environmentId: "env_test", environmentPublicKey: "environment-public-key", }); expect(revoked).toBe(true); expect(updateValues).toHaveLength(1); expect(updateValues[0]?.revokedAt).toEqual(updateValues[0]?.updatedAt); expect(whereConditions).toHaveLength(1); const query = new PgDialect().sqlToQuery(whereConditions[0] as never); expect(query.sql).toContain('"relay_environment_credentials"."environment_id" = $1'); expect(query.sql).toContain('"relay_environment_credentials"."environment_public_key" = $2'); expect(query.sql).toContain('"relay_environment_credentials"."revoked_at" is null'); expect(query.sql).toContain("not exists"); expect(query.sql).toContain('"relay_environment_links"."environment_id" = $3'); expect(query.sql).toContain('"relay_environment_links"."environment_public_key" = $4'); expect(query.sql).toContain('"relay_environment_links"."revoked_at" is null'); expect(query.params).toEqual([ "env_test", "environment-public-key", "env_test", "environment-public-key", ]); }).pipe( Effect.provide( EnvironmentCredentials.layer.pipe( Layer.provide(NodeCryptoLayer.layer), Layer.provide(Layer.succeed(RelayDb.RelayDb, fakeDb)), ), ), ); }); });