diff options
Diffstat (limited to 'worker/secret.ts')
-rw-r--r-- | worker/secret.ts | 83 |
1 files changed, 41 insertions, 42 deletions
diff --git a/worker/secret.ts b/worker/secret.ts index 30316bd..11daf06 100644 --- a/worker/secret.ts +++ b/worker/secret.ts @@ -2,12 +2,16 @@ import { Either, getRequiredEnvVars, getStdout, + getStdoutMany, type IEither, type ITraceable, type LogMetricTraceSupplier, Metric, TraceUtil, } from '@emprespresso/pengueno'; +import { randomUUID } from 'node:crypto'; +import { mkdirSync } from 'node:fs'; +import path from 'node:path'; // -- <ISecret> -- export interface SecretItem { @@ -35,55 +39,51 @@ export interface IVault<TClient, TKey, TItemId> { // -- <Vault> -- type TClient = ITraceable<unknown, LogMetricTraceSupplier>; -type TKey = string; +type TKey = { + BW_SESSION: string; + BITWARDENCLI_APPDATA_DIR: string; +}; type TItemId = string; export class Bitwarden implements IVault<TClient, TKey, TItemId> { constructor(private readonly config: BitwardenConfig) {} public unlock(client: TClient) { - const authed = client + return client .move(this.config) .flatMap(TraceUtil.withMetricTrace(Bitwarden.loginMetric)) - .flatMap((tConfig) => tConfig.move(`bw config server ${tConfig.get().server}`).map(getStdout)) - .map(async (tEitherWithConfig) => { - const eitherWithConfig = await tEitherWithConfig.get(); - return eitherWithConfig.flatMapAsync((_) => - tEitherWithConfig - .peek((t) => t.trace.trace('logging in~ ^.^')) - .move('bw login --apikey --quiet') - .map(getStdout) - .get(), - ); - }) - .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(Bitwarden.loginMetric))); - const unlocked = authed - .flatMap(TraceUtil.withMetricTrace(Bitwarden.unlockVaultMetric)) - .map(async (tEitherWithAuthd) => { - const eitherWithAuthd = await tEitherWithAuthd.get(); - return eitherWithAuthd.flatMapAsync((_) => - tEitherWithAuthd - .peek((t) => t.trace.trace('unlocking the secret vault~ (◕ᴗ◕✿)')) - .move('bw unlock --passwordenv BW_PASSWORD --raw') - .map(getStdout) - .get(), - ); - }) - .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(Bitwarden.unlockVaultMetric))); - return unlocked.get(); + .map((tConfig) => + Either.fromFailable<Error, { config: BitwardenConfig; key: Pick<TKey, 'BITWARDENCLI_APPDATA_DIR'> }>( + () => { + const sessionPath = path.join(this.config.sessionBaseDirectory, randomUUID()); + mkdirSync(sessionPath, { recursive: true }); + return { config: tConfig.get(), key: { BITWARDENCLI_APPDATA_DIR: sessionPath } }; + }, + ), + ) + .map((tEitherConfig) => + tEitherConfig + .get() + .flatMapAsync(({ config: { server }, key }) => + getStdoutMany( + tEitherConfig.move([ + `bw config server ${server}`, + `bw login --apikey --quiet`, + `bw unlock --passwordenv BW_PASSWORD --raw`, + ]), + { env: key }, + ).then((res) => res.mapRight((out) => ({ ...key, BW_SESSION: out.at(-1)! }))), + ), + ) + .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(Bitwarden.loginMetric))) + .get(); } - public fetchSecret<T extends SecretItem>(client: TClient, key: string, item: string): Promise<IEither<Error, T>> { + public fetchSecret<T extends SecretItem>(client: TClient, key: TKey, item: string): Promise<IEither<Error, T>> { return client .move(key) .flatMap(TraceUtil.withMetricTrace(Bitwarden.fetchSecretMetric)) .peek((tSession) => tSession.trace.trace(`looking for your secret ${item} (⑅˘꒳˘)`)) - .flatMap((tSession) => - tSession.move('bw list items').map((listCmd) => - getStdout(listCmd, { - env: { BW_SESSION: tSession.get() }, - }), - ), - ) + .flatMap((tSession) => tSession.move('bw list items').map((listCmd) => getStdout(listCmd, { env: key }))) .map( TraceUtil.promiseify((tEitherItemsJson) => tEitherItemsJson @@ -110,11 +110,7 @@ export class Bitwarden implements IVault<TClient, TKey, TItemId> { .flatMap(TraceUtil.withMetricTrace(Bitwarden.lockVaultMetric)) .peek((tSession) => tSession.trace.trace(`taking care of locking the vault :3`)) .flatMap((tSession) => - tSession.move('bw lock && bw logout').map((lockCmd) => - getStdout(lockCmd, { - env: { BW_SESSION: tSession.get() }, - }), - ), + tSession.move('bw lock && bw logout').map((lockCmd) => getStdout(lockCmd, { env: key })), ) .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(Bitwarden.lockVaultMetric))) .peek( @@ -124,12 +120,14 @@ export class Bitwarden implements IVault<TClient, TKey, TItemId> { .mapRight(() => tEitherWithLocked.trace.trace('all locked up and secure now~ (。•̀ᴗ-)✧')), ), ) + .map(TraceUtil.promiseify((e) => e.get().mapRight(() => key))) .get(); } - public static getConfigFromEnvironment(): IEither<Error, BitwardenConfig> { + public static getConfigFromEnvironment(sessionBaseDirectory = '/tmp/secret'): IEither<Error, BitwardenConfig> { return getRequiredEnvVars(['BW_SERVER', 'BW_CLIENTSECRET', 'BW_CLIENTID', 'BW_PASSWORD']).mapRight( ({ BW_SERVER, BW_CLIENTSECRET, BW_CLIENTID }) => ({ + sessionBaseDirectory, clientId: BW_CLIENTID, secret: BW_CLIENTSECRET, server: BW_SERVER, @@ -144,6 +142,7 @@ export class Bitwarden implements IVault<TClient, TKey, TItemId> { } export interface BitwardenConfig { + sessionBaseDirectory: string; server: string; secret: string; clientId: string; |