mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 14:28:35 +00:00
feat(ui): made changes from feedback and force delete feature
This commit is contained in:
@@ -27,7 +27,7 @@ export const AccordionTrigger = forwardRef<
|
|||||||
<AccordionPrimitive.Header className="flex">
|
<AccordionPrimitive.Header className="flex">
|
||||||
<AccordionPrimitive.Trigger
|
<AccordionPrimitive.Trigger
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"group flex h-11 flex-1 items-center justify-between py-2 px-4 outline-none hover:text-primary data-[state=open]:text-primary ",
|
"group flex h-11 flex-1 items-center justify-between py-2 px-4 outline-none hover:text-primary data-[state=open]:text-primary",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export type TDeleteDynamicSecretDTO = {
|
|||||||
path: string;
|
path: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
isForced?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDetailsDynamicSecretDTO = {
|
export type TDetailsDynamicSecretDTO = {
|
||||||
|
|||||||
@@ -44,4 +44,5 @@ export type TRevokeDynamicSecretLeaseDTO = {
|
|||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
path: string;
|
path: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
isForced?: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -53,8 +53,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
animate={{ opacity: 1, translateX: 0 }}
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
exit={{ opacity: 0, translateX: -30 }}
|
exit={{ opacity: 0, translateX: -30 }}
|
||||||
>
|
>
|
||||||
<div className="mb-4 text-center">Pick a service to connect</div>
|
<div className="mb-4 ">Pick a service to connect</div>
|
||||||
<div className="flex items-center justify-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<div
|
<div
|
||||||
className="flex h-32 w-32 cursor-pointer flex-col items-center space-y-4 rounded border border-mineshaft-500 bg-bunker-700 p-6 transition-all hover:bg-bunker-600"
|
className="flex h-32 w-32 cursor-pointer flex-col items-center space-y-4 rounded border border-mineshaft-500 bg-bunker-700 p-6 transition-all hover:bg-bunker-600"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
+79
-62
@@ -6,6 +6,10 @@ import { z } from "zod";
|
|||||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import {
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
@@ -74,7 +78,16 @@ export const SqlDatabaseInputForm = ({
|
|||||||
formState: { isSubmitting },
|
formState: { isSubmitting },
|
||||||
handleSubmit
|
handleSubmit
|
||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
resolver: zodResolver(formSchema)
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
provider: {
|
||||||
|
creationStatement:
|
||||||
|
"CREATE USER \"{{username}}\" WITH SUPERUSER ENCRYPTED PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{username}}\";",
|
||||||
|
renewStatement: "ALTER ROLE \"{{username}}\" VALID UNTIL '{{expiration}}';",
|
||||||
|
revocationStatement:
|
||||||
|
'REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM "{{username}}";\nDROP OWNED BY "{{username}}";\nDROP ROLE "{{username}}";'
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
@@ -265,70 +278,74 @@ export const SqlDatabaseInputForm = ({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Accordion
|
||||||
control={control}
|
type="single"
|
||||||
name="provider.creationStatement"
|
collapsible
|
||||||
defaultValue={
|
className="w-full bg-mineshaft-700"
|
||||||
"CREATE USER \"{{username}}\" WITH SUPERUSER ENCRYPTED PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{username}}\";"
|
>
|
||||||
}
|
<AccordionItem value="advance-statements">
|
||||||
render={({ field, fieldState: { error } }) => (
|
<AccordionTrigger>Modify SQL Statements</AccordionTrigger>
|
||||||
<FormControl
|
<AccordionContent>
|
||||||
label="Creation Statement"
|
<Controller
|
||||||
isError={Boolean(error?.message)}
|
control={control}
|
||||||
errorText={error?.message}
|
name="provider.creationStatement"
|
||||||
helperText="username, password and expiration are dynamically provisioned"
|
render={({ field, fieldState: { error } }) => (
|
||||||
>
|
<FormControl
|
||||||
<TextArea
|
label="Creation Statement"
|
||||||
{...field}
|
isError={Boolean(error?.message)}
|
||||||
reSize="none"
|
errorText={error?.message}
|
||||||
rows={3}
|
helperText="username, password and expiration are dynamically provisioned"
|
||||||
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
>
|
||||||
|
<TextArea
|
||||||
|
{...field}
|
||||||
|
reSize="none"
|
||||||
|
rows={3}
|
||||||
|
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
<Controller
|
||||||
)}
|
control={control}
|
||||||
/>
|
name="provider.revocationStatement"
|
||||||
<Controller
|
render={({ field, fieldState: { error } }) => (
|
||||||
control={control}
|
<FormControl
|
||||||
name="provider.revocationStatement"
|
label="Revocation Statement"
|
||||||
defaultValue={
|
isError={Boolean(error?.message)}
|
||||||
'REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM "{{username}}";\nDROP OWNED BY "{{username}}"; DROP ROLE "{{username}}";'
|
errorText={error?.message}
|
||||||
}
|
helperText="username is dynamically provisioned"
|
||||||
render={({ field, fieldState: { error } }) => (
|
>
|
||||||
<FormControl
|
<TextArea
|
||||||
label="Revocation Statement"
|
{...field}
|
||||||
isError={Boolean(error?.message)}
|
reSize="none"
|
||||||
errorText={error?.message}
|
rows={3}
|
||||||
helperText="username is dynamically provisioned"
|
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
||||||
>
|
/>
|
||||||
<TextArea
|
</FormControl>
|
||||||
{...field}
|
)}
|
||||||
reSize="none"
|
|
||||||
rows={3}
|
|
||||||
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
<Controller
|
||||||
)}
|
control={control}
|
||||||
/>
|
name="provider.renewStatement"
|
||||||
<Controller
|
render={({ field, fieldState: { error } }) => (
|
||||||
control={control}
|
<FormControl
|
||||||
name="provider.renewStatement"
|
label="Renew Statement"
|
||||||
defaultValue={"ALTER ROLE \"{{username}}\" VALID UNTIL '{{expiration}}';"}
|
helperText="username and expiration are dynamically provisioned"
|
||||||
render={({ field, fieldState: { error } }) => (
|
isError={Boolean(error?.message)}
|
||||||
<FormControl
|
errorText={error?.message}
|
||||||
label="Renew Statement"
|
>
|
||||||
helperText="username and expiration are dynamically provisioned"
|
<TextArea
|
||||||
isError={Boolean(error?.message)}
|
{...field}
|
||||||
errorText={error?.message}
|
reSize="none"
|
||||||
>
|
rows={3}
|
||||||
<TextArea
|
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
||||||
{...field}
|
/>
|
||||||
reSize="none"
|
</FormControl>
|
||||||
rows={3}
|
)}
|
||||||
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</AccordionContent>
|
||||||
)}
|
</AccordionItem>
|
||||||
/>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+2
-2
@@ -118,13 +118,13 @@ export const CreateDynamicSecretLease = ({
|
|||||||
});
|
});
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "success",
|
type: "success",
|
||||||
text: "Successfully deleted folder"
|
text: "Successfully created dynamic secret"
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: "Failed to delete folder"
|
text: "Failed to deleted dynamic secret"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+38
-7
@@ -1,5 +1,11 @@
|
|||||||
import { subject } from "@casl/ability";
|
import { subject } from "@casl/ability";
|
||||||
import { faClose, faFileContract, faInfoCircle, faRepeat } from "@fortawesome/free-solid-svg-icons";
|
import {
|
||||||
|
faClose,
|
||||||
|
faFileContract,
|
||||||
|
faRepeat,
|
||||||
|
faTrash,
|
||||||
|
faWarning
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { format, formatDistance } from "date-fns";
|
import { format, formatDistance } from "date-fns";
|
||||||
|
|
||||||
@@ -23,6 +29,7 @@ import {
|
|||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useGetDynamicSecretLeases, useRevokeDynamicSecretLease } from "@app/hooks/api";
|
import { useGetDynamicSecretLeases, useRevokeDynamicSecretLease } from "@app/hooks/api";
|
||||||
|
import { DynamicSecretLeaseStatus } from "@app/hooks/api/dynamicSecretLease/types";
|
||||||
|
|
||||||
import { RenewDynamicSecretLease } from "./RenewDynamicSecretLease";
|
import { RenewDynamicSecretLease } from "./RenewDynamicSecretLease";
|
||||||
|
|
||||||
@@ -59,13 +66,17 @@ export const DynamicSecretLease = ({
|
|||||||
|
|
||||||
const handleDynamicSecretDeleteLease = async () => {
|
const handleDynamicSecretDeleteLease = async () => {
|
||||||
try {
|
try {
|
||||||
const { leaseId } = popUp.deleteSecret.data as { leaseId: string };
|
const { leaseId, isForced } = popUp.deleteSecret.data as {
|
||||||
|
leaseId: string;
|
||||||
|
isForced?: boolean;
|
||||||
|
};
|
||||||
await deleteDynamicSecretLease.mutateAsync({
|
await deleteDynamicSecretLease.mutateAsync({
|
||||||
environment,
|
environment,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
path: secretPath,
|
path: secretPath,
|
||||||
slug,
|
slug,
|
||||||
leaseId
|
leaseId,
|
||||||
|
isForced
|
||||||
});
|
});
|
||||||
handlePopUpClose("deleteSecret");
|
handlePopUpClose("deleteSecret");
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -115,10 +126,7 @@ export const DynamicSecretLease = ({
|
|||||||
{id}
|
{id}
|
||||||
{Boolean(status) && (
|
{Boolean(status) && (
|
||||||
<Tooltip content={statusDetails || status || ""}>
|
<Tooltip content={statusDetails || status || ""}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon className="ml-2 text-yellow-600" icon={faWarning} />
|
||||||
className="relative bottom-2 left-1 text-red-600"
|
|
||||||
icon={faInfoCircle}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
@@ -169,6 +177,29 @@ export const DynamicSecretLease = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
|
{status === DynamicSecretLeaseStatus.FailedDeletion && (
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Delete}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||||
|
renderTooltip
|
||||||
|
allowedLabel="Force Delete. This action will remove the secret from internal storage, but it will remain in external systems."
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="delete-folder"
|
||||||
|
variant="plain"
|
||||||
|
size="md"
|
||||||
|
className="p-0 text-red-600"
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
onClick={() =>
|
||||||
|
handlePopUpOpen("deleteSecret", { leaseId: id, isForced: true })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faTrash} />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
|
|||||||
+41
-11
@@ -2,8 +2,8 @@ import { subject } from "@casl/ability";
|
|||||||
import {
|
import {
|
||||||
faClose,
|
faClose,
|
||||||
faFingerprint,
|
faFingerprint,
|
||||||
faInfoCircle,
|
faPencilSquare,
|
||||||
faPencilSquare
|
faWarning
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
@@ -64,23 +64,26 @@ export const DynamicSecretListView = ({
|
|||||||
|
|
||||||
const handleDynamicSecretDelete = async () => {
|
const handleDynamicSecretDelete = async () => {
|
||||||
try {
|
try {
|
||||||
const { slug } = popUp.deleteDynamicSecret.data as TDynamicSecret;
|
const { slug, isForced } = popUp.deleteDynamicSecret.data as TDynamicSecret & {
|
||||||
|
isForced?: boolean;
|
||||||
|
};
|
||||||
await deleteDynamicSecret.mutateAsync({
|
await deleteDynamicSecret.mutateAsync({
|
||||||
environment,
|
environment,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
path: secretPath,
|
path: secretPath,
|
||||||
slug
|
slug,
|
||||||
|
isForced
|
||||||
});
|
});
|
||||||
handlePopUpClose("deleteDynamicSecret");
|
handlePopUpClose("deleteDynamicSecret");
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "success",
|
type: "success",
|
||||||
text: "Successfully deleted secret"
|
text: "Successfully deleted dynamic secret"
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: "Failed to delete secret"
|
text: "Failed to delete dynamic secret"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -132,13 +135,17 @@ export const DynamicSecretListView = ({
|
|||||||
{Boolean(secret.status) && (
|
{Boolean(secret.status) && (
|
||||||
<Tooltip content={secret?.statusDetails || secret.status || ""}>
|
<Tooltip content={secret?.statusDetails || secret.status || ""}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
className="relative bottom-2 text-red-600"
|
className={
|
||||||
icon={faInfoCircle}
|
secret.status === DynamicSecretStatus.Deleting
|
||||||
|
? "text-yellow-600"
|
||||||
|
: "text-red-600"
|
||||||
|
}
|
||||||
|
icon={faWarning}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center px-4 py-2">
|
<div className="flex items-center space-x-2 px-4 py-2">
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
className="m-0 py-0.5 px-2 opacity-0 group-hover:opacity-100"
|
className="m-0 py-0.5 px-2 opacity-0 group-hover:opacity-100"
|
||||||
@@ -148,8 +155,27 @@ export const DynamicSecretListView = ({
|
|||||||
handlePopUpOpen("createDynamicSecretLease", secret);
|
handlePopUpOpen("createDynamicSecretLease", secret);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
lease
|
Generate
|
||||||
</Button>
|
</Button>
|
||||||
|
{secret.status === DynamicSecretStatus.FailedDeletion && (
|
||||||
|
<Tooltip content="This action will remove the secret from internal storage, but it will remain in external systems. Use this option only after you've confirmed that your external leases are handled.">
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
className="m-0 py-0.5 px-2"
|
||||||
|
colorSchema="danger"
|
||||||
|
isDisabled={isRevocking}
|
||||||
|
onClick={(evt) => {
|
||||||
|
evt.stopPropagation();
|
||||||
|
handlePopUpOpen("deleteDynamicSecret", {
|
||||||
|
...secret,
|
||||||
|
isForced: true
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Force Delete
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4 border-l border-mineshaft-600 px-3 py-3">
|
<div className="flex items-center space-x-4 border-l border-mineshaft-600 px-3 py-3">
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
@@ -250,7 +276,11 @@ export const DynamicSecretListView = ({
|
|||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.deleteDynamicSecret.isOpen}
|
isOpen={popUp.deleteDynamicSecret.isOpen}
|
||||||
deleteKey={(popUp.deleteDynamicSecret?.data as TDynamicSecret)?.slug}
|
deleteKey={(popUp.deleteDynamicSecret?.data as TDynamicSecret)?.slug}
|
||||||
title="Do you want to delete this dynamc secret?"
|
title={
|
||||||
|
(popUp.deleteDynamicSecret?.data as { isForced?: boolean })?.isForced
|
||||||
|
? "Do you want to force delete this dynamic secret?"
|
||||||
|
: "Do you want to delete this dynamic secret?"
|
||||||
|
}
|
||||||
onChange={(isOpen) => handlePopUpToggle("deleteDynamicSecret", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("deleteDynamicSecret", isOpen)}
|
||||||
onDeleteApproved={handleDynamicSecretDelete}
|
onDeleteApproved={handleDynamicSecretDelete}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+72
-61
@@ -6,6 +6,10 @@ import { z } from "zod";
|
|||||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import {
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
@@ -280,70 +284,77 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Accordion type="multiple" className="w-full bg-mineshaft-700">
|
||||||
control={control}
|
<AccordionItem value="modify-sql-statement">
|
||||||
name="inputs.creationStatement"
|
<AccordionTrigger>Modify SQL Statements</AccordionTrigger>
|
||||||
defaultValue={
|
<AccordionContent>
|
||||||
"CREATE USER \"{{username}}\" WITH SUPERUSER ENCRYPTED PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{username}}\";"
|
<Controller
|
||||||
}
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="inputs.creationStatement"
|
||||||
<FormControl
|
defaultValue={
|
||||||
label="Creation Statement"
|
"CREATE USER \"{{username}}\" WITH SUPERUSER ENCRYPTED PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{username}}\";"
|
||||||
isError={Boolean(error?.message)}
|
}
|
||||||
errorText={error?.message}
|
render={({ field, fieldState: { error } }) => (
|
||||||
helperText="username, password and expiration are dynamically provisioned"
|
<FormControl
|
||||||
>
|
label="Creation Statement"
|
||||||
<TextArea
|
isError={Boolean(error?.message)}
|
||||||
{...field}
|
errorText={error?.message}
|
||||||
reSize="none"
|
helperText="username, password and expiration are dynamically provisioned"
|
||||||
rows={3}
|
>
|
||||||
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
<TextArea
|
||||||
|
{...field}
|
||||||
|
reSize="none"
|
||||||
|
rows={3}
|
||||||
|
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
<Controller
|
||||||
)}
|
control={control}
|
||||||
/>
|
name="inputs.revocationStatement"
|
||||||
<Controller
|
defaultValue={
|
||||||
control={control}
|
'REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM "{{username}}";\nDROP OWNED BY "{{username}}"; DROP ROLE "{{username}}";'
|
||||||
name="inputs.revocationStatement"
|
}
|
||||||
defaultValue={
|
render={({ field, fieldState: { error } }) => (
|
||||||
'REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM "{{username}}";\nDROP OWNED BY "{{username}}"; DROP ROLE "{{username}}";'
|
<FormControl
|
||||||
}
|
label="Revocation Statement"
|
||||||
render={({ field, fieldState: { error } }) => (
|
isError={Boolean(error?.message)}
|
||||||
<FormControl
|
errorText={error?.message}
|
||||||
label="Revocation Statement"
|
helperText="username is dynamically provisioned"
|
||||||
isError={Boolean(error?.message)}
|
>
|
||||||
errorText={error?.message}
|
<TextArea
|
||||||
helperText="username is dynamically provisioned"
|
{...field}
|
||||||
>
|
reSize="none"
|
||||||
<TextArea
|
rows={3}
|
||||||
{...field}
|
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
||||||
reSize="none"
|
/>
|
||||||
rows={3}
|
</FormControl>
|
||||||
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
<Controller
|
||||||
)}
|
control={control}
|
||||||
/>
|
name="inputs.renewStatement"
|
||||||
<Controller
|
defaultValue={"ALTER ROLE \"{{username}}\" VALID UNTIL '{{expiration}}';"}
|
||||||
control={control}
|
render={({ field, fieldState: { error } }) => (
|
||||||
name="inputs.renewStatement"
|
<FormControl
|
||||||
defaultValue={"ALTER ROLE \"{{username}}\" VALID UNTIL '{{expiration}}';"}
|
label="Renew Statement"
|
||||||
render={({ field, fieldState: { error } }) => (
|
helperText="username and expiration are dynamically provisioned"
|
||||||
<FormControl
|
isError={Boolean(error?.message)}
|
||||||
label="Renew Statement"
|
errorText={error?.message}
|
||||||
helperText="username and expiration are dynamically provisioned"
|
>
|
||||||
isError={Boolean(error?.message)}
|
<TextArea
|
||||||
errorText={error?.message}
|
{...field}
|
||||||
>
|
reSize="none"
|
||||||
<TextArea
|
rows={3}
|
||||||
{...field}
|
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
||||||
reSize="none"
|
/>
|
||||||
rows={3}
|
</FormControl>
|
||||||
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
|
)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</AccordionContent>
|
||||||
)}
|
</AccordionItem>
|
||||||
/>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user