improvements: add ssl options to sql connections and update ui/docs

This commit is contained in:
Scott Wilson
2025-04-04 17:51:05 -07:00
parent 46e72e9fba
commit 0ee1b425df
25 changed files with 320 additions and 173 deletions

View File

@@ -7,7 +7,20 @@ export const MSSQL_CREDENTIALS_ROTATION_LIST_OPTION: TSecretRotationV2ListItem =
type: SecretRotation.MsSqlCredentials,
connection: AppConnection.MsSql,
template: {
createUserStatement: `CREATE LOGIN [my_mssql_user] WITH PASSWORD = 'my_temporary_password'; CREATE USER [my_mssql_user] FOR LOGIN [my_mssql_user]; GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO [my_mssql_user];`,
createUserStatement: `-- Create login at the server level
CREATE LOGIN [infisical_user] WITH PASSWORD = 'my-password';
-- Grant server-level connect permission
GRANT CONNECT SQL TO [infisical_user];
-- Switch to the database where you want to create the user
USE my_database;
-- Create the database user mapped to the login
CREATE USER [infisical_user] FOR LOGIN [infisical_user];
-- Grant permissions to the user on the schema in this database
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO [infisical_user];`,
secretsMapping: {
username: "MSSQL_DB_USERNAME",
password: "MSSQL_DB_PASSWORD"

View File

@@ -7,7 +7,14 @@ export const POSTGRES_CREDENTIALS_ROTATION_LIST_OPTION: TSecretRotationV2ListIte
type: SecretRotation.PostgresCredentials,
connection: AppConnection.Postgres,
template: {
createUserStatement: `CREATE USER "my_pg_user" WITH ENCRYPTED PASSWORD 'temporary_password'; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "my_pg_user";`,
createUserStatement: `-- create user role
CREATE USER infisical_user WITH ENCRYPTED PASSWORD 'temporary_password';
-- grant database connection permissions
GRANT CONNECT ON DATABASE my_database TO infisical_user;
-- grant relevant table permissions
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO infisical_user;`,
secretsMapping: {
username: "POSTGRES_DB_USERNAME",
password: "POSTGRES_DB_PASSWORD"

View File

@@ -1691,6 +1691,8 @@ export const AppConnections = {
database: "The name of the database to connect to.",
username: "The username to connect to the database with.",
password: "The password to connect to the database with.",
sslEnabled: "Whether or not to use SSL when connecting to the database.",
sslRejectUnauthorized: "Whether or not to reject unauthorized SSL certificates.",
sslCertificate: "The SSL certificate to use for connection."
}
}

View File

@@ -59,8 +59,6 @@ const envSchema = z
QUEUE_WORKERS_ENABLED: zodStrBool.default("true"),
HTTPS_ENABLED: zodStrBool,
ROTATION_DEVELOPMENT_MODE: zodStrBool.default("false").optional(),
DB_SSL_REJECT_UNAUTHORIZED: zodStrBool.default("true"),
DB_SSL_REQUIRED: zodStrBool.default("true"),
// smtp options
SMTP_HOST: zpStr(z.string().optional()),
SMTP_IGNORE_TLS: zodStrBool.default("false"),

View File

@@ -29,7 +29,9 @@ export const SanitizedMsSqlConnectionSchema = z.discriminatedUnion("method", [
host: true,
database: true,
port: true,
username: true
username: true,
sslEnabled: true,
sslRejectUnauthorized: true
})
})
]);

View File

@@ -27,7 +27,9 @@ export const SanitizedPostgresConnectionSchema = z.discriminatedUnion("method",
host: true,
database: true,
port: true,
username: true
username: true,
sslEnabled: true,
sslRejectUnauthorized: true
})
})
]);

View File

