From 22042d7e96b73af2776572eddc786183436664bc Mon Sep 17 00:00:00 2001 From: BluJ Date: Thu, 4 May 2023 15:59:25 -0600 Subject: [PATCH] feat: New utils of createOrUpdateVault --- lib/config/configTypes.ts | 14 ++--- lib/config/constants.ts | 2 + lib/util/getDefaultString.ts | 112 +++++++++++++++++++++++++++++++++++ lib/util/index.ts | 27 ++++++++- 4 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 lib/util/getDefaultString.ts diff --git a/lib/config/configTypes.ts b/lib/config/configTypes.ts index 0ad51ab..e90fea4 100644 --- a/lib/config/configTypes.ts +++ b/lib/config/configTypes.ts @@ -208,15 +208,13 @@ export type UniqueBy = | { all: readonly UniqueBy[] | UniqueBy[] } -export type DefaultString = - | string - | { - charset: string - len: number - } - +export type DefaultString = string | RandomString +export type RandomString = { + charset: string + len: number +} // sometimes the type checker needs just a little bit of help -export function isValueSpecListOf( +function isValueSpecListOf( t: ValueSpec, s: S, ): t is ValueSpecListOf & { spec: ListValueSpecOf } { diff --git a/lib/config/constants.ts b/lib/config/constants.ts index b2e4002..f8ecac8 100644 --- a/lib/config/constants.ts +++ b/lib/config/constants.ts @@ -59,3 +59,5 @@ export const smtpConfig = Value.filteredUnion(async ({ effects, utils }) => { }, }), ) + +export const password = diff --git a/lib/util/getDefaultString.ts b/lib/util/getDefaultString.ts new file mode 100644 index 0000000..8b5fa04 --- /dev/null +++ b/lib/util/getDefaultString.ts @@ -0,0 +1,112 @@ +import { DefaultString } from "../config/configTypes" + +export function getDefaultString(defaultSpec: DefaultString): string { + if (typeof defaultSpec === "string") { + return defaultSpec + } else { + let s = "" + for (let i = 0; i < defaultSpec.len; i++) { + s = s + getRandomCharInSet(defaultSpec.charset) + } + + return s + } +} + +// a,g,h,A-Z,,,,- +export function getRandomCharInSet(charset: string): string { + const set = stringToCharSet(charset) + let charIdx = Math.floor(Math.random() * set.len) + for (let range of set.ranges) { + if (range.len > charIdx) { + return String.fromCharCode(range.start.charCodeAt(0) + charIdx) + } + charIdx -= range.len + } + throw new Error("unreachable") +} + +function stringToCharSet(charset: string): CharSet { + let set: CharSet = { ranges: [], len: 0 } + let start: string | null = null + let end: string | null = null + let in_range = false + for (let char of charset) { + switch (char) { + case ",": + if (start !== null && end !== null) { + if (start!.charCodeAt(0) > end!.charCodeAt(0)) { + throw new Error("start > end of charset") + } + const len = end.charCodeAt(0) - start.charCodeAt(0) + 1 + set.ranges.push({ + start, + end, + len, + }) + set.len += len + start = null + end = null + in_range = false + } else if (start !== null && !in_range) { + set.len += 1 + set.ranges.push({ start, end: start, len: 1 }) + start = null + } else if (start !== null && in_range) { + end = "," + } else if (start === null && end === null && !in_range) { + start = "," + } else { + throw new Error('unexpected ","') + } + break + case "-": + if (start === null) { + start = "-" + } else if (!in_range) { + in_range = true + } else if (in_range && end === null) { + end = "-" + } else { + throw new Error('unexpected "-"') + } + break + default: + if (start === null) { + start = char + } else if (in_range && end === null) { + end = char + } else { + throw new Error(`unexpected "${char}"`) + } + } + } + if (start !== null && end !== null) { + if (start!.charCodeAt(0) > end!.charCodeAt(0)) { + throw new Error("start > end of charset") + } + const len = end.charCodeAt(0) - start.charCodeAt(0) + 1 + set.ranges.push({ + start, + end, + len, + }) + set.len += len + } else if (start !== null) { + set.len += 1 + set.ranges.push({ + start, + end: start, + len: 1, + }) + } + return set +} +type CharSet = { + ranges: { + start: string + end: string + len: number + }[] + len: number +} diff --git a/lib/util/index.ts b/lib/util/index.ts index 4f9525f..6fe40dd 100644 --- a/lib/util/index.ts +++ b/lib/util/index.ts @@ -1,4 +1,4 @@ -import { Parser } from "ts-matches" +import { Parser, string } from "ts-matches" import * as T from "../types" import FileHelper from "./fileHelper" import nullIfEmpty from "./nullIfEmpty" @@ -21,6 +21,8 @@ import { LocalBinding } from "../mainFn/LocalBinding" import { LocalPort } from "../mainFn/LocalPort" import { NetworkBuilder } from "../mainFn/NetworkBuilder" import { TorHostname } from "../mainFn/TorHostname" +import { DefaultString } from "../config/configTypes" +import { getDefaultString } from "./getDefaultString" // prettier-ignore export type FlattenIntersection = @@ -42,6 +44,11 @@ export type WrapperDataOptionals = { } export type Utils = { + createOrUpdateVault: (opts: { + key: string + value: string | null | undefined + generator: DefaultString + }) => Promise readFile: (fileHelper: FileHelper) => ReturnType["read"]> writeFile: ( fileHelper: FileHelper, @@ -84,6 +91,24 @@ export type Utils = { export const utils = ( effects: T.Effects, ): Utils => ({ + createOrUpdateVault: async ({ + key, + value, + generator, + }: { + key: string + value: string | null | undefined + generator: DefaultString + }) => { + if (value) { + await effects.vaultSet({ key, value }) + return + } + if (await effects.vaultList().then((x) => x.includes(key))) { + return + } + await effects.vaultSet({ key, value: getDefaultString(generator) }) + }, getSystemSmtp: () => new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite, readFile: (fileHelper: FileHelper) => fileHelper.read(effects),