feat(infisical-pg): completed root folder level secret, secret import and folder

This commit is contained in:
Akhil Mohan
2024-01-27 12:38:23 +05:30
parent 8a6ab7f2f6
commit 0e1191f2ea
70 changed files with 3105 additions and 278 deletions
+42 -3
View File
@@ -28,6 +28,14 @@ import { projectMembershipDalFactory } from "@app/services/project-membership/pr
import { projectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
import { projectRoleDalFactory } from "@app/services/project-role/project-role-dal";
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
import { secretBlindIndexDalFactory } from "@app/services/secret/secret-blind-index-dal";
import { secretDalFactory } from "@app/services/secret/secret-dal";
import { secretServiceFactory } from "@app/services/secret/secret-service";
import { secretVersionDalFactory } from "@app/services/secret/secret-version-dal";
import { secretFolderDalFactory } from "@app/services/secret-folder/secret-folder-dal";
import { secretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
import { secretImportDalFactory } from "@app/services/secret-import/secret-import-dal";
import { secretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
import { TSmtpService } from "@app/services/smtp/smtp-service";
import { superAdminDalFactory } from "@app/services/super-admin/super-admin-dal";
import { superAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
@@ -35,10 +43,10 @@ import { userDalFactory } from "@app/services/user/user-dal";
import { userServiceFactory } from "@app/services/user/user-service";
import { injectIdentity } from "../plugins/auth/inject-identity";
import { injectPermission } from "../plugins/auth/inject-permission";
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,
@@ -60,6 +68,12 @@ export const registerRoutes = async (
const projectEnvDal = projectEnvDalFactory(db);
const projectKeyDal = projectKeyDalFactory(db);
const secretDal = secretDalFactory(db);
const folderDal = secretFolderDalFactory(db);
const secretImportDal = secretImportDalFactory(db);
const secretVersionDal = secretVersionDalFactory(db);
const secretBlindIndexDal = secretBlindIndexDalFactory(db);
// ee db layer ops
const permissionDal = permissionDalFactory(db);
@@ -104,8 +118,10 @@ export const registerRoutes = async (
const projectService = projectServiceFactory({
permissionService,
projectDal,
secretBlindIndexDal,
projectEnvDal,
projectMembershipDal
projectMembershipDal,
folderDal
});
const projectMembershipService = projectMembershipServiceFactory({
projectMembershipDal,
@@ -125,6 +141,25 @@ export const registerRoutes = async (
});
const projectRoleService = projectRoleServiceFactory({ permissionService, projectRoleDal });
const secretService = secretServiceFactory({
folderDal,
secretVersionDal,
secretBlindIndexDal,
permissionService,
secretDal
});
const folderService = secretFolderServiceFactory({
permissionService,
folderDal,
projectEnvDal
});
const secretImportService = secretImportServiceFactory({
projectEnvDal,
folderDal,
permissionService,
secretImportDal
});
await superAdminService.initServerCfg();
// inject all services
server.decorate<FastifyZodProvider["services"]>("services", {
@@ -142,7 +177,10 @@ export const registerRoutes = async (
projectMembership: projectMembershipService,
projectKey: projectKeyService,
projectEnv: projectEnvService,
projectRole: projectRoleService
projectRole: projectRoleService,
secret: secretService,
folder: folderService,
secretImport: secretImportService
});
server.decorate<FastifyZodProvider["store"]>("store", {
@@ -169,6 +207,7 @@ export const registerRoutes = async (
},
handler: () => {
const appCfg = getConfig();
return {
date: new Date(),
message: "Ok" as const,
+4
View File
@@ -7,6 +7,8 @@ import { registerProjectEnvRouter } from "./project-env-router";
import { registerProjectKeyRouter } from "./project-key-router";
import { registerProjectMembershipRouter } from "./project-membership-router";
import { registerProjectRouter } from "./project-router";
import { registerSecretFolderRouter } from "./secret-folder-router";
import { registerSecretImportRouter } from "./secret-import-router";
import { registerSsoRouter } from "./sso-router";
import { registerUserActionRouter } from "./user-action-router";
import { registerUserRouter } from "./user-router";
@@ -20,6 +22,8 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
await server.register(registerUserRouter, { prefix: "/user" });
await server.register(registerInviteOrgRouter, { prefix: "/invite-org" });
await server.register(registerUserActionRouter, { prefix: "/user-action" });
await server.register(registerSecretImportRouter, { prefix: "/secret-imports" });
await server.register(registerSecretFolderRouter, { prefix: "/folders" });
await server.register(
async (projectServer) => {
@@ -0,0 +1,121 @@
import { z } from "zod";
import { SecretFoldersSchema } from "@app/db/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretFolderRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/",
method: "POST",
schema: {
body: z.object({
projectId: z.string().trim(),
environment: z.string().trim(),
name: z.string().trim(),
path: z.string().trim().default("/")
}),
response: {
200: z.object({
folder: SecretFoldersSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const folder = await server.services.folder.createFolder({
actorId: req.permission.id,
actor: req.permission.type,
...req.body
});
return { folder };
}
});
server.route({
url: "/:folderId",
method: "PATCH",
schema: {
params: z.object({
folderId: z.string()
}),
body: z.object({
projectId: z.string().trim(),
environment: z.string().trim(),
name: z.string().trim(),
path: z.string().trim().default("/")
}),
response: {
200: z.object({
folder: SecretFoldersSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const folder = await server.services.folder.updateFolder({
actorId: req.permission.id,
actor: req.permission.type,
...req.body,
id: req.params.folderId
});
return { folder };
}
});
server.route({
url: "/:folderId",
method: "DELETE",
schema: {
params: z.object({
folderId: z.string()
}),
body: z.object({
projectId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/")
}),
response: {
200: z.object({
folder: SecretFoldersSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const folder = await server.services.folder.deleteFolder({
actorId: req.permission.id,
actor: req.permission.type,
...req.body,
id: req.params.folderId
});
return { folder };
}
});
server.route({
url: "/",
method: "GET",
schema: {
querystring: z.object({
projectId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/")
}),
response: {
200: z.object({
folders: SecretFoldersSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const folders = await server.services.folder.getFolders({
actorId: req.permission.id,
actor: req.permission.type,
...req.query
});
return { folders };
}
});
};
@@ -0,0 +1,152 @@
import { z } from "zod";
import { SecretImportsSchema } from "@app/db/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretImportRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/",
method: "POST",
schema: {
body: z.object({
projectId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/"),
import: z.object({
environment: z.string().trim(),
path: z.string().trim()
})
}),
response: {
200: z.object({
message: z.string(),
secretImport: SecretImportsSchema.omit({ importEnv: true }).merge(
z.object({
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() })
})
)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secretImport = await server.services.secretImport.createImport({
actorId: req.permission.id,
actor: req.permission.type,
...req.body,
data: req.body.import
});
return { message: "Successfully created secret import", secretImport };
}
});
server.route({
url: "/:secretImportId",
method: "PATCH",
schema: {
params: z.object({
secretImportId: z.string().trim()
}),
body: z.object({
projectId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/"),
import: z.object({
environment: z.string().trim().optional(),
path: z.string().trim().optional(),
position: z.number().optional()
})
}),
response: {
200: z.object({
message: z.string(),
secretImport: SecretImportsSchema.omit({ importEnv: true }).merge(
z.object({
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() })
})
)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secretImport = await server.services.secretImport.updateImport({
actorId: req.permission.id,
actor: req.permission.type,
id: req.params.secretImportId,
...req.body,
data: req.body.import
});
return { message: "Successfully updated secret import", secretImport };
}
});
server.route({
url: "/:secretImportId",
method: "DELETE",
schema: {
params: z.object({
secretImportId: z.string().trim()
}),
body: z.object({
projectId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/")
}),
response: {
200: z.object({
message: z.string(),
secretImport: SecretImportsSchema.omit({ importEnv: true }).merge(
z.object({
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() })
})
)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secretImport = await server.services.secretImport.deleteImport({
actorId: req.permission.id,
actor: req.permission.type,
id: req.params.secretImportId,
...req.body
});
return { message: "Successfully deleted secret import", secretImport };
}
});
server.route({
url: "/",
method: "GET",
schema: {
querystring: z.object({
projectId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/")
}),
response: {
200: z.object({
message: z.string(),
secretImports: SecretImportsSchema.omit({ importEnv: true })
.merge(
z.object({
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() })
})
)
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secretImports = await server.services.secretImport.getImports({
actorId: req.permission.id,
actor: req.permission.type,
...req.query
});
return { message: "Successfully fetched secret imports", secretImports };
}
});
};
@@ -1,7 +1,8 @@
import { z } from "zod";
import { ProjectKeysSchema } from "@app/db/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { z } from "zod";
export const registerProjectRouter = async (server: FastifyZodProvider) => {
server.route({
+2
View File
@@ -1,4 +1,5 @@
import { registerLoginRouter } from "./login-router";
import { registerSecretRouter } from "./secret-router";
import { registerSignupRouter } from "./signup-router";
import { registerUserRouter } from "./user-router";
@@ -6,4 +7,5 @@ export const registerV3Routes = async (server: FastifyZodProvider) => {
await server.register(registerSignupRouter, { prefix: "/signup" });
await server.register(registerLoginRouter, { prefix: "/auth" });
await server.register(registerUserRouter, { prefix: "/users" });
await server.register(registerSecretRouter, { prefix: "/secrets" });
};
@@ -0,0 +1,372 @@
import { z } from "zod";
import { SecretsSchema,SecretType } from "@app/db/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/",
method: "GET",
schema: {
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
include_imports: z
.enum(["true", "false"])
.default("false")
.transform((value) => value === "true")
}),
response: {
200: z.object({
secrets: SecretsSchema.omit({ secretBlindIndex: true }).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secrets = await server.services.secret.getSecrets({
actorId: req.permission.id,
actor: req.permission.type,
environment: req.query.environment,
projectId: req.query.workspaceId,
path: req.query.secretPath
});
return { secrets };
}
});
server.route({
url: "/:secretName",
method: "GET",
schema: {
params: z.object({
secretName: z.string().trim()
}),
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
type: z.nativeEnum(SecretType).default(SecretType.Shared),
include_imports: z
.enum(["true", "false"])
.default("false")
.transform((value) => value === "true")
}),
response: {
200: z.object({
secret: SecretsSchema.omit({ secretBlindIndex: true })
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secret = await server.services.secret.getASecret({
actorId: req.permission.id,
actor: req.permission.type,
environment: req.query.environment,
projectId: req.query.workspaceId,
path: req.query.secretPath,
secretName: req.params.secretName,
type: req.query.type
});
return { secret };
}
});
server.route({
url: "/:secretName",
method: "POST",
schema: {
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
type: z.nativeEnum(SecretType).default(SecretType.Shared),
secretPath: z.string().trim().default("/"),
secretKeyCiphertext: z.string().trim(),
secretKeyIV: z.string().trim(),
secretKeyTag: z.string().trim(),
secretValueCiphertext: z.string().trim(),
secretValueIV: z.string().trim(),
secretValueTag: z.string().trim(),
secretCommentCiphertext: z.string().trim().optional(),
secretCommentIV: z.string().trim().optional(),
secretCommentTag: z.string().trim().optional(),
metadata: z.record(z.string()).optional(),
skipMultilineEncoding: z.boolean().optional()
}),
params: z.object({
secretName: z.string().trim()
}),
response: {
200: z.object({
secret: SecretsSchema.omit({ secretBlindIndex: true })
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secret = await server.services.secret.createSecret({
actorId: req.permission.id,
actor: req.permission.type,
path: req.body.secretPath,
type: req.body.type,
environment: req.body.environment,
secretName: req.params.secretName,
projectId: req.body.workspaceId,
secretKeyIV: req.body.secretKeyIV,
secretKeyTag: req.body.secretKeyTag,
secretKeyCiphertext: req.body.secretKeyCiphertext,
secretValueIV: req.body.secretValueIV,
secretValueTag: req.body.secretValueTag,
secretValueCiphertext: req.body.secretValueCiphertext,
secretCommentIV: req.body.secretCommentIV,
secretCommentTag: req.body.secretCommentTag,
secretCommentCiphertext: req.body.secretCommentCiphertext,
skipMultilineEncoding: req.body.skipMultilineEncoding,
metadata: req.body.metadata
});
return { secret };
}
});
server.route({
url: "/:secretName",
method: "PATCH",
schema: {
params: z.object({
secretName: z.string()
}),
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretId: z.string().trim().optional(),
type: z.nativeEnum(SecretType).default(SecretType.Shared),
secretPath: z.string().trim().default("/"),
secretValueCiphertext: z.string().trim(),
secretValueIV: z.string().trim(),
secretValueTag: z.string().trim(),
secretCommentCiphertext: z.string().trim().optional(),
secretCommentIV: z.string().trim().optional(),
secretCommentTag: z.string().trim().optional(),
secretReminderRepeatDays: z.number().min(1).max(365).optional().nullable(),
secretReminderNote: z.string().trim().nullable().optional(),
tags: z.string().array().optional(),
skipMultilineEncoding: z.boolean().optional(),
// to update secret name
secretName: z.string().trim().optional(),
secretKeyIV: z.string().trim().optional(),
secretKeyTag: z.string().trim().optional(),
secretKeyCiphertext: z.string().trim().optional(),
metadata: z.record(z.string()).optional()
}),
response: {
200: z.object({
secret: SecretsSchema.omit({ secretBlindIndex: true })
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secret = await server.services.secret.updateSecret({
actorId: req.permission.id,
actor: req.permission.type,
path: req.body.secretPath,
type: req.body.type,
environment: req.body.environment,
secretName: req.params.secretName,
projectId: req.body.workspaceId,
secretKeyIV: req.body.secretKeyIV,
secretKeyTag: req.body.secretKeyTag,
secretKeyCiphertext: req.body.secretKeyCiphertext,
secretValueIV: req.body.secretValueIV,
secretValueTag: req.body.secretValueTag,
secretValueCiphertext: req.body.secretValueCiphertext,
secretCommentIV: req.body.secretCommentIV,
secretCommentTag: req.body.secretCommentTag,
secretCommentCiphertext: req.body.secretCommentCiphertext,
skipMultilineEncoding: req.body.skipMultilineEncoding,
metadata: req.body.metadata,
secretReminderRepeatDays: req.body.secretReminderRepeatDays,
secretReminderNote: req.body.secretReminderNote,
newSecretName: req.body.secretName
});
return { secret };
}
});
server.route({
url: "/:secretName",
method: "DELETE",
schema: {
params: z.object({
secretName: z.string()
}),
body: z.object({
type: z.nativeEnum(SecretType).default(SecretType.Shared),
secretPath: z.string().trim().default("/"),
secretId: z.string().trim().optional(),
workspaceId: z.string().trim(),
environment: z.string().trim()
}),
response: {
200: z.object({
secret: SecretsSchema.omit({ secretBlindIndex: true })
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secret = await server.services.secret.deleteSecret({
actorId: req.permission.id,
actor: req.permission.type,
path: req.body.secretPath,
type: req.body.type,
environment: req.body.environment,
secretName: req.params.secretName,
projectId: req.body.workspaceId,
secretId: req.body.secretId
});
return { secret };
}
});
server.route({
url: "/batch",
method: "POST",
schema: {
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
secrets: z
.object({
secretName: z.string().trim(),
type: z.nativeEnum(SecretType).default(SecretType.Shared),
secretKeyCiphertext: z.string().trim(),
secretKeyIV: z.string().trim(),
secretKeyTag: z.string().trim(),
secretValueCiphertext: z.string().trim(),
secretValueIV: z.string().trim(),
secretValueTag: z.string().trim(),
secretCommentCiphertext: z.string().trim().optional(),
secretCommentIV: z.string().trim().optional(),
secretCommentTag: z.string().trim().optional(),
metadata: z.record(z.string()).optional(),
skipMultilineEncoding: z.boolean().optional()
})
.array()
.min(1)
}),
response: {
200: z.object({
secrets: SecretsSchema.omit({ secretBlindIndex: true }).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secrets = await server.services.secret.createManySecret({
actorId: req.permission.id,
actor: req.permission.type,
path: req.body.secretPath,
environment: req.body.environment,
projectId: req.body.workspaceId,
secrets: req.body.secrets
});
return { secrets };
}
});
server.route({
url: "/batch",
method: "PATCH",
schema: {
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
secrets: z
.object({
secretName: z.string().trim(),
type: z.nativeEnum(SecretType).default(SecretType.Shared),
secretValueCiphertext: z.string().trim(),
secretValueIV: z.string().trim(),
secretValueTag: z.string().trim(),
secretKeyCiphertext: z.string().trim(),
secretKeyIV: z.string().trim(),
secretKeyTag: z.string().trim(),
secretCommentCiphertext: z.string().trim().optional(),
secretCommentIV: z.string().trim().optional(),
secretCommentTag: z.string().trim().optional(),
skipMultilineEncoding: z.boolean().optional(),
tags: z.string().array().optional()
})
.array()
.min(1)
}),
response: {
200: z.object({
secrets: SecretsSchema.omit({ secretBlindIndex: true }).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secrets = await server.services.secret.updateManySecret({
actorId: req.permission.id,
actor: req.permission.type,
path: req.body.secretPath,
environment: req.body.environment,
projectId: req.body.workspaceId,
secrets: req.body.secrets
});
return { secrets };
}
});
server.route({
url: "/batch",
method: "DELETE",
schema: {
body: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().default("/"),
secrets: z
.object({
secretName: z.string().trim(),
type: z.nativeEnum(SecretType).default(SecretType.Shared)
})
.array()
.min(1)
}),
response: {
200: z.object({
secrets: SecretsSchema.omit({ secretBlindIndex: true }).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const secrets = await server.services.secret.deleteManySecret({
actorId: req.permission.id,
actor: req.permission.type,
path: req.body.secretPath,
environment: req.body.environment,
projectId: req.body.workspaceId,
secrets: req.body.secrets
});
return { secrets };
}
});
};