Improvements on Azure ADCS PKI feature

This commit is contained in:
Carlos Monastyrski
2025-08-27 21:20:10 -03:00
parent 0762de93d6
commit 13b20806ba
19 changed files with 922 additions and 600 deletions
@@ -1,4 +1,8 @@
import { useState } from "react";
import { Controller, FormProvider, useForm } from "react-hook-form";
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tab } from "@headlessui/react";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
@@ -9,7 +13,10 @@ import {
ModalClose,
SecretInput,
Select,
SelectItem
SelectItem,
Switch,
TextArea,
Tooltip
} from "@app/components/v2";
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
import { AzureADCSConnectionMethod, TAzureADCSConnection } from "@app/hooks/api/appConnections";
@@ -33,9 +40,19 @@ const formSchema = z.discriminatedUnion("method", [
rootSchema.extend({
method: z.literal(AzureADCSConnectionMethod.UsernamePassword),
credentials: z.object({
adcsUrl: z.string().url().trim().min(1, "ADCS URL required"),
adcsUrl: z
.string()
.trim()
.min(1, "ADCS URL required")
.refine((value) => value.startsWith("https://"), "ADCS URL must use HTTPS"),
username: z.string().trim().min(1, "Username required"),
password: z.string().trim().min(1, "Password required")
password: z.string().trim().min(1, "Password required"),
sslRejectUnauthorized: z.boolean().optional(),
sslCertificate: z
.string()
.trim()
.transform((value) => value || undefined)
.optional()
})
})
]);
@@ -44,6 +61,7 @@ type FormData = z.infer<typeof formSchema>;
export const AzureADCSConnectionForm = ({ appConnection, onSubmit }: Props) => {
const isUpdate = Boolean(appConnection);
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
@@ -55,7 +73,9 @@ export const AzureADCSConnectionForm = ({ appConnection, onSubmit }: Props) => {
credentials: {
adcsUrl: "",
username: "",
password: ""
password: "",
sslRejectUnauthorized: true,
sslCertificate: undefined
}
}
});
@@ -63,12 +83,20 @@ export const AzureADCSConnectionForm = ({ appConnection, onSubmit }: Props) => {
const {
handleSubmit,
control,
formState: { isSubmitting, isDirty }
formState: { isSubmitting, isDirty },
watch
} = form;
const sslEnabled = watch("credentials.adcsUrl")?.startsWith("https://") ?? false;
return (
<FormProvider {...form}>
<form onSubmit={handleSubmit(onSubmit)}>
<form
onSubmit={(e) => {
setSelectedTabIndex(0);
handleSubmit(onSubmit)(e);
}}
>
{!isUpdate && <GenericAppConnectionsFields />}
<Controller
name="method"
@@ -101,51 +129,148 @@ export const AzureADCSConnectionForm = ({ appConnection, onSubmit }: Props) => {
</FormControl>
)}
/>
<Controller
name="credentials.adcsUrl"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="ADCS URL"
<Tab.Group selectedIndex={selectedTabIndex} onChange={setSelectedTabIndex}>
<Tab.List
className={`-pb-1 ${selectedTabIndex === 1 ? "mb-3" : "mb-6"} w-full border-b-2 border-mineshaft-600`}
>
<Tab
className={({ selected }) =>
`w-30 -mb-[0.14rem] px-4 py-2 text-sm font-medium outline-none disabled:opacity-60 ${
selected
? "border-b-2 border-mineshaft-300 text-mineshaft-200"
: "text-bunker-300"
}`
}
>
<Input {...field} placeholder="https://your-adcs-server.com" />
</FormControl>
Configuration
</Tab>
<Tab
className={({ selected }) =>
`w-30 -mb-[0.14rem] px-4 py-2 text-sm font-medium outline-none disabled:opacity-60 ${
selected
? "border-b-2 border-mineshaft-300 text-mineshaft-200"
: "text-bunker-300"
}`
}
>
SSL ({sslEnabled ? "Enabled" : "Disabled"})
</Tab>
</Tab.List>
{selectedTabIndex === 1 && (
<div className="mb-2 text-xs text-mineshaft-300">
SSL configuration for HTTPS connections
</div>
)}
/>
<div className="grid grid-cols-2 gap-2">
<Controller
name="credentials.username"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Username"
>
<Input {...field} placeholder="domain\\username" />
</FormControl>
)}
/>
<Controller
name="credentials.password"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Password"
>
<SecretInput
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
value={value}
onChange={(e) => onChange(e.target.value)}
<Tab.Panels className="mb-4 rounded border border-mineshaft-600 bg-mineshaft-700/70 p-3 pb-0">
<Tab.Panel>
<Controller
name="credentials.adcsUrl"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="ADCS URL"
>
<Input {...field} placeholder="https://your-adcs-server.com/certsrv" />
</FormControl>
)}
/>
<div className="grid grid-cols-2 gap-2">
<Controller
name="credentials.username"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Username"
>
<Input {...field} placeholder="DOMAIN\\username or [email protected]" />
</FormControl>
)}
/>
</FormControl>
)}
/>
</div>
<Controller
name="credentials.password"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Password"
>
<SecretInput
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
value={value}
onChange={(e) => onChange(e.target.value)}
/>
</FormControl>
)}
/>
</div>
</Tab.Panel>
<Tab.Panel>
<Controller
name="credentials.sslCertificate"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
className={sslEnabled ? "" : "opacity-50"}
label="SSL Certificate"
isOptional
>
<TextArea
className="h-[3.6rem] !resize-none"
{...field}
isDisabled={!sslEnabled}
placeholder="-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----"
/>
</FormControl>
)}
/>
<Controller
name="credentials.sslRejectUnauthorized"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
className={sslEnabled ? "" : "opacity-50"}
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Switch
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
id="ssl-reject-unauthorized"
thumbClassName="bg-mineshaft-800"
isChecked={sslEnabled ? value : false}
onCheckedChange={onChange}
isDisabled={!sslEnabled}
>
<p className="w-[9.5rem]">
Reject Unauthorized
<Tooltip
className="max-w-md"
content={
<p>
If enabled, Infisical will only connect to the ADCS server if it has a
valid, trusted SSL certificate. Disable only in test environments with
self-signed certificates.
</p>
}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
</Tooltip>
</p>
</Switch>
</FormControl>
)}
/>
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
<div className="mt-8 flex items-center">
<Button
className="mr-4"