feat(infisical-pg): completed signup, login, password and backup key migration

This commit is contained in:
Akhil Mohan
2024-01-27 12:38:22 +05:30
parent 05205d1eff
commit fcfd6b3fb2
68 changed files with 7529 additions and 58 deletions
+42 -7
View File
@@ -1,16 +1,39 @@
import { Logger } from "pino";
import { z } from "zod";
import { zpStr } from "../zod";
const zodStrBool = z
.enum(["true", "false"])
.optional()
.transform((val) => val === "true");
const envSchema = z.object({
PORT: z.coerce.number().default(4000),
HOST: zpStr(z.string().default("localhost")),
DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database conntection string")),
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
SALT_ROUNDS: z.coerce.number().default(10),
// TODO(akhilmhdh): will be changed to one
ENCRYPTION_KEY: z.string().optional(),
ROOT_ENCRYPTION_KEY: z.string().optional(),
HTTPS_ENABLED: z
.enum(["true", "false"])
.optional()
.transform((val) => val === "true")
ENCRYPTION_KEY: zpStr(z.string().optional()),
ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()),
HTTPS_ENABLED: zodStrBool,
// smtp options
SMTP_HOST: zpStr(z.string().optional()),
SMTP_SECURE: zodStrBool,
SMTP_PORT: z.coerce.number().default(587),
SMTP_USERNAME: zpStr(z.string().optional()),
SMTP_PASSWORD: zpStr(z.string().optional()),
SMTP_FROM_ADDRESS: zpStr(z.string().optional()),
SMTP_FROM_NAME: zpStr(z.string().optional().default("Infisical")),
COOKIE_SECRET_SIGN_KEY: z.string().default("g5giLbOMpaJhqEogXApkiw2ZFW5Q0jvA"),
SITE_URL: zpStr(z.string().optional()),
// jwt options
JWT_AUTH_SECRET: zpStr(z.string()),
JWT_AUTH_LIFETIME: zpStr(z.string().default("10d")),
JWT_SIGNUP_LIFETIME: zpStr(z.string().default("15m")),
JWT_REFRESH_LIFETIME: zpStr(z.string().default("90d")),
JWT_MFA_LIFETIME: zpStr(z.string().default("5m"))
});
let envCfg: Readonly<z.infer<typeof envSchema>>;
@@ -21,8 +44,20 @@ export const initEnvConfig = (logger: Logger) => {
const parsedEnv = envSchema.safeParse(process.env);
if (!parsedEnv.success) {
logger.error("Invalid environment variables. Check the error below");
logger.error(parsedEnv.error);
logger.error(parsedEnv.error.issues);
process.exit(-1);
}
envCfg = Object.freeze(parsedEnv.data);
return envCfg;
};
export const formatSmtpConfig = () => ({
host: envCfg.SMTP_HOST,
port: envCfg.SMTP_PORT,
auth:
envCfg.SMTP_USERNAME && envCfg.SMTP_PASSWORD
? { user: envCfg.SMTP_USERNAME, pass: envCfg.SMTP_PASSWORD }
: undefined,
secure: envCfg.SMTP_SECURE,
from: `"${envCfg.SMTP_FROM_NAME}" <${envCfg.SMTP_FROM_ADDRESS}>`
});
+1
View File
@@ -0,0 +1 @@
export { generateSrpServerKey, srpCheckClientProof } from "./srp";
+26
View File
@@ -0,0 +1,26 @@
import jsrp from "jsrp";
export const generateSrpServerKey = async (salt: string, verifier: string) => {
// eslint-disable-next-line new-cap
const server = new jsrp.server();
await new Promise((resolve) => {
server.init({ salt, verifier }, () => resolve(null));
});
return { pubKey: server.getPublicKey(), privateKey: server.getPrivateKey() };
};
export const srpCheckClientProof = async (
salt: string,
verifier: string,
serverPrivateKey: string,
clientPublicKey: string,
clientProof: string
) => {
// eslint-disable-next-line new-cap
const server = new jsrp.server();
await new Promise((resolve) => {
server.init({ salt, verifier, b: serverPrivateKey }, () => resolve(null));
});
server.setClientPublicKey(clientPublicKey);
return server.checkClientProof(clientProof);
};
+36
View File
@@ -0,0 +1,36 @@
/* eslint-disable max-classes-per-file */
export class DatabaseError extends Error {
name: string;
error: unknown;
constructor({ name, error, message }: { message?: string; name: string; error: unknown }) {
super(message || "Failed to execute db ops");
this.name = name;
this.error = error;
}
}
export class UnauthorizedError extends Error {
name: string;
error: unknown;
constructor({ name, error, message }: { message?: string; name: string; error: unknown }) {
super(message ?? "You are not allowed to access this resourve");
this.name = name;
this.error = error;
}
}
export class BadRequestError extends Error {
name: string;
error: unknown;
constructor({ name, error, message }: { message?: string; name: string; error: unknown }) {
super(message ?? "The request is invalid");
this.name = name;
this.error = error;
}
}
+5
View File
@@ -0,0 +1,5 @@
export type RequiredKeys<T> = {
[K in keyof T]-?: undefined extends T[K] ? never : K;
}[keyof T];
export type PickRequired<T> = Pick<T, RequiredKeys<T>>;
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
export { isDisposableEmail } from "./validate-email";
@@ -0,0 +1,10 @@
import fs from "fs/promises";
import path from "path";
export const isDisposableEmail = async (email: string) => {
const emailDomain = email.split("@")[1];
const disposableEmails = await fs.readFile(path.join(__dirname, "disposable_emails.txt"), "utf8");
if (disposableEmails.split("\n").includes(emailDomain)) return true;
return false;
};
+12
View File
@@ -0,0 +1,12 @@
import { z } from "zod";
// this is a patched zod string to remove empty string to undefined
export const zpStr = <T extends z.ZodString>(
schema: T,
opt: { stripNull: boolean } = { stripNull: true }
) =>
z.preprocess((val) => {
if (opt.stripNull && val === null) return undefined;
if (typeof val !== "string") return val;
return val.trim() || undefined;
}, schema);