fix(secrets-permissions): UI fix for users with edit permissions but not read secret value permission

This commit is contained in:
carlosmonastyrski
2025-04-23 21:09:19 -03:00
parent f145a00ef5
commit 56b3e7a76d
3 changed files with 105 additions and 15 deletions

View File

@@ -87,6 +87,7 @@ export const SecretEditRow = ({
const { permission } = useProjectPermission();
const [isDeleting, setIsDeleting] = useToggle();
const [isSecretBlurFocus, setIsSecretBlurFocus] = useToggle(false);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const toggleModal = useCallback(() => {
@@ -124,7 +125,14 @@ export const SecretEditRow = ({
);
}
}
reset({ value });
if (secretValueHidden) {
setTimeout(() => {
reset({ value: defaultValue || null });
}, 0);
setIsSecretBlurFocus.off();
} else {
reset({ value });
}
};
const canReadSecretValue = hasSecretReadValueOrDescribePermission(
@@ -132,6 +140,22 @@ export const SecretEditRow = ({
ProjectPermissionSecretActions.ReadValue
);
const canEditSecretValue = permission.can(
ProjectPermissionSecretActions.Edit,
subject(ProjectPermissionSub.Secrets, {
environment,
secretPath,
secretName,
secretTags: ["*"]
})
);
const handleSecretBlurClick = () => {
if (canEditSecretValue) {
setIsSecretBlurFocus.toggle();
}
};
const handleDeleteSecret = useCallback(async () => {
setIsDeleting.on();
setIsModalOpen(false);
@@ -155,8 +179,17 @@ export const SecretEditRow = ({
/>
<div className="flex-grow border-r border-r-mineshaft-600 pl-1 pr-2">
{secretValueHidden ? (
<Blur tooltipText="You do not have permission to read the value of this secret." />
{secretValueHidden && !isSecretBlurFocus ? (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
className="flex flex-grow"
onClick={handleSecretBlurClick}
onBlur={() => setIsSecretBlurFocus.off()}
>
<Blur
tooltipText={`You do not have permission to read the value of this secret.${canEditSecretValue ? " But you have permission to edit it." : ""}`}
/>
</div>
) : (
<Controller
disabled={isImportedSecret && !defaultValue}
@@ -172,6 +205,7 @@ export const SecretEditRow = ({
secretPath={secretPath}
environment={environment}
isImport={isImportedSecret}
defaultValue={secretValueHidden ? "" : undefined}
/>
)}
/>

View File

@@ -1,3 +1,4 @@
import { subject } from "@casl/ability";
import { faCircle } from "@fortawesome/free-regular-svg-icons";
import {
faAngleDown,
@@ -14,6 +15,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { Button, Checkbox, TableContainer, Td, Tooltip, Tr } from "@app/components/v2";
import { useProjectPermission } from "@app/context";
import {
ProjectPermissionSecretActions,
ProjectPermissionSub
} from "@app/context/ProjectPermissionContext/types";
import { useToggle } from "@app/hooks";
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
import { WorkspaceEnv } from "@app/hooks/api/types";
@@ -64,6 +70,28 @@ export const SecretOverviewTableRow = ({
const totalCols = environments.length + 1; // secret key row
const [isSecretVisible, setIsSecretVisible] = useToggle();
const { permission } = useProjectPermission();
const getDefaultValue = (
secret: SecretV3RawSanitized | undefined,
importedSecret: { secret?: SecretV3RawSanitized } | undefined
) => {
const canEditSecretValue = permission.can(
ProjectPermissionSecretActions.Edit,
subject(ProjectPermissionSub.Secrets, {
environment: secret?.env || "",
secretPath: secret?.path || "",
secretName: secret?.key || "",
secretTags: ["*"]
})
);
if (secret?.secretValueHidden) {
return canEditSecretValue ? "<hidden-by-infisical>" : "";
}
return secret?.valueOverride || secret?.value || importedSecret?.secret?.value || "";
};
return (
<>
<Tr isHoverable isSelectable onClick={() => setIsFormExpanded.toggle()} className="group">
@@ -228,13 +256,7 @@ export const SecretOverviewTableRow = ({
isVisible={isSecretVisible}
secretName={secretKey}
secretValueHidden={secret?.secretValueHidden || false}
defaultValue={
secret?.secretValueHidden
? ""
: secret?.valueOverride ||
secret?.value ||
importedSecret?.secret?.value
}
defaultValue={getDefaultValue(secret, importedSecret)}
secretId={secret?.id}
isOverride={Boolean(secret?.valueOverride)}
isImportedSecret={isImportedSecret}

View File

@@ -94,6 +94,24 @@ export const SecretItem = memo(
const { currentWorkspace } = useWorkspace();
const { permission } = useProjectPermission();
const { isRotatedSecret } = secret;
const [isSecretBlurFocus, setIsSecretBlurFocus] = useToggle(false);
const canEditSecretValue = permission.can(
ProjectPermissionSecretActions.Edit,
subject(ProjectPermissionSub.Secrets, {
environment,
secretPath,
secretName: secret.key,
secretTags: ["*"]
})
);
const getDefaultValue = () => {
if (secret.secretValueHidden) {
return canEditSecretValue ? "<hidden-by-infisical>" : "";
}
return secret.valueOverride || secret.value || "";
};
const {
handleSubmit,
@@ -108,11 +126,11 @@ export const SecretItem = memo(
} = useForm<TFormSchema>({
defaultValues: {
...secret,
value: secret.secretValueHidden ? "" : secret.value
value: getDefaultValue()
},
values: {
...secret,
value: secret.secretValueHidden ? "" : secret.value
value: getDefaultValue()
},
resolver: zodResolver(formSchema)
});
@@ -154,6 +172,7 @@ export const SecretItem = memo(
secretTags: selectedTagSlugs
})
);
const { secretValueHidden } = secret;
const [isSecValueCopied, setIsSecValueCopied] = useToggle(false);
@@ -169,6 +188,12 @@ export const SecretItem = memo(
overrideAction === SecretActionType.Created || overrideAction === SecretActionType.Modified;
const hasTagsApplied = Boolean(fields.length);
const handleSecretBlurClick = () => {
if (canEditSecretValue) {
setIsSecretBlurFocus.toggle();
}
};
const handleOverrideClick = () => {
if (isOverriden) {
// override need not be flagged delete if it was never saved in server
@@ -301,8 +326,17 @@ export const SecretItem = memo(
/>
)}
/>
) : secretValueHidden ? (
<Blur tooltipText="You do not have permission to read the value of this secret." />
) : secretValueHidden && !isSecretBlurFocus ? (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
className="flex flex-grow"
onClick={handleSecretBlurClick}
onBlur={() => setIsSecretBlurFocus.off()}
>
<Blur
tooltipText={`You do not have permission to read the value of this secret.${canEditSecretValue ? " But you have permission to edit it." : ""}`}
/>
</div>
) : (
<Controller
name="value"
@@ -316,7 +350,7 @@ export const SecretItem = memo(
environment={environment}
secretPath={secretPath}
{...field}
defaultValue={secretValueHidden ? "" : undefined}
defaultValue={secretValueHidden ? "<hidden-by-infisical>" : undefined}
containerClassName="py-1.5 rounded-md transition-all"
/>
)}