improvements: address feedback

This commit is contained in:
Scott Wilson
2025-04-22 17:36:18 -07:00
parent 9032bbe514
commit 636aee2ea9
27 changed files with 70 additions and 80 deletions
@@ -39,7 +39,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
trial_end: null, trial_end: null,
has_used_trial: true, has_used_trial: true,
secretApproval: false, secretApproval: false,
secretRotation: false, secretRotation: true,
caCrl: false, caCrl: false,
instanceUserManagement: false, instanceUserManagement: false,
externalKms: false, externalKms: false,
@@ -5,7 +5,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
export const LDAP_PASSWORD_ROTATION_LIST_OPTION: TSecretRotationV2ListItem = { export const LDAP_PASSWORD_ROTATION_LIST_OPTION: TSecretRotationV2ListItem = {
name: "LDAP Password", name: "LDAP Password",
type: SecretRotation.LdapPassword, type: SecretRotation.LdapPassword,
connection: AppConnection.Ldap, connection: AppConnection.LDAP,
template: { template: {
secretsMapping: { secretsMapping: {
dn: "LDAP_DN", dn: "LDAP_DN",
@@ -59,7 +59,7 @@ export const UpdateLdapPasswordRotationSchema = BaseUpdateSecretRotationSchema(S
export const LdapPasswordRotationListItemSchema = z.object({ export const LdapPasswordRotationListItemSchema = z.object({
name: z.literal("LDAP Password"), name: z.literal("LDAP Password"),
connection: z.literal(AppConnection.Ldap), connection: z.literal(AppConnection.LDAP),
type: z.literal(SecretRotation.LdapPassword), type: z.literal(SecretRotation.LdapPassword),
template: LdapPasswordRotationTemplateSchema template: LdapPasswordRotationTemplateSchema
}); });
@@ -3,7 +3,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
export const SECRET_ROTATION_NAME_MAP: Record<SecretRotation, string> = { export const SECRET_ROTATION_NAME_MAP: Record<SecretRotation, string> = {
[SecretRotation.PostgresCredentials]: "PostgreSQL Credentials", [SecretRotation.PostgresCredentials]: "PostgreSQL Credentials",
[SecretRotation.MsSqlCredentials]: "Microsoft SQL Sever Credentials", [SecretRotation.MsSqlCredentials]: "Microsoft SQL Server Credentials",
[SecretRotation.Auth0ClientSecret]: "Auth0 Client Secret", [SecretRotation.Auth0ClientSecret]: "Auth0 Client Secret",
[SecretRotation.LdapPassword]: "LDAP Password" [SecretRotation.LdapPassword]: "LDAP Password"
}; };
@@ -12,5 +12,5 @@ export const SECRET_ROTATION_CONNECTION_MAP: Record<SecretRotation, AppConnectio
[SecretRotation.PostgresCredentials]: AppConnection.Postgres, [SecretRotation.PostgresCredentials]: AppConnection.Postgres,
[SecretRotation.MsSqlCredentials]: AppConnection.MsSql, [SecretRotation.MsSqlCredentials]: AppConnection.MsSql,
[SecretRotation.Auth0ClientSecret]: AppConnection.Auth0, [SecretRotation.Auth0ClientSecret]: AppConnection.Auth0,
[SecretRotation.LdapPassword]: AppConnection.Ldap [SecretRotation.LdapPassword]: AppConnection.LDAP
}; };
@@ -32,6 +32,9 @@ export const PasswordRequirementsSchema = z
.optional() .optional()
.describe(SecretRotations.PARAMETERS.GENERAL.PASSWORD_REQUIREMENTS.allowedSymbols) .describe(SecretRotations.PARAMETERS.GENERAL.PASSWORD_REQUIREMENTS.allowedSymbols)
}) })
.refine((data) => {
return Object.values(data.required).some((count) => count > 0);
}, "At least one character type must be required")
.refine((data) => { .refine((data) => {
const total = Object.values(data.required).reduce((sum, count) => sum + count, 0); const total = Object.values(data.required).reduce((sum, count) => sum + count, 0);
return total <= data.length; return total <= data.length;
@@ -34,5 +34,5 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
[AppConnection.Camunda]: registerCamundaConnectionRouter, [AppConnection.Camunda]: registerCamundaConnectionRouter,
[AppConnection.Windmill]: registerWindmillConnectionRouter, [AppConnection.Windmill]: registerWindmillConnectionRouter,
[AppConnection.Auth0]: registerAuth0ConnectionRouter, [AppConnection.Auth0]: registerAuth0ConnectionRouter,
[AppConnection.Ldap]: registerLdapConnectionRouter [AppConnection.LDAP]: registerLdapConnectionRouter
}; };
@@ -9,7 +9,7 @@ import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
export const registerLdapConnectionRouter = async (server: FastifyZodProvider) => { export const registerLdapConnectionRouter = async (server: FastifyZodProvider) => {
registerAppConnectionEndpoints({ registerAppConnectionEndpoints({
app: AppConnection.Ldap, app: AppConnection.LDAP,
server, server,
sanitizedResponseSchema: SanitizedLdapConnectionSchema, sanitizedResponseSchema: SanitizedLdapConnectionSchema,
createSchema: CreateLdapConnectionSchema, createSchema: CreateLdapConnectionSchema,
@@ -13,7 +13,7 @@ export enum AppConnection {
Camunda = "camunda", Camunda = "camunda",
Windmill = "windmill", Windmill = "windmill",
Auth0 = "auth0", Auth0 = "auth0",
Ldap = "ldap" LDAP = "ldap"
} }
export enum AWSRegion { export enum AWSRegion {
@@ -138,7 +138,7 @@ export const validateAppConnectionCredentials = async (
[AppConnection.TerraformCloud]: validateTerraformCloudConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.TerraformCloud]: validateTerraformCloudConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Auth0]: validateAuth0ConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.Auth0]: validateAuth0ConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Windmill]: validateWindmillConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.Windmill]: validateWindmillConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Ldap]: validateLdapConnectionCredentials as TAppConnectionCredentialsValidator [AppConnection.LDAP]: validateLdapConnectionCredentials as TAppConnectionCredentialsValidator
}; };
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection); return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection);
@@ -220,5 +220,5 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
[AppConnection.Vercel]: platformManagedCredentialsNotSupported, [AppConnection.Vercel]: platformManagedCredentialsNotSupported,
[AppConnection.Windmill]: platformManagedCredentialsNotSupported, [AppConnection.Windmill]: platformManagedCredentialsNotSupported,
[AppConnection.Auth0]: platformManagedCredentialsNotSupported, [AppConnection.Auth0]: platformManagedCredentialsNotSupported,
[AppConnection.Ldap]: platformManagedCredentialsNotSupported // we could support this in the future [AppConnection.LDAP]: platformManagedCredentialsNotSupported // we could support this in the future
}; };
@@ -15,5 +15,5 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
[AppConnection.Camunda]: "Camunda", [AppConnection.Camunda]: "Camunda",
[AppConnection.Windmill]: "Windmill", [AppConnection.Windmill]: "Windmill",
[AppConnection.Auth0]: "Auth0", [AppConnection.Auth0]: "Auth0",
[AppConnection.Ldap]: "LDAP" [AppConnection.LDAP]: "LDAP"
}; };
@@ -76,7 +76,7 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
[AppConnection.Camunda]: ValidateCamundaConnectionCredentialsSchema, [AppConnection.Camunda]: ValidateCamundaConnectionCredentialsSchema,
[AppConnection.Windmill]: ValidateWindmillConnectionCredentialsSchema, [AppConnection.Windmill]: ValidateWindmillConnectionCredentialsSchema,
[AppConnection.Auth0]: ValidateAuth0ConnectionCredentialsSchema, [AppConnection.Auth0]: ValidateAuth0ConnectionCredentialsSchema,
[AppConnection.Ldap]: ValidateLdapConnectionCredentialsSchema [AppConnection.LDAP]: ValidateLdapConnectionCredentialsSchema
}; };
export const appConnectionServiceFactory = ({ export const appConnectionServiceFactory = ({
@@ -11,7 +11,7 @@ import { TLdapConnectionConfig } from "./ldap-connection-types";
export const getLdapConnectionListItem = () => { export const getLdapConnectionListItem = () => {
return { return {
name: "LDAP" as const, name: "LDAP" as const,
app: AppConnection.Ldap as const, app: AppConnection.LDAP as const,
methods: Object.values(LdapConnectionMethod) as [LdapConnectionMethod.SimpleBind] methods: Object.values(LdapConnectionMethod) as [LdapConnectionMethod.SimpleBind]
}; };
}; };
@@ -12,7 +12,12 @@ import { LdapConnectionMethod, LdapProvider } from "./ldap-connection-enums";
export const LdapConnectionSimpleBindCredentialsSchema = z.object({ export const LdapConnectionSimpleBindCredentialsSchema = z.object({
provider: z.nativeEnum(LdapProvider).describe(AppConnections.CREDENTIALS.LDAP.provider), provider: z.nativeEnum(LdapProvider).describe(AppConnections.CREDENTIALS.LDAP.provider),
url: z.string().trim().min(1, "URL required").describe(AppConnections.CREDENTIALS.LDAP.url), url: z
.string()
.trim()
.min(1, "URL required")
.refine((value) => value.startsWith("ldap://") || value.startsWith("ldaps://"))
.describe(AppConnections.CREDENTIALS.LDAP.url),
dn: z.string().trim().min(1, "Distinguished Name (DN) required").describe(AppConnections.CREDENTIALS.LDAP.dn), dn: z.string().trim().min(1, "Distinguished Name (DN) required").describe(AppConnections.CREDENTIALS.LDAP.dn),
password: z.string().trim().min(1, "Password required").describe(AppConnections.CREDENTIALS.LDAP.password), password: z.string().trim().min(1, "Password required").describe(AppConnections.CREDENTIALS.LDAP.password),
sslRejectUnauthorized: z.boolean().optional().describe(AppConnections.CREDENTIALS.LDAP.sslRejectUnauthorized), sslRejectUnauthorized: z.boolean().optional().describe(AppConnections.CREDENTIALS.LDAP.sslRejectUnauthorized),
@@ -25,7 +30,7 @@ export const LdapConnectionSimpleBindCredentialsSchema = z.object({
}); });
const BaseLdapConnectionSchema = BaseAppConnectionSchema.extend({ const BaseLdapConnectionSchema = BaseAppConnectionSchema.extend({
app: z.literal(AppConnection.Ldap) app: z.literal(AppConnection.LDAP)
}); });
export const LdapConnectionSchema = z.intersection( export const LdapConnectionSchema = z.intersection(
@@ -54,28 +59,28 @@ export const SanitizedLdapConnectionSchema = z.discriminatedUnion("method", [
export const ValidateLdapConnectionCredentialsSchema = z.discriminatedUnion("method", [ export const ValidateLdapConnectionCredentialsSchema = z.discriminatedUnion("method", [
z.object({ z.object({
method: z.literal(LdapConnectionMethod.SimpleBind).describe(AppConnections.CREATE(AppConnection.Ldap).method), method: z.literal(LdapConnectionMethod.SimpleBind).describe(AppConnections.CREATE(AppConnection.LDAP).method),
credentials: LdapConnectionSimpleBindCredentialsSchema.describe( credentials: LdapConnectionSimpleBindCredentialsSchema.describe(
AppConnections.CREATE(AppConnection.Ldap).credentials AppConnections.CREATE(AppConnection.LDAP).credentials
) )
}) })
]); ]);
export const CreateLdapConnectionSchema = ValidateLdapConnectionCredentialsSchema.and( export const CreateLdapConnectionSchema = ValidateLdapConnectionCredentialsSchema.and(
GenericCreateAppConnectionFieldsSchema(AppConnection.Ldap) GenericCreateAppConnectionFieldsSchema(AppConnection.LDAP)
); );
export const UpdateLdapConnectionSchema = z export const UpdateLdapConnectionSchema = z
.object({ .object({
credentials: LdapConnectionSimpleBindCredentialsSchema.optional().describe( credentials: LdapConnectionSimpleBindCredentialsSchema.optional().describe(
AppConnections.UPDATE(AppConnection.Ldap).credentials AppConnections.UPDATE(AppConnection.LDAP).credentials
) )
}) })
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Ldap)); .and(GenericUpdateAppConnectionFieldsSchema(AppConnection.LDAP));
export const LdapConnectionListItemSchema = z.object({ export const LdapConnectionListItemSchema = z.object({
name: z.literal("LDAP"), name: z.literal("LDAP"),
app: z.literal(AppConnection.Ldap), app: z.literal(AppConnection.LDAP),
// the below is preferable but currently breaks with our zod to json schema parser // the below is preferable but currently breaks with our zod to json schema parser
// methods: z.tuple([z.literal(AwsConnectionMethod.ServicePrincipal), z.literal(AwsConnectionMethod.AccessKey)]), // methods: z.tuple([z.literal(AwsConnectionMethod.ServicePrincipal), z.literal(AwsConnectionMethod.AccessKey)]),
methods: z.nativeEnum(LdapConnectionMethod).array() methods: z.nativeEnum(LdapConnectionMethod).array()
@@ -12,7 +12,7 @@ import {
export type TLdapConnection = z.infer<typeof LdapConnectionSchema>; export type TLdapConnection = z.infer<typeof LdapConnectionSchema>;
export type TLdapConnectionInput = z.infer<typeof CreateLdapConnectionSchema> & { export type TLdapConnectionInput = z.infer<typeof CreateLdapConnectionSchema> & {
app: AppConnection.Ldap; app: AppConnection.LDAP;
}; };
export type TValidateLdapConnectionCredentialsSchema = typeof ValidateLdapConnectionCredentialsSchema; export type TValidateLdapConnectionCredentialsSchema = typeof ValidateLdapConnectionCredentialsSchema;
@@ -114,8 +114,8 @@ description: "Learn how to automatically rotate LDAP passwords."
{ {
"secretRotation": { "secretRotation": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "my-auth0-rotation", "name": "my-ldap-rotation",
"description": "my client secret rotation", "description": "my ldap password rotation",
"secretsMapping": { "secretsMapping": {
"dn": "LDAP_DN", "dn": "LDAP_DN",
"password": "LDAP_PASSWORD" "password": "LDAP_PASSWORD"
@@ -133,8 +133,8 @@ description: "Learn how to automatically rotate LDAP passwords."
"lastRotationJobId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", "lastRotationJobId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"nextRotationAt": "2023-11-07T05:31:56Z", "nextRotationAt": "2023-11-07T05:31:56Z",
"connection": { "connection": {
"app": "auth0", "app": "ldap",
"name": "my-auth0-connection", "name": "my-ldap-connection",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a" "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}, },
"environment": { "environment": {
+1 -1
View File
@@ -62,7 +62,7 @@ Depending on how you intend to use your LDAP connection, there may be additional
"provider": "active-directory", "provider": "active-directory",
"url": "ldaps://domain-or-ip:636", "url": "ldaps://domain-or-ip:636",
"dn": "CN=John,CN=Users,DC=example,DC=com", "dn": "CN=John,CN=Users,DC=example,DC=com",
"password": "my-strong-password", "password": "<your-secure-password>",
"sslRejectUnauthorized": true, "sslRejectUnauthorized": true,
"sslCertificate": "..." "sslCertificate": "..."
} }
@@ -1,10 +1,8 @@
import { Controller, useFormContext } from "react-hook-form"; import { Controller, useFormContext } from "react-hook-form";
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas"; import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { DEFAULT_PASSWORD_REQUIREMENTS } from "@app/components/secret-rotations-v2/forms/schemas/shared"; import { DEFAULT_PASSWORD_REQUIREMENTS } from "@app/components/secret-rotations-v2/forms/schemas/shared";
import { FormControl, Input, Tooltip } from "@app/components/v2"; import { FormControl, Input } from "@app/components/v2";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
export const LdapPasswordRotationParametersFields = () => { export const LdapPasswordRotationParametersFields = () => {
@@ -24,37 +22,7 @@ export const LdapPasswordRotationParametersFields = () => {
isError={Boolean(error)} isError={Boolean(error)}
errorText={error?.message} errorText={error?.message}
label="Distinguished Name (DN)" label="Distinguished Name (DN)"
helperText={
<Tooltip
className="max-w-md"
content={
<>
Ensure that your connection has the{" "}
<span className="font-semibold">read_clients</span> permission and the
application exists in the connection&#39;s audience.
</>
}
>
<div>
<span>Don&#39;t see the application you&#39;re looking for?</span>{" "}
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-400" />
</div>
</Tooltip>
}
> >
{/* <FilterableSelect
menuPlacement="top"
isLoading={isClientsPending && Boolean(connectionId)}
isDisabled={!connectionId}
value={clients?.find((client) => client.id === value) ?? null}
onChange={(option) => {
onChange((option as SingleValue<TAuth0Client>)?.id ?? null);
}}
options={clients}
placeholder="Select an application..."
getOptionLabel={(option) => option.name}
getOptionValue={(option) => option.id}
/> */}
<Input <Input
value={value} value={value}
onChange={onChange} onChange={onChange}
@@ -74,7 +42,7 @@ export const LdapPasswordRotationParametersFields = () => {
defaultValue={DEFAULT_PASSWORD_REQUIREMENTS.length} defaultValue={DEFAULT_PASSWORD_REQUIREMENTS.length}
render={({ field, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl <FormControl
label="Passsword Length" label="Password Length"
isError={Boolean(error)} isError={Boolean(error)}
errorText={error?.message} errorText={error?.message}
helperText="The length of the password to generate" helperText="The length of the password to generate"
@@ -189,7 +157,7 @@ export const LdapPasswordRotationParametersFields = () => {
placeholder="-_.~!*" placeholder="-_.~!*"
size="sm" size="sm"
{...field} {...field}
onChange={(e) => field.onChange(Number(e.target.value))} onChange={(e) => field.onChange(e.target.value)}
/> />
</FormControl> </FormControl>
)} )}
@@ -15,8 +15,17 @@ export const PasswordRequirementsSchema = z
allowedSymbols: z allowedSymbols: z
.string() .string()
.optional() .optional()
.transform((value) => value ?? "-_.~!*") .transform((value) => value || "-_.~!*")
}) })
.refine(
(data) => {
return Object.values(data.required).some((count) => count > 0);
},
{
message: "At least one character type must be required",
path: ["required.digits"]
}
)
.refine( .refine(
(data) => { (data) => {
const total = Object.values(data.required).reduce((sum, count) => sum + count, 0); const total = Object.values(data.required).reduce((sum, count) => sum + count, 0);
+1 -1
View File
@@ -52,7 +52,7 @@ export const APP_CONNECTION_MAP: Record<
[AppConnection.Camunda]: { name: "Camunda", image: "Camunda.png" }, [AppConnection.Camunda]: { name: "Camunda", image: "Camunda.png" },
[AppConnection.Windmill]: { name: "Windmill", image: "Windmill.png" }, [AppConnection.Windmill]: { name: "Windmill", image: "Windmill.png" },
[AppConnection.Auth0]: { name: "Auth0", image: "Auth0.png", size: 40 }, [AppConnection.Auth0]: { name: "Auth0", image: "Auth0.png", size: 40 },
[AppConnection.Ldap]: { name: "LDAP", image: "LDAP.png", size: 65 } [AppConnection.LDAP]: { name: "LDAP", image: "LDAP.png", size: 65 }
}; };
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => { export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
+1 -1
View File
@@ -31,7 +31,7 @@ export const SECRET_ROTATION_CONNECTION_MAP: Record<SecretRotation, AppConnectio
[SecretRotation.PostgresCredentials]: AppConnection.Postgres, [SecretRotation.PostgresCredentials]: AppConnection.Postgres,
[SecretRotation.MsSqlCredentials]: AppConnection.MsSql, [SecretRotation.MsSqlCredentials]: AppConnection.MsSql,
[SecretRotation.Auth0ClientSecret]: AppConnection.Auth0, [SecretRotation.Auth0ClientSecret]: AppConnection.Auth0,
[SecretRotation.LdapPassword]: AppConnection.Ldap [SecretRotation.LdapPassword]: AppConnection.LDAP
}; };
// if a rotation can potentially have downtime due to rotating a single credential set this to false // if a rotation can potentially have downtime due to rotating a single credential set this to false
@@ -13,5 +13,5 @@ export enum AppConnection {
Camunda = "camunda", Camunda = "camunda",
Windmill = "windmill", Windmill = "windmill",
Auth0 = "auth0", Auth0 = "auth0",
Ldap = "ldap" LDAP = "ldap"
} }
@@ -68,7 +68,7 @@ export type TAuth0ConnectionOption = TAppConnectionOptionBase & {
}; };
export type TLdapConnectionOption = TAppConnectionOptionBase & { export type TLdapConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.Ldap; app: AppConnection.LDAP;
}; };
export type TAppConnectionOption = export type TAppConnectionOption =
@@ -102,5 +102,5 @@ export type TAppConnectionOptionMap = {
[AppConnection.Camunda]: TCamundaConnectionOption; [AppConnection.Camunda]: TCamundaConnectionOption;
[AppConnection.Windmill]: TWindmillConnectionOption; [AppConnection.Windmill]: TWindmillConnectionOption;
[AppConnection.Auth0]: TAuth0ConnectionOption; [AppConnection.Auth0]: TAuth0ConnectionOption;
[AppConnection.Ldap]: TLdapConnectionOption; [AppConnection.LDAP]: TLdapConnectionOption;
}; };
@@ -89,5 +89,5 @@ export type TAppConnectionMap = {
[AppConnection.Camunda]: TCamundaConnection; [AppConnection.Camunda]: TCamundaConnection;
[AppConnection.Windmill]: TWindmillConnection; [AppConnection.Windmill]: TWindmillConnection;
[AppConnection.Auth0]: TAuth0Connection; [AppConnection.Auth0]: TAuth0Connection;
[AppConnection.Ldap]: TLdapConnection; [AppConnection.LDAP]: TLdapConnection;
}; };
@@ -9,7 +9,7 @@ export enum LdapConnectionProvider {
ActiveDirectory = "active-directory" ActiveDirectory = "active-directory"
} }
export type TLdapConnection = TRootAppConnection & { app: AppConnection.Ldap } & { export type TLdapConnection = TRootAppConnection & { app: AppConnection.LDAP } & {
method: LdapConnectionMethod.SimpleBind; method: LdapConnectionMethod.SimpleBind;
credentials: { credentials: {
provider: LdapConnectionProvider; provider: LdapConnectionProvider;
@@ -30,7 +30,7 @@ export type TLdapPasswordRotationGeneratedCredentialsResponse =
export type TLdapPasswordRotationOption = { export type TLdapPasswordRotationOption = {
name: string; name: string;
type: SecretRotation.LdapPassword; type: SecretRotation.LdapPassword;
connection: AppConnection.Ldap; connection: AppConnection.LDAP;
template: { template: {
secretsMapping: TLdapPasswordRotation["secretsMapping"]; secretsMapping: TLdapPasswordRotation["secretsMapping"];
}; };
@@ -90,7 +90,7 @@ const CreateForm = ({ app, onComplete }: CreateFormProps) => {
return <WindmillConnectionForm onSubmit={onSubmit} />; return <WindmillConnectionForm onSubmit={onSubmit} />;
case AppConnection.Auth0: case AppConnection.Auth0:
return <Auth0ConnectionForm onSubmit={onSubmit} />; return <Auth0ConnectionForm onSubmit={onSubmit} />;
case AppConnection.Ldap: case AppConnection.LDAP:
return <LdapConnectionForm onSubmit={onSubmit} />; return <LdapConnectionForm onSubmit={onSubmit} />;
default: default:
throw new Error(`Unhandled App ${app}`); throw new Error(`Unhandled App ${app}`);
@@ -156,7 +156,7 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
return <WindmillConnectionForm onSubmit={onSubmit} appConnection={appConnection} />; return <WindmillConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.Auth0: case AppConnection.Auth0:
return <Auth0ConnectionForm onSubmit={onSubmit} appConnection={appConnection} />; return <Auth0ConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.Ldap: case AppConnection.LDAP:
return <LdapConnectionForm onSubmit={onSubmit} appConnection={appConnection} />; return <LdapConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
default: default:
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`); throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
@@ -37,7 +37,7 @@ type Props = {
}; };
const rootSchema = genericAppConnectionFieldsSchema.extend({ const rootSchema = genericAppConnectionFieldsSchema.extend({
app: z.literal(AppConnection.Ldap) app: z.literal(AppConnection.LDAP)
}); });
const formSchema = z.discriminatedUnion("method", [ const formSchema = z.discriminatedUnion("method", [
@@ -45,7 +45,12 @@ const formSchema = z.discriminatedUnion("method", [
method: z.literal(LdapConnectionMethod.SimpleBind), method: z.literal(LdapConnectionMethod.SimpleBind),
credentials: z.object({ credentials: z.object({
provider: z.nativeEnum(LdapConnectionProvider), provider: z.nativeEnum(LdapConnectionProvider),
url: z.string().url().trim().min(1, "LDAP URL required"), url: z
.string()
.regex(/^ldaps?:\/\//, 'Must start with "ldaps://" or "ldap://"')
.url()
.trim()
.min(1, "LDAP URL required"),
dn: z.string().trim().min(1, "Distinguished Name (DN) required"), dn: z.string().trim().min(1, "Distinguished Name (DN) required"),
password: z.string().trim().min(1, "Password required"), password: z.string().trim().min(1, "Password required"),
sslRejectUnauthorized: z.boolean(), sslRejectUnauthorized: z.boolean(),
@@ -67,7 +72,7 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
const form = useForm<FormData>({ const form = useForm<FormData>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: appConnection ?? { defaultValues: appConnection ?? {
app: AppConnection.Ldap, app: AppConnection.LDAP,
method: LdapConnectionMethod.SimpleBind, method: LdapConnectionMethod.SimpleBind,
credentials: { credentials: {
provider: LdapConnectionProvider.ActiveDirectory, provider: LdapConnectionProvider.ActiveDirectory,
@@ -88,7 +93,7 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
} = form; } = form;
const selectedProvider = watch("credentials.provider"); const selectedProvider = watch("credentials.provider");
const sslEnabled = watch("credentials.url").startsWith("ldaps://"); const sslEnabled = watch("credentials.url")?.startsWith("ldaps://") ?? false;
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
@@ -106,7 +111,7 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
render={({ field: { value, onChange }, fieldState: { error } }) => ( render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl <FormControl
tooltipText={`The method you would like to use to connect with ${ tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.Ldap].name APP_CONNECTION_MAP[AppConnection.LDAP].name
}. This field cannot be changed after creation.`} }. This field cannot be changed after creation.`}
errorText={error?.message} errorText={error?.message}
isError={Boolean(error?.message)} isError={Boolean(error?.message)}
@@ -213,7 +218,7 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
<FormControl <FormControl
errorText={error?.message} errorText={error?.message}
isError={Boolean(error?.message)} isError={Boolean(error?.message)}
label="Binding Distinguised Name (DN)" label="Binding Distinguished Name (DN)"
> >
<Input {...field} placeholder="CN=John,OU=Users,DC=example,DC=com" /> <Input {...field} placeholder="CN=John,OU=Users,DC=example,DC=com" />
</FormControl> </FormControl>
@@ -263,7 +268,7 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
control={control} control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => ( render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl <FormControl
className={` ${sslEnabled ? "" : "opacity-50"}`} className={sslEnabled ? "" : "opacity-50"}
isError={Boolean(error?.message)} isError={Boolean(error?.message)}
errorText={error?.message} errorText={error?.message}
> >