feat(infisical-pg): auth injection completed and validation in password router

This commit is contained in:
Akhil Mohan
2023-12-05 14:53:45 +05:30
parent fcfd6b3fb2
commit f3b37de3f3
196 changed files with 1871 additions and 624 deletions

View File

@@ -18,6 +18,7 @@ module.exports = {
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"consistent-return": "off",
"simple-import-sort/imports": [
"warn",
{

View File

@@ -11,6 +11,7 @@
"lint:fix": "eslint --fix 'src/**/*.ts'",
"lint": "eslint 'src/**/*.ts'",
"generate:component": "tsx ./scripts/create-backend-file.ts",
"generate:schema": "tsx ./scripts/generate-schema-types.ts",
"migration:new": "tsx ./scripts/create-migration.ts",
"migration:up": "knex --knexfile ./src/db/knexfile.ts --client pg migrate:up",
"migration:down": "knex --knexfile ./src/db/knexfile.ts --client pg migrate:down",

View File

@@ -10,7 +10,6 @@ console.log(`
Component List
--------------
1. Service component
2. Schema file
`);
const componentType = parseInt(prompt("Select a component: "), 10);
@@ -55,27 +54,4 @@ export const ${serviceName} = ({ ${componentName}Dal }: ${serviceTypeName}Dep) =
`
);
writeFileSync(path.join(dir, `${componentName}-types.ts`), "");
} else if (componentType === 2) {
const componentName = prompt("Type component name in lowercase with space seperated: ");
const dashcase = componentName.split(" ").join("-");
const pascalCase = componentName
.split(" ")
.reduce(
(prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`,
""
);
writeFileSync(
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
`
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const ${pascalCase}Schema = z.object({});
export type T${pascalCase} = z.infer<typeof ${pascalCase}Schema>;
export type T${pascalCase}Insert = Omit<T${pascalCase}, TImmutableDBKeys>;
export type T${pascalCase}Update = Partial<Omit<T${pascalCase}, TImmutableDBKeys>>;
`
);
}

View File

@@ -0,0 +1,163 @@
import dotenv from "dotenv";
import path from "path";
import knex from "knex";
import { appendFileSync, readFileSync, writeFileSync } from "fs";
import promptSync from "prompt-sync";
const prompt = promptSync();
dotenv.config({
path: path.join(__dirname, "../.env"),
debug: true
});
const db = knex({
client: "pg",
connection: process.env.DB_CONNECTION_URI
});
const getZodPrimitiveType = (type: string) => {
switch (type) {
case "uuid":
return "z.string().uuid()";
case "character varying":
return "z.string()";
case "ARRAY":
return "z.string().array()";
case "boolean":
return "z.boolean()";
case "jsonb":
return "z.string()";
case "json":
return "z.string()";
case "timestamp with time zone":
return "z.string().datetime()";
case "integer":
return "z.number()";
case "text":
return "z.string()";
default:
throw new Error(`Invalid type: ${type}`);
}
};
const getZodDefaultValue = (type: unknown, value: string | number | boolean | Object) => {
if (!value || value === "null") return;
switch (type) {
case "uuid":
return;
case "character varying": {
if (typeof value === "string" && value.includes("::")) {
return `.default(${value.split("::")[0]})`;
}
return `.default(${value})`;
}
case "ARRAY":
return `.default(${value})`;
case "boolean":
return `.default(${value})`;
case "jsonb":
return "z.string()";
case "json":
return "z.string()";
case "timestamp with time zone": {
if (value === "CURRENT_TIMESTAMP") return;
return "z.string().datetime()";
}
case "integer": {
if ((value as string).includes("nextval")) return;
return `.default(${value})`;
}
case "text":
if (typeof value === "string" && value.includes("::")) {
return `.default(${value.split("::")[0]})`;
}
return `.default(${value})`;
default:
throw new Error(`Invalid type: ${type}`);
}
};
const main = async () => {
const tables = (
await db("information_schema.tables")
.whereRaw("table_schema = current_schema()")
.select<{ tableName: string }[]>("table_name as tableName")
.orderBy("table_name")
).filter(
(el) => el.tableName !== "infisical_migrations_lock" && el.tableName !== "infisical_migrations"
);
console.log("Select a table to generate schema");
console.table(tables);
console.log("all: all tables");
const selectedTables = prompt("Type table numbers comma seperated: ");
const tableNumbers =
selectedTables !== "all" ? selectedTables.split(",").map((el) => Number(el)) : [];
for (let i = 0; i < tables.length; i++) {
// skip if not desired table
if (selectedTables !== "all" && !tableNumbers.includes(i)) continue;
const tableName = tables[i].tableName;
const columns = await db(tableName).columnInfo();
const columnNames = Object.keys(columns);
let schema = "";
for (let colNum = 0; colNum < columnNames.length; colNum++) {
const columnName = columnNames[colNum];
const colInfo = columns[columnName];
let ztype = getZodPrimitiveType(colInfo.type);
if (colInfo.defaultValue) {
const defaultValue = colInfo.defaultValue;
const zSchema = getZodDefaultValue(colInfo.type, defaultValue);
if (zSchema) {
ztype = ztype.concat(zSchema);
}
}
if (colInfo.nullable) {
ztype = ztype.concat(".nullable().optional()");
}
schema = schema.concat(`${!schema ? "\n" : ""} ${columnName}: ${ztype},\n`);
}
const dashcase = tableName.split("_").join("-");
const pascalCase = tableName
.split("_")
.reduce(
(prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`,
""
);
writeFileSync(
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
`// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const ${pascalCase}Schema = z.object({${schema}});
export type T${pascalCase} = z.infer<typeof ${pascalCase}Schema>;
export type T${pascalCase}Insert = Omit<T${pascalCase}, TImmutableDBKeys>;
export type T${pascalCase}Update = Partial<Omit<T${pascalCase}, TImmutableDBKeys>>;
`
);
// const file = readFileSync(path.join(__dirname, "../src/db/schemas/index.ts"), "utf8");
// if (!file.includes(`export * from "./${dashcase};"`)) {
// appendFileSync(
// path.join(__dirname, "../src/db/schemas/index.ts"),
// `\nexport * from "./${dashcase}";`,
// "utf8"
// );
// }
}
process.exit(0);
};
main();

View File

@@ -3,6 +3,8 @@ import { TAuthDalFactory } from "@app/services/auth/auth-dal";
import { TAuthLoginFactory } from "@app/services/auth/auth-login-service";
import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service";
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
import { AuthMode } from "@app/services/auth/auth-signup-type";
import { TAuthTokenServiceFactory } from "@app/services/token/token-service";
import "fastify";
@@ -14,6 +16,12 @@ declare module "fastify" {
userId: string;
user: TUser;
};
// identity injection. depending on which kinda of token the information is filled in auth
auth: {
authMode: AuthMode.JWT | AuthMode.API_KEY_V2 | AuthMode.API_KEY;
userId: string;
user: TUser;
};
}
interface FastifyInstance {
@@ -21,6 +29,7 @@ declare module "fastify" {
login: TAuthLoginFactory;
password: TAuthPasswordFactory;
signup: TAuthSignupFactory;
authToken: TAuthTokenServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data

View File

@@ -2,42 +2,58 @@ import { Knex } from "knex";
import {
TableName,
TAuthTokens,
TAuthTokenSessions,
TAuthTokenSessionsInsert,
TAuthTokenSessionsUpdate,
TAuthTokensUpdate,
TBackupPrivateKey,
TBackupPrivateKeyInsert,
TToken,
TTokenInsert,
TTokenUpdate,
TUser,
TUserEncryptionKey,
TUserEncryptionKeyInsert,
TUserEncryptionKeyUpdate,
TUserInsert,
TUserUpdate
TBackupPrivateKeyUpdate,
TOrganizationMemberships,
TOrganizations,
TOrganizationsInsert,
TOrganizationsUpdate,
TUserEncryptionKeys,
TUserEncryptionKeysInsert,
TUserEncryptionKeysUpdate,
TUsers,
TUsersInsert,
TUsersUpdate
} from "@app/db/schemas";
import {
TTokenSession,
TTokenSessionInsert,
TTokenSessionUpdate
} from "@app/db/schemas/token-session";
declare module "knex/types/tables" {
interface Tables extends { [key in TableName]: Knex.CompositeTableType<any> } {
[TableName.Users]: Knex.CompositeTableType<TUser, TUserInsert, TUserUpdate>;
[TableName.Users]: Knex.CompositeTableType<TUsers, TUsersInsert, TUsersUpdate>;
[TableName.UserEncryptionKey]: Knex.CompositeTableType<
TUserEncryptionKey,
TUserEncryptionKeyInsert,
TUserEncryptionKeyUpdate
TUserEncryptionKeys,
TUserEncryptionKeysInsert,
TUserEncryptionKeysUpdate
>;
[TableName.AuthTokens]: Knex.CompositeTableType<
TAuthTokens,
TAuthTokensInsert,
TAuthTokensUpdate
>;
[TableName.AuthTokens]: Knex.CompositeTableType<TToken, TTokenInsert, TTokenUpdate>;
[TableName.AuthTokenSession]: Knex.CompositeTableType<
TTokenSession,
TTokenSessionInsert,
TTokenSessionUpdate
TAuthTokenSessions,
TAuthTokenSessionsInsert,
TAuthTokenSessionsUpdate
>;
[TableName.BackupPrivateKey]: Knex.CompositeTableType<
TBackupPrivateKey,
TBackupPrivateKeyInsert,
TTokenSessionUpdate
TBackupPrivateKeyUpdate
>;
[TableName.Organization]: Knex.CompositeTableType<
TOrganizations,
TOrganizationsInsert,
TOrganizationsUpdate
>;
[TableName.OrgMembership]: Knex.CompositeTableType<
TOrganizationMemberships,
TOrganizationsInsert,
TOrganizationsUpdate
>;
}
}

View File

@@ -12,7 +12,7 @@ export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.Users);
if (!isTablePresent) {
await knex.schema.createTable(TableName.Users, (t) => {
t.uuid("id").primary().defaultTo(knex.fn.uuid());
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("email").notNullable();
t.specificType("authMethods", "text[]");
t.boolean("superAdmin").defaultTo(false);

View File

@@ -6,10 +6,10 @@ export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.UserEncryptionKey);
if (!isTablePresent) {
await knex.schema.createTable(TableName.UserEncryptionKey, (t) => {
t.increments().primary();
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.text("clientPublicKey");
t.text("serverPrivateKey");
t.text("encryptionVersion").defaultTo(1);
t.integer("encryptionVersion").defaultTo(1);
t.text("protectedKey").notNullable();
t.text("protectedKeyIV").notNullable();
t.text("protectedKeyTag").notNullable();

View File

@@ -6,6 +6,7 @@ export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.AuthTokens);
if (!isTablePresent) {
await knex.schema.createTable(TableName.AuthTokens, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("type").notNullable();
t.string("phoneNumber");
t.string("tokenHash").notNullable();

View File

@@ -7,7 +7,7 @@ export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.AuthTokenSession);
if (!isTablePresent) {
await knex.schema.createTable(TableName.AuthTokenSession, (t) => {
t.increments();
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("ip").notNullable();
t.string("userAgent");
t.integer("refreshVersion").notNullable().defaultTo(1);

View File

@@ -6,7 +6,7 @@ export async function up(knex: Knex): Promise<void> {
const doesTableExist = await knex.schema.hasTable(TableName.BackupPrivateKey);
if (!doesTableExist) {
await knex.schema.createTable(TableName.BackupPrivateKey, (t) => {
t.increments();
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("encryptedPrivateKey").notNullable();
t.string("iv").notNullable();
t.string("tag").notNullable();

View File

@@ -0,0 +1,24 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.Organization);
if (!isTablePresent) {
await knex.schema.createTable(TableName.Organization, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("customerId");
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
});
}
// this is a one time function
await createOnUpdateTrigger(knex, TableName.Organization);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.Organization);
await dropOnUpdateTrigger(knex, TableName.Organization);
}

View File

@@ -0,0 +1,30 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { OrgMembershipStatus } from "../schemas/models";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.OrgMembership);
if (!isTablePresent) {
await knex.schema.createTable(TableName.OrgMembership, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("role").notNullable();
t.string("status").notNullable().defaultTo(OrgMembershipStatus.Invited);
t.string("inviteEmail");
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.uuid("userId").notNullable();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
});
}
// this is a one time function
await createOnUpdateTrigger(knex, TableName.OrgMembership);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.OrgMembership);
await dropOnUpdateTrigger(knex, TableName.OrgMembership);
}

View File

@@ -0,0 +1,24 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const AuthTokenSessionsSchema = z.object({
id: z.string().uuid(),
ip: z.string(),
userAgent: z.string().nullable().optional(),
refreshVersion: z.number().default(1),
accessVersion: z.number().default(1),
lastUsed: z.string().datetime(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
userId: z.string().uuid(),
});
export type TAuthTokenSessions = z.infer<typeof AuthTokenSessionsSchema>;
export type TAuthTokenSessionsInsert = Omit<TAuthTokenSessions, TImmutableDBKeys>;
export type TAuthTokenSessionsUpdate = Partial<Omit<TAuthTokenSessions, TImmutableDBKeys>>;

View File

@@ -0,0 +1,24 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const AuthTokensSchema = z.object({
id: z.string().uuid(),
type: z.string(),
phoneNumber: z.string().nullable().optional(),
tokenHash: z.string(),
triesLeft: z.number().nullable().optional(),
expiresAt: z.string().datetime(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
userId: z.string().uuid().nullable().optional(),
});
export type TAuthTokens = z.infer<typeof AuthTokensSchema>;
export type TAuthTokensInsert = Omit<TAuthTokens, TImmutableDBKeys>;
export type TAuthTokensUpdate = Partial<Omit<TAuthTokens, TImmutableDBKeys>>;

View File

@@ -1,19 +1,24 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { SecretEncryptionAlgo, SecretKeyEncoding, TImmutableDBKeys } from "./models";
import { TImmutableDBKeys } from "./models";
export const BackupPrivateKeySchema = z.object({
id: z.string(),
userId: z.string(),
id: z.string().uuid(),
encryptedPrivateKey: z.string(),
iv: z.string(),
tag: z.string(),
algorithm: z.nativeEnum(SecretEncryptionAlgo),
keyEncoding: z.nativeEnum(SecretKeyEncoding),
algorithm: z.string(),
keyEncoding: z.string(),
salt: z.string(),
verifier: z.string(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime()
updatedAt: z.string().datetime(),
userId: z.string().uuid(),
});
export type TBackupPrivateKey = z.infer<typeof BackupPrivateKeySchema>;

View File

@@ -1,15 +1,8 @@
export {
BackupPrivateKeySchema,
TBackupPrivateKey,
TBackupPrivateKeyInsert,
TBackupPrivateKeyUpdate
} from "./backup-private-key";
export { TableName } from "./models";
export { TokenSchema, TToken, TTokenInsert, TTokenUpdate } from "./token";
export { AuthMethod, TUser, TUserInsert, TUserUpdate, UserSchema } from "./user";
export {
TUserEncryptionKey,
TUserEncryptionKeyInsert,
TUserEncryptionKeyUpdate,
UserEncryptionKey
} from "./user-encryption-key";
export * from "./auth-token-sessions";
export * from "./auth-tokens";
export * from "./backup-private-key";
export * from "./models";
export * from "./organization-memberships";
export * from "./organizations";
export * from "./user-encryption-keys";
export * from "./users";

View File

@@ -1,13 +1,36 @@
import { z } from "zod";
export enum TableName {
Users = "users",
UserEncryptionKey = "user_encryption_keys",
AuthTokens = "auth_tokens",
AuthTokenSession = "auth_token_sessions",
BackupPrivateKey = "backup_private_key"
BackupPrivateKey = "backup_private_key",
Organization = "organizations",
OrgMembership = "organization_memberships"
}
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
export const UserDeviceSchema = z
.object({
ip: z.string(),
userAgent: z.string()
})
.array()
.default([]);
export enum OrgMembershipRole {
Admin = "admin",
Member = "member",
Custom = "custom"
}
export enum OrgMembershipStatus {
Invited = "invited",
Accepted = "accepted"
}
export enum SecretEncryptionAlgo {
AES_256_GCM = "aes-256-gcm"
}

View File

@@ -0,0 +1,23 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const OrganizationMembershipsSchema = z.object({
id: z.string().uuid(),
role: z.string(),
status: z.string().default('invited'),
inviteEmail: z.string().nullable().optional(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
userId: z.string().uuid(),
orgId: z.string().uuid(),
});
export type TOrganizationMemberships = z.infer<typeof OrganizationMembershipsSchema>;
export type TOrganizationMembershipsInsert = Omit<TOrganizationMemberships, TImmutableDBKeys>;
export type TOrganizationMembershipsUpdate = Partial<Omit<TOrganizationMemberships, TImmutableDBKeys>>;

View File

@@ -0,0 +1,20 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const OrganizationsSchema = z.object({
id: z.string().uuid(),
name: z.string(),
customerId: z.string().nullable().optional(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
});
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
export type TOrganizationsInsert = Omit<TOrganizations, TImmutableDBKeys>;
export type TOrganizationsUpdate = Partial<Omit<TOrganizations, TImmutableDBKeys>>;

View File

@@ -1,17 +0,0 @@
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const TokenSessionSchema = z.object({
id: z.string(),
userId: z.string(),
ip: z.string(),
userAgent: z.string(),
refreshVersion: z.number().default(1),
accessVersion: z.number().default(1),
lastUsed: z.string().datetime()
});
export type TTokenSession = z.infer<typeof TokenSessionSchema>;
export type TTokenSessionInsert = Omit<TTokenSession, TImmutableDBKeys>;
export type TTokenSessionUpdate = Partial<Omit<TTokenSession, TImmutableDBKeys>>;

View File

@@ -1,19 +0,0 @@
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const TokenSchema = z.object({
id: z.number(),
type: z.enum(["emailConfirmation", "emailMfa", "organizationInvitation", "passwordReset"]),
phoneNumber: z.string().optional(),
tokenHash: z.string(),
triesLeft: z.number().optional(),
expiresAt: z.string().datetime().optional(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
userId: z.string().optional()
});
export type TToken = z.infer<typeof TokenSchema>;
export type TTokenInsert = Omit<TToken, TImmutableDBKeys>;
export type TTokenUpdate = Partial<Omit<TToken, TImmutableDBKeys>>;

View File

@@ -1,26 +0,0 @@
import { z } from "zod";
import { PickRequired } from "@app/lib/types";
import { TImmutableDBKeys } from "./models";
export const UserEncryptionKey = z.object({
id: z.number(),
userId: z.string(),
serverPrivateKey: z.string().optional().nullable(),
clientPublicKey: z.string().optional().nullable(),
encryptionVersion: z.number().default(1).optional(),
protectedKey: z.string(),
protectedKeyIV: z.string(),
protectedKeyTag: z.string(),
publicKey: z.string(),
encryptedPrivateKey: z.string(),
iv: z.string(),
tag: z.string(),
salt: z.string(),
verifier: z.string()
});
export type TUserEncryptionKey = z.infer<typeof UserEncryptionKey>;
export type TUserEncryptionKeyInsert = Omit<PickRequired<TUserEncryptionKey>, TImmutableDBKeys>;
export type TUserEncryptionKeyUpdate = Partial<Omit<TUserEncryptionKey, TImmutableDBKeys>>;

View File

@@ -0,0 +1,29 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const UserEncryptionKeysSchema = z.object({
id: z.string().uuid(),
clientPublicKey: z.string().nullable().optional(),
serverPrivateKey: z.string().nullable().optional(),
encryptionVersion: z.number().default(1).nullable().optional(),
protectedKey: z.string(),
protectedKeyIV: z.string(),
protectedKeyTag: z.string(),
publicKey: z.string(),
encryptedPrivateKey: z.string(),
iv: z.string(),
tag: z.string(),
salt: z.string(),
verifier: z.string(),
userId: z.string().uuid(),
});
export type TUserEncryptionKeys = z.infer<typeof UserEncryptionKeysSchema>;
export type TUserEncryptionKeysInsert = Omit<TUserEncryptionKeys, TImmutableDBKeys>;
export type TUserEncryptionKeysUpdate = Partial<Omit<TUserEncryptionKeys, TImmutableDBKeys>>;

View File

@@ -1,38 +0,0 @@
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export enum AuthMethod {
EMAIL = "email",
GOOGLE = "google",
GITHUB = "github",
GITLAB = "gitlab",
OKTA_SAML = "okta-saml",
AZURE_SAML = "azure-saml",
JUMPCLOUD_SAML = "jumpcloud-saml"
}
export const UserSchema = z.object({
id: z.string(),
authMethods: z.nativeEnum(AuthMethod).array().default([AuthMethod.EMAIL]).optional().nullable(),
email: z.string(),
isSuperAdmin: z.boolean().default(false).optional(),
firstName: z.string().optional().nullable(),
lastName: z.string().optional().nullable(),
isMfaEnabled: z.boolean().default(false).optional(),
mfaMethods: z.string().array().default([]).optional().nullable(),
isAccepted: z.boolean().default(false).optional(),
devices: z.string().nullable().optional()
});
export const UserDeviceSchema = z
.object({
ip: z.string(),
userAgent: z.string()
})
.array()
.default([]);
export type TUser = z.infer<typeof UserSchema>;
export type TUserInsert = Omit<TUser, TImmutableDBKeys>;
export type TUserUpdate = Partial<Omit<TUser, "id" | "email">>;

View File

@@ -0,0 +1,27 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const UsersSchema = z.object({
id: z.string().uuid(),
email: z.string(),
authMethods: z.string().array().nullable().optional(),
superAdmin: z.boolean().default(false).nullable().optional(),
firstName: z.string().nullable().optional(),
lastName: z.string().nullable().optional(),
isAccepted: z.boolean().default(false).nullable().optional(),
isMfaEnabled: z.boolean().default(false).nullable().optional(),
mfaMethods: z.string().array().nullable().optional(),
devices: z.string().nullable().optional(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
});
export type TUsers = z.infer<typeof UsersSchema>;
export type TUsersInsert = Omit<TUsers, TImmutableDBKeys>;
export type TUsersUpdate = Partial<Omit<TUsers, TImmutableDBKeys>>;

View File

@@ -16,7 +16,7 @@ export class UnauthorizedError extends Error {
error: unknown;
constructor({ name, error, message }: { message?: string; 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;

View File

@@ -0,0 +1,10 @@
import { Knex } from "knex";
export const withTransaction = <K extends object>(db: Knex, dal: K) => ({
transaction: async <T>(cb: (tx: Knex) => T) =>
db.transaction(async (trx) => {
const res = await cb(trx);
return res;
}),
...dal
});

View File

@@ -1,7 +1,7 @@
import { z } from "zod";
import { z,ZodTypeAny } from "zod";
// this is a patched zod string to remove empty string to undefined
export const zpStr = <T extends z.ZodString>(
export const zpStr = <T extends ZodTypeAny>(
schema: T,
opt: { stripNull: boolean } = { stripNull: true }
) =>

View File

@@ -0,0 +1,75 @@
import { FastifyRequest } from "fastify";
import jwt, { JwtPayload } from "jsonwebtoken";
import { getConfig } from "@app/lib/config/env";
import { UnauthorizedError } from "@app/lib/errors";
import { AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
const apiKey = req.headers?.["x-api-key"];
if (apiKey) {
return { authMode: AuthMode.API_KEY, token: apiKey };
}
const authHeader = req.headers?.authorization;
if (!authHeader) return { authMode: null, token: null };
const authTokenValue = authHeader.slice(7); // slice of after Bearer
if (authTokenValue.startsWith("st.")) {
return { authMode: AuthMode.SERVICE_TOKEN, token: authTokenValue } as const;
}
const decodedToken = jwt.verify(authTokenValue, jwtSecret) as JwtPayload;
switch (decodedToken.authTokenType) {
case AuthTokenType.ACCESS_TOKEN:
return { authMode: AuthMode.JWT, token: decodedToken as AuthModeJwtTokenPayload } as const;
case AuthTokenType.API_KEY:
return { authMode: AuthMode.API_KEY_V2, token: decodedToken } as const;
case AuthMode.SERVICE_ACCESS_TOKEN:
return { authMode: AuthMode.SERVICE_ACCESS_TOKEN, token: decodedToken } as const;
default:
throw new UnauthorizedError({ name: "Invalid token type" });
}
};
const getJwtIdentity = async (server: FastifyZodProvider, token: AuthModeJwtTokenPayload) => {
const session = await server.services.authToken.getUserTokenSessionById(
token.tokenVersionId,
token.userId
);
if (!session) throw new UnauthorizedError({ name: "Session not found" });
if (token.accessVersion !== session.accessVersion)
throw new UnauthorizedError({ name: "Stale session" });
const user = await server.store.user.getUserById(session.userId);
if (!user || !user.isAccepted) throw new UnauthorizedError({ name: "Token user not found" });
return user;
};
export const injectIdentity = (server: FastifyZodProvider) => {
server.decorateRequest("auth", null);
server.addHook("onRequest", async (req) => {
const appCfg = getConfig();
const { authMode, token } = await extractAuth(req, appCfg.JWT_AUTH_SECRET);
if (!authMode) return;
// TODO(akhilmhdh-pg): fill in rest of auth mode logic
switch (authMode) {
case AuthMode.JWT: {
const user = await getJwtIdentity(server, token as AuthModeJwtTokenPayload);
req.auth = { authMode: AuthMode.JWT, user, userId: user.id };
break;
}
case AuthMode.SERVICE_TOKEN:
break;
case AuthMode.SERVICE_ACCESS_TOKEN:
break;
case AuthMode.API_KEY:
break;
case AuthMode.API_KEY_V2:
break;
default:
throw new UnauthorizedError({ name: "Unknown token strategy" });
}
});
};

View File

@@ -0,0 +1,17 @@
import { FastifyRequest } from "fastify";
import { UnauthorizedError } from "@app/lib/errors";
import { AuthMode } from "@app/services/auth/auth-type";
export const verifyAuth =
<T extends FastifyRequest>(authStrats: AuthMode[]) =>
(req: T) => {
if (!Array.isArray(authStrats)) throw new Error("Auth strategy must be array");
if (!req.auth)
throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" });
const isAccessAllowed = authStrats.some((strat) => strat === req.auth.authMode);
if (!isAccessAllowed) {
throw new UnauthorizedError({ name: `${req.url} Unauthorized Access` });
}
};

View File

@@ -11,6 +11,7 @@ import { tokenServiceFactory } from "@app/services/token/token-service";
import { registerV1Routes } from "./v1";
import { registerV2Routes } from "./v2";
import { registerV3Routes } from "./v3";
import { injectIdentity } from "../plugins/auth/inject-identity";
export const registerRoutes = async (
server: FastifyZodProvider,
@@ -37,6 +38,8 @@ export const registerRoutes = async (
user: authDal
} as FastifyZodProvider["store"]);
await server.register(injectIdentity);
// register routes for v1
await server.register(registerV1Routes, { prefix: "/v1" });
await server.register(registerV2Routes, { prefix: "/v2" });

View File

@@ -2,6 +2,8 @@ import { z } from "zod";
import { BackupPrivateKeySchema } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerPasswordRouter = async (server: FastifyZodProvider) => {
server.route({
@@ -18,6 +20,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { salt, serverPublicKey } = await server.services.password.generateServerPubKey(
req.auth.userId,
@@ -48,6 +51,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req, res) => {
const appCfg = getConfig();
await server.services.password.changePassword({ ...req.body, userId: req.auth.userId });
@@ -65,6 +69,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/backup-private-key",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
body: z.object({
clientProof: z.string().trim(),
@@ -95,6 +100,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/backup-private-key",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
response: {
200: z.object({

View File

@@ -2,20 +2,24 @@ import jwt, { JwtPayload } from "jsonwebtoken";
import { z } from "zod";
import { getConfig } from "@app/lib/config/env";
import { AuthTokenType } from "@app/services/auth/auth-signup-type";
import { AuthTokenType } from "@app/services/auth/auth-type";
export const registerMfaRouter = async (server: FastifyZodProvider) => {
const cfg = getConfig();
server.decorateRequest("mfa", null);
server.addHook("preParsing", async (req, res) => {
const authorizationHeader = req.headers.authorization;
if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) {
res.status(401).send({ error: "Missing bearer token" });
return;
return res;
}
const token = authorizationHeader.split(" ")[1];
if (!token) res.status(401).send({ error: "Missing bearer token" });
if (!token) {
res.status(401).send({ error: "Missing bearer token" });
return res;
}
const decodedToken = jwt.verify(token, cfg.JWT_AUTH_SECRET) as JwtPayload;
if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN)
@@ -23,8 +27,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
const user = await server.store.user.getUserById(decodedToken.userId);
if (!user) throw new Error("User not found");
req.mfa.userId = user.id;
req.mfa.user = user;
req.mfa = { userId: user.id, user };
});
server.route({
@@ -52,7 +55,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
encryptionVersion: z.number().default(1).optional(),
encryptionVersion: z.number().default(1).nullable().optional(),
protectedKey: z.string(),
protectedKeyIV: z.string(),
protectedKeyTag: z.string(),

View File

@@ -44,7 +44,7 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
z.object({ mfaEnabled: z.literal(true), token: z.string() }),
z.object({
mfaEnabled: z.literal(false),
encryptionVersion: z.number().default(1).optional(),
encryptionVersion: z.number().default(1).nullable().optional(),
protectedKey: z.string(),
protectedKeyIV: z.string(),
protectedKeyTag: z.string(),

View File

@@ -1,6 +1,6 @@
import { z } from "zod";
import { UserSchema } from "@app/db/schemas";
import { UsersSchema } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
export const registerSignupRouter = async (server: FastifyZodProvider) => {
@@ -35,7 +35,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
200: z.object({
message: z.string(),
token: z.string(),
user: UserSchema
user: UsersSchema
})
}
},
@@ -72,7 +72,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
response: {
200: z.object({
message: z.string(),
user: UserSchema,
user: UsersSchema,
token: z.string()
})
}

View File

@@ -1,16 +1,17 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { TableName, TBackupPrivateKey, TUser, TUserEncryptionKey } from "@app/db/schemas";
import { TableName, TBackupPrivateKey, TUserEncryptionKeys, TUsers } from "@app/db/schemas";
import { withTransaction } from "@app/lib/knex";
export type TAuthDalFactory = ReturnType<typeof authDalFactory>;
export const authDalFactory = (db: TDbClient) => {
// getters
const getUserByEmail = async (email: string): Promise<TUser | undefined> =>
const getUserByEmail = async (email: string): Promise<TUsers | undefined> =>
db(TableName.Users).where({ email }).select("*").first();
const getUserById = async (userId: string): Promise<TUser | undefined> =>
const getUserById = async (userId: string): Promise<TUsers | undefined> =>
db(TableName.Users).where({ id: userId }).select("*").first();
const getUserEncKeyByEmail = async (email: string) =>
@@ -39,8 +40,8 @@ export const authDalFactory = (db: TDbClient) => {
// all inserts and updates
const createUser = async (
email: string,
data: Partial<TUser> = {}
): Promise<TUser | undefined> => {
data: Partial<TUsers> = {}
): Promise<TUsers | undefined> => {
const [user] = await db(TableName.Users)
.insert({ email, ...data })
.returning("*");
@@ -49,8 +50,8 @@ export const authDalFactory = (db: TDbClient) => {
const updateUser = async (
email: string,
data: Partial<TUser> = {}
): Promise<TUser | undefined> => {
data: Partial<TUsers> = {}
): Promise<TUsers | undefined> => {
const [user] = await db(TableName.Users)
.where({ email })
.update({ ...data })
@@ -60,9 +61,9 @@ export const authDalFactory = (db: TDbClient) => {
const updateUserById = async (
id: string,
data: Partial<TUser> = {},
data: Partial<TUsers> = {},
tx?: Knex
): Promise<TUser | undefined> => {
): Promise<TUsers | undefined> => {
const [user] = await (tx ? tx(TableName.Users) : db(TableName.Users))
.where({ id })
.update({ ...data })
@@ -72,9 +73,9 @@ export const authDalFactory = (db: TDbClient) => {
const updateUserEncryptionByUserId = async (
userId: string,
data: Partial<TUserEncryptionKey> = {},
data: Partial<TUserEncryptionKeys> = {},
tx?: Knex
): Promise<TUserEncryptionKey | undefined> => {
): Promise<TUserEncryptionKeys | undefined> => {
const [userEnc] = await (tx ? tx(TableName.UserEncryptionKey) : db(TableName.UserEncryptionKey))
.where({ userId })
.update({ ...data })
@@ -85,12 +86,12 @@ export const authDalFactory = (db: TDbClient) => {
// all upserts
const upsertUserEncryptionKey = async (
userId: string,
data: Partial<TUserEncryptionKey>,
data: Partial<TUserEncryptionKeys>,
tx?: Knex
) => {
const [userEnc] = await (tx ? tx(TableName.UserEncryptionKey) : db(TableName.UserEncryptionKey))
// if user insert make sure to pass all required data
.insert({ userId, ...data } as TUserEncryptionKey)
.insert({ userId, ...data } as TUserEncryptionKeys)
.onConflict("userId")
.merge()
.returning("*");
@@ -110,12 +111,7 @@ export const authDalFactory = (db: TDbClient) => {
return backupKey;
};
return {
transaction: async <T>(cb: (tx: Knex) => T) =>
db.transaction(async (trx) => {
const res = await cb(trx);
return res;
}),
return withTransaction(db, {
getUserByEmail,
getUserById,
getUserEncKeyByEmail,
@@ -127,5 +123,5 @@ export const authDalFactory = (db: TDbClient) => {
updateUserEncryptionByUserId,
upsertUserEncryptionKey,
upsertBackupKey
};
});
};

View File

@@ -1,12 +1,11 @@
import jwt from "jsonwebtoken";
import { AuthMethod, TUser } from "@app/db/schemas";
import { UserDeviceSchema } from "@app/db/schemas/user";
import { TUsers, UserDeviceSchema } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
import { TTokenServiceFactory } from "../token/token-service";
import { TAuthTokenServiceFactory } from "../token/token-service";
import { TokenType } from "../token/token-types";
import { TAuthDalFactory } from "./auth-dal";
import {
@@ -14,7 +13,7 @@ import {
TLoginGenServerPublicKeyDTO,
TVerifyMfaTokenDTO
} from "./auth-login-type";
import { AuthTokenType } from "./auth-signup-type";
import { AuthMethod, AuthTokenType } from "./auth-type";
const isValidProviderAuthToken = (email: string, jwtSecret: string, providerAuthToken?: string) => {
if (!providerAuthToken) return false;
@@ -27,7 +26,7 @@ const isValidProviderAuthToken = (email: string, jwtSecret: string, providerAuth
type TAuthLoginServiceFactoryDep = {
authDal: TAuthDalFactory;
tokenService: TTokenServiceFactory;
tokenService: TAuthTokenServiceFactory;
smtpService: TSmtpService;
};
@@ -42,7 +41,7 @@ export const authLoginServiceFactory = ({
* Not exported. This is to update user device list
* If new device is found. Will be saved and a mail will be send
*/
const updateUserDeviceSession = async (user: TUser, ip: string, userAgent: string) => {
const updateUserDeviceSession = async (user: TUsers, ip: string, userAgent: string) => {
const devices = await UserDeviceSchema.parseAsync(JSON.parse(user.devices || "[]"));
const isDeviceSeen = devices.some(
(device) => device.ip === ip && device.userAgent === userAgent
@@ -69,7 +68,7 @@ export const authLoginServiceFactory = ({
* Private
* Send mfa code via email
* */
const sendUserMfaCode = async (user: TUser) => {
const sendUserMfaCode = async (user: TUsers) => {
const code = await tokenService.createTokenForUser({
type: TokenType.TOKEN_EMAIL_MFA,
userId: user.id
@@ -89,7 +88,7 @@ export const authLoginServiceFactory = ({
* Check user device and send mail if new device
* generate the auth and refresh token. fn shared by mfa verification and login verification with mfa disabled
*/
const generateUserTokens = async (user: TUser, ip: string, userAgent: string) => {
const generateUserTokens = async (user: TUsers, ip: string, userAgent: string) => {
const cfg = getConfig();
await updateUserDeviceSession(user, ip, userAgent);
const tokenSession = await tokenService.getUserTokenSession({

View File

@@ -4,7 +4,7 @@ import { getConfig } from "@app/lib/config/env";
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
import { TTokenServiceFactory } from "../token/token-service";
import { TAuthTokenServiceFactory } from "../token/token-service";
import { TokenType } from "../token/token-types";
import { TAuthDalFactory } from "./auth-dal";
import {
@@ -12,11 +12,11 @@ import {
TCreateBackupPrivateKeyDTO,
TResetPasswordViaBackupKeyDTO
} from "./auth-password-type";
import { AuthTokenType } from "./auth-signup-type";
import { AuthTokenType } from "./auth-type";
type TAuthPasswordServiceFactoryDep = {
authDal: TAuthDalFactory;
tokenService: TTokenServiceFactory;
tokenService: TAuthTokenServiceFactory;
smtpService: TSmtpService;
};

View File

@@ -1,16 +1,18 @@
import { AuthMethod } from "@app/db/schemas";
import jwt from "jsonwebtoken";
import { getConfig } from "@app/lib/config/env";
import { isDisposableEmail } from "@app/lib/validator";
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
import { TTokenServiceFactory } from "../token/token-service";
import { TAuthTokenServiceFactory } from "../token/token-service";
import { TokenType } from "../token/token-types";
import { TAuthDalFactory } from "./auth-dal";
import { AuthTokenType, TCompleteAccountSignupDTO } from "./auth-signup-type";
import { TCompleteAccountSignupDTO } from "./auth-signup-type";
import { AuthMethod, AuthTokenType } from "./auth-type";
type TAuthSignupDep = {
authDal: TAuthDalFactory;
tokenService: TTokenServiceFactory;
tokenService: TAuthTokenServiceFactory;
smtpService: TSmtpService;
};
@@ -66,7 +68,7 @@ export const authSignupServiceFactory = ({
});
// generate jwt token this is a temporary token
const jwtToken = tokenService.createJwtToken(
const jwtToken = jwt.sign(
{
authTokenType: AuthTokenType.SIGNUP_TOKEN,
userId: user.id.toString()
@@ -136,7 +138,7 @@ export const authSignupServiceFactory = ({
if (!tokenSession) throw new Error("Failed to create token");
const appCfg = getConfig();
const accessToken = tokenService.createJwtToken(
const accessToken = jwt.sign(
{
authTokenType: AuthTokenType.ACCESS_TOKEN,
userId: updateduser.info.id,
@@ -147,7 +149,7 @@ export const authSignupServiceFactory = ({
{ expiresIn: appCfg.JWT_SIGNUP_LIFETIME }
);
const refreshToken = tokenService.createJwtToken(
const refreshToken = jwt.sign(
{
authTokenType: AuthTokenType.REFRESH_TOKEN,
userId: updateduser.info.id,

View File

@@ -1,22 +1,3 @@
export enum AuthTokenType {
ACCESS_TOKEN = "accessToken",
REFRESH_TOKEN = "refreshToken",
SIGNUP_TOKEN = "signupToken", // TODO: remove in favor of claim
MFA_TOKEN = "mfaToken", // TODO: remove in favor of claim
PROVIDER_TOKEN = "providerToken", // TODO: remove in favor of claim
API_KEY = "apiKey",
SERVICE_ACCESS_TOKEN = "serviceAccessToken",
SERVICE_REFRESH_TOKEN = "serviceRefreshToken"
}
export enum AuthMode {
JWT = "jwt",
SERVICE_TOKEN = "serviceToken",
SERVICE_ACCESS_TOKEN = "serviceAccessToken",
API_KEY = "apiKey",
API_KEY_V2 = "apiKeyV2"
}
export type TCompleteAccountSignupDTO = {
email: string;
firstName: string;

View File

@@ -0,0 +1,35 @@
export enum AuthMethod {
EMAIL = "email",
GOOGLE = "google",
GITHUB = "github",
GITLAB = "gitlab",
OKTA_SAML = "okta-saml",
AZURE_SAML = "azure-saml",
JUMPCLOUD_SAML = "jumpcloud-saml"
}
export enum AuthTokenType {
ACCESS_TOKEN = "accessToken",
REFRESH_TOKEN = "refreshToken",
SIGNUP_TOKEN = "signupToken", // TODO: remove in favor of claim
MFA_TOKEN = "mfaToken", // TODO: remove in favor of claim
PROVIDER_TOKEN = "providerToken", // TODO: remove in favor of claim
API_KEY = "apiKey",
SERVICE_ACCESS_TOKEN = "serviceAccessToken",
SERVICE_REFRESH_TOKEN = "serviceRefreshToken"
}
export enum AuthMode {
JWT = "jwt",
SERVICE_TOKEN = "serviceToken",
SERVICE_ACCESS_TOKEN = "serviceAccessToken",
API_KEY = "apiKey",
API_KEY_V2 = "apiKeyV2"
}
export type AuthModeJwtTokenPayload = {
authTokenType: AuthTokenType.ACCESS_TOKEN;
userId: string;
tokenVersionId: string;
accessVersion: number;
};

View File

@@ -1,6 +1,5 @@
import { TDbClient } from "@app/db";
import { TableName, TToken } from "@app/db/schemas";
import { TTokenSession } from "@app/db/schemas/token-session";
import { TableName, TAuthTokens, TAuthTokenSessions } from "@app/db/schemas";
import {
TDeleteTokenForUserDalDTO,
@@ -19,7 +18,7 @@ export const tokenDalFactory = (db: TDbClient) => {
userId,
type,
triesLeft
}: TUpsertTokenForUserDalDTO): Promise<TToken | undefined> => {
}: TUpsertTokenForUserDalDTO): Promise<TAuthTokens | undefined> => {
const token = await db.transaction(async (tx) => {
await tx(TableName.AuthTokens).where({ userId, type }).delete().returning("*");
const [newToken] = await tx(TableName.AuthTokens)
@@ -33,13 +32,23 @@ export const tokenDalFactory = (db: TDbClient) => {
const getTokenForUser = async ({
userId,
type
}: TGetTokenForUserDalDTO): Promise<TToken | undefined> =>
}: TGetTokenForUserDalDTO): Promise<TAuthTokens | undefined> =>
db(TableName.AuthTokens).where({ userId, type }).first();
const getTokenSession = async (
userId: string,
ip: string,
userAgent: string
): Promise<TAuthTokenSessions | undefined> =>
db(TableName.AuthTokenSession).where({ userId, ip, userAgent }).first();
const getTokenSessionById = async (id: string, userId: string) =>
db(TableName.AuthTokenSession).where({ id, userId }).first();
const deleteTokenForUser = async ({
userId,
type
}: TDeleteTokenForUserDalDTO): Promise<TToken[] | undefined> =>
}: TDeleteTokenForUserDalDTO): Promise<TAuthTokens[] | undefined> =>
db(TableName.AuthTokens).where({ userId, type }).delete().returning("*");
const decrementTriesField = async ({
@@ -49,18 +58,11 @@ export const tokenDalFactory = (db: TDbClient) => {
await db(TableName.AuthTokens).where({ userId, type }).decrement("triesLeft", 1);
};
const getTokenSession = async (
userId: string,
ip: string,
userAgent: string
): Promise<TTokenSession | undefined> =>
db(TableName.AuthTokenSession).where({ userId, ip, userAgent }).first();
const insertTokenSession = async (
userId: string,
ip: string,
userAgent: string
): Promise<TTokenSession | undefined> => {
): Promise<TAuthTokenSessions | undefined> => {
const [session] = await db(TableName.AuthTokenSession)
.insert({
userId,
@@ -77,7 +79,7 @@ export const tokenDalFactory = (db: TDbClient) => {
const incrementVersion = async (
userId: string,
sessionId: string
): Promise<TTokenSession | undefined> => {
): Promise<TAuthTokenSessions | undefined> => {
const [session] = await db(TableName.AuthTokenSession)
.where({ userId, id: sessionId })
.increment("accessVersion", 1)
@@ -87,8 +89,9 @@ export const tokenDalFactory = (db: TDbClient) => {
};
return {
upsertTokenForUser,
getTokenForUser,
getTokenSessionById,
upsertTokenForUser,
deleteTokenForUser,
decrementTriesField,
getTokenSession,

View File

@@ -1,9 +1,7 @@
import crypto from "node:crypto";
import bcrypt from "bcrypt";
import jwt, { SignOptions } from "jsonwebtoken";
import { TToken } from "@app/db/schemas";
import { TTokenSession } from "@app/db/schemas/token-session";
import { TAuthTokens, TAuthTokenSessions } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { TTokenDalFactory } from "./token-dal";
@@ -14,11 +12,11 @@ import {
TValidateTokenForUserDTO
} from "./token-types";
type TTokenServiceFactoryDep = {
type TAuthTokenServiceFactoryDep = {
tokenDal: TTokenDalFactory;
// adjust the expiry from env through here
};
export type TTokenServiceFactory = ReturnType<typeof tokenServiceFactory>;
export type TAuthTokenServiceFactory = ReturnType<typeof tokenServiceFactory>;
export const getTokenConfig = (tokenType: TokenType) => {
// generate random token based on specified token use-case
@@ -57,7 +55,7 @@ export const getTokenConfig = (tokenType: TokenType) => {
}
};
export const tokenServiceFactory = ({ tokenDal }: TTokenServiceFactoryDep) => {
export const tokenServiceFactory = ({ tokenDal }: TAuthTokenServiceFactoryDep) => {
const createTokenForUser = async ({ type, userId }: TCreateTokenForUserDTO) => {
const { token, ...tkCfg } = getTokenConfig(type);
const appCfg = getConfig();
@@ -76,7 +74,7 @@ export const tokenServiceFactory = ({ tokenDal }: TTokenServiceFactoryDep) => {
type,
userId,
code
}: TValidateTokenForUserDTO): Promise<TToken | undefined> => {
}: TValidateTokenForUserDTO): Promise<TAuthTokens | undefined> => {
const token = await tokenDal.getTokenForUser({ type, userId });
// validate token
if (!token) throw new Error("Failed to find token");
@@ -105,7 +103,7 @@ export const tokenServiceFactory = ({ tokenDal }: TTokenServiceFactoryDep) => {
userId,
ip,
userAgent
}: TIssueAuthTokenDTO): Promise<TTokenSession | undefined> => {
}: TIssueAuthTokenDTO): Promise<TAuthTokenSessions | undefined> => {
let session = await tokenDal.getTokenSession(userId, ip, userAgent);
if (!session) {
session = await tokenDal.insertTokenSession(userId, ip, userAgent);
@@ -113,22 +111,19 @@ export const tokenServiceFactory = ({ tokenDal }: TTokenServiceFactoryDep) => {
return session;
};
const getUserTokenSessionById = async (id: string, userId: string) =>
tokenDal.getTokenSessionById(id, userId);
const clearTokenSessionById = async (
userId: string,
sessionId: string
): Promise<TTokenSession | undefined> => tokenDal.incrementVersion(userId, sessionId);
const createJwtToken = (
payload: string | Buffer | object,
secret: string,
options?: SignOptions
) => jwt.sign(payload, secret, options);
): Promise<TAuthTokenSessions | undefined> => tokenDal.incrementVersion(userId, sessionId);
return {
createTokenForUser,
validateTokenForUser,
createJwtToken,
getUserTokenSession,
clearTokenSessionById
clearTokenSessionById,
getUserTokenSessionById
};
};

View File

@@ -16,19 +16,19 @@ import { getUserAgentType } from "../../posthog";
export * from "./authDataExtractors";
interface ExtractAuthModeParams {
headers: { [key: string]: string | string[] | undefined }
headers: { [key: string]: string | string[] | undefined };
}
interface ExtractAuthModeReturn {
authMode: AuthMode;
authTokenValue: string;
authMode: AuthMode;
authTokenValue: string;
}
interface GetAuthDataParams {
authMode: AuthMode;
authTokenValue: string;
ipAddress: string;
userAgent: string;
authMode: AuthMode;
authTokenValue: string;
ipAddress: string;
userAgent: string;
}
/**
@@ -44,33 +44,30 @@ interface GetAuthDataParams {
* @throws {UnauthorizedError} Throws an error if no applicable authMode is found.
*/
export const extractAuthMode = async ({
headers
headers
}: ExtractAuthModeParams): Promise<ExtractAuthModeReturn> => {
const apiKey = headers["x-api-key"] as string;
const authHeader = headers["authorization"] as string;
const apiKey = headers["x-api-key"] as string;
const authHeader = headers["authorization"] as string;
if (apiKey) {
return { authMode: AuthMode.API_KEY, authTokenValue: apiKey };
}
if (!authHeader) throw UnauthorizedRequestError({
message: "Failed to authenticate unknown authentication method"
if (apiKey) {
return { authMode: AuthMode.API_KEY, authTokenValue: apiKey };
}
if (!authHeader)
throw UnauthorizedRequestError({
message: "Failed to authenticate unknown authentication method"
});
if (!authHeader.startsWith("Bearer ")) throw UnauthorizedRequestError({
message: "Failed to authenticate unknown authentication method"
if (!authHeader.startsWith("Bearer "))
throw UnauthorizedRequestError({
message: "Failed to authenticate unknown authentication method"
});
const authTokenValue = authHeader.slice(7);
if (authTokenValue.startsWith("st.")) {
return { authMode: AuthMode.SERVICE_TOKEN, authTokenValue };
}
const authTokenValue = authHeader.slice(7);
const decodedToken = <jwt.AuthnJwtPayload>(
jwt.verify(authTokenValue, await getAuthSecret())
);
if (authTokenValue.startsWith("st.")) {
return { authMode: AuthMode.SERVICE_TOKEN, authTokenValue };
}
switch (decodedToken.authTokenType) {
case AuthTokenType.ACCESS_TOKEN:
@@ -87,13 +84,18 @@ export const extractAuthMode = async ({
}
export const getAuthData = async ({
authMode,
authTokenValue,
ipAddress,
userAgent
authMode,
authTokenValue,
ipAddress,
userAgent
}: GetAuthDataParams): Promise<AuthData> => {
const userAgentType = getUserAgentType(userAgent);
const userAgentType = getUserAgentType(userAgent);
switch (authMode) {
case AuthMode.SERVICE_TOKEN: {
const serviceTokenData = await validateServiceTokenV2({
authTokenValue
});
switch (authMode) {
case AuthMode.SERVICE_TOKEN: {
@@ -193,4 +195,81 @@ export const getAuthData = async ({
}
}
}
}
case AuthMode.SERVICE_ACCESS_TOKEN: {
const serviceTokenData = await validateServiceTokenV3({
authTokenValue
});
return {
actor: {
type: ActorType.SERVICE_V3,
metadata: {
serviceId: serviceTokenData._id.toString(),
name: serviceTokenData.name
}
},
authPayload: serviceTokenData,
ipAddress,
userAgent,
userAgentType
};
}
case AuthMode.API_KEY: {
const user = await validateAPIKey({
authTokenValue
});
return {
actor: {
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
},
authPayload: user,
ipAddress,
userAgent,
userAgentType
};
}
case AuthMode.API_KEY_V2: {
const user = await validateAPIKeyV2({
authTokenValue
});
return {
actor: {
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
},
authPayload: user,
ipAddress,
userAgent,
userAgentType
};
}
case AuthMode.JWT: {
const user = await validateJWT({
authTokenValue
});
return {
actor: {
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
},
authPayload: user,
ipAddress,
userAgent,
userAgentType
};
}
}
};

View File

@@ -41,7 +41,7 @@ const AddTagPopoverContent = ({
<div className="flex flex-col space-y-1.5">
{wsTags?.map((wsTag: WsTag) => (
<div
key={`tag-${wsTag._id}`}
key={`tag-${wsTag.id}`}
className="mt-4 h-[32px] relative flex items-center justify-start hover:border-mineshaft-600 hover:border hover:bg-mineshaft-700 p-2 rounded-md hover:text-bunker-200 bg-none"
onClick={() => handleSelectTag(wsTag)}
onMouseEnter={() => handleTagOnMouseEnter(wsTag)}

View File

@@ -64,7 +64,7 @@ const UpgradePlanModal = ({
<button
type='button'
className='inline-flex justify-center rounded-md border border-transparent bg-primary opacity-80 hover:opacity-100 px-4 py-2 text-sm font-medium text-black hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
onClick={() => router.push(`/org/${currentOrg?._id}/billing`)}
onClick={() => router.push(`/org/${currentOrg?.id}/billing`)}
>
Upgrade Now
</button>

View File

@@ -41,7 +41,7 @@ type EnvironmentProps = {
const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoading }: Props) => {
const { currentWorkspace } = useWorkspace();
const { subscription } = useSubscription();
const { data: wsKey } = useGetUserWsKey(currentWorkspace?._id ?? "");
const { data: wsKey } = useGetUserWsKey(currentWorkspace?.id ?? "");
const { mutateAsync: deleteUserFromWorkspaceMutateAsync } = useDeleteUserFromWorkspace();
const { mutateAsync: uploadWsKeyMutateAsync } = useUploadWsKey();

View File

@@ -37,7 +37,7 @@ const AddTagsMenu = ({ allTags, currentTags, modifyTags, id }: { allTags: Tag[];
>
<Menu.Items className="absolute z-[90] text-sm drop-shadow-xl right-0 mt-0.5 w-[12rem] origin-top-right rounded-md bg-mineshaft-600 border border-mineshaft-500 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none p-1 space-y-1">
{allTags?.map((tag) => { return (
<Menu.Item key={tag._id}>
<Menu.Item key={tag.id}>
<button
type="button"
className={`${currentTags?.map(currentTag => currentTag.name).includes(tag.name) ? "opacity-30 cursor-default" : "hover:bg-mineshaft-700"} w-full text-left bg-mineshaft-800 px-2 py-0.5 text-bunker-200 rounded-sm flex items-center`}

View File

@@ -147,9 +147,9 @@ const KeyPair = ({
<div className="w-2/12 h-10 flex items-center overflow-visible overflow-r-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
<div className="flex items-center max-h-16">
{keyPair.tags?.map((tag, index) => (
index < 2 && <div key={keyPair.pos} className={`ml-2 px-1.5 ${tagData.filter(tagDp => tagDp._id === tag._id)[0]?.color} rounded-sm text-sm ${tagData.filter(tagDp => tagDp._id === tag._id)[0]?.colorText} flex items-center`}>
index < 2 && <div key={keyPair.pos} className={`ml-2 px-1.5 ${tagData.filter(tagDp => tagDp.id === tag.id)[0]?.color} rounded-sm text-sm ${tagData.filter(tagDp => tagDp.id === tag.id)[0]?.colorText} flex items-center`}>
<span className='mb-0.5 cursor-default'>{tag.name}</span>
<FontAwesomeIcon icon={faXmark} className="ml-1 cursor-pointer p-1" onClick={() => modifyTags(keyPair.tags.filter(ttag => ttag._id !== tag._id), keyPair.id)}/>
<FontAwesomeIcon icon={faXmark} className="ml-1 cursor-pointer p-1" onClick={() => modifyTags(keyPair.tags.filter(ttag => ttag.id !== tag.id), keyPair.id)}/>
</div>
))}

View File

@@ -59,7 +59,7 @@ export default function NavHeader({
<div className="mr-2 flex h-5 w-5 items-center justify-center rounded-md bg-primary text-sm text-black">
{currentOrg?.name?.charAt(0)}
</div>
<Link passHref legacyBehavior href={`/org/${currentOrg?._id}/overview`}>
<Link passHref legacyBehavior href={`/org/${currentOrg?.id}/overview`}>
<a className="pl-0.5 text-sm font-semibold text-primary/80 hover:text-primary">
{currentOrg?.name}
</a>

View File

@@ -191,14 +191,14 @@ export default function UserInfoStep({
const userOrgs = await fetchOrganizations();
const orgId = userOrgs[0]?._id;
const orgId = userOrgs[0]?.id;
const project = await ProjectService.initProject({
organizationId: orgId,
projectName: "Example Project"
});
localStorage.setItem("orgData.id", orgId);
localStorage.setItem("projectData.id", project._id);
localStorage.setItem("projectData.id", project.id);
incrementStep();
} catch (error) {

View File

@@ -112,7 +112,7 @@ export const CreateTagModal = ({ isOpen, onToggle }: Props): JSX.Element => {
});
const { createNotification } = useNotificationContext();
const { currentWorkspace } = useWorkspace();
const workspaceId = currentWorkspace?._id || "";
const workspaceId = currentWorkspace?.id || "";
const { mutateAsync: createWsTag } = useCreateWsTag();

View File

@@ -2,11 +2,11 @@ const ENV = process.env.NEXT_PUBLIC_ENV! || "development"; // investigate
const POSTHOG_API_KEY = process.env.NEXT_PUBLIC_POSTHOG_API_KEY!;
const POSTHOG_HOST =
process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com";
const INTERCOM_ID = process.env.NEXT_PUBLIC_INTERCOM_ID!;
const INTERCOMid = process.env.NEXT_PUBLIC_INTERCOMid!;
export {
ENV,
INTERCOM_ID,
INTERCOMid,
POSTHOG_API_KEY,
POSTHOG_HOST
};

View File

@@ -9,7 +9,7 @@
/* eslint-disable func-names */
// @ts-nocheck
import { INTERCOM_ID as APP_ID } from "@app/components/utilities/config";
import { INTERCOMid as APPid } from "@app/components/utilities/config";
// Loads Intercom with the snippet
// This must be run before boot, it initializes window.Intercom
@@ -37,7 +37,7 @@ export const load = () => {
var s=d.createElement("script");
s.type="text/javascript";
s.async=true;
s.src="https://widget.intercom.io/widget/" + APP_ID;
s.src="https://widget.intercom.io/widget/" + APPid;
var x=d.getElementsByTagName("script")[0];
x.parentNode.insertBefore(s, x);
};
@@ -56,7 +56,7 @@ export const load = () => {
export const boot = (options = {}) => {
window &&
window.Intercom &&
window.Intercom("boot", { app_id: APP_ID, ...options });
window.Intercom("boot", { appid: APPid, ...options });
};
export const update = () => {

View File

@@ -16,7 +16,7 @@ export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Ele
const { mutateAsync, isLoading } = useGetOrgTrialUrl();
const link =
subscription && subscription.slug !== null
? `/org/${currentOrg?._id}/billing`
? `/org/${currentOrg?.id}/billing`
: "https://infisical.com/scheduledemo";
const handleUpgradeBtnClick = async () => {
@@ -27,7 +27,7 @@ export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Ele
// direct user to start pro trial
const url = await mutateAsync({
orgId: currentOrg._id,
orgId: currentOrg.id,
success_url: window.location.href
});

View File

@@ -17,7 +17,7 @@ const OrgPermissionContext = createContext<null | {
export const OrgPermissionProvider = ({ children }: Props): JSX.Element => {
const { currentOrg } = useOrganization();
const orgId = currentOrg?._id || "";
const orgId = currentOrg?.id || "";
const { data: permission, isLoading } = useGetUserOrgPermissions({ orgId });
if (isLoading) {

View File

@@ -26,7 +26,7 @@ export const OrgProvider = ({ children }: Props): JSX.Element => {
const value = useMemo<TOrgContext>(
() => ({
orgs: userOrgs,
currentOrg: (userOrgs || []).find(({ _id }) => _id === currentWsOrgID) || (userOrgs || [])[0],
currentOrg: (userOrgs || []).find(({ id }) => id === currentWsOrgID) || (userOrgs || [])[0],
isLoading
}),
[currentWsOrgID, userOrgs, isLoading]

View File

@@ -13,7 +13,7 @@ const ProjectPermissionContext = createContext<null | TProjectPermission>(null);
export const ProjectPermissionProvider = ({ children }: Props): JSX.Element => {
const { currentWorkspace, isLoading: isWsLoading } = useWorkspace();
const workspaceId = currentWorkspace?._id || "";
const workspaceId = currentWorkspace?.id || "";
const { data: permission, isLoading } = useGetUserProjectPermissions({ workspaceId });
if ((isLoading && currentWorkspace) || isWsLoading) {

View File

@@ -20,7 +20,7 @@ export const SubscriptionProvider = ({ children }: Props): JSX.Element => {
const { currentOrg } = useOrganization();
const { data, isLoading } = useGetOrgSubscription({
orgID: currentOrg?._id || ""
orgID: currentOrg?.id || ""
});
// memorize the workspace details for the context

View File

@@ -26,7 +26,7 @@ export const WorkspaceProvider = ({ children }: Props): JSX.Element => {
const wsId = workspaceId || localStorage.getItem("projectData.id");
return {
workspaces: ws || [],
currentWorkspace: (ws || []).find(({ _id: id }) => id === wsId),
currentWorkspace: (ws || []).find(({ id: id }) => id === wsId),
isLoading
};
}, [ws, workspaceId, isLoading]);

View File

@@ -1,5 +1,5 @@
export type APIKeyDataV2 = {
_id: string;
id: string;
name: string;
user: string;
lastUsed?: string;

View File

@@ -1,5 +1,5 @@
export type TBot = {
_id: string;
id: string;
name: string;
workspace: string;
isActive: boolean;

View File

@@ -1,5 +1,5 @@
export type IncidentContact = {
_id: string;
id: string;
email: string;
organization: string;
__v: number;

View File

@@ -1,5 +1,5 @@
export type IntegrationAuth = {
_id: string;
id: string;
integration: string;
workspace: string;
__v: number;

View File

@@ -10,7 +10,7 @@ export type TCloudIntegration = {
};
export type TIntegration = {
_id: string;
id: string;
workspace: string;
environment: string;
isActive: boolean;

View File

@@ -1,5 +1,5 @@
export type UserWsKeyPair = {
_id: string;
id: string;
encryptedKey: string;
nonce: string;
sender: Sender;
@@ -11,7 +11,7 @@ export type UserWsKeyPair = {
};
export type Sender = {
_id: string;
id: string;
email: string;
createdAt: string;
updatedAt: string;

View File

@@ -1,5 +1,5 @@
export type Organization = {
_id: string;
id: string;
name: string;
createAt: string;
updatedAt: string;
@@ -25,7 +25,7 @@ export type PlanBillingInfo = {
}
export type Invoice = {
_id: string;
id: string;
created: number;
invoice_pdf: string;
number: string;
@@ -34,7 +34,7 @@ export type Invoice = {
}
export type PmtMethod = {
_id: string;
id: string;
brand: string;
exp_month: number;
exp_year: number;
@@ -43,14 +43,14 @@ export type PmtMethod = {
}
export type TaxID = {
_id: string;
id: string;
country: string;
type: string;
value: string;
}
export type License = {
_id: string;
id: string;
customerId: string;
prefix: string;
licenseKey: string;

View File

@@ -4,7 +4,7 @@ export type TGetRolesDTO = {
};
export type TRole<T extends string | undefined> = {
_id: string;
id: string;
organization: string;
workspace: T;
name: string;

View File

@@ -1,5 +1,5 @@
export type TSecretApprovalPolicy = {
_id: string;
id: string;
workspace: string;
name: string;
environment: string;

View File

@@ -78,7 +78,7 @@ export const decryptSecretApprovalSecret = (
key
});
return {
_id: encSecret._id,
id: encSecret.id,
version: encSecret.version,
secretKey,
secretValue,

View File

@@ -16,7 +16,7 @@ export enum CommitType {
}
export type TSecretApprovalSecChangeData = {
_id: string;
id: string;
secretKeyCiphertext: string;
secretKeyIV: string;
secretKeyTag: string;
@@ -34,7 +34,7 @@ export type TSecretApprovalSecChangeData = {
};
export type TSecretApprovalSecChange = {
_id: string;
id: string;
version: number;
secretKey: string;
secretValue: string;
@@ -46,7 +46,7 @@ export type TSecretApprovalRequest<
T extends unknown = TSecretApprovalSecChangeData,
J extends unknown = EncryptedSecret
> = {
_id: string;
id: string;
slug: string;
createdAt: string;
committer: string;

View File

@@ -142,7 +142,7 @@ export const useGetImportedSecrets = ({
});
return {
_id: encSecret._id,
id: encSecret.id,
env: encSecret.environment,
key: secretKey,
value: secretValue,

View File

@@ -2,7 +2,7 @@ import { UserWsKeyPair } from "../keys/types";
import { EncryptedSecret } from "../secrets/types";
export type TSecretImports = {
_id: string;
id: string;
workspaceId: string;
environment: string;
folderId: string;

View File

@@ -71,7 +71,7 @@ export type TProviderTemplate = {
};
export type TSecretRotation<T extends unknown = EncryptedSecret> = {
_id: string;
id: string;
interval: number;
provider: string;
customProvider: string;

View File

@@ -102,7 +102,7 @@ export const useGetSnapshotSecrets = ({ decryptFileKey, env, snapshotId }: TSnap
const secretComment = "";
const decryptedSecret = {
_id: encSecret.secret,
id: encSecret.secret,
env: encSecret.environment,
key: secretKey,
value: secretValue,

View File

@@ -2,7 +2,7 @@ import { UserWsKeyPair } from "../keys/types";
import { EncryptedSecretVersion } from "../secrets/types";
export type TSecretSnapshot = {
_id: string;
id: string;
workspace: string;
version: number;
secretVersions: string[];

View File

@@ -63,7 +63,7 @@ export const decryptSecrets = (
});
const decryptedSecret: DecryptedSecret = {
_id: encSecret._id,
id: encSecret.id,
env: encSecret.environment,
key: secretKey,
value: secretValue,
@@ -79,7 +79,7 @@ export const decryptSecrets = (
if (encSecret.type === "personal") {
personalSecrets[decryptedSecret.key] = {
id: encSecret._id,
id: encSecret.id,
value: secretValue
};
} else {
@@ -225,7 +225,7 @@ export const useGetSecretVersion = (dto: GetSecretVersionsDTO) =>
return data
.map((el) => ({
createdAt: el.createdAt,
id: el._id,
id: el.id,
value: decryptSymmetric({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,

View File

@@ -2,7 +2,7 @@ import type { UserWsKeyPair } from "../keys/types";
import type { WsTag } from "../tags/types";
export type EncryptedSecret = {
_id: string;
id: string;
version: number;
workspace: string;
type: "shared" | "personal";
@@ -26,7 +26,7 @@ export type EncryptedSecret = {
};
export type DecryptedSecret = {
_id: string;
id: string;
version: number;
key: string;
value: string;
@@ -45,7 +45,7 @@ export type DecryptedSecret = {
};
export type EncryptedSecretVersion = {
_id: string;
id: string;
secret: string;
version: number;
workspace: string;

View File

@@ -1,5 +1,5 @@
export type SubscriptionPlan = {
_id: string;
id: string;
membersUsed: number;
memberLimit: number;
auditLogs: boolean;

View File

@@ -1,7 +1,7 @@
export type UserWsTags = WsTag[];
export type WsTag = {
_id: string;
id: string;
name: string;
slug: string;
tagColor?: string;
@@ -11,7 +11,7 @@ export type WsTag = {
__v: number;
}
export type WorkspaceTag = { _id: string; name: string; slug: string };
export type WorkspaceTag = { id: string; name: string; slug: string };
export type CreateTagDTO = {
workspaceID: string;
@@ -27,7 +27,7 @@ export type CreateTagRes = {
createdAt: string;
tagColor?: string;
user: string;
_id: string;
id: string;
};
export type DeleteTagDTO = { tagID: string; };
@@ -38,12 +38,12 @@ export type DeleteWsTagRes = {
workspace: string;
createdAt: string;
user: string;
_id: string;
id: string;
};
export type SecretTags = {
id: string;
_id: string;
id: string;
slug: string;
tagColor: string;
}

View File

@@ -1,5 +1,5 @@
export type TrustedIp = {
_id: string;
id: string;
workspace: string;
ipAddress: string;
type: "ipv4" | "ipv6";

View File

@@ -1,5 +1,5 @@
export type TWebhook = {
_id: string;
id: string;
workspace: string;
environment: string;
secretPath: string;

View File

@@ -1,6 +1,6 @@
export type Workspace = {
__v: number;
_id: string;
id: string;
name: string;
organization: string;
autoCapitalization: boolean;
@@ -14,13 +14,13 @@ export type WorkspaceEnv = {
isWriteDenied: boolean;
};
export type WorkspaceTag = { _id: string; name: string; slug: string };
export type WorkspaceTag = { id: string; name: string; slug: string };
export type NameWorkspaceSecretsDTO = {
workspaceId: string;
secretsToUpdate: {
secretName: string;
_id: string;
id: string;
}[];
}

View File

@@ -101,7 +101,7 @@ export const AdminLayout = ({ children }: LayoutProps) => {
<div>
{!router.asPath.includes("personal") && (
<div className="flex h-12 cursor-default justify-between items-center px-3 pt-6">
<Link href={`/org/${currentOrg?._id}/overview`}>
<Link href={`/org/${currentOrg?.id}/overview`}>
<div className="my-6 flex cursor-default items-center justify-center pr-2 text-sm text-mineshaft-300 hover:text-mineshaft-100">
<FontAwesomeIcon icon={faArrowLeft} className="pr-3" />
Back to organization
@@ -264,7 +264,7 @@ export const AdminLayout = ({ children }: LayoutProps) => {
// direct user to start pro trial
const url = await mutateAsync({
orgId: currentOrg._id,
orgId: currentOrg.id,
success_url: window.location.href
});

View File

@@ -267,15 +267,15 @@ export const Navbar = () => {
</div>
<div className="mt-3 mb-2 flex flex-col items-start px-1">
{orgs
?.filter((org: { _id: string }) => org._id !== currentOrg?._id)
.map((org: { _id: string; name: string }) => (
?.filter((org: { id: string }) => org.id !== currentOrg?.id)
.map((org: { id: string; name: string }) => (
<div
onKeyDown={() => null}
role="button"
tabIndex={0}
key={guidGenerator()}
onClick={() => {
localStorage.setItem("orgData.id", org._id);
localStorage.setItem("orgData.id", org.id);
router.reload();
}}
className="flex w-full cursor-pointer flex-row items-center justify-start rounded-md p-1.5 hover:bg-white/5"
@@ -326,7 +326,7 @@ export const Navbar = () => {
// direct user to start pro trial
const url = await mutateAsync({
orgId: currentOrg._id,
orgId: currentOrg.id,
success_url: window.location.href
});

View File

@@ -1,7 +1,7 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
export type IGitRisks = {
_id: string;
id: string;
description: string;
startLine: string;
endLine: string;

View File

@@ -18,7 +18,7 @@ export default function DashboardRedirect() {
if (localStorage.getItem("orgData.id")) {
router.push(`/org/${localStorage.getItem("orgData.id")}/overview`);
} else if (userOrgs) {
userOrg = userOrgs[0]._id;
userOrg = userOrgs[0].id;
router.push(`/org/${userOrg}/overview`);
}
} catch (error) {

View File

@@ -52,7 +52,7 @@ export default function AWSParameterStoreAuthorizeIntegrationPage() {
setIsLoading(false);
router.push(
`/integrations/aws-parameter-store/create?integrationAuthId=${integrationAuth._id}`
`/integrations/aws-parameter-store/create?integrationAuthId=${integrationAuth.id}`
);
} catch (err) {
console.error(err);

View File

@@ -95,12 +95,12 @@ export default function AWSParameterStoreCreateIntegrationPage() {
}
}
if (!integrationAuth?._id) return;
if (!integrationAuth?.id) return;
setIsLoading(true);
await mutateAsync({
integrationAuthId: integrationAuth?._id,
integrationAuthId: integrationAuth?.id,
isActive: true,
sourceEnvironment: selectedSourceEnvironment,
path,

View File

@@ -50,7 +50,7 @@ export default function AWSSecretManagerCreateIntegrationPage() {
setIsLoading(false);
router.push(
`/integrations/aws-secret-manager/create?integrationAuthId=${integrationAuth._id}`
`/integrations/aws-secret-manager/create?integrationAuthId=${integrationAuth.id}`
);
} catch (err) {
console.error(err);

View File

@@ -94,12 +94,12 @@ export default function AWSSecretManagerCreateIntegrationPage() {
return;
}
if (!integrationAuth?._id) return;
if (!integrationAuth?.id) return;
setIsLoading(true);
await mutateAsync({
integrationAuthId: integrationAuth?._id,
integrationAuthId: integrationAuth?.id,
isActive: true,
app: targetSecretName.trim(),
sourceEnvironment: selectedSourceEnvironment,

View File

@@ -53,11 +53,11 @@ export default function AzureKeyVaultCreateIntegrationPage() {
return;
}
if (!integrationAuth?._id) return;
if (!integrationAuth?.id) return;
setIsLoading(true);
await mutateAsync({
integrationAuthId: integrationAuth?._id,
integrationAuthId: integrationAuth?.id,
isActive: true,
app: vaultBaseUrl,
sourceEnvironment: selectedSourceEnvironment,

View File

@@ -26,7 +26,7 @@ export default function AzureKeyVaultOAuth2CallbackPage() {
});
router.push(
`/integrations/azure-key-vault/create?integrationAuthId=${integrationAuth._id}`
`/integrations/azure-key-vault/create?integrationAuthId=${integrationAuth.id}`
);
} catch (err) {
console.error(err);

View File

@@ -72,7 +72,7 @@ export default function BitBucketCreateIntegrationPage() {
try {
setIsLoading(true);
if (!integrationAuth?._id) return;
if (!integrationAuth?.id) return;
const targetApp = integrationAuthApps?.find(
(integrationAuthApp) => integrationAuthApp.appId === targetAppId
@@ -84,7 +84,7 @@ export default function BitBucketCreateIntegrationPage() {
if (!targetApp || !targetApp.appId || !targetEnvironment) return;
await mutateAsync({
integrationAuthId: integrationAuth?._id,
integrationAuthId: integrationAuth?.id,
isActive: true,
app: targetApp.name,
appId: targetApp.appId,

View File

@@ -24,7 +24,7 @@ export default function BitBucketOAuth2CallbackPage() {
integration: "bitbucket"
});
router.push(`/integrations/bitbucket/create?integrationAuthId=${integrationAuth._id}`);
router.push(`/integrations/bitbucket/create?integrationAuthId=${integrationAuth.id}`);
} catch (err) {
console.error(err);
}

View File

@@ -38,7 +38,7 @@ export default function ChecklyCreateIntegrationPage() {
setIsLoading(false);
router.push(`/integrations/checkly/create?integrationAuthId=${integrationAuth._id}`);
router.push(`/integrations/checkly/create?integrationAuthId=${integrationAuth.id}`);
} catch (err) {
console.error(err);
}

View File

@@ -80,7 +80,7 @@ export default function ChecklyCreateIntegrationPage() {
const handleButtonClick = async () => {
try {
if (!integrationAuth?._id) return;
if (!integrationAuth?.id) return;
setIsLoading(true);
@@ -94,7 +94,7 @@ export default function ChecklyCreateIntegrationPage() {
if (!targetApp) return;
await mutateAsync({
integrationAuthId: integrationAuth?._id,
integrationAuthId: integrationAuth?.id,
isActive: true,
app: targetApp?.name,
appId: targetApp?.appId,

View File

@@ -38,7 +38,7 @@ export default function CircleCICreateIntegrationPage() {
setIsLoading(false);
router.push(`/integrations/circleci/create?integrationAuthId=${integrationAuth._id}`);
router.push(`/integrations/circleci/create?integrationAuthId=${integrationAuth.id}`);
} catch (err) {
console.error(err);
}

View File

@@ -63,12 +63,12 @@ export default function CircleCICreateIntegrationPage() {
const handleButtonClick = async () => {
try {
if (!integrationAuth?._id) return;
if (!integrationAuth?.id) return;
setIsLoading(true);
await mutateAsync({
integrationAuthId: integrationAuth?._id,
integrationAuthId: integrationAuth?.id,
isActive: true,
app: targetApp,
appId: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.name === targetApp)?.appId,

Some files were not shown because too many files have changed in this diff Show More