mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 13:27:46 +00:00
fix(secrets-permissions): UI fix for users with edit permissions but not read secret value permission
This commit is contained in:
+37
-3
@@ -87,6 +87,7 @@ export const SecretEditRow = ({
|
|||||||
const { permission } = useProjectPermission();
|
const { permission } = useProjectPermission();
|
||||||
|
|
||||||
const [isDeleting, setIsDeleting] = useToggle();
|
const [isDeleting, setIsDeleting] = useToggle();
|
||||||
|
const [isSecretBlurFocus, setIsSecretBlurFocus] = useToggle(false);
|
||||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const toggleModal = useCallback(() => {
|
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(
|
const canReadSecretValue = hasSecretReadValueOrDescribePermission(
|
||||||
@@ -132,6 +140,22 @@ export const SecretEditRow = ({
|
|||||||
ProjectPermissionSecretActions.ReadValue
|
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 () => {
|
const handleDeleteSecret = useCallback(async () => {
|
||||||
setIsDeleting.on();
|
setIsDeleting.on();
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
@@ -155,8 +179,17 @@ export const SecretEditRow = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex-grow border-r border-r-mineshaft-600 pl-1 pr-2">
|
<div className="flex-grow border-r border-r-mineshaft-600 pl-1 pr-2">
|
||||||
{secretValueHidden ? (
|
{secretValueHidden && !isSecretBlurFocus ? (
|
||||||
<Blur tooltipText="You do not have permission to read the value of this secret." />
|
// 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
|
<Controller
|
||||||
disabled={isImportedSecret && !defaultValue}
|
disabled={isImportedSecret && !defaultValue}
|
||||||
@@ -172,6 +205,7 @@ export const SecretEditRow = ({
|
|||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environment={environment}
|
||||||
isImport={isImportedSecret}
|
isImport={isImportedSecret}
|
||||||
|
defaultValue={secretValueHidden ? "" : undefined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+29
-7
@@ -1,3 +1,4 @@
|
|||||||
|
import { subject } from "@casl/ability";
|
||||||
import { faCircle } from "@fortawesome/free-regular-svg-icons";
|
import { faCircle } from "@fortawesome/free-regular-svg-icons";
|
||||||
import {
|
import {
|
||||||
faAngleDown,
|
faAngleDown,
|
||||||
@@ -14,6 +15,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { Button, Checkbox, TableContainer, Td, Tooltip, Tr } from "@app/components/v2";
|
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 { useToggle } from "@app/hooks";
|
||||||
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
|
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
|
||||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
@@ -64,6 +70,28 @@ export const SecretOverviewTableRow = ({
|
|||||||
const totalCols = environments.length + 1; // secret key row
|
const totalCols = environments.length + 1; // secret key row
|
||||||
const [isSecretVisible, setIsSecretVisible] = useToggle();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Tr isHoverable isSelectable onClick={() => setIsFormExpanded.toggle()} className="group">
|
<Tr isHoverable isSelectable onClick={() => setIsFormExpanded.toggle()} className="group">
|
||||||
@@ -228,13 +256,7 @@ export const SecretOverviewTableRow = ({
|
|||||||
isVisible={isSecretVisible}
|
isVisible={isSecretVisible}
|
||||||
secretName={secretKey}
|
secretName={secretKey}
|
||||||
secretValueHidden={secret?.secretValueHidden || false}
|
secretValueHidden={secret?.secretValueHidden || false}
|
||||||
defaultValue={
|
defaultValue={getDefaultValue(secret, importedSecret)}
|
||||||
secret?.secretValueHidden
|
|
||||||
? ""
|
|
||||||
: secret?.valueOverride ||
|
|
||||||
secret?.value ||
|
|
||||||
importedSecret?.secret?.value
|
|
||||||
}
|
|
||||||
secretId={secret?.id}
|
secretId={secret?.id}
|
||||||
isOverride={Boolean(secret?.valueOverride)}
|
isOverride={Boolean(secret?.valueOverride)}
|
||||||
isImportedSecret={isImportedSecret}
|
isImportedSecret={isImportedSecret}
|
||||||
|
|||||||
+39
-5
@@ -94,6 +94,24 @@ export const SecretItem = memo(
|
|||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const { permission } = useProjectPermission();
|
const { permission } = useProjectPermission();
|
||||||
const { isRotatedSecret } = secret;
|
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 {
|
const {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -108,11 +126,11 @@ export const SecretItem = memo(
|
|||||||
} = useForm<TFormSchema>({
|
} = useForm<TFormSchema>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
...secret,
|
...secret,
|
||||||
value: secret.secretValueHidden ? "" : secret.value
|
value: getDefaultValue()
|
||||||
},
|
},
|
||||||
values: {
|
values: {
|
||||||
...secret,
|
...secret,
|
||||||
value: secret.secretValueHidden ? "" : secret.value
|
value: getDefaultValue()
|
||||||
},
|
},
|
||||||
resolver: zodResolver(formSchema)
|
resolver: zodResolver(formSchema)
|
||||||
});
|
});
|
||||||
@@ -154,6 +172,7 @@ export const SecretItem = memo(
|
|||||||
secretTags: selectedTagSlugs
|
secretTags: selectedTagSlugs
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const { secretValueHidden } = secret;
|
const { secretValueHidden } = secret;
|
||||||
|
|
||||||
const [isSecValueCopied, setIsSecValueCopied] = useToggle(false);
|
const [isSecValueCopied, setIsSecValueCopied] = useToggle(false);
|
||||||
@@ -169,6 +188,12 @@ export const SecretItem = memo(
|
|||||||
overrideAction === SecretActionType.Created || overrideAction === SecretActionType.Modified;
|
overrideAction === SecretActionType.Created || overrideAction === SecretActionType.Modified;
|
||||||
const hasTagsApplied = Boolean(fields.length);
|
const hasTagsApplied = Boolean(fields.length);
|
||||||
|
|
||||||
|
const handleSecretBlurClick = () => {
|
||||||
|
if (canEditSecretValue) {
|
||||||
|
setIsSecretBlurFocus.toggle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleOverrideClick = () => {
|
const handleOverrideClick = () => {
|
||||||
if (isOverriden) {
|
if (isOverriden) {
|
||||||
// override need not be flagged delete if it was never saved in server
|
// override need not be flagged delete if it was never saved in server
|
||||||
@@ -301,8 +326,17 @@ export const SecretItem = memo(
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
) : secretValueHidden ? (
|
) : secretValueHidden && !isSecretBlurFocus ? (
|
||||||
<Blur tooltipText="You do not have permission to read the value of this secret." />
|
// 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
|
<Controller
|
||||||
name="value"
|
name="value"
|
||||||
@@ -316,7 +350,7 @@ export const SecretItem = memo(
|
|||||||
environment={environment}
|
environment={environment}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
{...field}
|
{...field}
|
||||||
defaultValue={secretValueHidden ? "" : undefined}
|
defaultValue={secretValueHidden ? "<hidden-by-infisical>" : undefined}
|
||||||
containerClassName="py-1.5 rounded-md transition-all"
|
containerClassName="py-1.5 rounded-md transition-all"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user