mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 22:27:48 +00:00
feat(secret-approval): implemented backend api for secret policies
This commit is contained in:
@@ -16,6 +16,7 @@ import * as workspaceController from "./workspaceController";
|
|||||||
import * as secretScanningController from "./secretScanningController";
|
import * as secretScanningController from "./secretScanningController";
|
||||||
import * as webhookController from "./webhookController";
|
import * as webhookController from "./webhookController";
|
||||||
import * as secretImpsController from "./secretImpsController";
|
import * as secretImpsController from "./secretImpsController";
|
||||||
|
import * as secretApprovalController from "./secretApprovalController";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authController,
|
authController,
|
||||||
@@ -35,5 +36,6 @@ export {
|
|||||||
workspaceController,
|
workspaceController,
|
||||||
secretScanningController,
|
secretScanningController,
|
||||||
webhookController,
|
webhookController,
|
||||||
secretImpsController
|
secretImpsController,
|
||||||
|
secretApprovalController
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../ee/services/ProjectRoleService";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import { SecretApproval } from "../../models/secretApproval";
|
||||||
|
import { BadRequestError } from "../../utils/errors";
|
||||||
|
import * as reqValidator from "../../validation/secretApproval";
|
||||||
|
|
||||||
|
const ERR_SECRET_APPROVAL_NOT_FOUND = BadRequestError({ message: "secret approval not found" });
|
||||||
|
|
||||||
|
export const createSecretApprovalRule = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
body: { approvals, secretPath, approvers, environment, workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.CreateSecretApprovalRule, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
|
||||||
|
const secretApproval = new SecretApproval({
|
||||||
|
workspace: workspaceId,
|
||||||
|
secretPath,
|
||||||
|
environment,
|
||||||
|
approvals,
|
||||||
|
approvers
|
||||||
|
});
|
||||||
|
await secretApproval.save();
|
||||||
|
|
||||||
|
return res.send({
|
||||||
|
approval: secretApproval
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateSecretApprovalRule = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
body: { approvals, approvers, secretPath },
|
||||||
|
params: { id }
|
||||||
|
} = await validateRequest(reqValidator.UpdateSecretApprovalRule, req);
|
||||||
|
|
||||||
|
const secretApproval = await SecretApproval.findById(id);
|
||||||
|
if (!secretApproval) throw ERR_SECRET_APPROVAL_NOT_FOUND;
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
secretApproval.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedDoc = await SecretApproval.findByIdAndUpdate(id, {
|
||||||
|
approvals,
|
||||||
|
approvers,
|
||||||
|
$set: secretPath === "-" ? undefined : { secretPath }
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.send({
|
||||||
|
approval: updatedDoc
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteSecretApprovalRule = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
params: { id }
|
||||||
|
} = await validateRequest(reqValidator.DeleteSecretApprovalRule, req);
|
||||||
|
|
||||||
|
const secretApproval = await SecretApproval.findById(id);
|
||||||
|
if (!secretApproval) throw ERR_SECRET_APPROVAL_NOT_FOUND;
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
secretApproval.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
|
||||||
|
const deletedDoc = await SecretApproval.findByIdAndDelete(id);
|
||||||
|
|
||||||
|
return res.send({
|
||||||
|
approval: deletedDoc
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSecretApprovalRules = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
query: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetSecretApprovalRuleList, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
|
||||||
|
const doc = await SecretApproval.find({ workspace: workspaceId });
|
||||||
|
|
||||||
|
return res.send({
|
||||||
|
approvals: doc
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -49,7 +49,8 @@ export enum ProjectPermissionSub {
|
|||||||
IpAllowList = "ip-allowlist",
|
IpAllowList = "ip-allowlist",
|
||||||
Workspace = "workspace",
|
Workspace = "workspace",
|
||||||
Secrets = "secrets",
|
Secrets = "secrets",
|
||||||
SecretRollback = "secret-rollback"
|
SecretRollback = "secret-rollback",
|
||||||
|
SecretApproval = "secret-approval"
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubjectFields = {
|
type SubjectFields = {
|
||||||
@@ -72,6 +73,7 @@ export type ProjectPermissionSet =
|
|||||||
| [ProjectPermissionActions, ProjectPermissionSub.IpAllowList]
|
| [ProjectPermissionActions, ProjectPermissionSub.IpAllowList]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.Settings]
|
| [ProjectPermissionActions, ProjectPermissionSub.Settings]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.ServiceTokens]
|
| [ProjectPermissionActions, ProjectPermissionSub.ServiceTokens]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.SecretApproval]
|
||||||
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace]
|
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace]
|
||||||
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace]
|
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace]
|
||||||
| [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback]
|
| [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback]
|
||||||
@@ -85,6 +87,11 @@ const buildAdminPermission = () => {
|
|||||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets);
|
||||||
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets);
|
||||||
|
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||||
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretApproval);
|
||||||
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||||
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback);
|
||||||
|
|
||||||
@@ -154,6 +161,8 @@ const buildMemberPermission = () => {
|
|||||||
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets);
|
||||||
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets);
|
||||||
|
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback);
|
||||||
|
|
||||||
@@ -203,6 +212,7 @@ const buildViewerPermission = () => {
|
|||||||
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
membership as v1MembershipRouter,
|
membership as v1MembershipRouter,
|
||||||
organization as v1OrganizationRouter,
|
organization as v1OrganizationRouter,
|
||||||
password as v1PasswordRouter,
|
password as v1PasswordRouter,
|
||||||
|
secretApproval as v1SecretApproval,
|
||||||
secretImps as v1SecretImpsRouter,
|
secretImps as v1SecretImpsRouter,
|
||||||
secret as v1SecretRouter,
|
secret as v1SecretRouter,
|
||||||
secretsFolder as v1SecretsFolder,
|
secretsFolder as v1SecretsFolder,
|
||||||
@@ -176,6 +177,7 @@ const main = async () => {
|
|||||||
app.use("/api/v1/webhooks", v1WebhooksRouter);
|
app.use("/api/v1/webhooks", v1WebhooksRouter);
|
||||||
app.use("/api/v1/secret-imports", v1SecretImpsRouter);
|
app.use("/api/v1/secret-imports", v1SecretImpsRouter);
|
||||||
app.use("/api/v1/roles", v1RoleRouter);
|
app.use("/api/v1/roles", v1RoleRouter);
|
||||||
|
app.use("/api/v1/secret-approvals", v1SecretApproval);
|
||||||
|
|
||||||
// v2 routes (improvements)
|
// v2 routes (improvements)
|
||||||
app.use("/api/v2/signup", v2SignupRouter);
|
app.use("/api/v2/signup", v2SignupRouter);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import integrationAuth from "./integrationAuth";
|
|||||||
import secretsFolder from "./secretsFolder";
|
import secretsFolder from "./secretsFolder";
|
||||||
import webhooks from "./webhook";
|
import webhooks from "./webhook";
|
||||||
import secretImps from "./secretImps";
|
import secretImps from "./secretImps";
|
||||||
|
import secretApproval from "./secretApproval";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
signup,
|
signup,
|
||||||
@@ -37,5 +38,6 @@ export {
|
|||||||
integrationAuth,
|
integrationAuth,
|
||||||
secretsFolder,
|
secretsFolder,
|
||||||
webhooks,
|
webhooks,
|
||||||
secretImps
|
secretImps,
|
||||||
|
secretApproval
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import express from "express";
|
||||||
|
const router = express.Router();
|
||||||
|
import { requireAuth } from "../../middleware";
|
||||||
|
import { secretApprovalController } from "../../controllers/v1";
|
||||||
|
import { AuthMode } from "../../variables";
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
secretApprovalController.getSecretApprovalRules
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
secretApprovalController.createSecretApprovalRule
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:id",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
secretApprovalController.updateSecretApprovalRule
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
"/:id",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
secretApprovalController.deleteSecretApprovalRule
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from "./secretApproval";
|
||||||
export * from "./user";
|
export * from "./user";
|
||||||
export * from "./workspace";
|
export * from "./workspace";
|
||||||
export * from "./bot";
|
export * from "./bot";
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const GetSecretApprovalRuleList = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateSecretApprovalRule = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string(),
|
||||||
|
environment: z.string(),
|
||||||
|
secretPath: z.string().optional(),
|
||||||
|
approvers: z.string().array().optional(),
|
||||||
|
approvals: z.number().min(1).default(1)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateSecretApprovalRule = z.object({
|
||||||
|
params: z.object({
|
||||||
|
id: z.string()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
approvers: z.string().array().optional(),
|
||||||
|
approvals: z.number().min(1).optional(),
|
||||||
|
secretPath: z.string().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteSecretApprovalRule = z.object({
|
||||||
|
params: z.object({
|
||||||
|
id: z.string()
|
||||||
|
})
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user