From 2f7d22688b9893ec4a05d9c5264a00deb195c0c1 Mon Sep 17 00:00:00 2001 From: x032205 Date: Fri, 5 Sep 2025 14:23:37 -0400 Subject: [PATCH] Custom provider header input improvement --- .../audit-log-stream-service.ts | 39 ++++++++++++++++++- .../custom/custom-provider-schemas.ts | 6 ++- .../CustomProviderAuditLogStreamForm.tsx | 20 ++++++++++ .../components/AuditLogStreamHeader.tsx | 2 +- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-service.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-service.ts index 773f32407..5dd0fd4ba 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-service.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-service.ts @@ -20,6 +20,7 @@ import { TAuditLogStreamDALFactory } from "./audit-log-stream-dal"; import { LogProvider } from "./audit-log-stream-enums"; import { LOG_STREAM_FACTORY_MAP } from "./audit-log-stream-factory"; import { TAuditLogStream, TCreateAuditLogStreamDTO, TUpdateAuditLogStreamDTO } from "./audit-log-stream-types"; +import { TCustomProviderCredentials } from "./custom/custom-provider-types"; export type TAuditLogStreamServiceFactoryDep = { auditLogStreamDAL: TAuditLogStreamDALFactory; @@ -103,8 +104,44 @@ export const auditLogStreamServiceFactory = ({ ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings); + const finalCredentials = { ...credentials }; + + // For the "Custom" provider, we must handle masked header values ('******'). + // These are placeholders from the frontend for secrets that haven't been changed. + // We need to replace them with the original, unmasked values from the database. + if ( + provider === LogProvider.Custom && + "headers" in finalCredentials && + Array.isArray(finalCredentials.headers) && + finalCredentials.headers.some((header) => header.value === "******") + ) { + const decryptedOldCredentials = (await decryptLogStreamCredentials({ + encryptedCredentials: logStream.encryptedCredentials, + orgId: logStream.orgId, + kmsService + })) as TCustomProviderCredentials; + + const oldHeadersMap = decryptedOldCredentials.headers.reduce>((acc, header) => { + acc[header.key] = header.value; + return acc; + }, {}); + + const finalHeaders: { key: string; value: string }[] = []; + for (const header of finalCredentials.headers) { + if (header.value === "******") { + const oldValue = oldHeadersMap[header.key]; + if (oldValue) { + finalHeaders.push({ key: header.key, value: oldValue }); + } + } else { + finalHeaders.push(header); + } + } + finalCredentials.headers = finalHeaders; + } + const factory = LOG_STREAM_FACTORY_MAP[provider](); - const validatedCredentials = await factory.validateCredentials({ credentials }); + const validatedCredentials = await factory.validateCredentials({ credentials: finalCredentials }); const encryptedCredentials = await encryptLogStreamCredentials({ credentials: validatedCredentials, diff --git a/backend/src/ee/services/audit-log-stream/custom/custom-provider-schemas.ts b/backend/src/ee/services/audit-log-stream/custom/custom-provider-schemas.ts index dbb4e5d59..d960d0fe9 100644 --- a/backend/src/ee/services/audit-log-stream/custom/custom-provider-schemas.ts +++ b/backend/src/ee/services/audit-log-stream/custom/custom-provider-schemas.ts @@ -29,8 +29,10 @@ export const CustomProviderSchema = BaseCustomProviderSchema.extend({ export const SanitizedCustomProviderSchema = BaseCustomProviderSchema.extend({ credentials: z.object({ url: CustomProviderCredentialsSchema.shape.url, - // Only return header keys - headers: CustomProviderCredentialsSchema.shape.headers.element.pick({ key: true }).array() + // Return header keys and a redacted value + headers: CustomProviderCredentialsSchema.shape.headers.transform((headers) => + headers.map((header) => ({ ...header, value: "******" })) + ) }) }); diff --git a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/CustomProviderAuditLogStreamForm.tsx b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/CustomProviderAuditLogStreamForm.tsx index e0dd68155..30c2739e2 100644 --- a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/CustomProviderAuditLogStreamForm.tsx +++ b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/CustomProviderAuditLogStreamForm.tsx @@ -99,6 +99,26 @@ export const CustomProviderAuditLogStreamForm = ({ auditLogStream, onSubmit }: P type="password" placeholder="Bearer " autoComplete="new-password" + onFocus={(e) => { + if ( + auditLogStream && + auditLogStream.credentials.headers[i].value === "******" && + field.value === "******" + ) { + field.onChange(""); + } + e.target.type = "text"; + }} + onBlur={(e) => { + if ( + auditLogStream && + auditLogStream.credentials.headers[i].value === "******" && + field.value === "" + ) { + field.onChange("******"); + } + e.target.type = "password"; + }} /> )} diff --git a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/components/AuditLogStreamHeader.tsx b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/components/AuditLogStreamHeader.tsx index 77c8e8eee..3175ffa46 100644 --- a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/components/AuditLogStreamHeader.tsx +++ b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/components/AuditLogStreamHeader.tsx @@ -1,8 +1,8 @@ +import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { AUDIT_LOG_STREAM_PROVIDER_MAP } from "@app/helpers/auditLogStreams"; import { LogProvider } from "@app/hooks/api/auditLogStreams/enums"; -import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons"; type Props = { provider: LogProvider;