Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/server/integration/providerService.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
import { ServerSettingsService } from "../src/serverSettings.ts";
import { AnalyticsService } from "../src/telemetry/Services/AnalyticsService.ts";
import { SqlitePersistenceMemory } from "../src/persistence/Layers/Sqlite.ts";
import { ProviderSessionRuntimeRepositoryLive } from "../src/persistence/Layers/ProviderSessionRuntime.ts";
import * as ProviderSessionRuntime from "../src/persistence/ProviderSessionRuntime.ts";

import {
makeTestProviderAdapterHarness,
Expand Down Expand Up @@ -63,7 +63,7 @@ const makeIntegrationFixture = Effect.gen(function* () {
});

const directoryLayer = ProviderSessionDirectoryLive.pipe(
Layer.provide(ProviderSessionRuntimeRepositoryLive),
Layer.provide(ProviderSessionRuntime.layer),
);

const shared = Layer.mergeAll(
Expand Down
7 changes: 3 additions & 4 deletions apps/server/src/auth/PairingGrantStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import * as Stream from "effect/Stream";
import * as Option from "effect/Option";

import { ServerConfig } from "../config.ts";
import { AuthPairingLinkRepositoryLive } from "../persistence/Layers/AuthPairingLinks.ts";
import { AuthPairingLinkRepository } from "../persistence/Services/AuthPairingLinks.ts";
import * as AuthPairingLinks from "../persistence/AuthPairingLinks.ts";

export interface BootstrapGrant {
readonly method: ServerAuthBootstrapMethod;
Expand Down Expand Up @@ -126,7 +125,7 @@ const internalBootstrapCredentialError = (message: string, cause: unknown) =>
export const make = Effect.fn("makePairingGrantStore")(function* () {
const crypto = yield* Crypto.Crypto;
const config = yield* ServerConfig;
const pairingLinks = yield* AuthPairingLinkRepository;
const pairingLinks = yield* AuthPairingLinks.AuthPairingLinkRepository;
const seededGrantsRef = yield* Ref.make(new Map<string, StoredBootstrapGrant>());
const changesPubSub = yield* PubSub.unbounded<BootstrapCredentialChange>();
const generatePairingToken = Effect.gen(function* () {
Expand Down Expand Up @@ -417,5 +416,5 @@ export const make = Effect.fn("makePairingGrantStore")(function* () {
});

export const layer = Layer.effect(PairingGrantStore, make()).pipe(
Layer.provideMerge(AuthPairingLinkRepositoryLive),
Layer.provideMerge(AuthPairingLinks.layer),
);
2 changes: 1 addition & 1 deletion apps/server/src/auth/SessionStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as TestClock from "effect/testing/TestClock";
import * as ServerConfig from "../config.ts";
import { PersistenceSqlError } from "../persistence/Errors.ts";
import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts";
import * as AuthSessions from "../persistence/Services/AuthSessions.ts";
import * as AuthSessions from "../persistence/AuthSessions.ts";
import * as SessionStore from "./SessionStore.ts";
import * as ServerSecretStore from "./ServerSecretStore.ts";

Expand Down
7 changes: 3 additions & 4 deletions apps/server/src/auth/SessionStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import * as Stream from "effect/Stream";
import * as Option from "effect/Option";

import { ServerConfig } from "../config.ts";
import { AuthSessionRepositoryLive } from "../persistence/Layers/AuthSessions.ts";
import { AuthSessionRepository } from "../persistence/Services/AuthSessions.ts";
import * as AuthSessions from "../persistence/AuthSessions.ts";
import * as ServerSecretStore from "./ServerSecretStore.ts";
import {
base64UrlDecodeUtf8,
Expand Down Expand Up @@ -195,7 +194,7 @@ export const make = Effect.fn("makeSessionStore")(function* () {
const crypto = yield* Crypto.Crypto;
const serverConfig = yield* ServerConfig;
const secretStore = yield* ServerSecretStore.ServerSecretStore;
const authSessions = yield* AuthSessionRepository;
const authSessions = yield* AuthSessions.AuthSessionRepository;
const signingSecret = yield* secretStore.getOrCreateRandom(SIGNING_SECRET_NAME, 32);
const connectedSessionsRef = yield* Ref.make(new Map<string, number>());
const changesPubSub = yield* PubSub.unbounded<SessionCredentialChange>();
Expand Down Expand Up @@ -640,5 +639,5 @@ export const make = Effect.fn("makeSessionStore")(function* () {
});

export const layer = Layer.effect(SessionStore, make()).pipe(
Layer.provideMerge(AuthSessionRepositoryLive),
Layer.provideMerge(AuthSessions.layer),
);
Original file line number Diff line number Diff line change
@@ -1,24 +1,91 @@
import * as SqlClient from "effect/unstable/sql/SqlClient";
import * as SqlSchema from "effect/unstable/sql/SqlSchema";
import * as Context from "effect/Context";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as Schema from "effect/Schema";
import * as SqlClient from "effect/unstable/sql/SqlClient";
import * as SqlSchema from "effect/unstable/sql/SqlSchema";

import { AuthEnvironmentScopes } from "@t3tools/contracts";

import {
type AuthPairingLinkRepositoryError,
toPersistenceDecodeError,
toPersistenceSqlError,
type AuthPairingLinkRepositoryError,
} from "../Errors.ts";
import {
AuthPairingLinkRecord,
} from "./Errors.ts";

export const AuthPairingLinkRecord = Schema.Struct({
id: Schema.String,
credential: Schema.String,
method: Schema.Literals(["desktop-bootstrap", "one-time-token"]),
scopes: Schema.fromJsonString(AuthEnvironmentScopes),
subject: Schema.String,
label: Schema.NullOr(Schema.String),
proofKeyThumbprint: Schema.NullOr(Schema.String),
createdAt: Schema.DateTimeUtcFromString,
expiresAt: Schema.DateTimeUtcFromString,
consumedAt: Schema.NullOr(Schema.DateTimeUtcFromString),
revokedAt: Schema.NullOr(Schema.DateTimeUtcFromString),
});
export type AuthPairingLinkRecord = typeof AuthPairingLinkRecord.Type;

export const CreateAuthPairingLinkInput = Schema.Struct({
id: Schema.String,
credential: Schema.String,
method: Schema.Literals(["desktop-bootstrap", "one-time-token"]),
scopes: AuthEnvironmentScopes,
subject: Schema.String,
label: Schema.NullOr(Schema.String),
proofKeyThumbprint: Schema.NullOr(Schema.String),
createdAt: Schema.DateTimeUtcFromString,
expiresAt: Schema.DateTimeUtcFromString,
});
export type CreateAuthPairingLinkInput = typeof CreateAuthPairingLinkInput.Type;

export const ConsumeAuthPairingLinkInput = Schema.Struct({
credential: Schema.String,
proofKeyThumbprint: Schema.NullOr(Schema.String),
consumedAt: Schema.DateTimeUtcFromString,
now: Schema.DateTimeUtcFromString,
});
export type ConsumeAuthPairingLinkInput = typeof ConsumeAuthPairingLinkInput.Type;

export const ListActiveAuthPairingLinksInput = Schema.Struct({
now: Schema.DateTimeUtcFromString,
});
export type ListActiveAuthPairingLinksInput = typeof ListActiveAuthPairingLinksInput.Type;

export const RevokeAuthPairingLinkInput = Schema.Struct({
id: Schema.String,
revokedAt: Schema.DateTimeUtcFromString,
});
export type RevokeAuthPairingLinkInput = typeof RevokeAuthPairingLinkInput.Type;

export const GetAuthPairingLinkByCredentialInput = Schema.Struct({
credential: Schema.String,
});
export type GetAuthPairingLinkByCredentialInput = typeof GetAuthPairingLinkByCredentialInput.Type;

export class AuthPairingLinkRepository extends Context.Service<
AuthPairingLinkRepository,
type AuthPairingLinkRepositoryShape,
ConsumeAuthPairingLinkInput,
CreateAuthPairingLinkInput,
GetAuthPairingLinkByCredentialInput,
ListActiveAuthPairingLinksInput,
RevokeAuthPairingLinkInput,
} from "../Services/AuthPairingLinks.ts";
{
readonly create: (
input: CreateAuthPairingLinkInput,
) => Effect.Effect<void, AuthPairingLinkRepositoryError>;
readonly consumeAvailable: (
input: ConsumeAuthPairingLinkInput,
) => Effect.Effect<Option.Option<AuthPairingLinkRecord>, AuthPairingLinkRepositoryError>;
readonly listActive: (
input: ListActiveAuthPairingLinksInput,
) => Effect.Effect<ReadonlyArray<AuthPairingLinkRecord>, AuthPairingLinkRepositoryError>;
readonly revoke: (
input: RevokeAuthPairingLinkInput,
) => Effect.Effect<boolean, AuthPairingLinkRepositoryError>;
readonly getByCredential: (
input: GetAuthPairingLinkByCredentialInput,
) => Effect.Effect<Option.Option<AuthPairingLinkRecord>, AuthPairingLinkRepositoryError>;
}
>()("t3/persistence/AuthPairingLinks/AuthPairingLinkRepository") {}

function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: string) {
return (cause: unknown): AuthPairingLinkRepositoryError =>
Expand All @@ -27,7 +94,7 @@ function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: st
: toPersistenceSqlError(sqlOperation)(cause);
}

const makeAuthPairingLinkRepository = Effect.gen(function* () {
export const make = Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;

const createPairingLinkRow = SqlSchema.void({
Expand Down Expand Up @@ -154,7 +221,7 @@ const makeAuthPairingLinkRepository = Effect.gen(function* () {
`,
});

const create: AuthPairingLinkRepositoryShape["create"] = (input) =>
const create: AuthPairingLinkRepository["Service"]["create"] = (input) =>
createPairingLinkRow(input).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
Expand All @@ -164,7 +231,7 @@ const makeAuthPairingLinkRepository = Effect.gen(function* () {
),
);

const consumeAvailable: AuthPairingLinkRepositoryShape["consumeAvailable"] = (input) =>
const consumeAvailable: AuthPairingLinkRepository["Service"]["consumeAvailable"] = (input) =>
consumeAvailablePairingLinkRow(input).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
Expand All @@ -174,7 +241,7 @@ const makeAuthPairingLinkRepository = Effect.gen(function* () {
),
);

const listActive: AuthPairingLinkRepositoryShape["listActive"] = (input) =>
const listActive: AuthPairingLinkRepository["Service"]["listActive"] = (input) =>
listActivePairingLinkRows(input).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
Expand All @@ -184,7 +251,7 @@ const makeAuthPairingLinkRepository = Effect.gen(function* () {
),
);

const revoke: AuthPairingLinkRepositoryShape["revoke"] = (input) =>
const revoke: AuthPairingLinkRepository["Service"]["revoke"] = (input) =>
revokePairingLinkRow(input).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
Expand All @@ -195,7 +262,7 @@ const makeAuthPairingLinkRepository = Effect.gen(function* () {
Effect.map((rows) => rows.length > 0),
);

const getByCredential: AuthPairingLinkRepositoryShape["getByCredential"] = (input) =>
const getByCredential: AuthPairingLinkRepository["Service"]["getByCredential"] = (input) =>
getPairingLinkRowByCredential(input).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
Expand All @@ -211,10 +278,7 @@ const makeAuthPairingLinkRepository = Effect.gen(function* () {
listActive,
revoke,
getByCredential,
} satisfies AuthPairingLinkRepositoryShape;
} satisfies AuthPairingLinkRepository["Service"];
});

export const AuthPairingLinkRepositoryLive = Layer.effect(
AuthPairingLinkRepository,
makeAuthPairingLinkRepository,
);
export const layer = Layer.effect(AuthPairingLinkRepository, make);
Loading
Loading