fix: allow rotation account credentials to be removed from resources

This commit is contained in:
x032205
2025-10-22 23:40:30 -04:00
parent 39cbdca9eb
commit 4cdb8cf4c5
4 changed files with 73 additions and 21 deletions
+1 -1
View File
@@ -3,13 +3,13 @@ import { z } from "zod";
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { QueueWorkerProfile } from "@app/lib/types"; import { QueueWorkerProfile } from "@app/lib/types";
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
import { BadRequestError } from "../errors"; import { BadRequestError } from "../errors";
import { removeTrailingSlash } from "../fn"; import { removeTrailingSlash } from "../fn";
import { CustomLogger } from "../logger/logger"; import { CustomLogger } from "../logger/logger";
import { zpStr } from "../zod"; import { zpStr } from "../zod";
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
export const GITLAB_URL = "https://gitlab.com"; export const GITLAB_URL = "https://gitlab.com";
@@ -12,3 +12,31 @@ export const BaseSqlAccountSchema = z.object({
.min(1, "Password required") .min(1, "Password required")
.max(256, "Password must be 256 characters or less") .max(256, "Password must be 256 characters or less")
}); });
export const BaseSqlRotationAccountSchema = z
.object({
username: z.string().trim().max(63, "Username must be 63 characters or less"),
password: z.string().trim().max(256, "Password must be 256 characters or less")
})
.superRefine((data, ctx) => {
if (data.username && !data.password) {
ctx.addIssue({
path: ["password"],
message: "Password is required",
code: z.ZodIssueCode.custom
});
}
if (data.password && !data.username) {
ctx.addIssue({
path: ["username"],
message: "Username is required",
code: z.ZodIssueCode.custom
});
}
})
.transform((val) => {
if (!val.username && !val.password) {
return null;
}
return val;
});
@@ -6,7 +6,7 @@ import { z } from "zod";
import { Button, ModalClose } from "@app/components/v2"; import { Button, ModalClose } from "@app/components/v2";
import { PamResourceType, TPostgresResource } from "@app/hooks/api/pam"; import { PamResourceType, TPostgresResource } from "@app/hooks/api/pam";
import { UNCHANGED_PASSWORD_SENTINEL } from "@app/hooks/api/pam/constants"; import { UNCHANGED_PASSWORD_SENTINEL } from "@app/hooks/api/pam/constants";
import { BaseSqlAccountSchema } from "@app/pages/pam/PamAccountsPage/components/PamAccountForm/shared/sql-account-schemas"; import { BaseSqlRotationAccountSchema } from "@app/pages/pam/PamAccountsPage/components/PamAccountForm/shared/sql-account-schemas";
import { BaseSqlResourceSchema } from "./shared/sql-resource-schemas"; import { BaseSqlResourceSchema } from "./shared/sql-resource-schemas";
import { SqlResourceFields } from "./shared/SqlResourceFields"; import { SqlResourceFields } from "./shared/SqlResourceFields";
@@ -21,7 +21,7 @@ type Props = {
const formSchema = genericResourceFieldsSchema.extend({ const formSchema = genericResourceFieldsSchema.extend({
resourceType: z.literal(PamResourceType.Postgres), resourceType: z.literal(PamResourceType.Postgres),
connectionDetails: BaseSqlResourceSchema, connectionDetails: BaseSqlResourceSchema,
rotationAccountCredentials: BaseSqlAccountSchema.nullable().optional() rotationAccountCredentials: BaseSqlRotationAccountSchema.nullable().optional()
}); });
type FormData = z.infer<typeof formSchema>; type FormData = z.infer<typeof formSchema>;
@@ -1,5 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form"; import { Controller, useFormContext, useWatch } from "react-hook-form";
import { faTimes } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import {
Accordion, Accordion,
@@ -44,7 +46,18 @@ export const SqlRotateAccountFields = ({ isUpdate }: { isUpdate: boolean }) => {
isError={Boolean(error?.message)} isError={Boolean(error?.message)}
label="Username" label="Username"
> >
<Input {...field} autoComplete="off" /> <div className="relative">
<Input {...field} autoComplete="off" />
{field.value && (
<button
type="button"
className="absolute inset-y-0 right-0 flex cursor-pointer items-center pr-3"
onClick={() => field.onChange("")}
>
<FontAwesomeIcon icon={faTimes} className="text-gray-500" />
</button>
)}
</div>
</FormControl> </FormControl>
)} )}
/> />
@@ -58,23 +71,34 @@ export const SqlRotateAccountFields = ({ isUpdate }: { isUpdate: boolean }) => {
isError={Boolean(error?.message)} isError={Boolean(error?.message)}
label="Password" label="Password"
> >
<Input <div className="relative">
{...field} <Input
type={showPassword ? "text" : "password"} {...field}
autoComplete="new-password" type={showPassword ? "text" : "password"}
onFocus={() => { autoComplete="new-password"
if (isUpdate && field.value === UNCHANGED_PASSWORD_SENTINEL) { onFocus={() => {
field.onChange(""); if (isUpdate && field.value === UNCHANGED_PASSWORD_SENTINEL) {
} field.onChange("");
setShowPassword(true); }
}} setShowPassword(true);
onBlur={() => { }}
if (isUpdate && field.value === "") { onBlur={() => {
field.onChange(UNCHANGED_PASSWORD_SENTINEL); if (isUpdate && field.value === "") {
} field.onChange(UNCHANGED_PASSWORD_SENTINEL);
setShowPassword(false); }
}} setShowPassword(false);
/> }}
/>
{field.value && (
<button
type="button"
className="absolute inset-y-0 right-0 flex cursor-pointer items-center pr-3"
onClick={() => field.onChange("")}
>
<FontAwesomeIcon icon={faTimes} className="text-gray-500" />
</button>
)}
</div>
</FormControl> </FormControl>
)} )}
/> />