fix: cleanup and bug fixes

This commit is contained in:
Daniel Hougaard
2025-03-05 22:47:39 +04:00
parent 2e6d525d27
commit 2a0c0590f1
10 changed files with 86 additions and 61 deletions
@@ -165,6 +165,7 @@ export const secretSnapshotServiceFactory = ({
}); });
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
const shouldUseBridge = snapshot.projectVersion === 3; const shouldUseBridge = snapshot.projectVersion === 3;
let snapshotDetails; let snapshotDetails;
if (shouldUseBridge) { if (shouldUseBridge) {
@@ -173,68 +174,93 @@ export const secretSnapshotServiceFactory = ({
projectId: snapshot.projectId projectId: snapshot.projectId
}); });
const encryptedSnapshotDetails = await snapshotDAL.findSecretSnapshotV2DataById(id); const encryptedSnapshotDetails = await snapshotDAL.findSecretSnapshotV2DataById(id);
const fullFolderPath = await getFullFolderPath({
folderDAL,
folderId: encryptedSnapshotDetails.folderId,
envId: encryptedSnapshotDetails.environment.id
});
snapshotDetails = { snapshotDetails = {
...encryptedSnapshotDetails, ...encryptedSnapshotDetails,
secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => ({ secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => {
...el, ForbiddenError.from(permission).throwUnlessCan(
secretKey: el.key, ProjectPermissionSecretActions.ReadValue,
secretValue: el.encryptedValue subject(ProjectPermissionSub.Secrets, {
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() environment: encryptedSnapshotDetails.environment.slug,
: "", secretPath: fullFolderPath,
secretComment: el.encryptedComment secretName: el.key,
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() secretTags: el.tags.length ? el.tags.map((tag) => tag.slug) : undefined
: "" })
})) );
return {
...el,
secretKey: el.key,
secretValue: el.encryptedValue
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
: "",
secretComment: el.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
: ""
};
})
}; };
} else { } else {
const encryptedSnapshotDetails = await snapshotDAL.findSecretSnapshotDataById(id); const encryptedSnapshotDetails = await snapshotDAL.findSecretSnapshotDataById(id);
const fullFolderPath = await getFullFolderPath({
folderDAL,
folderId: encryptedSnapshotDetails.folderId,
envId: encryptedSnapshotDetails.environment.id
});
const { botKey } = await projectBotService.getBotKey(snapshot.projectId); const { botKey } = await projectBotService.getBotKey(snapshot.projectId);
if (!botKey) if (!botKey)
throw new NotFoundError({ message: `Project bot key not found for project with ID '${snapshot.projectId}'` }); throw new NotFoundError({ message: `Project bot key not found for project with ID '${snapshot.projectId}'` });
snapshotDetails = { snapshotDetails = {
...encryptedSnapshotDetails, ...encryptedSnapshotDetails,
secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => ({ secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => {
...el, const secretKey = decryptSymmetric128BitHexKeyUTF8({
secretKey: decryptSymmetric128BitHexKeyUTF8({
ciphertext: el.secretKeyCiphertext, ciphertext: el.secretKeyCiphertext,
iv: el.secretKeyIV, iv: el.secretKeyIV,
tag: el.secretKeyTag, tag: el.secretKeyTag,
key: botKey key: botKey
}), });
secretValue: decryptSymmetric128BitHexKeyUTF8({
ciphertext: el.secretValueCiphertext, ForbiddenError.from(permission).throwUnlessCan(
iv: el.secretValueIV, ProjectPermissionSecretActions.ReadValue,
tag: el.secretValueTag, subject(ProjectPermissionSub.Secrets, {
key: botKey environment: encryptedSnapshotDetails.environment.slug,
}), secretPath: fullFolderPath,
secretComment: secretName: secretKey,
el.secretCommentTag && el.secretCommentIV && el.secretCommentCiphertext secretTags: el.tags.length ? el.tags.map((tag) => tag.slug) : undefined
? decryptSymmetric128BitHexKeyUTF8({ })
ciphertext: el.secretCommentCiphertext, );
iv: el.secretCommentIV,
tag: el.secretCommentTag, return {
key: botKey ...el,
}) secretKey,
: "" secretValue: decryptSymmetric128BitHexKeyUTF8({
})) ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
}),
secretComment:
el.secretCommentTag && el.secretCommentIV && el.secretCommentCiphertext
? decryptSymmetric128BitHexKeyUTF8({
ciphertext: el.secretCommentCiphertext,
iv: el.secretCommentIV,
tag: el.secretCommentTag,
key: botKey
})
: ""
};
})
}; };
} }
const fullFolderPath = await getFullFolderPath({
folderDAL,
folderId: snapshotDetails.folderId,
envId: snapshotDetails.environment.id
});
// We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder.
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionSecretActions.ReadValue,
subject(ProjectPermissionSub.Secrets, {
environment: snapshotDetails.environment.slug,
secretPath: fullFolderPath
})
);
return snapshotDetails; return snapshotDetails;
}; };
@@ -1,6 +1,5 @@
/* eslint-disable react/no-danger */ /* eslint-disable react/no-danger */
import { forwardRef, TextareaHTMLAttributes } from "react"; import { forwardRef, TextareaHTMLAttributes } from "react";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { useToggle } from "@app/hooks"; import { useToggle } from "@app/hooks";
@@ -79,6 +79,7 @@ export const decryptSecrets = (
id: encSecret.id, id: encSecret.id,
env: encSecret.environment, env: encSecret.environment,
key: secretKey, key: secretKey,
secretValueHidden: encSecret.secretValueHidden,
value: secretValue, value: secretValue,
tags: encSecret.tags, tags: encSecret.tags,
comment: secretComment, comment: secretComment,
@@ -75,6 +75,7 @@ export const useGetSnapshotSecrets = ({ snapshotId }: TSnapshotDataProps) =>
id: secretVersion.secretId, id: secretVersion.secretId,
env: data.environment.slug, env: data.environment.slug,
key: secretVersion.secretKey, key: secretVersion.secretKey,
secretValueHidden: false,
value: secretVersion.secretValue || "", value: secretVersion.secretValue || "",
tags: secretVersion.tags, tags: secretVersion.tags,
comment: secretVersion.secretComment, comment: secretVersion.secretComment,
+1
View File
@@ -19,6 +19,7 @@ export type EncryptedSecret = {
secretValueCiphertext: string; secretValueCiphertext: string;
secretValueIV: string; secretValueIV: string;
secretValueTag: string; secretValueTag: string;
secretValueHidden: boolean;
__v: number; __v: number;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
@@ -19,6 +19,7 @@ import {
import { getKeyValue } from "@app/helpers/parseEnvVar"; import { getKeyValue } from "@app/helpers/parseEnvVar";
import { useCreateFolder, useCreateSecretV3, useCreateWsTag, useGetWsTags } from "@app/hooks/api"; import { useCreateFolder, useCreateSecretV3, useCreateWsTag, useGetWsTags } from "@app/hooks/api";
import { SecretType } from "@app/hooks/api/types"; import { SecretType } from "@app/hooks/api/types";
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
const typeSchema = z const typeSchema = z
.object({ .object({
@@ -275,7 +276,7 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => {
isMulti isMulti
options={environments.filter((environment) => options={environments.filter((environment) =>
permission.can( permission.can(
ProjectPermissionActions.Create, ProjectPermissionSecretActions.Create,
subject(ProjectPermissionSub.Secrets, { subject(ProjectPermissionSub.Secrets, {
environment: environment.slug, environment: environment.slug,
secretPath, secretPath,
@@ -10,12 +10,8 @@ import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { IconButton, Input, Spinner, Tooltip } from "@app/components/v2"; import { IconButton, Input, Spinner, Tooltip } from "@app/components/v2";
import { import { ProjectPermissionSub, useProjectPermission, useWorkspace } from "@app/context";
ProjectPermissionActions, import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
ProjectPermissionSub,
useProjectPermission,
useWorkspace
} from "@app/context";
import { useToggle } from "@app/hooks"; import { useToggle } from "@app/hooks";
import { useUpdateSecretV3 } from "@app/hooks/api"; import { useUpdateSecretV3 } from "@app/hooks/api";
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/types"; import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/types";
@@ -55,8 +51,8 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath }
secretTags: (secretDetails?.tags || []).map((i) => i.slug) secretTags: (secretDetails?.tags || []).map((i) => i.slug)
}); });
const isSecretInEnvReadOnly = const isSecretInEnvReadOnly =
permission.can(ProjectPermissionActions.Read, secretPermissionSubject) && permission.can(ProjectPermissionSecretActions.DescribeSecret, secretPermissionSubject) &&
permission.cannot(ProjectPermissionActions.Edit, secretPermissionSubject); permission.cannot(ProjectPermissionSecretActions.Edit, secretPermissionSubject);
if (isSecretInEnvReadOnly) { if (isSecretInEnvReadOnly) {
return true; return true;
} }
@@ -11,6 +11,7 @@ import {
useProjectPermission, useProjectPermission,
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api"; import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api";
import { import {
@@ -58,7 +59,7 @@ export const SelectionPanel = ({ secretPath, resetSelectedEntries, selectedEntri
// user should have the ability to delete secrets/folders in at least one of the envs // user should have the ability to delete secrets/folders in at least one of the envs
const shouldShowDelete = userAvailableEnvs.some((env) => const shouldShowDelete = userAvailableEnvs.some((env) =>
permission.can( permission.can(
ProjectPermissionActions.Delete, ProjectPermissionSecretActions.Delete,
subject(ProjectPermissionSub.Secrets, { subject(ProjectPermissionSub.Secrets, {
environment: env.slug, environment: env.slug,
secretPath, secretPath,
@@ -110,7 +111,7 @@ export const SelectionPanel = ({ secretPath, resetSelectedEntries, selectedEntri
(accum: TDeleteSecretBatchDTO["secrets"], secretRecord) => { (accum: TDeleteSecretBatchDTO["secrets"], secretRecord) => {
const entry = secretRecord[env.slug]; const entry = secretRecord[env.slug];
const canDeleteSecret = permission.can( const canDeleteSecret = permission.can(
ProjectPermissionActions.Delete, ProjectPermissionSecretActions.Delete,
subject(ProjectPermissionSub.Secrets, { subject(ProjectPermissionSub.Secrets, {
environment: env.slug, environment: env.slug,
secretPath, secretPath,
@@ -25,7 +25,8 @@ import {
Spinner, Spinner,
Switch Switch
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context"; import { ProjectPermissionSub, useProjectPermission } from "@app/context";
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
import { useDebounce } from "@app/hooks"; import { useDebounce } from "@app/hooks";
import { useMoveSecrets } from "@app/hooks/api"; import { useMoveSecrets } from "@app/hooks/api";
import { useGetProjectSecretsQuickSearch } from "@app/hooks/api/dashboard"; import { useGetProjectSecretsQuickSearch } from "@app/hooks/api/dashboard";
@@ -95,7 +96,7 @@ const Content = ({
env.slug, env.slug,
{ {
missingPermissions: permission.cannot( missingPermissions: permission.cannot(
ProjectPermissionActions.Delete, ProjectPermissionSecretActions.Delete,
subject(ProjectPermissionSub.Secrets, { subject(ProjectPermissionSub.Secrets, {
environment: env.slug, environment: env.slug,
secretPath: sourceSecretPath, secretPath: sourceSecretPath,
@@ -122,8 +122,6 @@ const Page = () => {
}) })
); );
console.log("Can read secret value", canReadSecret);
const canReadSecretImports = permission.can( const canReadSecretImports = permission.can(
ProjectPermissionActions.Read, ProjectPermissionActions.Read,
subject(ProjectPermissionSub.SecretImports, { environment, secretPath }) subject(ProjectPermissionSub.SecretImports, { environment, secretPath })