checkpoint

This commit is contained in:
Akhil Mohan
2023-09-27 11:50:52 +05:30
parent ddae305fdb
commit 9cda85f03e
7 changed files with 218 additions and 72 deletions
+44
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);
+56 -72
View File
@@ -1,81 +1,65 @@
import mongoose, { Schema, model } from "mongoose"; import { Schema, Types, model } from "mongoose";
import { ISecret, Secret } from "./secret"; import { ISecretVersion, SecretVersion } from "../ee/models/secretVersion";
interface ISecretApprovalRequest { enum ApprovalStatus {
secret: mongoose.Types.ObjectId; PENDING = "pending",
requestedChanges: ISecret; APPROVED = "approved",
requestedBy: mongoose.Types.ObjectId; REJECTED = "rejected"
approvers: IApprover[];
status: ApprovalStatus;
timestamp: Date;
requestType: RequestType;
requestId: string;
} }
interface IApprover { enum CommitType {
userId: mongoose.Types.ObjectId; DELETE = "delete",
status: ApprovalStatus; UPDATE = "update",
CREATE = "create"
} }
export enum ApprovalStatus { export interface ISecretApprovalRequest {
PENDING = "pending", _id: Types.ObjectId;
APPROVED = "approved", committer: Types.ObjectId;
REJECTED = "rejected" approvers: {
member: Types.ObjectId;
status: ApprovalStatus;
}[];
approvals: number;
hasMerged: boolean;
status: ApprovalStatus;
commits: {
secretVersion: Types.ObjectId;
newVersion: ISecretVersion;
op: CommitType;
}[];
} }
export enum RequestType { const secretApprovalSchema = new Schema<ISecretApprovalRequest>(
UPDATE = "update", {
DELETE = "delete", approvers: [
CREATE = "create" {
} member: {
// user associated with the personal secret
const approverSchema = new mongoose.Schema({ type: Schema.Types.ObjectId,
user: { ref: "Membership"
type: mongoose.Schema.Types.ObjectId, },
ref: "User", status: { type: String, enum: ApprovalStatus, default: ApprovalStatus.PENDING }
required: true, }
}, ],
status: { approvals: {
type: String, type: Number,
enum: [ApprovalStatus], required: true
default: ApprovalStatus.PENDING, },
}, hasMerged: { type: Boolean, default: false },
}); status: { type: String, enum: ApprovalStatus, default: ApprovalStatus.PENDING },
committer: { type: Schema.Types.ObjectId, ref: "Membership" },
const secretApprovalRequestSchema = new Schema<ISecretApprovalRequest>( commits: [
{ {
secret: { secretVersion: { type: Types.ObjectId, ref: "SecretVersion" },
type: mongoose.Schema.Types.ObjectId, newVersion: SecretVersion,
ref: "Secret", op: { type: String, enum: [CommitType], required: true }
}, }
requestedChanges: Secret, ]
requestedBy: { },
type: mongoose.Schema.Types.ObjectId, {
ref: "User", timestamps: true
}, }
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,
}
); );
export const SecretApprovalRequest = model<ISecretApprovalRequest>("SecretApprovalRequest", secretApprovalRequestSchema); export const SecretApproval = model<ISecretApprovalRequest>("SecretApproval", secretApprovalSchema);
@@ -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": { "integrations": {
"title": "Project Integrations", "title": "Project Integrations",
"description": "Manage your integrations of Infisical with third-party services.", "description": "Manage your integrations of Infisical with third-party services.",
@@ -475,6 +475,18 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem> </MenuItem>
</a> </a>
</Link> </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> <Link href={`/project/${currentWorkspace?._id}/allowlist`} passHref>
<a> <a>
<MenuItem <MenuItem
@@ -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;
@@ -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>
);
};
@@ -0,0 +1 @@
export { SecretApprovalListPage } from "./SecretApprovalListPage";