mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 14:27:23 +00:00
Add Humanitec secret sync integration
This commit is contained in:
@@ -4,7 +4,8 @@ export enum AppConnection {
|
||||
Databricks = "databricks",
|
||||
GCP = "gcp",
|
||||
AzureKeyVault = "azure-key-vault",
|
||||
AzureAppConfiguration = "azure-app-configuration"
|
||||
AzureAppConfiguration = "azure-app-configuration",
|
||||
Humanitec = "humanitec"
|
||||
}
|
||||
|
||||
export enum AWSRegion {
|
||||
|
||||
@@ -35,6 +35,11 @@ import {
|
||||
getAzureKeyVaultConnectionListItem,
|
||||
validateAzureKeyVaultConnectionCredentials
|
||||
} from "./azure-key-vault";
|
||||
import {
|
||||
getHumanitecConnectionListItem,
|
||||
HumanitecConnectionMethod,
|
||||
validateHumanitecConnectionCredentials
|
||||
} from "./humanitec";
|
||||
|
||||
export const listAppConnectionOptions = () => {
|
||||
return [
|
||||
@@ -43,7 +48,8 @@ export const listAppConnectionOptions = () => {
|
||||
getGcpConnectionListItem(),
|
||||
getAzureKeyVaultConnectionListItem(),
|
||||
getAzureAppConfigurationConnectionListItem(),
|
||||
getDatabricksConnectionListItem()
|
||||
getDatabricksConnectionListItem(),
|
||||
getHumanitecConnectionListItem()
|
||||
].sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
@@ -106,6 +112,8 @@ export const validateAppConnectionCredentials = async (
|
||||
return validateAzureKeyVaultConnectionCredentials(appConnection);
|
||||
case AppConnection.AzureAppConfiguration:
|
||||
return validateAzureAppConfigurationConnectionCredentials(appConnection);
|
||||
case AppConnection.Humanitec:
|
||||
return validateHumanitecConnectionCredentials(appConnection);
|
||||
default:
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
throw new Error(`Unhandled App Connection ${app}`);
|
||||
@@ -128,6 +136,8 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
||||
return "Service Account Impersonation";
|
||||
case DatabricksConnectionMethod.ServicePrincipal:
|
||||
return "Service Principal";
|
||||
case HumanitecConnectionMethod.AccessKey:
|
||||
return "Access Key";
|
||||
default:
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
throw new Error(`Unhandled App Connection Method: ${method}`);
|
||||
|
||||
@@ -6,5 +6,6 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
||||
[AppConnection.GCP]: "GCP",
|
||||
[AppConnection.AzureKeyVault]: "Azure Key Vault",
|
||||
[AppConnection.AzureAppConfiguration]: "Azure App Configuration",
|
||||
[AppConnection.Databricks]: "Databricks"
|
||||
[AppConnection.Databricks]: "Databricks",
|
||||
[AppConnection.Humanitec]: "Humanitec"
|
||||
};
|
||||
|
||||
@@ -35,6 +35,8 @@ import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
|
||||
import { gcpConnectionService } from "./gcp/gcp-connection-service";
|
||||
import { ValidateGitHubConnectionCredentialsSchema } from "./github";
|
||||
import { githubConnectionService } from "./github/github-connection-service";
|
||||
import { ValidateHumanitecConnectionCredentialsSchema } from "./humanitec";
|
||||
import { humanitecConnectionService } from "./humanitec/humanitec-connection-service";
|
||||
|
||||
export type TAppConnectionServiceFactoryDep = {
|
||||
appConnectionDAL: TAppConnectionDALFactory;
|
||||
@@ -50,7 +52,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
||||
[AppConnection.GCP]: ValidateGcpConnectionCredentialsSchema,
|
||||
[AppConnection.AzureKeyVault]: ValidateAzureKeyVaultConnectionCredentialsSchema,
|
||||
[AppConnection.AzureAppConfiguration]: ValidateAzureAppConfigurationConnectionCredentialsSchema,
|
||||
[AppConnection.Databricks]: ValidateDatabricksConnectionCredentialsSchema
|
||||
[AppConnection.Databricks]: ValidateDatabricksConnectionCredentialsSchema,
|
||||
[AppConnection.Humanitec]: ValidateHumanitecConnectionCredentialsSchema
|
||||
};
|
||||
|
||||
export const appConnectionServiceFactory = ({
|
||||
@@ -371,6 +374,7 @@ export const appConnectionServiceFactory = ({
|
||||
github: githubConnectionService(connectAppConnectionById),
|
||||
gcp: gcpConnectionService(connectAppConnectionById),
|
||||
databricks: databricksConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
||||
aws: awsConnectionService(connectAppConnectionById)
|
||||
aws: awsConnectionService(connectAppConnectionById),
|
||||
humanitec: humanitecConnectionService(connectAppConnectionById)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -32,6 +32,12 @@ import {
|
||||
TValidateAzureKeyVaultConnectionCredentials
|
||||
} from "./azure-key-vault";
|
||||
import { TGcpConnection, TGcpConnectionConfig, TGcpConnectionInput, TValidateGcpConnectionCredentials } from "./gcp";
|
||||
import {
|
||||
THumanitecConnection,
|
||||
THumanitecConnectionConfig,
|
||||
THumanitecConnectionInput,
|
||||
TValidateHumanitecConnectionCredentials
|
||||
} from "./humanitec";
|
||||
|
||||
export type TAppConnection = { id: string } & (
|
||||
| TAwsConnection
|
||||
@@ -40,6 +46,7 @@ export type TAppConnection = { id: string } & (
|
||||
| TAzureKeyVaultConnection
|
||||
| TAzureAppConfigurationConnection
|
||||
| TDatabricksConnection
|
||||
| THumanitecConnection
|
||||
);
|
||||
|
||||
export type TAppConnectionInput = { id: string } & (
|
||||
@@ -49,6 +56,7 @@ export type TAppConnectionInput = { id: string } & (
|
||||
| TAzureKeyVaultConnectionInput
|
||||
| TAzureAppConfigurationConnectionInput
|
||||
| TDatabricksConnectionInput
|
||||
| THumanitecConnectionInput
|
||||
);
|
||||
|
||||
export type TCreateAppConnectionDTO = Pick<
|
||||
@@ -66,7 +74,8 @@ export type TAppConnectionConfig =
|
||||
| TGcpConnectionConfig
|
||||
| TAzureKeyVaultConnectionConfig
|
||||
| TAzureAppConfigurationConnectionConfig
|
||||
| TDatabricksConnectionConfig;
|
||||
| TDatabricksConnectionConfig
|
||||
| THumanitecConnectionConfig;
|
||||
|
||||
export type TValidateAppConnectionCredentials =
|
||||
| TValidateAwsConnectionCredentials
|
||||
@@ -74,7 +83,8 @@ export type TValidateAppConnectionCredentials =
|
||||
| TValidateGcpConnectionCredentials
|
||||
| TValidateAzureKeyVaultConnectionCredentials
|
||||
| TValidateAzureAppConfigurationConnectionCredentials
|
||||
| TValidateDatabricksConnectionCredentials;
|
||||
| TValidateDatabricksConnectionCredentials
|
||||
| TValidateHumanitecConnectionCredentials;
|
||||
|
||||
export type TListAwsConnectionKmsKeys = {
|
||||
connectionId: string;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export enum HumanitecConnectionMethod {
|
||||
AccessKey = "access-key"
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { AxiosError, AxiosResponse } from "axios";
|
||||
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
|
||||
import { HumanitecConnectionMethod } from "./humanitec-connection-enums";
|
||||
import {
|
||||
HumanitecApp,
|
||||
HumanitecOrg,
|
||||
HumanitecOrgWithApps,
|
||||
THumanitecConnection,
|
||||
THumanitecConnectionConfig
|
||||
} from "./humanitec-connection-types";
|
||||
|
||||
export const getHumanitecConnectionListItem = () => {
|
||||
return {
|
||||
name: "Humanitec" as const,
|
||||
app: AppConnection.Humanitec as const,
|
||||
methods: Object.values(HumanitecConnectionMethod) as [HumanitecConnectionMethod.AccessKey]
|
||||
};
|
||||
};
|
||||
|
||||
export const validateHumanitecConnectionCredentials = async (config: THumanitecConnectionConfig) => {
|
||||
const { credentials: inputCredentials } = config;
|
||||
|
||||
let response: AxiosResponse<HumanitecOrg[]> | null = null;
|
||||
|
||||
try {
|
||||
response = await request.get<HumanitecOrg[]>(`${IntegrationUrls.HUMANITEC_API_URL}/orgs`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${inputCredentials.accessKeyId}`
|
||||
}
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof AxiosError) {
|
||||
throw new BadRequestError({
|
||||
message: `Failed to validate credentials: ${error.response?.data || "Unknown error"}`
|
||||
});
|
||||
}
|
||||
throw new BadRequestError({
|
||||
message: "Unable to validate connection - verify credentials"
|
||||
});
|
||||
}
|
||||
|
||||
if (!response?.data) {
|
||||
throw new InternalServerError({
|
||||
message: "Failed to get organizations: Response was empty"
|
||||
});
|
||||
}
|
||||
|
||||
return inputCredentials;
|
||||
};
|
||||
|
||||
export const listOrganizations = async (appConnection: THumanitecConnection): Promise<HumanitecOrgWithApps[]> => {
|
||||
const {
|
||||
credentials: { accessKeyId }
|
||||
} = appConnection;
|
||||
const response = await request.get<HumanitecOrg[]>(`${IntegrationUrls.HUMANITEC_API_URL}/orgs`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessKeyId}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.data) {
|
||||
throw new InternalServerError({
|
||||
message: "Failed to get organizations: Response was empty"
|
||||
});
|
||||
}
|
||||
|
||||
const orgs = response.data;
|
||||
const appPromises = orgs.map(async (org) => {
|
||||
return request.get<HumanitecApp[]>(`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${org.id}/apps`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessKeyId}`
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const appsResponses = await Promise.all(appPromises);
|
||||
|
||||
const orgsWithApps: HumanitecOrgWithApps[] = orgs.map((org, index) => {
|
||||
if (!appsResponses[index].data) {
|
||||
throw new InternalServerError({
|
||||
message: "Failed to get apps for organization: Response was empty"
|
||||
});
|
||||
}
|
||||
|
||||
const apps = appsResponses[index].data;
|
||||
return {
|
||||
...org,
|
||||
apps: apps.map((app) => ({
|
||||
name: app.name,
|
||||
id: app.id,
|
||||
envs: app.envs
|
||||
}))
|
||||
};
|
||||
});
|
||||
return orgsWithApps;
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
import z from "zod";
|
||||
|
||||
import { AppConnections } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
BaseAppConnectionSchema,
|
||||
GenericCreateAppConnectionFieldsSchema,
|
||||
GenericUpdateAppConnectionFieldsSchema
|
||||
} from "@app/services/app-connection/app-connection-schemas";
|
||||
|
||||
import { HumanitecConnectionMethod } from "./humanitec-connection-enums";
|
||||
|
||||
export const HumanitecConnectionAccessTokenCredentialsSchema = z.object({
|
||||
accessKeyId: z.string().trim().min(1, "Access Key ID required")
|
||||
});
|
||||
|
||||
const BaseHumanitecConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Humanitec) });
|
||||
|
||||
export const HumanitecConnectionSchema = BaseHumanitecConnectionSchema.extend({
|
||||
method: z.literal(HumanitecConnectionMethod.AccessKey),
|
||||
credentials: HumanitecConnectionAccessTokenCredentialsSchema
|
||||
});
|
||||
|
||||
export const SanitizedHumanitecConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseHumanitecConnectionSchema.extend({
|
||||
method: z.literal(HumanitecConnectionMethod.AccessKey),
|
||||
credentials: HumanitecConnectionAccessTokenCredentialsSchema.pick({})
|
||||
})
|
||||
]);
|
||||
|
||||
export const ValidateHumanitecConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z
|
||||
.literal(HumanitecConnectionMethod.AccessKey)
|
||||
.describe(AppConnections?.CREATE(AppConnection.Humanitec).method),
|
||||
credentials: HumanitecConnectionAccessTokenCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.Humanitec).credentials
|
||||
)
|
||||
})
|
||||
]);
|
||||
|
||||
export const CreateHumanitecConnectionSchema = ValidateHumanitecConnectionCredentialsSchema.and(
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.Humanitec)
|
||||
);
|
||||
|
||||
export const UpdateHumanitecConnectionSchema = z
|
||||
.object({
|
||||
credentials: HumanitecConnectionAccessTokenCredentialsSchema.optional().describe(
|
||||
AppConnections.UPDATE(AppConnection.Humanitec).credentials
|
||||
)
|
||||
})
|
||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Humanitec));
|
||||
|
||||
export const HumanitecConnectionListItemSchema = z.object({
|
||||
name: z.literal("Humanitec"),
|
||||
app: z.literal(AppConnection.Humanitec),
|
||||
methods: z.nativeEnum(HumanitecConnectionMethod).array()
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { listOrganizations as getHumanitecOrganizations } from "./humanitec-connection-fns";
|
||||
import { THumanitecConnection } from "./humanitec-connection-types";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
app: AppConnection,
|
||||
connectionId: string,
|
||||
actor: OrgServiceActor
|
||||
) => Promise<THumanitecConnection>;
|
||||
|
||||
export const humanitecConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
||||
const listOrganizations = async (connectionId: string, actor: OrgServiceActor) => {
|
||||
const appConnection = await getAppConnection(AppConnection.Humanitec, connectionId, actor);
|
||||
try {
|
||||
const organizations = await getHumanitecOrganizations(appConnection);
|
||||
return organizations;
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
listOrganizations
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import z from "zod";
|
||||
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import {
|
||||
CreateHumanitecConnectionSchema,
|
||||
HumanitecConnectionSchema,
|
||||
ValidateHumanitecConnectionCredentialsSchema
|
||||
} from "./humanitec-connection-schemas";
|
||||
|
||||
export type THumanitecConnection = z.infer<typeof HumanitecConnectionSchema>;
|
||||
|
||||
export type THumanitecConnectionInput = z.infer<typeof CreateHumanitecConnectionSchema> & {
|
||||
app: AppConnection.Humanitec;
|
||||
};
|
||||
|
||||
export type TValidateHumanitecConnectionCredentials = typeof ValidateHumanitecConnectionCredentialsSchema;
|
||||
|
||||
export type THumanitecConnectionConfig = DiscriminativePick<
|
||||
THumanitecConnectionInput,
|
||||
"method" | "app" | "credentials"
|
||||
> & {
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export type HumanitecOrg = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type HumanitecApp = {
|
||||
name: string;
|
||||
id: string;
|
||||
envs: { name: string; id: string }[];
|
||||
};
|
||||
|
||||
export type HumanitecOrgWithApps = HumanitecOrg & {
|
||||
apps: HumanitecApp[];
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "./humanitec-connection-enums";
|
||||
export * from "./humanitec-connection-fns";
|
||||
export * from "./humanitec-connection-schemas";
|
||||
export * from "./humanitec-connection-types";
|
||||
Reference in New Issue
Block a user