summaryrefslogtreecommitdiff
path: root/worker/secret
diff options
context:
space:
mode:
authorElizabeth Alexander Hunt <me@liz.coffee>2025-05-12 09:40:12 -0700
committerElizabeth <me@liz.coffee>2025-05-26 14:15:42 -0700
commitd51c9d74857aca3c2f172609297266968bc7f809 (patch)
tree64327f9cc4219729aa11af32d7d4c70cddfc2292 /worker/secret
parent30729a0cf707d9022bae0a7baaba77379dc31fd5 (diff)
downloadci-d51c9d74857aca3c2f172609297266968bc7f809.tar.gz
ci-d51c9d74857aca3c2f172609297266968bc7f809.zip
The big refactor TM
Diffstat (limited to 'worker/secret')
-rw-r--r--worker/secret/bitwarden.ts153
-rw-r--r--worker/secret/ivault.ts24
-rw-r--r--worker/secret/mod.ts2
3 files changed, 179 insertions, 0 deletions
diff --git a/worker/secret/bitwarden.ts b/worker/secret/bitwarden.ts
new file mode 100644
index 0000000..0172a1b
--- /dev/null
+++ b/worker/secret/bitwarden.ts
@@ -0,0 +1,153 @@
+import {
+ Either,
+ getRequiredEnvVars,
+ getStdout,
+ type IEither,
+ type ITraceable,
+ type LogMetricTraceSupplier,
+ Metric,
+ TraceUtil,
+} from "@emprespresso/pengueno";
+import type { IVault, SecretItem } from "./mod.ts";
+
+type TClient = ITraceable<unknown, LogMetricTraceSupplier>;
+type TKey = string;
+type TItemId = string;
+export class Bitwarden implements IVault<TClient, TKey, TItemId> {
+ constructor(private readonly config: BitwardenConfig) {}
+
+ public unlock(client: TClient) {
+ return client.move(this.config)
+ .bimap(TraceUtil.withMetricTrace(Bitwarden.loginMetric))
+ .flatMap((tConfig) =>
+ tConfig.move(`bw config server ${tConfig.get().server}`).map(getStdout)
+ )
+ .map(async (tEitherWithConfig) => {
+ const eitherWithConfig = await tEitherWithConfig.get();
+ tEitherWithConfig.trace.trace("logging in~ ^.^");
+ return eitherWithConfig.flatMapAsync((_) =>
+ tEitherWithConfig.move("bw login --apikey --quiet").map(getStdout)
+ .get()
+ );
+ })
+ .peek(async (tEitherWithAuthd) => {
+ const eitherWithAuthd = await tEitherWithAuthd.get();
+ return tEitherWithAuthd.trace.trace(
+ eitherWithAuthd.fold((err, _val) =>
+ Bitwarden.loginMetric[err ? "failure" : "success"]
+ ),
+ );
+ })
+ .map(async (tEitherWithAuthd) => {
+ const eitherWithAuthd = await tEitherWithAuthd.get();
+ tEitherWithAuthd.trace.trace("unlocking the secret vault~ (◕ᴗ◕✿)");
+ return eitherWithAuthd.flatMapAsync((_) =>
+ tEitherWithAuthd.move("bw unlock --passwordenv BW_PASSWORD --raw")
+ .map(getStdout)
+ .get()
+ );
+ })
+ .peek(async (tEitherWithSession) => {
+ const eitherWithAuthd = await tEitherWithSession.get();
+ return tEitherWithSession.trace.trace(
+ eitherWithAuthd.fold((err, _val) =>
+ Bitwarden.unlockVaultMetric[err ? "failure" : "success"]
+ ),
+ );
+ })
+ .get();
+ }
+
+ public fetchSecret<T extends SecretItem>(
+ client: TClient,
+ key: string,
+ item: string,
+ ): Promise<IEither<Error, T>> {
+ return client.move(key)
+ .bimap(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() } })
+ )
+ )
+ .map(
+ TraceUtil.promiseify((tEitherItemsJson) =>
+ tEitherItemsJson.get()
+ .flatMap((itemsJson): IEither<Error, Array<T & { name: string }>> =>
+ Either.fromFailable(() => JSON.parse(itemsJson))
+ )
+ .flatMap((itemsList): IEither<Error, T> => {
+ const secret = itemsList.find(({ name }) => name === item);
+ if (!secret) {
+ return Either.left(
+ new Error(`couldn't find the item ${item} (。•́︿•̀。)`),
+ );
+ }
+ return Either.right(secret);
+ })
+ ),
+ )
+ .peek(async (tEitherWithSecret) => {
+ const eitherWithSecret = await tEitherWithSecret.get();
+ return tEitherWithSecret.trace.trace(
+ eitherWithSecret.fold((err, _val) =>
+ Bitwarden.fetchSecretMetric[err ? "failure" : "success"]
+ ),
+ );
+ })
+ .get();
+ }
+
+ public lock(client: TClient, key: TKey) {
+ return client.move(key)
+ .bimap(TraceUtil.withMetricTrace(Bitwarden.lockVaultMetric))
+ .peek((tSession) =>
+ tSession.trace.trace(`taking care of locking the vault :3`)
+ )
+ .flatMap((tSession) =>
+ tSession.move("bw lock").map((lockCmd) =>
+ getStdout(lockCmd, { env: { BW_SESSION: tSession.get() } })
+ )
+ )
+ .peek(async (tEitherWithLocked) => {
+ const eitherWithLocked = await tEitherWithLocked.get();
+ return eitherWithLocked.fold((err, _val) => {
+ tEitherWithLocked.trace.trace(
+ Bitwarden.lockVaultMetric[err ? "failure" : "success"],
+ );
+ if (err) return;
+ tEitherWithLocked.trace.trace(
+ "all locked up and secure now~ (。•̀ᴗ-)✧",
+ );
+ });
+ })
+ .get();
+ }
+
+ public static getConfigFromEnvironment(): IEither<Error, BitwardenConfig> {
+ return getRequiredEnvVars([
+ "BW_SERVER",
+ "BW_CLIENTSECRET",
+ "BW_CLIENTID",
+ "BW_PASSWORD",
+ ]).mapRight(({ BW_SERVER, BW_CLIENTSECRET, BW_CLIENTID }) => ({
+ clientId: BW_CLIENTID,
+ secret: BW_CLIENTSECRET,
+ server: BW_SERVER,
+ }));
+ }
+
+ private static loginMetric = Metric.fromName("Bitwarden.login");
+ private static unlockVaultMetric = Metric.fromName("Bitwarden.unlockVault");
+ private static fetchSecretMetric = Metric.fromName("Bitwarden.fetchSecret");
+ private static lockVaultMetric = Metric.fromName("Bitwarden.lock");
+}
+
+export interface BitwardenConfig {
+ server: string;
+ secret: string;
+ clientId: string;
+}
diff --git a/worker/secret/ivault.ts b/worker/secret/ivault.ts
new file mode 100644
index 0000000..e101e56
--- /dev/null
+++ b/worker/secret/ivault.ts
@@ -0,0 +1,24 @@
+import type { IEither } from "@emprespresso/pengueno";
+
+export interface LoginItem {
+ login: {
+ username: string;
+ password: string;
+ };
+}
+
+export interface SecureNote {
+ notes: string;
+}
+
+export type SecretItem = LoginItem | SecureNote;
+export interface IVault<TClient, TKey, TItemId> {
+ unlock: (client: TClient) => Promise<IEither<Error, TKey>>;
+ lock: (client: TClient, key: TKey) => Promise<IEither<Error, TKey>>;
+
+ fetchSecret: <T extends SecretItem>(
+ client: TClient,
+ key: TKey,
+ item: TItemId,
+ ) => Promise<IEither<Error, T>>;
+}
diff --git a/worker/secret/mod.ts b/worker/secret/mod.ts
new file mode 100644
index 0000000..70a1ea9
--- /dev/null
+++ b/worker/secret/mod.ts
@@ -0,0 +1,2 @@
+export * from "./ivault.ts";
+export * from "./bitwarden.ts";