Revert "Revert "feat(api/secrets): view secret value permission""

This commit is contained in:
Daniel Hougaard
2025-03-11 00:07:47 +04:00
committed by Daniel Hougaard
parent 3986df8e8a
commit 97567d06d4
70 changed files with 2105 additions and 729 deletions
@@ -19,6 +19,7 @@ import {
import { getKeyValue } from "@app/helpers/parseEnvVar";
import { useCreateFolder, useCreateSecretV3, useCreateWsTag, useGetWsTags } from "@app/hooks/api";
import { SecretType } from "@app/hooks/api/types";
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
const typeSchema = z
.object({
@@ -275,7 +276,7 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => {
isMulti
options={environments.filter((environment) =>
permission.can(
ProjectPermissionActions.Create,
ProjectPermissionSecretActions.Create,
subject(ProjectPermissionSub.Secrets, {
environment: environment.slug,
secretPath,
@@ -25,6 +25,7 @@ import {
ModalTrigger,
Tooltip
} from "@app/components/v2";
import { Blur } from "@app/components/v2/Blur";
import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { useToggle } from "@app/hooks";
@@ -39,6 +40,7 @@ type Props = {
isVisible?: boolean;
isImportedSecret: boolean;
environment: string;
secretValueHidden: boolean;
secretPath: string;
onSecretCreate: (env: string, key: string, value: string) => Promise<void>;
onSecretUpdate: (
@@ -58,6 +60,7 @@ export const SecretEditRow = ({
isImportedSecret,
onSecretUpdate,
secretName,
secretValueHidden,
onSecretCreate,
onSecretDelete,
environment,
@@ -140,24 +143,29 @@ export const SecretEditRow = ({
/>
<div className="flex-grow border-r border-r-mineshaft-600 pl-1 pr-2">
<Controller
disabled={isImportedSecret && !defaultValue}
control={control}
name="value"
render={({ field }) => (
<InfisicalSecretInput
{...field}
isReadOnly={isImportedSecret}
value={field.value as string}
key="secret-input"
isVisible={isVisible}
secretPath={secretPath}
environment={environment}
isImport={isImportedSecret}
/>
)}
/>
{secretValueHidden ? (
<Blur tooltipText="You do not have permission to read the value of this secret." />
) : (
<Controller
disabled={isImportedSecret && !defaultValue}
control={control}
name="value"
render={({ field }) => (
<InfisicalSecretInput
{...field}
isReadOnly={isImportedSecret}
value={field.value as string}
key="secret-input"
isVisible={isVisible}
secretPath={secretPath}
environment={environment}
isImport={isImportedSecret}
/>
)}
/>
)}
</div>
<div
className={twMerge(
"flex w-24 justify-center space-x-3 pl-2 transition-all",
@@ -211,6 +219,7 @@ export const SecretEditRow = ({
<div className="opacity-0 group-hover:opacity-100">
<Tooltip content="Copy Secret">
<IconButton
isDisabled={secretValueHidden}
ariaLabel="copy-value"
onClick={handleCopySecretToClipboard}
variant="plain"
@@ -3,6 +3,7 @@ import { faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Td, Tooltip, Tr } from "@app/components/v2";
import { Blur } from "@app/components/v2/Blur";
type Props = {
environments: { name: string; slug: string }[];
@@ -25,7 +26,7 @@ export const SecretNoAccessOverviewTableRow = ({ environments = [], count }: Pro
<div className="text-bunker-300">
<FontAwesomeIcon className="block" icon={faLock} />
</div>
<div className="blur-sm">NO ACCESS</div>
<Blur />
</div>
</Tooltip>
</div>
@@ -221,10 +221,13 @@ export const SecretOverviewTableRow = ({
secretPath={secretPath}
isVisible={isSecretVisible}
secretName={secretKey}
secretValueHidden={secret?.secretValueHidden || false}
defaultValue={
secret?.valueOverride ||
secret?.value ||
importedSecret?.secret?.value
secret?.secretValueHidden
? ""
: secret?.valueOverride ||
secret?.value ||
importedSecret?.secret?.value
}
secretId={secret?.id}
isOverride={Boolean(secret?.valueOverride)}
@@ -10,12 +10,8 @@ import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { IconButton, Input, Spinner, Tooltip } from "@app/components/v2";
import {
ProjectPermissionActions,
ProjectPermissionSub,
useProjectPermission,
useWorkspace
} from "@app/context";
import { ProjectPermissionSub, useProjectPermission, useWorkspace } from "@app/context";
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
import { useToggle } from "@app/hooks";
import { useUpdateSecretV3 } from "@app/hooks/api";
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)
});
const isSecretInEnvReadOnly =
permission.can(ProjectPermissionActions.Read, secretPermissionSubject) &&
permission.cannot(ProjectPermissionActions.Edit, secretPermissionSubject);
permission.can(ProjectPermissionSecretActions.DescribeSecret, secretPermissionSubject) &&
permission.cannot(ProjectPermissionSecretActions.Edit, secretPermissionSubject);
if (isSecretInEnvReadOnly) {
return true;
}
@@ -110,21 +110,31 @@ export const QuickSearchSecretItem = ({
</Badge>
)}
{isSingleEnv ? (
<IconButton
size="md"
variant="plain"
colorSchema="secondary"
ariaLabel="Copy secret value"
onClick={(e) => {
e.stopPropagation();
const el = envSlugMap.get(groupSecret.env)?.name;
if (el) {
handleCopy(groupSecret.value!, el);
}
}}
<Tooltip
isDisabled={!groupSecret?.secretValueHidden}
content={
groupSecret?.secretValueHidden
? "You do not have permission to view this secret value"
: ""
}
>
<FontAwesomeIcon icon={isUrlCopied ? faCheck : faCopy} />
</IconButton>
<IconButton
size="md"
isDisabled={groupSecret?.secretValueHidden}
variant="plain"
colorSchema="secondary"
ariaLabel="Copy secret value"
onClick={(e) => {
e.stopPropagation();
const el = envSlugMap.get(groupSecret.env)?.name;
if (el) {
handleCopy(groupSecret.value!, el);
}
}}
>
<FontAwesomeIcon icon={isUrlCopied ? faCheck : faCopy} />
</IconButton>
</Tooltip>
) : (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -158,14 +168,24 @@ export const QuickSearchSecretItem = ({
)}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<IconButton
size="md"
variant="plain"
colorSchema="secondary"
ariaLabel="View secret value"
<Tooltip
isDisabled={!groupSecret?.secretValueHidden}
content={
groupSecret?.secretValueHidden
? "You do not have permission to view this secret value"
: ""
}
>
<FontAwesomeIcon icon={faEye} />
</IconButton>
<IconButton
size="md"
isDisabled={groupSecret?.secretValueHidden}
variant="plain"
colorSchema="secondary"
ariaLabel="View secret value"
>
<FontAwesomeIcon icon={faEye} />
</IconButton>
</Tooltip>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Hover to Reveal...</DropdownMenuLabel>
@@ -11,6 +11,7 @@ import {
useProjectPermission,
useWorkspace
} from "@app/context";
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
import { usePopUp } from "@app/hooks";
import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api";
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
const shouldShowDelete = userAvailableEnvs.some((env) =>
permission.can(
ProjectPermissionActions.Delete,
ProjectPermissionSecretActions.Delete,
subject(ProjectPermissionSub.Secrets, {
environment: env.slug,
secretPath,
@@ -110,7 +111,7 @@ export const SelectionPanel = ({ secretPath, resetSelectedEntries, selectedEntri
(accum: TDeleteSecretBatchDTO["secrets"], secretRecord) => {
const entry = secretRecord[env.slug];
const canDeleteSecret = permission.can(
ProjectPermissionActions.Delete,
ProjectPermissionSecretActions.Delete,
subject(ProjectPermissionSub.Secrets, {
environment: env.slug,
secretPath,
@@ -25,7 +25,8 @@ import {
Spinner,
Switch
} 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 { useMoveSecrets } from "@app/hooks/api";
import { useGetProjectSecretsQuickSearch } from "@app/hooks/api/dashboard";
@@ -95,7 +96,7 @@ const Content = ({
env.slug,
{
missingPermissions: permission.cannot(
ProjectPermissionActions.Delete,
ProjectPermissionSecretActions.Delete,
subject(ProjectPermissionSub.Secrets, {
environment: env.slug,
secretPath: sourceSecretPath,