mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 09:28:06 +00:00
initial configuration for tests
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import ldap from "ldapjs";
|
||||
|
||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
@@ -8,6 +12,22 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
|
||||
import { LdapConnectionMethod } from "./ldap-connection-enums";
|
||||
import { TLdapConnectionConfig } from "./ldap-connection-types";
|
||||
|
||||
const parseLdapUrl = (url: string): { protocol: string; host: string; port: number } => {
|
||||
const urlObj = new URL(url);
|
||||
const isSSL = urlObj.protocol === "ldaps:";
|
||||
const defaultPort = isSSL ? 636 : 389;
|
||||
|
||||
return {
|
||||
protocol: urlObj.protocol.replace(":", ""),
|
||||
host: urlObj.hostname,
|
||||
port: urlObj.port ? parseInt(urlObj.port, 10) : defaultPort
|
||||
};
|
||||
};
|
||||
|
||||
const constructLdapUrl = (protocol: string, host: string, port: number): string => {
|
||||
return `${protocol}://${host}:${port}`;
|
||||
};
|
||||
|
||||
export const getLdapConnectionListItem = () => {
|
||||
return {
|
||||
name: "LDAP" as const,
|
||||
@@ -80,6 +100,183 @@ export const getLdapConnectionClient = async ({
|
||||
});
|
||||
};
|
||||
|
||||
export const executeWithPotentialGateway = async <T>(
|
||||
config: TLdapConnectionConfig,
|
||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
|
||||
operation: (client: ldap.Client) => Promise<T>
|
||||
): Promise<T> => {
|
||||
const { gatewayId, credentials } = config;
|
||||
const { protocol, host, port } = parseLdapUrl(credentials.url);
|
||||
|
||||
if (gatewayId && gatewayService && gatewayV2Service) {
|
||||
const platformConnectionDetails = await gatewayV2Service.getPlatformConnectionDetailsByGatewayId({
|
||||
gatewayId,
|
||||
targetHost: host,
|
||||
targetPort: port
|
||||
});
|
||||
|
||||
if (platformConnectionDetails) {
|
||||
return withGatewayV2Proxy(
|
||||
async (proxyPort) => {
|
||||
const proxyUrl = constructLdapUrl(protocol, "localhost", proxyPort);
|
||||
const isSSL = protocol === "ldaps";
|
||||
const client = ldap.createClient({
|
||||
url: proxyUrl,
|
||||
timeout: LDAP_TIMEOUT,
|
||||
connectTimeout: LDAP_TIMEOUT,
|
||||
tlsOptions: isSSL
|
||||
? {
|
||||
rejectUnauthorized: sslRejectUnauthorized,
|
||||
ca: sslCertificate ? [sslCertificate] : undefined
|
||||
}
|
||||
: undefined
|
||||
});
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
client.on("error", (err: Error) => {
|
||||
logger.error(err, "LDAP Error");
|
||||
client.destroy();
|
||||
reject(new Error(`Provider Error - ${err.message}`));
|
||||
});
|
||||
|
||||
client.on("connectError", (err: Error) => {
|
||||
logger.error(err, "LDAP Connection Error");
|
||||
client.destroy();
|
||||
reject(new Error(`Provider Connect Error - ${err.message}`));
|
||||
});
|
||||
|
||||
client.on("connectRefused", (err: Error) => {
|
||||
logger.error(err, "LDAP Connection Refused");
|
||||
client.destroy();
|
||||
reject(new Error(`Provider Connection Refused - ${err.message}`));
|
||||
});
|
||||
|
||||
client.on("connectTimeout", (err: Error) => {
|
||||
logger.error(err, "LDAP Connection Timeout");
|
||||
client.destroy();
|
||||
reject(new Error(`Provider Connection Timeout - ${err.message}`));
|
||||
});
|
||||
|
||||
client.on("connect", () => {
|
||||
client.bind(credentials.dn, credentials.password, async (err) => {
|
||||
if (err) {
|
||||
logger.error(err, "LDAP Bind Error");
|
||||
client.destroy();
|
||||
reject(new Error(`Bind Error: ${err.message}`));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await operation(client);
|
||||
resolve(result);
|
||||
} catch (opError) {
|
||||
reject(opError);
|
||||
} finally {
|
||||
client.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
{
|
||||
protocol: GatewayProxyProtocol.Tcp,
|
||||
relayHost: platformConnectionDetails.relayHost,
|
||||
gateway: platformConnectionDetails.gateway,
|
||||
relay: platformConnectionDetails.relay
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(gatewayId);
|
||||
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||
return withGatewayProxy(
|
||||
async (proxyPort) => {
|
||||
const proxyUrl = constructLdapUrl(protocol, "localhost", proxyPort);
|
||||
const isSSL = protocol === "ldaps";
|
||||
const client = ldap.createClient({
|
||||
url: proxyUrl,
|
||||
timeout: LDAP_TIMEOUT,
|
||||
connectTimeout: LDAP_TIMEOUT,
|
||||
tlsOptions: isSSL
|
||||
? {
|
||||
rejectUnauthorized: sslRejectUnauthorized,
|
||||
ca: sslCertificate ? [sslCertificate] : undefined
|
||||
}
|
||||
: undefined
|
||||
});
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
client.on("error", (err: Error) => {
|
||||
logger.error(err, "LDAP Error");
|
||||
client.destroy();
|
||||
reject(new Error(`Provider Error - ${err.message}`));
|
||||
});
|
||||
|
||||
client.on("connectError", (err: Error) => {
|
||||
logger.error(err, "LDAP Connection Error");
|
||||
client.destroy();
|
||||
reject(new Error(`Provider Connect Error - ${err.message}`));
|
||||
});
|
||||
|
||||
client.on("connectRefused", (err: Error) => {
|
||||
logger.error(err, "LDAP Connection Refused");
|
||||
client.destroy();
|
||||
reject(new Error(`Provider Connection Refused - ${err.message}`));
|
||||
});
|
||||
|
||||
client.on("connectTimeout", (err: Error) => {
|
||||
logger.error(err, "LDAP Connection Timeout");
|
||||
client.destroy();
|
||||
reject(new Error(`Provider Connection Timeout - ${err.message}`));
|
||||
});
|
||||
|
||||
client.on("connect", () => {
|
||||
client.bind(credentials.dn, credentials.password, async (err) => {
|
||||
if (err) {
|
||||
logger.error(err, "LDAP Bind Error");
|
||||
client.destroy();
|
||||
reject(new Error(`Bind Error: ${err.message}`));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await operation(client);
|
||||
resolve(result);
|
||||
} catch (opError) {
|
||||
reject(opError);
|
||||
} finally {
|
||||
client.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
{
|
||||
protocol: GatewayProxyProtocol.Tcp,
|
||||
targetHost: host,
|
||||
targetPort: 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 - calls getLdapConnectionClient which has validation
|
||||
const client = await getLdapConnectionClient(credentials);
|
||||
try {
|
||||
return await operation(client);
|
||||
} finally {
|
||||
client.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
export const validateLdapConnectionCredentials = async ({ credentials }: TLdapConnectionConfig) => {
|
||||
let client: ldap.Client | undefined;
|
||||
|
||||
|
||||
@@ -74,7 +74,9 @@ export const ValidateLdapConnectionCredentialsSchema = z.discriminatedUnion("met
|
||||
]);
|
||||
|
||||
export const CreateLdapConnectionSchema = ValidateLdapConnectionCredentialsSchema.and(
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.LDAP)
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.LDAP, {
|
||||
supportsGateways: true
|
||||
})
|
||||
);
|
||||
|
||||
export const UpdateLdapConnectionSchema = z
|
||||
|
||||
@@ -17,6 +17,9 @@ export type TLdapConnectionInput = z.infer<typeof CreateLdapConnectionSchema> &
|
||||
|
||||
export type TValidateLdapConnectionCredentialsSchema = typeof ValidateLdapConnectionCredentialsSchema;
|
||||
|
||||
export type TLdapConnectionConfig = DiscriminativePick<TLdapConnection, "method" | "app" | "credentials"> & {
|
||||
export type TLdapConnectionConfig = DiscriminativePick<
|
||||
TLdapConnectionInput,
|
||||
"method" | "app" | "credentials" | "gatewayId"
|
||||
> & {
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
+59
@@ -4,8 +4,10 @@ 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 { useQuery } from "@tanstack/react-query";
|
||||
import { z } from "zod";
|
||||
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
@@ -18,8 +20,11 @@ import {
|
||||
TextArea,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionSubjects, useSubscription } from "@app/context";
|
||||
import { OrgGatewayPermissionActions } from "@app/context/OrgPermissionContext/types";
|
||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||
import { DistinguishedNameRegex, UserPrincipalNameRegex } from "@app/helpers/string";
|
||||
import { gatewaysQueryKeys } from "@app/hooks/api";
|
||||
import {
|
||||
LdapConnectionMethod,
|
||||
LdapConnectionProvider,
|
||||
@@ -84,6 +89,7 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
defaultValues: appConnection ?? {
|
||||
app: AppConnection.LDAP,
|
||||
method: LdapConnectionMethod.SimpleBind,
|
||||
gatewayId: null,
|
||||
credentials: {
|
||||
provider: LdapConnectionProvider.ActiveDirectory,
|
||||
url: "",
|
||||
@@ -104,6 +110,8 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
|
||||
const selectedProvider = watch("credentials.provider");
|
||||
const sslEnabled = watch("credentials.url")?.startsWith("ldaps://") ?? false;
|
||||
const { subscription } = useSubscription();
|
||||
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
@@ -114,6 +122,57 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
}}
|
||||
>
|
||||
{!isUpdate && <GenericAppConnectionsFields />}
|
||||
{subscription.gateway && (
|
||||
<OrgPermissionCan
|
||||
I={OrgGatewayPermissionActions.AttachGateways}
|
||||
a={OrgPermissionSubjects.Gateway}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Controller
|
||||
control={control}
|
||||
name="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 as string}
|
||||
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>
|
||||
)}
|
||||
<div className="grid grid-cols-2 items-center gap-2">
|
||||
<Controller
|
||||
name="method"
|
||||
|
||||
Reference in New Issue
Block a user