mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 03:29:35 +00:00
Merge remote-tracking branch 'origin' into pg-ssl
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { buildMongoQueryMatcher, MongoAbility } from "@casl/ability";
|
||||
import { FieldCondition, FieldInstruction, JsInterpreter } from "@ucast/mongo2js";
|
||||
import picomatch from "picomatch";
|
||||
@@ -12,7 +13,7 @@ const $glob: FieldInstruction<string> = {
|
||||
};
|
||||
|
||||
const glob: JsInterpreter<FieldCondition<string>> = (node, object, context) => {
|
||||
const secretPath = context.get(object, node.field);
|
||||
const secretPath = context.get(object, node.field) as string;
|
||||
const permissionSecretGlobPath = node.value;
|
||||
return picomatch.isMatch(secretPath, permissionSecretGlobPath, { strictSlashes: false });
|
||||
};
|
||||
@@ -23,7 +24,7 @@ export const conditionsMatcher = buildMongoQueryMatcher({ $glob }, { glob });
|
||||
* Extracts and formats permissions from a CASL Ability object or a raw permission set.
|
||||
*/
|
||||
const extractPermissions = (ability: MongoAbility) =>
|
||||
ability.rules.map((permission) => `${permission.action}_${permission.subject}`);
|
||||
ability.rules.map((permission) => `${permission.action as string}_${permission.subject as string}`);
|
||||
|
||||
/**
|
||||
* Compares two sets of permissions to determine if the first set is at least as privileged as the second set.
|
||||
|
||||
@@ -39,9 +39,7 @@ const envSchema = z
|
||||
// Telemetry
|
||||
TELEMETRY_ENABLED: zodStrBool.default("true"),
|
||||
POSTHOG_HOST: zpStr(z.string().optional().default("https://app.posthog.com")),
|
||||
POSTHOG_PROJECT_API_KEY: zpStr(
|
||||
z.string().optional().default("phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE")
|
||||
),
|
||||
POSTHOG_PROJECT_API_KEY: zpStr(z.string().optional().default("phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE")),
|
||||
LOOPS_API_KEY: zpStr(z.string().optional()),
|
||||
// jwt options
|
||||
AUTH_SECRET: zpStr(z.string()).default(process.env.JWT_AUTH_SECRET), // for those still using old JWT_AUTH_SECRET
|
||||
@@ -57,7 +55,12 @@ const envSchema = z
|
||||
CLIENT_SECRET_GITHUB_LOGIN: zpStr(z.string().optional()),
|
||||
CLIENT_ID_GITLAB_LOGIN: zpStr(z.string().optional()),
|
||||
CLIENT_SECRET_GITLAB_LOGIN: zpStr(z.string().optional()),
|
||||
CLIENT_GITLAB_LOGIN_URL: zpStr(z.string().optional().default(process.env.URL_GITLAB_LOGIN ?? GITLAB_URL)), // fallback since URL_GITLAB_LOGIN has been renamed
|
||||
CLIENT_GITLAB_LOGIN_URL: zpStr(
|
||||
z
|
||||
.string()
|
||||
.optional()
|
||||
.default(process.env.URL_GITLAB_LOGIN ?? GITLAB_URL)
|
||||
), // fallback since URL_GITLAB_LOGIN has been renamed
|
||||
// integration client secrets
|
||||
// heroku
|
||||
CLIENT_ID_HEROKU: zpStr(z.string().optional()),
|
||||
@@ -122,7 +125,7 @@ export const initEnvConfig = (logger: Logger) => {
|
||||
logger.error(parsedEnv.error.issues);
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
|
||||
envCfg = Object.freeze(parsedEnv.data);
|
||||
return envCfg;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ export const request = axios.create();
|
||||
|
||||
axiosRetry(request, {
|
||||
retries: 3,
|
||||
// eslint-disable-next-line
|
||||
retryDelay: axiosRetry.exponentialDelay,
|
||||
retryCondition: (err) => axiosRetry.isNetworkError(err) || axiosRetry.isRetryableError(err)
|
||||
});
|
||||
|
||||
@@ -20,11 +20,7 @@ export const BLOCK_SIZE_BYTES_16 = 16;
|
||||
export const decryptSymmetric = ({ ciphertext, iv, tag, key }: TDecryptSymmetricInput): string => {
|
||||
const secretKey = crypto.createSecretKey(key, "base64");
|
||||
|
||||
const decipher = crypto.createDecipheriv(
|
||||
SecretEncryptionAlgo.AES_256_GCM,
|
||||
secretKey,
|
||||
Buffer.from(iv, "base64")
|
||||
);
|
||||
const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, secretKey, Buffer.from(iv, "base64"));
|
||||
decipher.setAuthTag(Buffer.from(tag, "base64"));
|
||||
let cleartext = decipher.update(ciphertext, "base64", "utf8");
|
||||
cleartext += decipher.final("utf8");
|
||||
@@ -62,17 +58,8 @@ export const encryptSymmetric128BitHexKeyUTF8 = (plaintext: string, key: string)
|
||||
};
|
||||
};
|
||||
|
||||
export const decryptSymmetric128BitHexKeyUTF8 = ({
|
||||
ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
key
|
||||
}: TDecryptSymmetricInput): string => {
|
||||
const decipher = crypto.createDecipheriv(
|
||||
SecretEncryptionAlgo.AES_256_GCM,
|
||||
key,
|
||||
Buffer.from(iv, "base64")
|
||||
);
|
||||
export const decryptSymmetric128BitHexKeyUTF8 = ({ ciphertext, iv, tag, key }: TDecryptSymmetricInput): string => {
|
||||
const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, key, Buffer.from(iv, "base64"));
|
||||
|
||||
decipher.setAuthTag(Buffer.from(tag, "base64"));
|
||||
|
||||
@@ -104,12 +91,7 @@ export type TDecryptAsymmetricInput = {
|
||||
privateKey: string;
|
||||
};
|
||||
|
||||
export const decryptAsymmetric = ({
|
||||
ciphertext,
|
||||
nonce,
|
||||
publicKey,
|
||||
privateKey
|
||||
}: TDecryptAsymmetricInput) => {
|
||||
export const decryptAsymmetric = ({ ciphertext, nonce, publicKey, privateKey }: TDecryptAsymmetricInput) => {
|
||||
const plaintext: Uint8Array | null = nacl.box.open(
|
||||
naclUtils.decodeBase64(ciphertext),
|
||||
naclUtils.decodeBase64(nonce),
|
||||
@@ -223,7 +205,7 @@ export const infisicalSymmetricEncypt = (data: string) => {
|
||||
throw new Error("Missing both encryption keys");
|
||||
};
|
||||
|
||||
export const infisicalSymmetricDecrypt = <T extends any = string>({
|
||||
export const infisicalSymmetricDecrypt = <T = string>({
|
||||
keyEncoding,
|
||||
ciphertext,
|
||||
tag,
|
||||
|
||||
@@ -23,13 +23,10 @@ export const groupBy = <T, Key extends string | number | symbol>(
|
||||
* to convert each item in the list to a comparable identity
|
||||
* value
|
||||
*/
|
||||
export const unique = <T, K extends string | number | symbol>(
|
||||
array: readonly T[],
|
||||
toKey?: (item: T) => K
|
||||
): T[] => {
|
||||
export const unique = <T, K extends string | number | symbol>(array: readonly T[], toKey?: (item: T) => K): T[] => {
|
||||
const valueMap = array.reduce(
|
||||
(acc, item) => {
|
||||
const key = toKey ? toKey(item) : (item as any as string | number | symbol);
|
||||
const key = toKey ? toKey(item) : (item as unknown as string | number | symbol);
|
||||
if (acc[key]) return acc;
|
||||
acc[key] = item;
|
||||
return acc;
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
* Pick a list of properties from an object
|
||||
* into a new object
|
||||
*/
|
||||
export const pick = <T extends object, TKeys extends keyof T>(
|
||||
obj: T,
|
||||
keys: TKeys[]
|
||||
): Pick<T, TKeys> => {
|
||||
export const pick = <T extends object, TKeys extends keyof T>(obj: T, keys: TKeys[]): Pick<T, TKeys> => {
|
||||
if (!obj) return {} as Pick<T, TKeys>;
|
||||
return keys.reduce(
|
||||
(acc, key) => {
|
||||
@@ -21,9 +18,9 @@ export const pick = <T extends object, TKeys extends keyof T>(
|
||||
* object. Optional second argument shakes out values
|
||||
* by custom evaluation.
|
||||
*/
|
||||
export const shake = <RemovedKeys extends string, T = {}>(
|
||||
export const shake = <RemovedKeys extends string, T = object>(
|
||||
obj: T,
|
||||
filter: (value: any) => boolean = (x) => x === undefined || x === null
|
||||
filter: (value: unknown) => boolean = (x) => x === undefined || x === null
|
||||
): Omit<T, RemovedKeys> => {
|
||||
if (!obj) return {} as T;
|
||||
const keys = Object.keys(obj) as (keyof T)[];
|
||||
|
||||
@@ -111,13 +111,7 @@ export type TIp = {
|
||||
/**
|
||||
* Validates the IP address [ipAddress] against the trusted IPs [trustedIps].
|
||||
*/
|
||||
export const checkIPAgainstBlocklist = ({
|
||||
ipAddress,
|
||||
trustedIps
|
||||
}: {
|
||||
ipAddress: string;
|
||||
trustedIps: TIp[];
|
||||
}) => {
|
||||
export const checkIPAgainstBlocklist = ({ ipAddress, trustedIps }: { ipAddress: string; trustedIps: TIp[] }) => {
|
||||
const blockList = new net.BlockList();
|
||||
|
||||
for (const trustedIp of trustedIps) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
import { Knex } from "knex";
|
||||
import { Tables } from "knex/types/tables";
|
||||
|
||||
@@ -15,22 +16,22 @@ export const withTransaction = <K extends object>(db: Knex, dal: K) => ({
|
||||
...dal
|
||||
});
|
||||
|
||||
export type TFindFilter<R extends {} = any> = Partial<R> & {
|
||||
export type TFindFilter<R extends object = object> = Partial<R> & {
|
||||
$in?: Partial<{ [k in keyof R]: R[k][] }>;
|
||||
};
|
||||
export const buildFindFilter =
|
||||
<R extends {} = any>({ $in, ...filter }: TFindFilter<R>) =>
|
||||
<R extends object = object>({ $in, ...filter }: TFindFilter<R>) =>
|
||||
(bd: Knex.QueryBuilder<R, R>) => {
|
||||
bd.where(filter);
|
||||
void bd.where(filter);
|
||||
if ($in) {
|
||||
Object.entries($in).forEach(([key, val]) => {
|
||||
bd.whereIn(key as any, val as any);
|
||||
void bd.whereIn(key as never, val as never);
|
||||
});
|
||||
}
|
||||
return bd;
|
||||
};
|
||||
|
||||
export type TFindOpt<R extends {} = any> = {
|
||||
export type TFindOpt<R extends object = object> = {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
sort?: Array<[keyof R, "asc" | "desc"] | [keyof R, "asc" | "desc", "first" | "last"]>;
|
||||
@@ -40,11 +41,7 @@ export type TFindOpt<R extends {} = any> = {
|
||||
// What is ormify
|
||||
// It is to inject typical operations like find, findOne, update, delete, create
|
||||
// This will avoid writing most common ones each time
|
||||
export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
||||
db: Knex,
|
||||
tableName: Tname,
|
||||
dal?: DbOps
|
||||
) => ({
|
||||
export const ormify = <DbOps extends object, Tname extends keyof Tables>(db: Knex, tableName: Tname, dal?: DbOps) => ({
|
||||
transaction: async <T>(cb: (tx: Knex) => Promise<T>) =>
|
||||
db.transaction(async (trx) => {
|
||||
const res = await cb(trx);
|
||||
@@ -53,7 +50,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
||||
findById: async (id: string, tx?: Knex) => {
|
||||
try {
|
||||
const result = await (tx || db)(tableName)
|
||||
.where({ id } as any)
|
||||
.where({ id } as never)
|
||||
.first("*");
|
||||
return result;
|
||||
} catch (error) {
|
||||
@@ -74,12 +71,10 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
||||
) => {
|
||||
try {
|
||||
const query = (tx || db)(tableName).where(buildFindFilter(filter));
|
||||
if (limit) query.limit(limit);
|
||||
if (offset) query.offset(offset);
|
||||
if (limit) void query.limit(limit);
|
||||
if (offset) void query.offset(offset);
|
||||
if (sort) {
|
||||
query.orderBy(
|
||||
sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))
|
||||
);
|
||||
void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })));
|
||||
}
|
||||
const res = await query;
|
||||
return res;
|
||||
@@ -90,7 +85,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
||||
create: async (data: Tables[Tname]["insert"], tx?: Knex) => {
|
||||
try {
|
||||
const [res] = await (tx || db)(tableName)
|
||||
.insert(data as any)
|
||||
.insert(data as never)
|
||||
.returning("*");
|
||||
return res;
|
||||
} catch (error) {
|
||||
@@ -101,7 +96,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
||||
try {
|
||||
if (!data.length) return [];
|
||||
const res = await (tx || db)(tableName)
|
||||
.insert(data as any)
|
||||
.insert(data as never)
|
||||
.returning("*");
|
||||
return res;
|
||||
} catch (error) {
|
||||
@@ -111,23 +106,19 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
||||
updateById: async (id: string, data: Tables[Tname]["update"], tx?: Knex) => {
|
||||
try {
|
||||
const [res] = await (tx || db)(tableName)
|
||||
.where({ id } as any)
|
||||
.update(data as any)
|
||||
.where({ id } as never)
|
||||
.update(data as never)
|
||||
.returning("*");
|
||||
return res;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Update by id" });
|
||||
}
|
||||
},
|
||||
update: async (
|
||||
filter: TFindFilter<Tables[Tname]["base"]>,
|
||||
data: Tables[Tname]["update"],
|
||||
tx?: Knex
|
||||
) => {
|
||||
update: async (filter: TFindFilter<Tables[Tname]["base"]>, data: Tables[Tname]["update"], tx?: Knex) => {
|
||||
try {
|
||||
const res = await (tx || db)(tableName)
|
||||
.where(buildFindFilter(filter))
|
||||
.update(data as any)
|
||||
.update(data as never)
|
||||
.returning("*");
|
||||
return res;
|
||||
} catch (error) {
|
||||
@@ -137,7 +128,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
||||
deleteById: async (id: string, tx?: Knex) => {
|
||||
try {
|
||||
const [res] = await (tx || db)(tableName)
|
||||
.where({ id } as any)
|
||||
.where({ id } as never)
|
||||
.delete()
|
||||
.returning("*");
|
||||
return res;
|
||||
@@ -147,10 +138,7 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
||||
},
|
||||
delete: async (filter: TFindFilter<Tables[Tname]["base"]>, tx?: Knex) => {
|
||||
try {
|
||||
const res = await (tx || db)(tableName)
|
||||
.where(buildFindFilter(filter))
|
||||
.delete()
|
||||
.returning("*");
|
||||
const res = await (tx || db)(tableName).where(buildFindFilter(filter)).delete().returning("*");
|
||||
return res;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Delete" });
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
// TODO(akhilmhdh): make this better later
|
||||
|
||||
export const mergeOneToManyRelation = <
|
||||
T extends Record<string, any>,
|
||||
T extends Record<string, unknown>,
|
||||
Pk extends keyof T,
|
||||
P extends Record<string, any>,
|
||||
P extends Record<string, unknown>,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
|
||||
C extends any,
|
||||
Ck extends string = "child"
|
||||
>(
|
||||
@@ -22,7 +31,7 @@ export const mergeOneToManyRelation = <
|
||||
const parent = parentMapper(row) as any;
|
||||
parent[childKey] = [];
|
||||
groupedRecord.push(parent);
|
||||
prevPkId = pk;
|
||||
prevPkId = pk as string;
|
||||
prevPkIndex += 1;
|
||||
}
|
||||
groupedRecord[prevPkIndex][childKey].push(childMapper(row));
|
||||
@@ -41,6 +50,7 @@ export type TSqlPackRelationships<
|
||||
childrenMapper: C;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
|
||||
export type TChildMapper<T extends {}, U extends string = string, R extends unknown = unknown> = {
|
||||
key: keyof T;
|
||||
label: U;
|
||||
@@ -82,7 +92,7 @@ const sqlChildMapper = <
|
||||
const ck = `${prefix}-${label}-${doc[childPk]}`;
|
||||
const val = mapper(doc);
|
||||
if (!lookupTable.has(ck)) {
|
||||
if (typeof val !== "undefined" && val !== null) docsByPk[pk as keyof P][label].push(val);
|
||||
if (typeof val !== "undefined" && val !== null) docsByPk[pk][label].push(val);
|
||||
lookupTable.add(ck);
|
||||
}
|
||||
if (nestedMappers && val) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { initLogger,logger } from "./logger";
|
||||
export { initLogger, logger } from "./logger";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
// logger follows a singleton pattern
|
||||
// easier to use it that's all.
|
||||
import pino, { Logger } from "pino";
|
||||
@@ -14,10 +15,10 @@ const logLevelToSeverityLookup: Record<string, string> = {
|
||||
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let logger: Readonly<Logger>;
|
||||
// akhilmhdh:
|
||||
// The logger is not placed in the main app config to avoid a circular dependency.
|
||||
// The config requires the logger to display errors when an invalid environment is supplied.
|
||||
// On the other hand, the logger needs the config to obtain credentials for AWS or other transports.
|
||||
// akhilmhdh:
|
||||
// The logger is not placed in the main app config to avoid a circular dependency.
|
||||
// The config requires the logger to display errors when an invalid environment is supplied.
|
||||
// On the other hand, the logger needs the config to obtain credentials for AWS or other transports.
|
||||
// By keeping the logger separate, it becomes an independent package.
|
||||
|
||||
const loggerConfig = z.object({
|
||||
@@ -25,14 +26,23 @@ const loggerConfig = z.object({
|
||||
AWS_CLOUDWATCH_LOG_REGION: z.string().default("us-east-1"),
|
||||
AWS_CLOUDWATCH_LOG_ACCESS_KEY_ID: z.string().min(1).optional(),
|
||||
AWS_CLOUDWATCH_LOG_ACCESS_KEY_SECRET: z.string().min(1).optional(),
|
||||
AWS_CLOUDWATCH_LOG_INTERVAL: z.coerce.number().default(1000)
|
||||
AWS_CLOUDWATCH_LOG_INTERVAL: z.coerce.number().default(1000),
|
||||
NODE_ENV: z.enum(["development", "test", "production"]).default("production")
|
||||
});
|
||||
|
||||
export const initLogger = async () => {
|
||||
const targets: pino.TransportMultiOptions["targets"][number][] = [
|
||||
{ level: "info", target: "pino/file", options: {} }
|
||||
];
|
||||
const cfg = loggerConfig.parse(process.env);
|
||||
const targets: pino.TransportMultiOptions["targets"][number][] = [
|
||||
{
|
||||
level: "info",
|
||||
target: "pino/file",
|
||||
options: {
|
||||
destination: 1,
|
||||
mkdir: true
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
if (cfg.AWS_CLOUDWATCH_LOG_ACCESS_KEY_ID && cfg.AWS_CLOUDWATCH_LOG_ACCESS_KEY_SECRET) {
|
||||
targets.push({
|
||||
target: "@serdnam/pino-cloudwatch-transport",
|
||||
@@ -66,6 +76,7 @@ export const initLogger = async () => {
|
||||
})
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
transport
|
||||
);
|
||||
return logger;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { z,ZodTypeAny } from "zod";
|
||||
import { z, ZodTypeAny } from "zod";
|
||||
|
||||
// this is a patched zod string to remove empty string to undefined
|
||||
export const zpStr = <T extends ZodTypeAny>(
|
||||
schema: T,
|
||||
opt: { stripNull: boolean } = { stripNull: true }
|
||||
) =>
|
||||
export const zpStr = <T extends ZodTypeAny>(schema: T, opt: { stripNull: boolean } = { stripNull: true }) =>
|
||||
z.preprocess((val) => {
|
||||
if (opt.stripNull && val === null) return undefined;
|
||||
if (typeof val !== "string") return val;
|
||||
|
||||
Reference in New Issue
Block a user