feat(infisical-pg): connected project api changes with frontend

This commit is contained in:
Akhil Mohan
2024-01-27 12:38:22 +05:30
parent 4f3582a98b
commit 8a6ab7f2f6
39 changed files with 343 additions and 339 deletions
@@ -7,13 +7,13 @@ export async function up(knex: Knex): Promise<void> {
if (!doesTableExist) {
await knex.schema.createTable(TableName.BackupPrivateKey, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("encryptedPrivateKey").notNullable();
t.string("iv").notNullable();
t.string("tag").notNullable();
t.text("encryptedPrivateKey").notNullable();
t.text("iv").notNullable();
t.text("tag").notNullable();
t.string("algorithm").notNullable();
t.string("keyEncoding").notNullable();
t.string("salt").notNullable();
t.string("verifier").notNullable();
t.text("salt").notNullable();
t.text("verifier").notNullable();
t.timestamps(true, true, true);
t.uuid("userId").notNullable().unique();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
@@ -14,8 +14,8 @@ export async function up(knex: Knex): Promise<void> {
t.timestamps(true, true, true);
});
}
// environments
await createOnUpdateTrigger(knex, TableName.Project);
// environments
if (!(await knex.schema.hasTable(TableName.Environment))) {
await knex.schema.createTable(TableName.Environment, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
@@ -47,7 +47,8 @@ export async function up(knex: Knex): Promise<void> {
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.Environment);
await knex.schema.dropTableIfExists(TableName.ProjectKeys);
await knex.schema.dropTableIfExists(TableName.Project);
await dropOnUpdateTrigger(knex, TableName.Project);
await dropOnUpdateTrigger(knex, TableName.ProjectKeys);
await dropOnUpdateTrigger(knex, TableName.Project);
}
@@ -14,7 +14,7 @@ export async function up(knex: Knex): Promise<void> {
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.uuid("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.ProjectRoles).onDelete("CASCADE");
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
});
}
@@ -132,8 +132,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
membership: ProjectMembershipsSchema,
permissions: z.any().array()
data: z.object({
membership: ProjectMembershipsSchema,
permissions: z.any().array()
})
})
}
},
@@ -143,7 +145,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
req.auth.userId,
req.params.projectId
);
return { permissions, membership };
return { data: { permissions, membership } };
}
});
};
+8 -4
View File
@@ -11,18 +11,22 @@ export const mergeOneToManyRelation = <
childMapper: (arg: T) => C,
childKey: Ck
) => {
const recordFirstVisitIndex: Record<string, number> = {};
let prevPkIndex = -1;
let prevPkId: null | string = null;
const groupedRecord: (P & Record<Ck, C[]>)[] = [];
for (let i = 0; i < data.length; i += 1) {
const pk = data[i][key];
const row = data[i];
if (typeof recordFirstVisitIndex[pk] === "undefined") {
if (pk !== prevPkId) {
const parent = parentMapper(row) as any;
parent[childKey] = [];
groupedRecord.push(parent);
recordFirstVisitIndex[pk] = i;
prevPkId = pk;
prevPkIndex += 1;
}
groupedRecord[recordFirstVisitIndex[pk]][childKey].push(childMapper(row));
console.log(prevPkIndex, prevPkId);
groupedRecord[prevPkIndex][childKey].push(childMapper(row));
}
return groupedRecord;
};
@@ -6,6 +6,8 @@ import { ActorType } from "@app/services/auth/auth-type";
export const injectPermission = fp(async (server) => {
server.decorateRequest("permission", null);
server.addHook("onRequest", async (req) => {
if (!req.auth) return;
if (req.auth.actor === ActorType.USER) {
req.permission = { type: ActorType.USER, id: req.auth.userId };
}
+2
View File
@@ -38,6 +38,7 @@ import { injectIdentity } from "../plugins/auth/inject-identity";
import { registerV1Routes } from "./v1";
import { registerV2Routes } from "./v2";
import { registerV3Routes } from "./v3";
import { injectPermission } from "../plugins/auth/inject-permission";
export const registerRoutes = async (
server: FastifyZodProvider,
@@ -149,6 +150,7 @@ export const registerRoutes = async (
});
await server.register(injectIdentity);
await server.register(injectPermission);
server.route({
url: "/status",
@@ -82,7 +82,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
response: {
200: z.object({
message: z.string(),
backupPrivateKey: BackupPrivateKeySchema
backupPrivateKey: BackupPrivateKeySchema.omit({ verifier: true })
})
}
},
@@ -105,7 +105,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
response: {
200: z.object({
message: z.string(),
backupPrivateKey: BackupPrivateKeySchema
backupPrivateKey: BackupPrivateKeySchema.omit({ verifier: true })
})
}
},
@@ -10,7 +10,13 @@ import {
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerProjectRouter = (server: FastifyZodProvider) => {
const projectWithEnv = ProjectsSchema.merge(
z.object({
environments: z.object({ name: z.string(), slug: z.string(), id: z.string() }).array()
})
);
export const registerProjectRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/:workspaceId/keys",
method: "GET",
@@ -81,9 +87,7 @@ export const registerProjectRouter = (server: FastifyZodProvider) => {
schema: {
response: {
200: z.object({
workspaces: ProjectsSchema.merge(
z.object({ environments: z.object({ name: z.string(), slug: z.string() }).array() })
).array()
workspaces: projectWithEnv.array()
})
}
},
@@ -103,9 +107,7 @@ export const registerProjectRouter = (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
workspace: ProjectsSchema.merge(
z.object({ environments: z.object({ name: z.string(), slug: z.string() }).array() })
).optional()
workspace: projectWithEnv.optional()
})
}
},
@@ -130,7 +132,7 @@ export const registerProjectRouter = (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
workspace: ProjectsSchema
workspace: projectWithEnv
})
}
},
@@ -1,6 +1,6 @@
import { z } from "zod";
import { UsersSchema } from "@app/db/schemas";
import { UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -11,7 +11,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
schema: {
response: {
200: z.object({
user: UsersSchema
user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true }))
})
}
},
@@ -1,6 +1,11 @@
import { z } from "zod";
import { AuthTokenSessionsSchema, OrganizationsSchema, UsersSchema } from "@app/db/schemas";
import {
AuthTokenSessionsSchema,
OrganizationsSchema,
UserEncryptionKeysSchema,
UsersSchema
} from "@app/db/schemas";
import { ApiKeysSchema } from "@app/db/schemas/api-keys";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMethod, AuthMode } from "@app/services/auth/auth-type";
@@ -195,7 +200,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
schema: {
response: {
200: z.object({
user: UsersSchema
user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true }))
})
}
},
@@ -63,14 +63,17 @@ export const tokenServiceFactory = ({ tokenDal }: TAuthTokenServiceFactoryDep) =
const tokenHash = await bcrypt.hash(token, appCfg.SALT_ROUNDS);
await tokenDal.transaction(async (tx) => {
await tokenDal.delete({ userId, type, orgId: orgId || null }, tx);
const newToken = await tokenDal.create({
tokenHash,
expiresAt: tkCfg.expiresAt.toUTCString(),
type,
userId,
orgId,
triesLeft: tkCfg?.triesLeft
});
const newToken = await tokenDal.create(
{
tokenHash,
expiresAt: tkCfg.expiresAt.toUTCString(),
type,
userId,
orgId,
triesLeft: tkCfg?.triesLeft
},
tx
);
return newToken;
});
@@ -14,6 +14,7 @@ import {
TResetPasswordViaBackupKeyDTO
} from "./auth-password-type";
import { AuthTokenType } from "./auth-type";
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
type TAuthPasswordServiceFactoryDep = {
authDal: TAuthDalFactory;
@@ -212,13 +213,19 @@ export const authPaswordServiceFactory = ({
);
if (!isValidClientProff) throw new Error("failed to create backup key");
const backup = await authDal.transaction(async (tx) => {
const backupKey = await authDal.upsertBackupKey(userEnc.userId, {
encryptedPrivateKey,
iv,
tag,
salt,
verifier
});
const backupKey = await authDal.upsertBackupKey(
userEnc.userId,
{
encryptedPrivateKey,
iv,
tag,
salt,
verifier,
algorithm: SecretEncryptionAlgo.AES_256_GCM,
keyEncoding: SecretKeyEncoding.UTF8
},
tx
);
await userDal.updateUserEncryptionByUserId(
userEnc.userId,
@@ -68,8 +68,11 @@ export const projectRoleServiceFactory = ({
if (existingRole && existingRole.id !== roleId)
throw new BadRequestError({ name: "Update Role", message: "Duplicate role" });
}
const [updatedRole] = await projectRoleDal.update({ id: roleId, projectId }, { ...data });
if (!updateRole) throw new BadRequestError({ message: "Role not found", name: "Update role" });
const [updatedRole] = await projectRoleDal.update(
{ id: roleId, projectId },
{ ...data, permissions: data.permissions ? JSON.stringify(data.permissions) : undefined }
);
if (!updatedRole) throw new BadRequestError({ message: "Role not found", name: "Update role" });
return updatedRole;
};
@@ -15,13 +15,13 @@ export const projectDalFactory = (db: TDbClient) => {
const workspaces = await db(TableName.ProjectMembership)
.where({ userId })
.join(
TableName.Environment,
`${TableName.Environment}.projectId`,
TableName.Project,
`${TableName.ProjectMembership}.projectId`,
`${TableName.Project}.id`
)
.join(
TableName.Project,
`${TableName.ProjectMembership}.projectId`,
TableName.Environment,
`${TableName.Environment}.projectId`,
`${TableName.Project}.id`
)
.select(
@@ -57,13 +57,13 @@ export const projectDalFactory = (db: TDbClient) => {
const workspaces = await db(TableName.ProjectMembership)
.where(`${TableName.Project}.id`, id)
.join(
TableName.Environment,
`${TableName.Environment}.projectId`,
TableName.Project,
`${TableName.ProjectMembership}.projectId`,
`${TableName.Project}.id`
)
.join(
TableName.Project,
`${TableName.ProjectMembership}.projectId`,
TableName.Environment,
`${TableName.Environment}.projectId`,
`${TableName.Project}.id`
)
.select(
@@ -50,11 +50,14 @@ export const projectServiceFactory = ({
// TODO(backend-pg): licence server
const newProject = projectDal.transaction(async (tx) => {
const project = await projectDal.create({ name: workspaceName, orgId }, tx);
await projectMembershipDal.create({
userId: actorId,
role: ProjectMembershipRole.Admin,
projectId: project.id
});
await projectMembershipDal.create(
{
userId: actorId,
role: ProjectMembershipRole.Admin,
projectId: project.id
},
tx
);
const envs = await projectEnvDal.insertMany(
DEFAULT_PROJECT_ENVS.map((el) => ({ ...el, projectId: project.id })),
tx
+6 -1
View File
@@ -37,7 +37,7 @@ export const userDalFactory = (db: TDbClient) => {
const findUserEncKeyByUserId = async (userId: string) => {
try {
return await db(TableName.Users)
const user = await db(TableName.Users)
.where(`${TableName.Users}.id`, userId)
.join(
TableName.UserEncryptionKey,
@@ -45,6 +45,11 @@ export const userDalFactory = (db: TDbClient) => {
`${TableName.UserEncryptionKey}.userId`
)
.first();
if (user?.id) {
// change to user id
user.id = user.userId;
}
return user;
} catch (error) {
throw new DatabaseError({ error, name: "Find user enc by user id" });
}