mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 17:27:40 +00:00
review fixes
This commit is contained in:
@@ -516,6 +516,7 @@ export enum EventType {
|
|||||||
PAM_ACCOUNT_UPDATE = "pam-account-update",
|
PAM_ACCOUNT_UPDATE = "pam-account-update",
|
||||||
PAM_ACCOUNT_DELETE = "pam-account-delete",
|
PAM_ACCOUNT_DELETE = "pam-account-delete",
|
||||||
PAM_ACCOUNT_CREDENTIAL_ROTATION = "pam-account-credential-rotation",
|
PAM_ACCOUNT_CREDENTIAL_ROTATION = "pam-account-credential-rotation",
|
||||||
|
PAM_ACCOUNT_CREDENTIAL_ROTATION_FAILED = "pam-account-credential-rotation-failed",
|
||||||
PAM_RESOURCE_LIST = "pam-resource-list",
|
PAM_RESOURCE_LIST = "pam-resource-list",
|
||||||
PAM_RESOURCE_GET = "pam-resource-get",
|
PAM_RESOURCE_GET = "pam-resource-get",
|
||||||
PAM_RESOURCE_CREATE = "pam-resource-create",
|
PAM_RESOURCE_CREATE = "pam-resource-create",
|
||||||
@@ -3834,6 +3835,16 @@ interface PamAccountCredentialRotationEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PamAccountCredentialRotationFailedEvent {
|
||||||
|
type: EventType.PAM_ACCOUNT_CREDENTIAL_ROTATION_FAILED;
|
||||||
|
metadata: {
|
||||||
|
accountName: string;
|
||||||
|
accountId: string;
|
||||||
|
resourceId: string;
|
||||||
|
errorMessage: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface PamResourceListEvent {
|
interface PamResourceListEvent {
|
||||||
type: EventType.PAM_RESOURCE_LIST;
|
type: EventType.PAM_RESOURCE_LIST;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -4225,6 +4236,7 @@ export type Event =
|
|||||||
| PamAccountUpdateEvent
|
| PamAccountUpdateEvent
|
||||||
| PamAccountDeleteEvent
|
| PamAccountDeleteEvent
|
||||||
| PamAccountCredentialRotationEvent
|
| PamAccountCredentialRotationEvent
|
||||||
|
| PamAccountCredentialRotationFailedEvent
|
||||||
| PamResourceListEvent
|
| PamResourceListEvent
|
||||||
| PamResourceGetEvent
|
| PamResourceGetEvent
|
||||||
| PamResourceCreateEvent
|
| PamResourceCreateEvent
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export const pamAccountDALFactory = (db: TDbClient) => {
|
|||||||
.select(
|
.select(
|
||||||
// resource
|
// resource
|
||||||
db.ref("name").withSchema(TableName.PamResource).as("resourceName"),
|
db.ref("name").withSchema(TableName.PamResource).as("resourceName"),
|
||||||
db.ref("resourceType").withSchema(TableName.PamResource)
|
db.ref("resourceType").withSchema(TableName.PamResource),
|
||||||
|
db.ref("encryptedRotationAccountCredentials").withSchema(TableName.PamResource)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
@@ -28,15 +29,18 @@ export const pamAccountDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const accounts = await query;
|
const accounts = await query;
|
||||||
|
|
||||||
return accounts.map(({ resourceId, resourceName, resourceType, ...account }) => ({
|
return accounts.map(
|
||||||
...account,
|
({ resourceId, resourceName, resourceType, encryptedRotationAccountCredentials, ...account }) => ({
|
||||||
resourceId,
|
...account,
|
||||||
resource: {
|
resourceId,
|
||||||
id: resourceId,
|
resource: {
|
||||||
name: resourceName,
|
id: resourceId,
|
||||||
resourceType
|
name: resourceName,
|
||||||
}
|
resourceType,
|
||||||
}));
|
encryptedRotationAccountCredentials
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const findAccountsDueForRotation = async (tx?: Knex) => {
|
const findAccountsDueForRotation = async (tx?: Knex) => {
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ export const pamAccountServiceFactory = ({
|
|||||||
|
|
||||||
const decryptedAndPermittedAccounts: Array<
|
const decryptedAndPermittedAccounts: Array<
|
||||||
TPamAccounts & {
|
TPamAccounts & {
|
||||||
resource: Pick<TPamResources, "id" | "name" | "resourceType">;
|
resource: Pick<TPamResources, "id" | "name" | "resourceType"> & { rotationCredentialsConfigured: boolean };
|
||||||
credentials: TPamAccountCredentials;
|
credentials: TPamAccountCredentials;
|
||||||
}
|
}
|
||||||
> = [];
|
> = [];
|
||||||
@@ -360,7 +360,8 @@ export const pamAccountServiceFactory = ({
|
|||||||
resource: {
|
resource: {
|
||||||
id: account.resource.id,
|
id: account.resource.id,
|
||||||
name: account.resource.name,
|
name: account.resource.name,
|
||||||
resourceType: account.resource.resourceType
|
resourceType: account.resource.resourceType,
|
||||||
|
rotationCredentialsConfigured: !!account.resource.encryptedRotationAccountCredentials
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -608,6 +609,25 @@ export const pamAccountServiceFactory = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
|
||||||
|
|
||||||
|
await auditLogService.createAuditLog({
|
||||||
|
projectId: account.projectId,
|
||||||
|
actor: {
|
||||||
|
type: ActorType.PLATFORM,
|
||||||
|
metadata: {}
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
type: EventType.PAM_ACCOUNT_CREDENTIAL_ROTATION_FAILED,
|
||||||
|
metadata: {
|
||||||
|
accountId: account.id,
|
||||||
|
accountName: account.name,
|
||||||
|
resourceId: account.resourceId,
|
||||||
|
errorMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
logger.error(error, `Failed to rotate credentials for account ${account.id}`);
|
logger.error(error, `Failed to rotate credentials for account ${account.id}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -204,13 +204,26 @@ export const pamResourceServiceFactory = ({
|
|||||||
finalCredentials.password = decryptedCredentials.password;
|
finalCredentials.password = decryptedCredentials.password;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedRotationAccountCredentials = await factory.validateAccountCredentials(finalCredentials);
|
try {
|
||||||
|
const validatedRotationAccountCredentials = await factory.validateAccountCredentials(finalCredentials);
|
||||||
|
|
||||||
updateDoc.encryptedRotationAccountCredentials = await encryptAccountCredentials({
|
updateDoc.encryptedRotationAccountCredentials = await encryptAccountCredentials({
|
||||||
credentials: validatedRotationAccountCredentials,
|
credentials: validatedRotationAccountCredentials,
|
||||||
projectId: resource.projectId,
|
projectId: resource.projectId,
|
||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
err instanceof BadRequestError &&
|
||||||
|
err.message === "Account credentials invalid: Username or password incorrect"
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Rotation Account credentials invalid: Username or password incorrect"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,11 +43,3 @@ Here’s how it works:
|
|||||||

|

|
||||||
|
|
||||||
Infisical will then use the rotation account on the resource to automatically update the credentials of the target account at the specified interval, eliminating credential staleness.
|
Infisical will then use the rotation account on the resource to automatically update the credentials of the target account at the specified interval, eliminating credential staleness.
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
<AccordionGroup>
|
|
||||||
<Accordion title="What resources does Infisical PAM currently support?">
|
|
||||||
Infisical PAM currently supports PostgreSQL, with support for more databases, RDP, Kubernetes, social media accounts, and more coming soon.
|
|
||||||
</Accordion>
|
|
||||||
</AccordionGroup>
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const pamKeys = {
|
|||||||
session: () => [...pamKeys.all, "session"] as const,
|
session: () => [...pamKeys.all, "session"] as const,
|
||||||
listResourceOptions: () => [...pamKeys.resource(), "options"] as const,
|
listResourceOptions: () => [...pamKeys.resource(), "options"] as const,
|
||||||
listResources: (projectId: string) => [...pamKeys.resource(), "list", projectId],
|
listResources: (projectId: string) => [...pamKeys.resource(), "list", projectId],
|
||||||
|
getResource: (resourceId?: string) => [...pamKeys.resource(), "get", resourceId],
|
||||||
listAccounts: (projectId: string) => [...pamKeys.account(), "list", projectId],
|
listAccounts: (projectId: string) => [...pamKeys.account(), "list", projectId],
|
||||||
getSession: (sessionId: string) => [...pamKeys.session(), "get", sessionId],
|
getSession: (sessionId: string) => [...pamKeys.session(), "get", sessionId],
|
||||||
listSessions: (projectId: string) => [...pamKeys.session(), "list", projectId]
|
listSessions: (projectId: string) => [...pamKeys.session(), "list", projectId]
|
||||||
@@ -68,6 +69,27 @@ export const useListPamResources = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetPamResourceById = (
|
||||||
|
resourceId?: string,
|
||||||
|
options?: Omit<
|
||||||
|
UseQueryOptions<TPamResource, unknown, TPamResource, ReturnType<typeof pamKeys.getResource>>,
|
||||||
|
"queryKey" | "queryFn" | "enabled"
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: pamKeys.getResource(resourceId),
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await apiRequest.get<{ resource: TPamResource }>(
|
||||||
|
`/api/v1/pam/resources/${resourceId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return data.resource;
|
||||||
|
},
|
||||||
|
enabled: !!resourceId,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Accounts
|
// Accounts
|
||||||
export const useListPamAccounts = (
|
export const useListPamAccounts = (
|
||||||
projectId: string,
|
projectId: string,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export interface TBasePamAccount {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
resourceType: PamResourceType;
|
resourceType: PamResourceType;
|
||||||
|
rotationCredentialsConfigured: boolean;
|
||||||
};
|
};
|
||||||
name: string;
|
name: string;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const CreateForm = ({
|
|||||||
|
|
||||||
switch (resourceType) {
|
switch (resourceType) {
|
||||||
case PamResourceType.Postgres:
|
case PamResourceType.Postgres:
|
||||||
return <PostgresAccountForm onSubmit={onSubmit} />;
|
return <PostgresAccountForm onSubmit={onSubmit} resourceId={resourceId} />;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled resource: ${resourceType}`);
|
throw new Error(`Unhandled resource: ${resourceType}`);
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-3
@@ -1,9 +1,10 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { Button, ModalClose } from "@app/components/v2";
|
import { Button, ModalClose } from "@app/components/v2";
|
||||||
import { TPostgresAccount } from "@app/hooks/api/pam";
|
import { TPostgresAccount, useGetPamResourceById } from "@app/hooks/api/pam";
|
||||||
|
|
||||||
import { BaseSqlAccountSchema } from "./shared/sql-account-schemas";
|
import { BaseSqlAccountSchema } from "./shared/sql-account-schemas";
|
||||||
import { SqlAccountFields } from "./shared/SqlAccountFields";
|
import { SqlAccountFields } from "./shared/SqlAccountFields";
|
||||||
@@ -12,6 +13,7 @@ import { RotateAccountFields, rotateAccountFieldsSchema } from "./RotateAccountF
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
account?: TPostgresAccount;
|
account?: TPostgresAccount;
|
||||||
|
resourceId?: string;
|
||||||
onSubmit: (formData: FormData) => Promise<void>;
|
onSubmit: (formData: FormData) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ const formSchema = genericAccountFieldsSchema.extend(rotateAccountFieldsSchema.s
|
|||||||
|
|
||||||
type FormData = z.infer<typeof formSchema>;
|
type FormData = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
export const PostgresAccountForm = ({ account, onSubmit }: Props) => {
|
export const PostgresAccountForm = ({ account, resourceId, onSubmit }: Props) => {
|
||||||
const isUpdate = Boolean(account);
|
const isUpdate = Boolean(account);
|
||||||
|
|
||||||
const form = useForm<FormData>({
|
const form = useForm<FormData>({
|
||||||
@@ -42,6 +44,18 @@ export const PostgresAccountForm = ({ account, onSubmit }: Props) => {
|
|||||||
formState: { isSubmitting, isDirty }
|
formState: { isSubmitting, isDirty }
|
||||||
} = form;
|
} = form;
|
||||||
|
|
||||||
|
const [rotationCredentialsConfigured, setRotationCredentialsConfigured] = useState(false);
|
||||||
|
|
||||||
|
const { data: resource } = useGetPamResourceById(resourceId);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (account) {
|
||||||
|
setRotationCredentialsConfigured(account.resource.rotationCredentialsConfigured);
|
||||||
|
} else {
|
||||||
|
setRotationCredentialsConfigured(!!resource?.rotationAccountCredentials);
|
||||||
|
}
|
||||||
|
}, [account]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<form
|
<form
|
||||||
@@ -51,7 +65,7 @@ export const PostgresAccountForm = ({ account, onSubmit }: Props) => {
|
|||||||
>
|
>
|
||||||
<GenericAccountFields />
|
<GenericAccountFields />
|
||||||
<SqlAccountFields isUpdate={isUpdate} />
|
<SqlAccountFields isUpdate={isUpdate} />
|
||||||
<RotateAccountFields />
|
<RotateAccountFields rotationCredentialsConfigured={rotationCredentialsConfigured} />
|
||||||
<div className="mt-6 flex items-center">
|
<div className="mt-6 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
|
|||||||
+62
-49
@@ -1,14 +1,19 @@
|
|||||||
import { Controller, useFormContext } from "react-hook-form";
|
import { Controller, useFormContext } from "react-hook-form";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { FormControl, Select, SelectItem, Switch } from "@app/components/v2";
|
import { FormControl, Select, SelectItem, Switch, Tooltip } from "@app/components/v2";
|
||||||
|
|
||||||
export const rotateAccountFieldsSchema = z.object({
|
export const rotateAccountFieldsSchema = z.object({
|
||||||
rotationEnabled: z.boolean(),
|
rotationEnabled: z.boolean(),
|
||||||
rotationIntervalSeconds: z.number()
|
rotationIntervalSeconds: z.number()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const RotateAccountFields = () => {
|
export const RotateAccountFields = ({
|
||||||
|
rotationCredentialsConfigured
|
||||||
|
}: {
|
||||||
|
rotationCredentialsConfigured: boolean;
|
||||||
|
}) => {
|
||||||
const { control, watch } = useFormContext<{
|
const { control, watch } = useFormContext<{
|
||||||
rotationEnabled: boolean;
|
rotationEnabled: boolean;
|
||||||
rotationIntervalSeconds: number;
|
rotationIntervalSeconds: number;
|
||||||
@@ -17,55 +22,63 @@ export const RotateAccountFields = () => {
|
|||||||
const rotationEnabled = watch("rotationEnabled");
|
const rotationEnabled = watch("rotationEnabled");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-9 items-center gap-3">
|
<Tooltip content="The resource which owns this account does not have rotation credentials configured.">
|
||||||
<Controller
|
<div
|
||||||
control={control}
|
className={twMerge(
|
||||||
name="rotationEnabled"
|
"flex h-9 w-fit items-center gap-3",
|
||||||
defaultValue={false}
|
!rotationCredentialsConfigured && "opacity-50"
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
|
||||||
<FormControl isError={Boolean(error)} errorText={error?.message} className="mb-0">
|
|
||||||
<Switch
|
|
||||||
className="ml-0 bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
|
||||||
id="rotation-enabled"
|
|
||||||
thumbClassName="bg-mineshaft-800"
|
|
||||||
onCheckedChange={onChange}
|
|
||||||
isChecked={value}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
)}
|
||||||
/>
|
>
|
||||||
<span className="text-sm">Rotate Credentials Every</span>
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="rotationEnabled"
|
||||||
|
defaultValue={false}
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message} className="mb-0">
|
||||||
|
<Switch
|
||||||
|
className="ml-0 bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
||||||
|
id="rotation-enabled"
|
||||||
|
thumbClassName="bg-mineshaft-800"
|
||||||
|
onCheckedChange={onChange}
|
||||||
|
isChecked={value}
|
||||||
|
isDisabled={!rotationCredentialsConfigured}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<span className="text-sm">Rotate Credentials Every</span>
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
name="rotationIntervalSeconds"
|
name="rotationIntervalSeconds"
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue={2592000}
|
defaultValue={2592000}
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
className="mb-0"
|
className="mb-0"
|
||||||
>
|
|
||||||
<Select
|
|
||||||
value={value.toString()}
|
|
||||||
onValueChange={(val) => onChange(parseInt(val, 10))}
|
|
||||||
className="w-full border border-mineshaft-500 capitalize"
|
|
||||||
position="popper"
|
|
||||||
placeholder="Select an interval..."
|
|
||||||
dropdownContainerClassName="max-w-none"
|
|
||||||
isDisabled={!rotationEnabled}
|
|
||||||
dropdownContainerStyle={{
|
|
||||||
width: "130px"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<SelectItem value="2592000">30 Days</SelectItem>
|
<Select
|
||||||
<SelectItem value="604800">7 Days</SelectItem>
|
value={value.toString()}
|
||||||
<SelectItem value="259200">3 Days</SelectItem>
|
onValueChange={(val) => onChange(parseInt(val, 10))}
|
||||||
<SelectItem value="86400">1 Day</SelectItem>
|
className="w-full border border-mineshaft-500 capitalize"
|
||||||
</Select>
|
position="popper"
|
||||||
</FormControl>
|
placeholder="Select an interval..."
|
||||||
)}
|
dropdownContainerClassName="max-w-none"
|
||||||
/>
|
isDisabled={!rotationEnabled || !rotationCredentialsConfigured}
|
||||||
</div>
|
dropdownContainerStyle={{
|
||||||
|
width: "130px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectItem value="2592000">30 Days</SelectItem>
|
||||||
|
<SelectItem value="604800">7 Days</SelectItem>
|
||||||
|
<SelectItem value="259200">3 Days</SelectItem>
|
||||||
|
<SelectItem value="86400">1 Day</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-1
@@ -17,7 +17,7 @@ export const SqlAccountFields = ({ isUpdate }: { isUpdate: boolean }) => {
|
|||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
label="Username"
|
label="Username"
|
||||||
>
|
>
|
||||||
<Input {...field} />
|
<Input {...field} autoComplete="off" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -34,6 +34,7 @@ export const SqlAccountFields = ({ isUpdate }: { isUpdate: boolean }) => {
|
|||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
type="password"
|
type="password"
|
||||||
|
autoComplete="new-password"
|
||||||
onFocus={(e) => {
|
onFocus={(e) => {
|
||||||
if (isUpdate && field.value === "******") {
|
if (isUpdate && field.value === "******") {
|
||||||
field.onChange("");
|
field.onChange("");
|
||||||
|
|||||||
+4
-3
@@ -16,11 +16,11 @@ export const SqlRotateAccountFields = ({ isUpdate }: { isUpdate: boolean }) => {
|
|||||||
<Accordion type="single" collapsible className="w-full">
|
<Accordion type="single" collapsible className="w-full">
|
||||||
<AccordionItem value="advance-settings" className="data-[state=open]:border-none">
|
<AccordionItem value="advance-settings" className="data-[state=open]:border-none">
|
||||||
<AccordionTrigger className="h-fit flex-none pl-1 text-sm">
|
<AccordionTrigger className="h-fit flex-none pl-1 text-sm">
|
||||||
<div className="order-1 ml-3">Credential Rotation Account</div>
|
<div className="order-1 ml-3">Rotation Account</div>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent childrenClassName="px-0 py-0">
|
<AccordionContent childrenClassName="px-0 py-0">
|
||||||
<p className="mb-2 text-xs">
|
<p className="mb-2 text-xs">
|
||||||
Credentials to the high privilege account which will be used for rotating other accounts
|
Credentials of the privileged account which will be used for rotating other accounts
|
||||||
under this resource
|
under this resource
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
@@ -34,7 +34,7 @@ export const SqlRotateAccountFields = ({ isUpdate }: { isUpdate: boolean }) => {
|
|||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
label="Username"
|
label="Username"
|
||||||
>
|
>
|
||||||
<Input {...field} />
|
<Input {...field} autoComplete="off" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -51,6 +51,7 @@ export const SqlRotateAccountFields = ({ isUpdate }: { isUpdate: boolean }) => {
|
|||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
type="password"
|
type="password"
|
||||||
|
autoComplete="new-password"
|
||||||
onFocus={(e) => {
|
onFocus={(e) => {
|
||||||
if (isUpdate && field.value === "******") {
|
if (isUpdate && field.value === "******") {
|
||||||
field.onChange("");
|
field.onChange("");
|
||||||
|
|||||||
Reference in New Issue
Block a user