Custom provider header input improvement

This commit is contained in:
x032205
2025-09-05 14:23:37 -04:00
parent 3a75a50d0b
commit 2f7d22688b
4 changed files with 63 additions and 4 deletions

View File

@@ -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<Record<string, string>>((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,

View File

@@ -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: "******" }))
)
})
});

View File

@@ -99,6 +99,26 @@ export const CustomProviderAuditLogStreamForm = ({ auditLogStream, onSubmit }: P
type="password"
placeholder="Bearer <token>"
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";
}}
/>
</FormControl>
)}

View File

@@ -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;