mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 12:27:28 +00:00
initial configuration for tests
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
import ldap from "ldapjs";
|
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 { 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 { logger } from "@app/lib/logger";
|
||||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
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 { LdapConnectionMethod } from "./ldap-connection-enums";
|
||||||
import { TLdapConnectionConfig } from "./ldap-connection-types";
|
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 = () => {
|
export const getLdapConnectionListItem = () => {
|
||||||
return {
|
return {
|
||||||
name: "LDAP" as const,
|
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) => {
|
export const validateLdapConnectionCredentials = async ({ credentials }: TLdapConnectionConfig) => {
|
||||||
let client: ldap.Client | undefined;
|
let client: ldap.Client | undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ export const ValidateLdapConnectionCredentialsSchema = z.discriminatedUnion("met
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const CreateLdapConnectionSchema = ValidateLdapConnectionCredentialsSchema.and(
|
export const CreateLdapConnectionSchema = ValidateLdapConnectionCredentialsSchema.and(
|
||||||
GenericCreateAppConnectionFieldsSchema(AppConnection.LDAP)
|
GenericCreateAppConnectionFieldsSchema(AppConnection.LDAP, {
|
||||||
|
supportsGateways: true
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export const UpdateLdapConnectionSchema = z
|
export const UpdateLdapConnectionSchema = z
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export type TLdapConnectionInput = z.infer<typeof CreateLdapConnectionSchema> &
|
|||||||
|
|
||||||
export type TValidateLdapConnectionCredentialsSchema = typeof ValidateLdapConnectionCredentialsSchema;
|
export type TValidateLdapConnectionCredentialsSchema = typeof ValidateLdapConnectionCredentialsSchema;
|
||||||
|
|
||||||
export type TLdapConnectionConfig = DiscriminativePick<TLdapConnection, "method" | "app" | "credentials"> & {
|
export type TLdapConnectionConfig = DiscriminativePick<
|
||||||
|
TLdapConnectionInput,
|
||||||
|
"method" | "app" | "credentials" | "gatewayId"
|
||||||
|
> & {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|||||||
+59
@@ -4,8 +4,10 @@ import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { Tab } from "@headlessui/react";
|
import { Tab } from "@headlessui/react";
|
||||||
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 { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -18,8 +20,11 @@ import {
|
|||||||
TextArea,
|
TextArea,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} 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 { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||||
import { DistinguishedNameRegex, UserPrincipalNameRegex } from "@app/helpers/string";
|
import { DistinguishedNameRegex, UserPrincipalNameRegex } from "@app/helpers/string";
|
||||||
|
import { gatewaysQueryKeys } from "@app/hooks/api";
|
||||||
import {
|
import {
|
||||||
LdapConnectionMethod,
|
LdapConnectionMethod,
|
||||||
LdapConnectionProvider,
|
LdapConnectionProvider,
|
||||||
@@ -84,6 +89,7 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
defaultValues: appConnection ?? {
|
defaultValues: appConnection ?? {
|
||||||
app: AppConnection.LDAP,
|
app: AppConnection.LDAP,
|
||||||
method: LdapConnectionMethod.SimpleBind,
|
method: LdapConnectionMethod.SimpleBind,
|
||||||
|
gatewayId: null,
|
||||||
credentials: {
|
credentials: {
|
||||||
provider: LdapConnectionProvider.ActiveDirectory,
|
provider: LdapConnectionProvider.ActiveDirectory,
|
||||||
url: "",
|
url: "",
|
||||||
@@ -104,6 +110,8 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
|
|
||||||
const selectedProvider = watch("credentials.provider");
|
const selectedProvider = watch("credentials.provider");
|
||||||
const sslEnabled = watch("credentials.url")?.startsWith("ldaps://") ?? false;
|
const sslEnabled = watch("credentials.url")?.startsWith("ldaps://") ?? false;
|
||||||
|
const { subscription } = useSubscription();
|
||||||
|
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
@@ -114,6 +122,57 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isUpdate && <GenericAppConnectionsFields />}
|
{!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">
|
<div className="grid grid-cols-2 items-center gap-2">
|
||||||
<Controller
|
<Controller
|
||||||
name="method"
|
name="method"
|
||||||
|
|||||||
Reference in New Issue
Block a user