@@ -5,7 +5,6 @@ import {
TSqlCredentialsRotationGeneratedCredentials,
TSqlCredentialsRotationWithConnection
} from "@app/ee/services/secret-rotation-v2/shared/sql-credentials/sql-credentials-rotation-types";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, DatabaseError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
@@ -21,16 +20,14 @@ const SQL_CONNECTION_CLIENT_MAP = {
const getConnectionConfig = ({
app,
credentials: { sslCertificate, host }
credentials: { host, sslCertificate, sslEnabled, sslRejectUnauthorized }
}: Pick<TSqlConnection, "credentials" | "app">) => {
const appCfg = getConfig();
switch (app) {
case AppConnection.Postgres: {
return {
ssl: appCfg.DB_SSL_REQUIRED
ssl: sslEnabled
? {
rejectUnauthorized: appCfg.DB_SSL_REJECT_UNAUTHORIZED,
rejectUnauthorized: sslRejectUnauthorized,
ca: sslCertificate,
servername: host
}
@@ -39,13 +36,13 @@ const getConnectionConfig = ({
}
case AppConnection.MsSql: {
return {
options: appCfg.DB_SSL_REQUIRED
options: sslEnabled
? {
trustServerCertificate: !appCfg.DB_SSL_REJECT_UNAUTHORIZED,
trustServerCertificate: !sslRejectUnauthorized,
encrypt: true,
cryptoCredentialsDetails: sslCertificate ? { ca: sslCertificate } : {}
}
: undefined
: { encrypt: false }
};
}
default:

View File

@@ -8,5 +8,12 @@ export const BaseSqlUsernameAndPasswordConnectionSchema = z.object({
database: z.string().trim().min(1, "Database required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.database),
username: z.string().trim().min(1, "Username required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.username),
password: z.string().trim().min(1, "Password required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.password),
sslCertificate: z.string().trim().optional().describe(AppConnections.CREDENTIALS.SQL_CONNECTION.sslCertificate)
sslEnabled: z.boolean().describe(AppConnections.CREDENTIALS.SQL_CONNECTION.sslEnabled),
sslRejectUnauthorized: z.boolean().describe(AppConnections.CREDENTIALS.SQL_CONNECTION.sslRejectUnauthorized),
sslCertificate: z
.string()
.trim()
.transform((value) => value || undefined)
.optional()
.describe(AppConnections.CREDENTIALS.SQL_CONNECTION.sslCertificate)
});

View File

@@ -11,19 +11,19 @@ description: "Learn how to automatically rotate Microsoft SQL Server credentials
An example creation statement might look like:
```SQL
-- create server-level logins
CREATE LOGIN infisical_user_1 WITH PASSWORD = 'my-password';
CREATE LOGIN infisical_user_2 WITH PASSWORD = 'my-password';
CREATE LOGIN [infisical_user_1] WITH PASSWORD = 'my-password';
CREATE LOGIN [infisical_user_2] WITH PASSWORD = 'my-password';
GRANT CONNECT SQL TO [infisical_user_1];
GRANT CONNECT SQL TO [infisical_user_2];
-- create database-level users with login from above
USE my_database;
CREATE USER infisical_user_1 FOR LOGIN infisical_user_1;
CREATE USER infisical_user_2 FOR LOGIN infisical_user_2;
GRANT CONNECT TO infisical_user_1;
GRANT CONNECT TO infisical_user_2;
CREATE USER [infisical_user_1] FOR LOGIN [infisical_user_1];
CREATE USER [infisical_user_2] FOR LOGIN [infisical_user_2];
-- grant relevant permissions
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO infisical_user_1;
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO infisical_user_2;
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO [infisical_user_1];
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO [infisical_user_2];
```
<Tip>

View File

@@ -10,14 +10,16 @@ description: "Learn how to automatically rotate PostgreSQL credentials."
An example creation statement might look like:
```SQL
-- first user
-- create user roles
CREATE USER infisical_user_1 WITH ENCRYPTED PASSWORD 'temporary_password';
GRANT CONNECT ON DATABASE my_database TO infisical_user_1;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO infisical_user_1;
-- second user
CREATE USER infisical_user_2 WITH ENCRYPTED PASSWORD 'temporary_password';
-- grant database connection permissions
GRANT CONNECT ON DATABASE my_database TO infisical_user_1;
GRANT CONNECT ON DATABASE my_database TO infisical_user_2;
-- grant relevant table permissions
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO infisical_user_1;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO infisical_user_2;
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 KiB

After

Width:  |  Height:  |  Size: 826 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 KiB

After

Width:  |  Height:  |  Size: 825 KiB

View File

@@ -11,16 +11,20 @@ Infisical supports connecting to Microsoft SQL Server using database principals.
<Step title="Create a Principal">
Infisical recommends creating a designated server login and database user in your Microsoft SQL Server database for your connection.
```SQL
-- create server-level login
CREATE LOGIN infisical_login WITH PASSWORD = 'my-password';
-- Create login at the server level
CREATE LOGIN [infisical_app] WITH PASSWORD = 'my-password';
-- create database-level user with login from above
-- Grant server-level connect permission
GRANT CONNECT SQL TO [infisical_app];
-- Switch to the specific database where you want to create the user
USE my_database;
CREATE USER infisical_user FOR LOGIN infisical_login;
GRANT CONNECT TO infisical_user;
-- Create the database user mapped to the login
CREATE USER [infisical_app] FOR LOGIN [infisical_app];
-- If you intend to use Platform Managed Credentials (see below)
GRANT ALTER ANY LOGIN TO infisical_login;
GRANT ALTER ANY LOGIN TO [infisical_app];
```
</Step>
<Step title="Grant Relevant Permissions">
@@ -95,6 +99,8 @@ Infisical supports connecting to Microsoft SQL Server using database principals.
"database": "default",
"username": "infisical_login",
"password": "my-password",
"sslEnabled": true,
"sslRejectUnauthorized": true
},
}'
```
@@ -117,7 +123,9 @@ Infisical supports connecting to Microsoft SQL Server using database principals.
"host": "123.4.5.6",
"port": 1433,
"database": "default",
"username": "infisical_login"
"username": "infisical_login",
"sslEnabled": true,
"sslRejectUnauthorized": true
}
}
}

View File

@@ -12,7 +12,7 @@ Infisical supports connecting to PostgreSQL using a database role.
Infisical recommends creating a designated role in your PostgreSQL database for your connection.
```SQL
-- create user role
CREATE ROLE infisical_role WITH LOGIN PASSWORD 'my-password'
CREATE ROLE infisical_role WITH LOGIN PASSWORD 'my-password';
-- grant login access to the specified database
GRANT CONNECT ON DATABASE my_database TO infisical_role;
@@ -27,6 +27,7 @@ Infisical supports connecting to PostgreSQL using a database role.
<Tab title="Secret Rotation">
For Secret Rotations, your Infisical user will require the ability to alter other users' passwords:
```SQL
-- enable permissions to alter login credentials
ALTER ROLE infisical_role WITH CREATEROLE;
```
</Tab>
@@ -88,6 +89,8 @@ Infisical supports connecting to PostgreSQL using a database role.
"database": "default",
"username": "infisical_role",
"password": "my-password",
"sslEnabled": true,
"sslRejectUnauthorized": true
},
}'
```
@@ -110,7 +113,9 @@ Infisical supports connecting to PostgreSQL using a database role.
"host": "123.4.5.6",
"port": 5432,
"database": "default",
"username": "infisical_role"
"username": "infisical_role",
"sslEnabled": true,
"sslRejectUnauthorized": true
}
}
}

View File

@@ -439,19 +439,6 @@ When set, all visits to the Infisical login page will automatically redirect use
information.
</Accordion>
## External Database Connections
<ParamField query="DB_SSL_REJECT_UNAUTHORIZED" type="boolean" default="true" optional>
Specify whether external database connections should reject unauthorized SSL certificates.
We highly recommend keeping this value set to `true` for production use-cases.
</ParamField>
<ParamField query="DB_SSL_REQUIRED" type="boolean" default="true" optional>
Specify whether external database connections should require SSL.
We highly recommend keeping this value set to `true` for production use-cases.
</ParamField>
## App Connections
You can configure third-party app connections for re-use across Infisical Projects.

View File

@@ -57,7 +57,7 @@ export const SqlRotationParametersFields = () => {
to suit your needs.
</p>
<p className="mb-3 text-sm">
<pre className="whitespace-pre-wrap rounded border border-mineshaft-700 bg-mineshaft-800 p-2 text-mineshaft-300">
<pre className="max-h-[10rem] overflow-y-auto whitespace-pre-wrap rounded border border-mineshaft-700 bg-mineshaft-800 p-2 text-mineshaft-300">
{rotationOption!.template.createUserStatement}
</pre>
</p>

View File

@@ -4,4 +4,6 @@ export type TBaseSqlConnectionCredentials = {
username: string;
password: string;
database: string;
sslEnabled: boolean;
sslRejectUnauthorized: boolean;
};

View File

@@ -22,7 +22,7 @@ import {
type Props = {
appConnection?: TAwsConnection;
onSubmit: (formData: FormData) => void;
onSubmit: (formData: FormData) => Promise<void>;
};
const rootSchema = genericAppConnectionFieldsSchema.extend({

View File

@@ -25,7 +25,7 @@ import {
type Props = {
appConnection?: TDatabricksConnection;
onSubmit: (formData: FormData) => void;
onSubmit: (formData: FormData) => Promise<void>;
};
const rootSchema = genericAppConnectionFieldsSchema.extend({

View File

@@ -28,7 +28,7 @@ import {
type Props = {
appConnection?: TGcpConnection;
onSubmit: (formData: FormData) => void;
onSubmit: (formData: FormData) => Promise<void>;
};
const rootSchema = genericAppConnectionFieldsSchema.extend({

View File

@@ -21,7 +21,7 @@ import {
type Props = {
appConnection?: THumanitecConnection;
onSubmit: (formData: FormData) => void;
onSubmit: (formData: FormData) => Promise<void>;
};
const rootSchema = genericAppConnectionFieldsSchema.extend({

View File

@@ -24,7 +24,7 @@ import {
type Props = {
appConnection?: TMsSqlConnection;
onSubmit: (formData: FormData) => void;
onSubmit: (formData: FormData) => Promise<void>;
};
const rootSchema = genericAppConnectionFieldsSchema.extend({
@@ -44,6 +44,7 @@ type FormData = z.infer<typeof formSchema>;
export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
const isUpdate = Boolean(appConnection);
const [showConfirmation, setShowConfirmation] = useState(false);
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
@@ -56,7 +57,9 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
database: "default",
username: "",
password: "",
sslCertificate: ""
sslEnabled: true,
sslRejectUnauthorized: true,
sslCertificate: undefined
}
}
});
@@ -69,18 +72,23 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
const confirmSubmit = (formData: FormData) => {
const confirmSubmit = async (formData: FormData) => {
if (formData.isPlatformManagedCredentials) {
setShowConfirmation(true);
return;
}
onSubmit(formData);
await onSubmit(formData);
};
return (
<FormProvider {...form}>
<form onSubmit={handleSubmit(confirmSubmit)}>
<form
onSubmit={(e) => {
setSelectedTabIndex(0);
handleSubmit(confirmSubmit)(e);
}}
>
{!isUpdate && <GenericAppConnectionsFields />}
<Controller
name="method"
@@ -113,7 +121,11 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
</FormControl>
)}
/>
<SqlConnectionFields isPlatformManagedCredentials={isPlatformManagedCredentials} />
<SqlConnectionFields
isPlatformManagedCredentials={isPlatformManagedCredentials}
selectedTabIndex={selectedTabIndex}
setSelectedTabIndex={setSelectedTabIndex}
/>
{isPlatformManagedCredentials ? (
<PlatformManagedNoticeBanner />
) : (

View File

@@ -21,7 +21,7 @@ import {
type Props = {
appConnection?: TPostgresConnection;
onSubmit: (formData: FormData) => void;
onSubmit: (formData: FormData) => Promise<void>;
};
const rootSchema = genericAppConnectionFieldsSchema.extend({
@@ -41,6 +41,7 @@ type FormData = z.infer<typeof formSchema>;
export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
const isUpdate = Boolean(appConnection);
const [showConfirmation, setShowConfirmation] = useState(false);
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
@@ -53,7 +54,9 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
database: "default",
username: "",
password: "",
sslCertificate: ""
sslEnabled: true,
sslRejectUnauthorized: true,
sslCertificate: undefined
}
}
});
@@ -66,18 +69,23 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
const confirmSubmit = (formData: FormData) => {
const confirmSubmit = async (formData: FormData) => {
if (formData.isPlatformManagedCredentials) {
setShowConfirmation(true);
return;
}
onSubmit(formData);
await onSubmit(formData);
};
return (
<FormProvider {...form}>
<form onSubmit={handleSubmit(confirmSubmit)}>
<form
onSubmit={(e) => {
setSelectedTabIndex(0);
handleSubmit(confirmSubmit)(e);
}}
>
{!isUpdate && <GenericAppConnectionsFields />}
<Controller
name="method"
@@ -110,7 +118,11 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
</FormControl>
)}
/>
<SqlConnectionFields isPlatformManagedCredentials={isPlatformManagedCredentials} />
<SqlConnectionFields
isPlatformManagedCredentials={isPlatformManagedCredentials}
selectedTabIndex={selectedTabIndex}
setSelectedTabIndex={setSelectedTabIndex}
/>
{isPlatformManagedCredentials ? (
<PlatformManagedNoticeBanner />
) : (

View File

@@ -1,122 +1,207 @@
import { Dispatch, SetStateAction } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tab } from "@headlessui/react";
import { FormControl, Input, SecretInput, Switch, TextArea, Tooltip } from "@app/components/v2";
type Props = {
isPlatformManagedCredentials: boolean;
selectedTabIndex: number;
setSelectedTabIndex: Dispatch<SetStateAction<number>>;
};
export const SqlConnectionFields = ({ isPlatformManagedCredentials }: Props) => {
const { control } = useFormContext();
export const SqlConnectionFields = ({
isPlatformManagedCredentials,
setSelectedTabIndex,
selectedTabIndex
}: Props) => {
const { control, watch } = useFormContext();
const sslEnabled = watch("credentials.sslEnabled");
return (
<>
<div className="flex items-start gap-2">
<Controller
name="credentials.host"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
className="flex-1"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Host"
>
<Input {...field} isDisabled={isPlatformManagedCredentials} />
</FormControl>
)}
/>
<Controller
name="credentials.database"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
className="flex-1"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Database Name"
>
<Input {...field} isDisabled={isPlatformManagedCredentials} />
</FormControl>
)}
/>
<Controller
name="credentials.port"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
className="w-28"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Port"
>
<Input type="number" {...field} isDisabled={isPlatformManagedCredentials} />
</FormControl>
)}
/>
</div>
<div className="flex items-start gap-2">
<Controller
name="credentials.username"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Username"
className="flex-1"
>
<Input {...field} isDisabled={isPlatformManagedCredentials} />
</FormControl>
)}
/>
<Controller
name="credentials.password"
control={control}
shouldUnregister
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Password"
className="flex-1"
>
<SecretInput
containerClassName="text-gray-400 w-full 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)}
isDisabled={isPlatformManagedCredentials}
/>
</FormControl>
)}
/>
</div>
<Controller
name="credentials.sslCertificate"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="SSL Certificate"
isOptional
<Tab.Group selectedIndex={selectedTabIndex} onChange={setSelectedTabIndex}>
<Tab.List className="-pb-1 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"
}`
}
>
<TextArea
className="!resize-none"
rows={1}
{...field}
isDisabled={isPlatformManagedCredentials}
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>
<Tab.Panels className="mb-4 rounded border border-mineshaft-600 bg-mineshaft-700/70 p-3 pb-0">
<Tab.Panel>
<div className="mt-[0.675rem] flex items-start gap-2">
<Controller
name="credentials.host"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
className="flex-1"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Host"
>
<Input {...field} isDisabled={isPlatformManagedCredentials} />
</FormControl>
)}
/>
<Controller
name="credentials.database"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
className="flex-1"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Database Name"
>
<Input {...field} isDisabled={isPlatformManagedCredentials} />
</FormControl>
)}
/>
<Controller
name="credentials.port"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
className="w-28"
errorText={error?.message}
isError={Boolean(error?.message)}
label="Port"
>
<Input type="number" {...field} isDisabled={isPlatformManagedCredentials} />
</FormControl>
)}
/>
</div>
<div className="mb-[0.675rem] flex items-start gap-2">
<Controller
name="credentials.username"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Username"
className="flex-1"
>
<Input {...field} isDisabled={isPlatformManagedCredentials} />
</FormControl>
)}
/>
<Controller
name="credentials.password"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Password"
className="flex-1"
>
<SecretInput
containerClassName="text-gray-400 w-full 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)}
isDisabled={isPlatformManagedCredentials}
/>
</FormControl>
)}
/>
</div>
</Tab.Panel>
<Tab.Panel>
<Controller
name="credentials.sslEnabled"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error?.message)} errorText={error?.message}>
<Switch
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
id="platform-managed"
thumbClassName="bg-mineshaft-800"
isChecked={value}
onCheckedChange={onChange}
isDisabled={isPlatformManagedCredentials}
>
Enable SSL
</Switch>
</FormControl>
)}
/>
</FormControl>
)}
/>
<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.5rem] !resize-none"
{...field}
isDisabled={isPlatformManagedCredentials || !sslEnabled}
/>
</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="platform-managed"
thumbClassName="bg-mineshaft-800"
isChecked={sslEnabled ? value : false}
onCheckedChange={onChange}
isDisabled={isPlatformManagedCredentials || !sslEnabled}
>
<p className="w-[9.5rem]">
Reject Unauthorized
<Tooltip
className="max-w-md"
content={
<p>
If enabled, Infisical will only connect to servers that have valid,
trusted SSL certificates.
</p>
}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
</Tooltip>
</p>
</Switch>
</FormControl>
)}
/>
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
{!isPlatformManagedCredentials && (
<Controller
name="isPlatformManagedCredentials"

View File

@@ -6,5 +6,11 @@ export const BaseSqlUsernameAndPasswordConnectionSchema = z.object({
database: z.string().trim().min(1, "Database required").default("default"),
username: z.string().trim().min(1, "Username required"),
password: z.string().trim().min(1, "Password required"),
sslCertificate: z.string().trim().optional()
sslEnabled: z.boolean().default(true),
sslRejectUnauthorized: z.boolean().default(true),
sslCertificate: z
.string()
.trim()
.transform((value) => value || undefined)
.optional()
});