feat(secret-approval): implemented the new policy based approval system bare version

This commit is contained in:
Akhil Mohan
2023-10-05 20:34:50 +05:30
parent fc43511f5d
commit 63588b4e44
27 changed files with 1033 additions and 165 deletions
@@ -19,6 +19,7 @@ import {
useGetImportedSecrets,
useGetProjectFolders,
useGetProjectSecrets,
useGetSecretApprovalPolicyOfABoard,
useGetSecretImports,
useGetUserWsKey,
useGetWorkspaceSnapshotList,
@@ -118,6 +119,13 @@ export const SecretMainPage = () => {
// fetch tags
const { data: tags } = useGetWsTags(canReadSecret ? workspaceId : "");
const { data: boardPolicy } = useGetSecretApprovalPolicyOfABoard({
workspaceId,
environment,
secretPath
});
const isProtectedBranch = Boolean(boardPolicy);
const {
data: snapshotList,
isFetchingNextPage: isFetchingNextSnapshotList,
@@ -207,6 +215,7 @@ export const SecretMainPage = () => {
secretPath={secretPath}
isProjectRelated
onEnvChange={handleEnvChange}
isProtectedBranch={isProtectedBranch}
/>
</div>
{!isRollbackMode ? (
@@ -281,6 +290,7 @@ export const SecretMainPage = () => {
workspaceId={workspaceId}
secretPath={secretPath}
decryptFileKey={decryptFileKey!}
isProtectedBranch={isProtectedBranch}
/>
)}
{!canReadSecret && folders?.length === 0 && <PermissionDeniedBanner />}
@@ -292,6 +302,7 @@ export const SecretMainPage = () => {
decryptFileKey={decryptFileKey!}
secretPath={secretPath}
autoCapitalize={currentWorkspace?.autoCapitalization}
isProtectedBranch={isProtectedBranch}
/>
<SecretDropzone
secrets={secrets}
@@ -301,6 +312,7 @@ export const SecretMainPage = () => {
secretPath={secretPath}
isSmaller={isNotEmtpy}
environments={currentWorkspace?.environments}
isProtectedBranch={isProtectedBranch}
/>
<PitDrawer
secretSnaphots={snapshotList}
@@ -23,6 +23,7 @@ type Props = {
secretPath?: string;
// modal props
autoCapitalize?: boolean;
isProtectedBranch?: boolean;
};
export const CreateSecretForm = ({
@@ -30,7 +31,8 @@ export const CreateSecretForm = ({
workspaceId,
decryptFileKey,
secretPath = "/",
autoCapitalize = true
autoCapitalize = true,
isProtectedBranch = false
}: Props) => {
const {
register,
@@ -62,7 +64,9 @@ export const CreateSecretForm = ({
reset();
createNotification({
type: "success",
text: "Successfully created secret"
text: isProtectedBranch
? "Requested changes have been send for review"
: "Successfully created secret"
});
} catch (error) {
console.log(error);
@@ -43,6 +43,7 @@ type Props = {
environment: string;
secretPath: string;
secrets?: DecryptedSecret[];
isProtectedBranch?: boolean;
};
export const SecretDropzone = ({
@@ -52,7 +53,8 @@ export const SecretDropzone = ({
decryptFileKey,
environment,
secretPath,
secrets = []
secrets = [],
isProtectedBranch = false
}: Props): JSX.Element => {
const { t } = useTranslation();
const [isDragActive, setDragActive] = useToggle();
@@ -195,7 +197,9 @@ export const SecretDropzone = ({
handlePopUpClose("overlapKeyWarning");
createNotification({
type: "success",
text: "Successfully uploaded secrets"
text: isProtectedBranch
? "Uploaded changes have been send for review"
: "Successfully uploaded secrets"
});
} catch (err) {
console.log(err);
@@ -49,7 +49,8 @@ type Props = {
onDeleteSecret: () => void;
onSaveSecret: (
orgSec: DecryptedSecret,
modSec: Omit<DecryptedSecret, "tags"> & { tags: { _id: string }[] }
modSec: Omit<DecryptedSecret, "tags"> & { tags: { _id: string }[] },
cb?: () => void
) => Promise<void>;
tags: WsTag[];
onCreateTag: () => void;
@@ -143,7 +144,7 @@ export const SecretDetailSidebar = ({
};
const handleFormSubmit = async (data: TFormSchema) => {
await onSaveSecret(secret, { ...secret, ...data });
await onSaveSecret(secret, { ...secret, ...data }, () => reset());
};
return (
@@ -54,7 +54,8 @@ type Props = {
secret: DecryptedSecret;
onSaveSecret: (
orgSec: DecryptedSecret,
modSec: Omit<DecryptedSecret, "tags"> & { tags: { _id: string }[] }
modSec: Omit<DecryptedSecret, "tags"> & { tags: { _id: string }[] },
cb?: () => void
) => Promise<void>;
onDeleteSecret: (sec: DecryptedSecret) => void;
onDetailViewSecret: (sec: DecryptedSecret) => void;
@@ -148,13 +149,14 @@ export const SecretItem = memo(
);
setValue("valueOverride", secret?.valueOverride, { shouldDirty: !isUnsavedOverride });
} else {
reset();
setValue("overrideAction", SecretActionType.Modified, { shouldDirty: true });
setValue("valueOverride", "", { shouldDirty: true });
}
};
const handleFormSubmit = async (data: TFormSchema) => {
await onSaveSecret(secret, { ...secret, ...data });
await onSaveSecret(secret, { ...secret, ...data }, () => reset());
};
const handleTagSelect = (tag: WsTag) => {
@@ -27,6 +27,7 @@ type Props = {
sortDir?: SortDir;
tags?: WsTag[];
isVisible?: boolean;
isProtectedBranch?: boolean;
};
const reorderSecretGroupByUnderscore = (secrets: DecryptedSecret[], sortDir: SortDir) => {
@@ -84,7 +85,8 @@ export const SecretListView = ({
filter,
sortDir = SortDir.ASC,
tags: wsTags = [],
isVisible
isVisible,
isProtectedBranch = false
}: Props) => {
const { createNotification } = useNotificationContext();
const queryClient = useQueryClient();
@@ -178,8 +180,10 @@ export const SecretListView = ({
const handleSaveSecret = useCallback(
async (
orgSecret: DecryptedSecret,
modSecret: Omit<DecryptedSecret, "tags"> & { tags: { _id: string }[] }
modSecret: Omit<DecryptedSecret, "tags"> & { tags: { _id: string }[] },
cb?: () => void
) => {
console.log(orgSecret, modSecret);
const { key: oldKey } = orgSecret;
const { key, value, overrideAction, idOverride, valueOverride, tags, comment } = modSecret;
const hasKeyChanged = oldKey !== key;
@@ -193,6 +197,19 @@ export const SecretListView = ({
) && isSameTags;
try {
// personal secret change
if (overrideAction === "deleted") {
await handleSecretOperation("delete", "personal", oldKey);
} else if (overrideAction && idOverride) {
await handleSecretOperation("update", "personal", oldKey, {
value: valueOverride,
newKey: hasKeyChanged ? key : undefined,
skipMultilineEncoding: modSecret.skipMultilineEncoding
});
} else if (overrideAction) {
await handleSecretOperation("create", "personal", oldKey, { value: valueOverride });
}
// shared secret change
if (!isSharedSecUnchanged) {
await handleSecretOperation("update", "shared", oldKey, {
@@ -202,19 +219,7 @@ export const SecretListView = ({
newKey: hasKeyChanged ? key : undefined,
skipMultilineEncoding: modSecret.skipMultilineEncoding
});
}
// personal secret change
if (overrideAction === "deleted") {
await handleSecretOperation("delete", "personal", key);
} else if (overrideAction && idOverride) {
await handleSecretOperation("update", "personal", oldKey, {
value: valueOverride,
newKey: hasKeyChanged ? key : undefined,
skipMultilineEncoding: modSecret.skipMultilineEncoding
});
} else if (overrideAction) {
await handleSecretOperation("create", "personal", key, { value: valueOverride });
if (isProtectedBranch) cb?.();
}
queryClient.invalidateQueries(
@@ -229,7 +234,9 @@ export const SecretListView = ({
handlePopUpClose("secretDetail");
createNotification({
type: "success",
text: "Successfully saved secrets"
text: isProtectedBranch
? "Requested changes have been sent for review"
: "Successfully saved secrets"
});
} catch (error) {
console.log(error);
@@ -259,7 +266,9 @@ export const SecretListView = ({
handlePopUpClose("secretDetail");
createNotification({
type: "success",
text: "Successfully deleted secret"
text: isProtectedBranch
? "Requested changes have been sent for review"
: "Successfully deleted secret"
});
} catch (error) {
console.log(error);