checkpoint

This commit is contained in:
Akhil Mohan
2023-09-04 15:05:54 +05:30
parent ddae305fdb
commit 9cda85f03e
7 changed files with 218 additions and 72 deletions

View File

@@ -0,0 +1,44 @@
import { Schema, Types, model } from "mongoose";
export interface ISecretApproval {
_id: Types.ObjectId;
workspace: Types.ObjectId;
environment: string;
secretPath?: string;
approvers: Types.ObjectId[];
approvals: number;
}
const secretApprovalSchema = new Schema<ISecretApproval>(
{
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true
},
approvers: [
{
// user associated with the personal secret
type: Schema.Types.ObjectId,
ref: "Membership"
}
],
environment: {
type: String,
required: true
},
secretPath: {
type: String,
required: false
},
approvals: {
type: Number,
default: 1
}
},
{
timestamps: true
}
);
export const SecretApproval = model<ISecretApproval>("SecretApproval", secretApprovalSchema);

View File

@@ -1,81 +1,65 @@
import mongoose, { Schema, model } from "mongoose";
import { ISecret, Secret } from "./secret";
import { Schema, Types, model } from "mongoose";
import { ISecretVersion, SecretVersion } from "../ee/models/secretVersion";
interface ISecretApprovalRequest {
secret: mongoose.Types.ObjectId;
requestedChanges: ISecret;
requestedBy: mongoose.Types.ObjectId;
approvers: IApprover[];
status: ApprovalStatus;
timestamp: Date;
requestType: RequestType;
requestId: string;
enum ApprovalStatus {
PENDING = "pending",
APPROVED = "approved",
REJECTED = "rejected"
}
interface IApprover {
userId: mongoose.Types.ObjectId;
status: ApprovalStatus;
enum CommitType {
DELETE = "delete",
UPDATE = "update",
CREATE = "create"
}
export enum ApprovalStatus {
PENDING = "pending",
APPROVED = "approved",
REJECTED = "rejected"
export interface ISecretApprovalRequest {
_id: Types.ObjectId;
committer: Types.ObjectId;
approvers: {
member: Types.ObjectId;
status: ApprovalStatus;
}[];
approvals: number;
hasMerged: boolean;
status: ApprovalStatus;
commits: {
secretVersion: Types.ObjectId;
newVersion: ISecretVersion;
op: CommitType;
}[];
}
export enum RequestType {
UPDATE = "update",
DELETE = "delete",
CREATE = "create"
}
const approverSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
status: {
type: String,
enum: [ApprovalStatus],
default: ApprovalStatus.PENDING,
},
});
const secretApprovalRequestSchema = new Schema<ISecretApprovalRequest>(
{
secret: {
type: mongoose.Schema.Types.ObjectId,
ref: "Secret",
},
requestedChanges: Secret,
requestedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
approvers: [approverSchema],
status: {
type: String,
enum: ApprovalStatus,
default: ApprovalStatus.PENDING,
},
timestamp: {
type: Date,
default: Date.now,
},
requestType: {
type: String,
enum: RequestType,
required: true,
},
requestId: {
type: String,
required: false,
},
},
{
timestamps: true,
}
const secretApprovalSchema = new Schema<ISecretApprovalRequest>(
{
approvers: [
{
member: {
// user associated with the personal secret
type: Schema.Types.ObjectId,
ref: "Membership"
},
status: { type: String, enum: ApprovalStatus, default: ApprovalStatus.PENDING }
}
],
approvals: {
type: Number,
required: true
},
hasMerged: { type: Boolean, default: false },
status: { type: String, enum: ApprovalStatus, default: ApprovalStatus.PENDING },
committer: { type: Schema.Types.ObjectId, ref: "Membership" },
commits: [
{
secretVersion: { type: Types.ObjectId, ref: "SecretVersion" },
newVersion: SecretVersion,
op: { type: String, enum: [CommitType], required: true }
}
]
},
{
timestamps: true
}
);
export const SecretApprovalRequest = model<ISecretApprovalRequest>("SecretApprovalRequest", secretApprovalRequestSchema);
export const SecretApproval = model<ISecretApprovalRequest>("SecretApproval", secretApprovalSchema);

View File

@@ -107,6 +107,11 @@
}
}
},
"approval": {
"title": "Admin Panel",
"og-title": "Manage your secret change management",
"og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files."
},
"integrations": {
"title": "Project Integrations",
"description": "Manage your integrations of Infisical with third-party services.",

View File

@@ -475,6 +475,18 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem>
</a>
</Link>
<Link href={`/project/${currentWorkspace?._id}/approval`} passHref>
<a>
<MenuItem
isSelected={
router.asPath === `/project/${currentWorkspace?._id}/allowlist`
}
icon="system-outline-126-verified"
>
Secret Change Management
</MenuItem>
</a>
</Link>
<Link href={`/project/${currentWorkspace?._id}/allowlist`} passHref>
<a>
<MenuItem

View File

@@ -0,0 +1,27 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import { SecretApprovalListPage } from "@app/views/SecretApproval/SecretApprovalListPage";
const SecretApproval = () => {
const { t } = useTranslation();
return (
<>
<Head>
<title>{t("common.head-title", { title: t("approval.title") })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
<meta property="og:title" content={String(t("approval.og-title"))} />
<meta name="og:description" content={String(t("approval.og-description"))} />
</Head>
<div className="h-full">
<SecretApprovalListPage />
</div>
</>
);
};
export default SecretApproval;
SecretApproval.requireAuth = true;

View File

@@ -0,0 +1,73 @@
import { faCheck, faCodeBranch, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button, Table, TableContainer, TBody, Td, Th, THead, Tr } from "@app/components/v2";
export const SecretApprovalListPage = () => {
return (
<div className="container mx-auto bg-bunker-800 text-white w-full h-full">
<div className="my-6">
<p className="text-3xl font-semibold text-gray-200">Admin Panels</p>
</div>
<div className="rounded-md bg-mineshaft-800 text-gray-300">
<div className="p-4 px-8 flex items-center space-x-8">
<div>
<FontAwesomeIcon icon={faCodeBranch} className="mr-2" />
27 Open
</div>
<div className="text-gray-500">
<FontAwesomeIcon icon={faCheck} className="mr-2" />
27 Closed
</div>
</div>
<div className="flex flex-col border-t border-mineshaft-600">
<div className="flex flex-col px-8 py-4">
<div className="mb-1">
<FontAwesomeIcon icon={faCodeBranch} className="mr-2" />2 secrets added and 1 deleted
</div>
<span className="text-xs text-gray-500">
Opened 2 hours ago by akhilmhdh - Review required
</span>
</div>
</div>
</div>
<div className="my-4 p-4 rounded-md bg-bunker-600 text-gray-300">
<div className="mb-4 text-lg">
Request for <span className="text-yellow-600">secret change</span>
</div>
<div>
<TableContainer>
<Table>
<THead>
<Tr>
<Th className="min-table-row">Secret</Th>
<Th>Value</Th>
<Th className="min-table-row">Comment</Th>
<Th className="min-table-row">Tags</Th>
</Tr>
</THead>
<TBody>
<Tr>
<Td>TWILIO_SECRET_TOKEN</Td>
<Td>value</Td>
<Td>Some values</Td>
<Td>-</Td>
</Tr>
</TBody>
</Table>
</TableContainer>
</div>
<div className="flex items-center mt-8 space-x-6">
<Button leftIcon={<FontAwesomeIcon icon={faCheck} />}>Approve</Button>
<Button
variant="outline_bg"
leftIcon={<FontAwesomeIcon icon={faXmark} />}
colorSchema="danger"
>
Deny
</Button>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1 @@
export { SecretApprovalListPage } from "./SecretApprovalListPage";