mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 23:27:35 +00:00
feat(app-connection): Gateway support for SQL connections
This commit is contained in:
@@ -24,6 +24,7 @@ export const SanitizedOracleDBConnectionSchema = z.discriminatedUnion("method",
|
|||||||
BaseOracleDBConnectionSchema.extend({
|
BaseOracleDBConnectionSchema.extend({
|
||||||
method: z.literal(OracleDBConnectionMethod.UsernameAndPassword),
|
method: z.literal(OracleDBConnectionMethod.UsernameAndPassword),
|
||||||
credentials: OracleDBConnectionCredentialsSchema.pick({
|
credentials: OracleDBConnectionCredentialsSchema.pick({
|
||||||
|
gatewayId: true,
|
||||||
host: true,
|
host: true,
|
||||||
database: true,
|
database: true,
|
||||||
port: true,
|
port: true,
|
||||||
|
|||||||
@@ -1706,7 +1706,8 @@ export const registerRoutes = async (
|
|||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
kmsService,
|
kmsService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
gatewayService
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretSyncService = secretSyncServiceFactory({
|
const secretSyncService = secretSyncServiceFactory({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ForbiddenError, subject } from "@casl/ability";
|
|||||||
import { ValidateOCIConnectionCredentialsSchema } from "@app/ee/services/app-connections/oci";
|
import { ValidateOCIConnectionCredentialsSchema } from "@app/ee/services/app-connections/oci";
|
||||||
import { ociConnectionService } from "@app/ee/services/app-connections/oci/oci-connection-service";
|
import { ociConnectionService } from "@app/ee/services/app-connections/oci/oci-connection-service";
|
||||||
import { ValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/app-connections/oracledb";
|
import { ValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/app-connections/oracledb";
|
||||||
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { OrgPermissionAppConnectionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
import { OrgPermissionAppConnectionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
@@ -92,6 +93,7 @@ export type TAppConnectionServiceFactoryDep = {
|
|||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
|
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
|
||||||
@@ -135,7 +137,8 @@ export const appConnectionServiceFactory = ({
|
|||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
kmsService,
|
kmsService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
gatewayService
|
||||||
}: TAppConnectionServiceFactoryDep) => {
|
}: TAppConnectionServiceFactoryDep) => {
|
||||||
const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => {
|
const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
@@ -273,7 +276,8 @@ export const appConnectionServiceFactory = ({
|
|||||||
credentials: validatedCredentials,
|
credentials: validatedCredentials,
|
||||||
method
|
method
|
||||||
} as TAppConnectionConfig,
|
} as TAppConnectionConfig,
|
||||||
(platformCredentials) => createConnection(platformCredentials)
|
(platformCredentials) => createConnection(platformCredentials),
|
||||||
|
gatewayService
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
connection = await createConnection(validatedCredentials);
|
connection = await createConnection(validatedCredentials);
|
||||||
@@ -387,7 +391,8 @@ export const appConnectionServiceFactory = ({
|
|||||||
credentials: updatedCredentials,
|
credentials: updatedCredentials,
|
||||||
method
|
method
|
||||||
} as TAppConnectionConfig,
|
} as TAppConnectionConfig,
|
||||||
(platformCredentials) => updateConnection(platformCredentials)
|
(platformCredentials) => updateConnection(platformCredentials),
|
||||||
|
gatewayService
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updatedConnection = await updateConnection(updatedCredentials);
|
updatedConnection = await updateConnection(updatedCredentials);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
TOracleDBConnectionInput,
|
TOracleDBConnectionInput,
|
||||||
TValidateOracleDBConnectionCredentialsSchema
|
TValidateOracleDBConnectionCredentialsSchema
|
||||||
} from "@app/ee/services/app-connections/oracledb";
|
} from "@app/ee/services/app-connections/oracledb";
|
||||||
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||||
import { TSqlConnectionConfig } from "@app/services/app-connection/shared/sql/sql-connection-types";
|
import { TSqlConnectionConfig } from "@app/services/app-connection/shared/sql/sql-connection-types";
|
||||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
@@ -354,7 +355,8 @@ export type TAppConnectionCredentialsValidator = (
|
|||||||
|
|
||||||
export type TAppConnectionTransitionCredentialsToPlatform = (
|
export type TAppConnectionTransitionCredentialsToPlatform = (
|
||||||
appConnection: TAppConnectionConfig,
|
appConnection: TAppConnectionConfig,
|
||||||
callback: (credentials: TAppConnection["credentials"]) => Promise<TAppConnectionRaw>
|
callback: (credentials: TAppConnection["credentials"]) => Promise<TAppConnectionRaw>,
|
||||||
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
||||||
) => Promise<TAppConnectionRaw>;
|
) => Promise<TAppConnectionRaw>;
|
||||||
|
|
||||||
export type TAppConnectionBaseConfig = {
|
export type TAppConnectionBaseConfig = {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export const SanitizedMsSqlConnectionSchema = z.discriminatedUnion("method", [
|
|||||||
BaseMsSqlConnectionSchema.extend({
|
BaseMsSqlConnectionSchema.extend({
|
||||||
method: z.literal(MsSqlConnectionMethod.UsernameAndPassword),
|
method: z.literal(MsSqlConnectionMethod.UsernameAndPassword),
|
||||||
credentials: MsSqlConnectionAccessTokenCredentialsSchema.pick({
|
credentials: MsSqlConnectionAccessTokenCredentialsSchema.pick({
|
||||||
|
gatewayId: true,
|
||||||
host: true,
|
host: true,
|
||||||
database: true,
|
database: true,
|
||||||
port: true,
|
port: true,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export const SanitizedMySqlConnectionSchema = z.discriminatedUnion("method", [
|
|||||||
BaseMySqlConnectionSchema.extend({
|
BaseMySqlConnectionSchema.extend({
|
||||||
method: z.literal(MySqlConnectionMethod.UsernameAndPassword),
|
method: z.literal(MySqlConnectionMethod.UsernameAndPassword),
|
||||||
credentials: MySqlConnectionAccessTokenCredentialsSchema.pick({
|
credentials: MySqlConnectionAccessTokenCredentialsSchema.pick({
|
||||||
|
gatewayId: true,
|
||||||
host: true,
|
host: true,
|
||||||
database: true,
|
database: true,
|
||||||
port: true,
|
port: true,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export const SanitizedPostgresConnectionSchema = z.discriminatedUnion("method",
|
|||||||
BasePostgresConnectionSchema.extend({
|
BasePostgresConnectionSchema.extend({
|
||||||
method: z.literal(PostgresConnectionMethod.UsernameAndPassword),
|
method: z.literal(PostgresConnectionMethod.UsernameAndPassword),
|
||||||
credentials: PostgresConnectionAccessTokenCredentialsSchema.pick({
|
credentials: PostgresConnectionAccessTokenCredentialsSchema.pick({
|
||||||
|
gatewayId: true,
|
||||||
host: true,
|
host: true,
|
||||||
database: true,
|
database: true,
|
||||||
port: true,
|
port: true,
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import knex, { Knex } from "knex";
|
import knex, { Knex } from "knex";
|
||||||
|
|
||||||
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
||||||
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
import {
|
import {
|
||||||
TSqlCredentialsRotationGeneratedCredentials,
|
TSqlCredentialsRotationGeneratedCredentials,
|
||||||
TSqlCredentialsRotationWithConnection
|
TSqlCredentialsRotationWithConnection
|
||||||
} from "@app/ee/services/secret-rotation-v2/shared/sql-credentials/sql-credentials-rotation-types";
|
} from "@app/ee/services/secret-rotation-v2/shared/sql-credentials/sql-credentials-rotation-types";
|
||||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||||
|
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import { TAppConnectionRaw, TSqlConnection } from "@app/services/app-connection/app-connection-types";
|
import { TAppConnectionRaw, TSqlConnection } from "@app/services/app-connection/app-connection-types";
|
||||||
@@ -98,25 +100,80 @@ export const getSqlConnectionClient = async (appConnection: Pick<TSqlConnection,
|
|||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validateSqlConnectionCredentials = async (config: TSqlConnectionConfig) => {
|
const executeWithPotentialGateway = async <T>(
|
||||||
|
config: TSqlConnectionConfig,
|
||||||
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
operation: (client: Knex) => Promise<T>
|
||||||
|
): Promise<T> => {
|
||||||
const { credentials, app } = config;
|
const { credentials, app } = config;
|
||||||
|
|
||||||
let client: Knex | undefined;
|
if (credentials.gatewayId && gatewayService) {
|
||||||
|
const [targetHost] = await verifyHostInputValidity(credentials.host, true);
|
||||||
|
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(credentials.gatewayId);
|
||||||
|
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||||
|
|
||||||
|
return withGatewayProxy(
|
||||||
|
async (proxyPort) => {
|
||||||
|
const client = knex({
|
||||||
|
client: SQL_CONNECTION_CLIENT_MAP[app],
|
||||||
|
connection: {
|
||||||
|
database: credentials.database,
|
||||||
|
port: proxyPort,
|
||||||
|
host: "localhost",
|
||||||
|
user: credentials.username,
|
||||||
|
password: credentials.password,
|
||||||
|
connectionTimeoutMillis: EXTERNAL_REQUEST_TIMEOUT,
|
||||||
|
...getConnectionConfig({ app, credentials })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
return await operation(client);
|
||||||
|
} finally {
|
||||||
|
await client.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: GatewayProxyProtocol.Tcp,
|
||||||
|
targetHost,
|
||||||
|
targetPort: credentials.port,
|
||||||
|
relayHost,
|
||||||
|
relayPort: Number(relayPort),
|
||||||
|
identityId: relayDetails.identityId,
|
||||||
|
orgId: relayDetails.orgId,
|
||||||
|
tlsOptions: {
|
||||||
|
ca: relayDetails.certChain,
|
||||||
|
cert: relayDetails.certificate,
|
||||||
|
key: relayDetails.privateKey.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-gateway path
|
||||||
|
const client = await getSqlConnectionClient({ app, credentials });
|
||||||
try {
|
try {
|
||||||
client = await getSqlConnectionClient({ app, credentials });
|
return await operation(client);
|
||||||
|
} finally {
|
||||||
|
await client.destroy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
await client.raw(`Select 1`);
|
export const validateSqlConnectionCredentials = async (
|
||||||
|
config: TSqlConnectionConfig & { gatewayId?: string },
|
||||||
return credentials;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
await executeWithPotentialGateway(config, gatewayService, async (client) => {
|
||||||
|
await client.raw(`Select 1`);
|
||||||
|
});
|
||||||
|
return config.credentials;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: `Unable to validate connection: ${
|
message: `Unable to validate connection: ${
|
||||||
(error as Error)?.message?.replaceAll(credentials.password, "********************") ?? "verify credentials"
|
(error as Error)?.message?.replaceAll(config.credentials.password, "********************") ??
|
||||||
|
"verify credentials"
|
||||||
}`
|
}`
|
||||||
});
|
});
|
||||||
} finally {
|
|
||||||
await client?.destroy();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,22 +189,23 @@ export const SQL_CONNECTION_ALTER_LOGIN_STATEMENT: Record<
|
|||||||
|
|
||||||
export const transferSqlConnectionCredentialsToPlatform = async (
|
export const transferSqlConnectionCredentialsToPlatform = async (
|
||||||
config: TSqlConnectionConfig,
|
config: TSqlConnectionConfig,
|
||||||
callback: (credentials: TSqlConnectionConfig["credentials"]) => Promise<TAppConnectionRaw>
|
callback: (credentials: TSqlConnectionConfig["credentials"]) => Promise<TAppConnectionRaw>,
|
||||||
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
const { credentials, app } = config;
|
const { credentials, app } = config;
|
||||||
|
|
||||||
const client = await getSqlConnectionClient({ app, credentials });
|
|
||||||
|
|
||||||
const newPassword = alphaNumericNanoId(32);
|
const newPassword = alphaNumericNanoId(32);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await client.transaction(async (tx) => {
|
return await executeWithPotentialGateway(config, gatewayService, (client) => {
|
||||||
await tx.raw(
|
return client.transaction(async (tx) => {
|
||||||
...SQL_CONNECTION_ALTER_LOGIN_STATEMENT[app]({ username: credentials.username, password: newPassword })
|
await tx.raw(
|
||||||
);
|
...SQL_CONNECTION_ALTER_LOGIN_STATEMENT[app]({ username: credentials.username, password: newPassword })
|
||||||
return callback({
|
);
|
||||||
...credentials,
|
return callback({
|
||||||
password: newPassword
|
...credentials,
|
||||||
|
password: newPassword
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -161,7 +219,5 @@ export const transferSqlConnectionCredentialsToPlatform = async (
|
|||||||
(error as Error)?.message?.replaceAll(newPassword, "********************") ??
|
(error as Error)?.message?.replaceAll(newPassword, "********************") ??
|
||||||
"Encountered an error transferring credentials to platform"
|
"Encountered an error transferring credentials to platform"
|
||||||
});
|
});
|
||||||
} finally {
|
|
||||||
await client.destroy();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|||||||
import { AppConnections } from "@app/lib/api-docs";
|
import { AppConnections } from "@app/lib/api-docs";
|
||||||
|
|
||||||
export const BaseSqlUsernameAndPasswordConnectionSchema = z.object({
|
export const BaseSqlUsernameAndPasswordConnectionSchema = z.object({
|
||||||
|
gatewayId: z.string().optional(),
|
||||||
host: z.string().trim().min(1, "Host required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.host),
|
host: z.string().trim().min(1, "Host required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.host),
|
||||||
port: z.coerce.number().describe(AppConnections.CREDENTIALS.SQL_CONNECTION.port),
|
port: z.coerce.number().describe(AppConnections.CREDENTIALS.SQL_CONNECTION.port),
|
||||||
database: z.string().trim().min(1, "Database required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.database),
|
database: z.string().trim().min(1, "Database required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.database),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export type TBaseSqlConnectionCredentials = {
|
export type TBaseSqlConnectionCredentials = {
|
||||||
|
gatewayId?: string;
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
username: string;
|
username: string;
|
||||||
|
|||||||
+59
-1
@@ -1,10 +1,17 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { Button, FormControl, ModalClose, Select, SelectItem } from "@app/components/v2";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
|
import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2";
|
||||||
|
import {
|
||||||
|
OrgGatewayPermissionActions,
|
||||||
|
OrgPermissionSubjects
|
||||||
|
} from "@app/context/OrgPermissionContext/types";
|
||||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||||
|
import { gatewaysQueryKeys } from "@app/hooks/api";
|
||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
import {
|
import {
|
||||||
MsSqlConnectionMethod,
|
MsSqlConnectionMethod,
|
||||||
@@ -52,6 +59,7 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
app: AppConnection.MsSql,
|
app: AppConnection.MsSql,
|
||||||
method: MsSqlConnectionMethod.UsernameAndPassword,
|
method: MsSqlConnectionMethod.UsernameAndPassword,
|
||||||
credentials: {
|
credentials: {
|
||||||
|
gatewayId: "",
|
||||||
host: "",
|
host: "",
|
||||||
port: 1433,
|
port: 1433,
|
||||||
database: "default",
|
database: "default",
|
||||||
@@ -71,6 +79,7 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
} = form;
|
} = form;
|
||||||
|
|
||||||
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
|
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
|
||||||
|
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||||
|
|
||||||
const confirmSubmit = async (formData: FormData) => {
|
const confirmSubmit = async (formData: FormData) => {
|
||||||
if (formData.isPlatformManagedCredentials) {
|
if (formData.isPlatformManagedCredentials) {
|
||||||
@@ -121,6 +130,55 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgGatewayPermissionActions.AttachGateways}
|
||||||
|
a={OrgPermissionSubjects.Gateway}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="credentials.gatewayId"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Gateway"
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
isDisabled={isAllowed}
|
||||||
|
content="Restricted access. You don't have permission to attach gateways to resources."
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
value={value}
|
||||||
|
onValueChange={onChange}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
isLoading={isGatewaysLoading}
|
||||||
|
placeholder="Default: Internet Gateway"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
<SelectItem
|
||||||
|
value={null as unknown as string}
|
||||||
|
onClick={() => onChange(undefined)}
|
||||||
|
>
|
||||||
|
Internet Gateway
|
||||||
|
</SelectItem>
|
||||||
|
{gateways?.map((el) => (
|
||||||
|
<SelectItem value={el.id} key={el.id}>
|
||||||
|
{el.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
<SqlConnectionFields
|
<SqlConnectionFields
|
||||||
isPlatformManagedCredentials={isPlatformManagedCredentials}
|
isPlatformManagedCredentials={isPlatformManagedCredentials}
|
||||||
selectedTabIndex={selectedTabIndex}
|
selectedTabIndex={selectedTabIndex}
|
||||||
|
|||||||
+59
-1
@@ -1,10 +1,17 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { Button, FormControl, ModalClose, Select, SelectItem } from "@app/components/v2";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
|
import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2";
|
||||||
|
import {
|
||||||
|
OrgGatewayPermissionActions,
|
||||||
|
OrgPermissionSubjects
|
||||||
|
} from "@app/context/OrgPermissionContext/types";
|
||||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||||
|
import { gatewaysQueryKeys } from "@app/hooks/api";
|
||||||
import { MySqlConnectionMethod, TMySqlConnection } from "@app/hooks/api/appConnections";
|
import { MySqlConnectionMethod, TMySqlConnection } from "@app/hooks/api/appConnections";
|
||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
import { PlatformManagedConfirmationModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/shared/PlatformManagedConfirmationModal";
|
import { PlatformManagedConfirmationModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/shared/PlatformManagedConfirmationModal";
|
||||||
@@ -49,6 +56,7 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
app: AppConnection.MySql,
|
app: AppConnection.MySql,
|
||||||
method: MySqlConnectionMethod.UsernameAndPassword,
|
method: MySqlConnectionMethod.UsernameAndPassword,
|
||||||
credentials: {
|
credentials: {
|
||||||
|
gatewayId: "",
|
||||||
host: "",
|
host: "",
|
||||||
port: 3306,
|
port: 3306,
|
||||||
database: "default",
|
database: "default",
|
||||||
@@ -68,6 +76,7 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
} = form;
|
} = form;
|
||||||
|
|
||||||
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
|
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
|
||||||
|
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||||
|
|
||||||
const confirmSubmit = async (formData: FormData) => {
|
const confirmSubmit = async (formData: FormData) => {
|
||||||
if (formData.isPlatformManagedCredentials) {
|
if (formData.isPlatformManagedCredentials) {
|
||||||
@@ -118,6 +127,55 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgGatewayPermissionActions.AttachGateways}
|
||||||
|
a={OrgPermissionSubjects.Gateway}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="credentials.gatewayId"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Gateway"
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
isDisabled={isAllowed}
|
||||||
|
content="Restricted access. You don't have permission to attach gateways to resources."
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
value={value}
|
||||||
|
onValueChange={onChange}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
isLoading={isGatewaysLoading}
|
||||||
|
placeholder="Default: Internet Gateway"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
<SelectItem
|
||||||
|
value={null as unknown as string}
|
||||||
|
onClick={() => onChange(undefined)}
|
||||||
|
>
|
||||||
|
Internet Gateway
|
||||||
|
</SelectItem>
|
||||||
|
{gateways?.map((el) => (
|
||||||
|
<SelectItem value={el.id} key={el.id}>
|
||||||
|
{el.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
<SqlConnectionFields
|
<SqlConnectionFields
|
||||||
isPlatformManagedCredentials={isPlatformManagedCredentials}
|
isPlatformManagedCredentials={isPlatformManagedCredentials}
|
||||||
selectedTabIndex={selectedTabIndex}
|
selectedTabIndex={selectedTabIndex}
|
||||||
|
|||||||
+59
-1
@@ -1,10 +1,17 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { Button, FormControl, ModalClose, Select, SelectItem } from "@app/components/v2";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
|
import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2";
|
||||||
|
import {
|
||||||
|
OrgGatewayPermissionActions,
|
||||||
|
OrgPermissionSubjects
|
||||||
|
} from "@app/context/OrgPermissionContext/types";
|
||||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||||
|
import { gatewaysQueryKeys } from "@app/hooks/api";
|
||||||
import { OracleDBConnectionMethod, TOracleDBConnection } from "@app/hooks/api/appConnections";
|
import { OracleDBConnectionMethod, TOracleDBConnection } from "@app/hooks/api/appConnections";
|
||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
import { PlatformManagedConfirmationModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/shared/PlatformManagedConfirmationModal";
|
import { PlatformManagedConfirmationModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/shared/PlatformManagedConfirmationModal";
|
||||||
@@ -49,6 +56,7 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
app: AppConnection.OracleDB,
|
app: AppConnection.OracleDB,
|
||||||
method: OracleDBConnectionMethod.UsernameAndPassword,
|
method: OracleDBConnectionMethod.UsernameAndPassword,
|
||||||
credentials: {
|
credentials: {
|
||||||
|
gatewayId: "",
|
||||||
host: "",
|
host: "",
|
||||||
port: 1521,
|
port: 1521,
|
||||||
database: "ORCL", // Typically FREEPDB1 or ORCL
|
database: "ORCL", // Typically FREEPDB1 or ORCL
|
||||||
@@ -68,6 +76,7 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
} = form;
|
} = form;
|
||||||
|
|
||||||
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
|
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
|
||||||
|
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||||
|
|
||||||
const confirmSubmit = async (formData: FormData) => {
|
const confirmSubmit = async (formData: FormData) => {
|
||||||
if (formData.isPlatformManagedCredentials) {
|
if (formData.isPlatformManagedCredentials) {
|
||||||
@@ -118,6 +127,55 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgGatewayPermissionActions.AttachGateways}
|
||||||
|
a={OrgPermissionSubjects.Gateway}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="credentials.gatewayId"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Gateway"
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
isDisabled={isAllowed}
|
||||||
|
content="Restricted access. You don't have permission to attach gateways to resources."
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
value={value}
|
||||||
|
onValueChange={onChange}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
isLoading={isGatewaysLoading}
|
||||||
|
placeholder="Default: Internet Gateway"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
<SelectItem
|
||||||
|
value={null as unknown as string}
|
||||||
|
onClick={() => onChange(undefined)}
|
||||||
|
>
|
||||||
|
Internet Gateway
|
||||||
|
</SelectItem>
|
||||||
|
{gateways?.map((el) => (
|
||||||
|
<SelectItem value={el.id} key={el.id}>
|
||||||
|
{el.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
<SqlConnectionFields
|
<SqlConnectionFields
|
||||||
isPlatformManagedCredentials={isPlatformManagedCredentials}
|
isPlatformManagedCredentials={isPlatformManagedCredentials}
|
||||||
selectedTabIndex={selectedTabIndex}
|
selectedTabIndex={selectedTabIndex}
|
||||||
|
|||||||
+59
-1
@@ -1,10 +1,17 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { Button, FormControl, ModalClose, Select, SelectItem } from "@app/components/v2";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
|
import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2";
|
||||||
|
import {
|
||||||
|
OrgGatewayPermissionActions,
|
||||||
|
OrgPermissionSubjects
|
||||||
|
} from "@app/context/OrgPermissionContext/types";
|
||||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||||
|
import { gatewaysQueryKeys } from "@app/hooks/api";
|
||||||
import { PostgresConnectionMethod, TPostgresConnection } from "@app/hooks/api/appConnections";
|
import { PostgresConnectionMethod, TPostgresConnection } from "@app/hooks/api/appConnections";
|
||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
import { PlatformManagedConfirmationModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/shared/PlatformManagedConfirmationModal";
|
import { PlatformManagedConfirmationModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/shared/PlatformManagedConfirmationModal";
|
||||||
@@ -49,6 +56,7 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
app: AppConnection.Postgres,
|
app: AppConnection.Postgres,
|
||||||
method: PostgresConnectionMethod.UsernameAndPassword,
|
method: PostgresConnectionMethod.UsernameAndPassword,
|
||||||
credentials: {
|
credentials: {
|
||||||
|
gatewayId: "",
|
||||||
host: "",
|
host: "",
|
||||||
port: 5432,
|
port: 5432,
|
||||||
database: "default",
|
database: "default",
|
||||||
@@ -68,6 +76,7 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
} = form;
|
} = form;
|
||||||
|
|
||||||
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
|
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
|
||||||
|
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||||
|
|
||||||
const confirmSubmit = async (formData: FormData) => {
|
const confirmSubmit = async (formData: FormData) => {
|
||||||
if (formData.isPlatformManagedCredentials) {
|
if (formData.isPlatformManagedCredentials) {
|
||||||
@@ -118,6 +127,55 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgGatewayPermissionActions.AttachGateways}
|
||||||
|
a={OrgPermissionSubjects.Gateway}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="credentials.gatewayId"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Gateway"
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
isDisabled={isAllowed}
|
||||||
|
content="Restricted access. You don't have permission to attach gateways to resources."
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
value={value}
|
||||||
|
onValueChange={onChange}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
isLoading={isGatewaysLoading}
|
||||||
|
placeholder="Default: Internet Gateway"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
<SelectItem
|
||||||
|
value={null as unknown as string}
|
||||||
|
onClick={() => onChange(undefined)}
|
||||||
|
>
|
||||||
|
Internet Gateway
|
||||||
|
</SelectItem>
|
||||||
|
{gateways?.map((el) => (
|
||||||
|
<SelectItem value={el.id} key={el.id}>
|
||||||
|
{el.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
<SqlConnectionFields
|
<SqlConnectionFields
|
||||||
isPlatformManagedCredentials={isPlatformManagedCredentials}
|
isPlatformManagedCredentials={isPlatformManagedCredentials}
|
||||||
selectedTabIndex={selectedTabIndex}
|
selectedTabIndex={selectedTabIndex}
|
||||||
|
|||||||
+1
@@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const BaseSqlUsernameAndPasswordConnectionSchema = z.object({
|
export const BaseSqlUsernameAndPasswordConnectionSchema = z.object({
|
||||||
|
gatewayId: z.string().optional(),
|
||||||
host: z.string().trim().min(1, "Host required"),
|
host: z.string().trim().min(1, "Host required"),
|
||||||
port: z.coerce.number().default(5432),
|
port: z.coerce.number().default(5432),
|
||||||
database: z.string().trim().min(1, "Database required").default("default"),
|
database: z.string().trim().min(1, "Database required").default("default"),
|
||||||
|
|||||||
Reference in New Issue
Block a user