mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 18:41:21 +00:00
Add Azure Client Secrets App Connection
This commit is contained in:
@@ -8,6 +8,10 @@ import {
|
||||
AzureAppConfigurationConnectionListItemSchema,
|
||||
SanitizedAzureAppConfigurationConnectionSchema
|
||||
} from "@app/services/app-connection/azure-app-configuration";
|
||||
import {
|
||||
AzureClientSecretsConnectionListItemSchema,
|
||||
SanitizedAzureClientSecretsConnectionSchema
|
||||
} from "@app/services/app-connection/azure-client-secrets";
|
||||
import {
|
||||
AzureKeyVaultConnectionListItemSchema,
|
||||
SanitizedAzureKeyVaultConnectionSchema
|
||||
@@ -51,7 +55,8 @@ const SanitizedAppConnectionSchema = z.union([
|
||||
...SanitizedVercelConnectionSchema.options,
|
||||
...SanitizedPostgresConnectionSchema.options,
|
||||
...SanitizedMsSqlConnectionSchema.options,
|
||||
...SanitizedCamundaConnectionSchema.options
|
||||
...SanitizedCamundaConnectionSchema.options,
|
||||
...SanitizedAzureClientSecretsConnectionSchema.options
|
||||
]);
|
||||
|
||||
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
@@ -66,7 +71,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
VercelConnectionListItemSchema,
|
||||
PostgresConnectionListItemSchema,
|
||||
MsSqlConnectionListItemSchema,
|
||||
CamundaConnectionListItemSchema
|
||||
CamundaConnectionListItemSchema,
|
||||
AzureClientSecretsConnectionListItemSchema
|
||||
]);
|
||||
|
||||
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateAzureClientSecretsConnectionSchema,
|
||||
SanitizedAzureClientSecretsConnectionSchema,
|
||||
UpdateAzureClientSecretsConnectionSchema
|
||||
} from "@app/services/app-connection/azure-client-secrets";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
export const registerAzureClientSecretsConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.AzureClientSecrets,
|
||||
server,
|
||||
sanitizedResponseSchema: SanitizedAzureClientSecretsConnectionSchema,
|
||||
createSchema: CreateAzureClientSecretsConnectionSchema,
|
||||
updateSchema: UpdateAzureClientSecretsConnectionSchema
|
||||
});
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
|
||||
|
||||
import { registerAwsConnectionRouter } from "./aws-connection-router";
|
||||
import { registerAzureAppConfigurationConnectionRouter } from "./azure-app-configuration-connection-router";
|
||||
import { registerAzureClientSecretsConnectionRouter } from "./azure-client-secrets-connection-router";
|
||||
import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router";
|
||||
import { registerCamundaConnectionRouter } from "./camunda-connection-router";
|
||||
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
||||
@@ -28,5 +29,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
|
||||
[AppConnection.Vercel]: registerVercelConnectionRouter,
|
||||
[AppConnection.Postgres]: registerPostgresConnectionRouter,
|
||||
[AppConnection.MsSql]: registerMsSqlConnectionRouter,
|
||||
[AppConnection.Camunda]: registerCamundaConnectionRouter
|
||||
[AppConnection.Camunda]: registerCamundaConnectionRouter,
|
||||
[AppConnection.AzureClientSecrets]: registerAzureClientSecretsConnectionRouter
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ export enum AppConnection {
|
||||
GCP = "gcp",
|
||||
AzureKeyVault = "azure-key-vault",
|
||||
AzureAppConfiguration = "azure-app-configuration",
|
||||
AzureClientSecrets = "azure-client-secrets",
|
||||
Humanitec = "humanitec",
|
||||
TerraformCloud = "terraform-cloud",
|
||||
Vercel = "vercel",
|
||||
|
||||
@@ -22,6 +22,11 @@ import {
|
||||
getAzureAppConfigurationConnectionListItem,
|
||||
validateAzureAppConfigurationConnectionCredentials
|
||||
} from "./azure-app-configuration";
|
||||
import {
|
||||
AzureClientSecretsConnectionMethod,
|
||||
getAzureClientSecretsConnectionListItem,
|
||||
validateAzureClientSecretsConnectionCredentials
|
||||
} from "./azure-client-secrets";
|
||||
import {
|
||||
AzureKeyVaultConnectionMethod,
|
||||
getAzureKeyVaultConnectionListItem,
|
||||
@@ -63,7 +68,8 @@ export const listAppConnectionOptions = () => {
|
||||
getVercelConnectionListItem(),
|
||||
getPostgresConnectionListItem(),
|
||||
getMsSqlConnectionListItem(),
|
||||
getCamundaConnectionListItem()
|
||||
getCamundaConnectionListItem(),
|
||||
getAzureClientSecretsConnectionListItem()
|
||||
].sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
@@ -122,7 +128,9 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TAppConnect
|
||||
[AppConnection.MsSql]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.TerraformCloud]: validateTerraformCloudConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.Camunda]: validateCamundaConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.Vercel]: validateVercelConnectionCredentials as TAppConnectionCredentialsValidator
|
||||
[AppConnection.Vercel]: validateVercelConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.AzureClientSecrets]:
|
||||
validateAzureClientSecretsConnectionCredentials as TAppConnectionCredentialsValidator
|
||||
};
|
||||
|
||||
export const validateAppConnectionCredentials = async (
|
||||
@@ -135,6 +143,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
||||
return "GitHub App";
|
||||
case AzureKeyVaultConnectionMethod.OAuth:
|
||||
case AzureAppConfigurationConnectionMethod.OAuth:
|
||||
case AzureClientSecretsConnectionMethod.OAuth:
|
||||
case GitHubConnectionMethod.OAuth:
|
||||
return "OAuth";
|
||||
case AwsConnectionMethod.AccessKey:
|
||||
@@ -196,5 +205,6 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
|
||||
[AppConnection.MsSql]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform,
|
||||
[AppConnection.TerraformCloud]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.Camunda]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.Vercel]: platformManagedCredentialsNotSupported
|
||||
[AppConnection.Vercel]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.AzureClientSecrets]: platformManagedCredentialsNotSupported
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
||||
[AppConnection.GCP]: "GCP",
|
||||
[AppConnection.AzureKeyVault]: "Azure Key Vault",
|
||||
[AppConnection.AzureAppConfiguration]: "Azure App Configuration",
|
||||
[AppConnection.AzureClientSecrets]: "Azure Client Secrets",
|
||||
[AppConnection.Databricks]: "Databricks",
|
||||
[AppConnection.Humanitec]: "Humanitec",
|
||||
[AppConnection.TerraformCloud]: "Terraform Cloud",
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
import { ValidateAwsConnectionCredentialsSchema } from "./aws";
|
||||
import { awsConnectionService } from "./aws/aws-connection-service";
|
||||
import { ValidateAzureAppConfigurationConnectionCredentialsSchema } from "./azure-app-configuration";
|
||||
import { ValidateAzureClientSecretsConnectionCredentialsSchema } from "./azure-client-secrets";
|
||||
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
|
||||
import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
|
||||
import { camundaConnectionService } from "./camunda/camunda-connection-service";
|
||||
@@ -68,7 +69,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
||||
[AppConnection.Vercel]: ValidateVercelConnectionCredentialsSchema,
|
||||
[AppConnection.Postgres]: ValidatePostgresConnectionCredentialsSchema,
|
||||
[AppConnection.MsSql]: ValidateMsSqlConnectionCredentialsSchema,
|
||||
[AppConnection.Camunda]: ValidateCamundaConnectionCredentialsSchema
|
||||
[AppConnection.Camunda]: ValidateCamundaConnectionCredentialsSchema,
|
||||
[AppConnection.AzureClientSecrets]: ValidateAzureClientSecretsConnectionCredentialsSchema
|
||||
};
|
||||
|
||||
export const appConnectionServiceFactory = ({
|
||||
|
||||
@@ -15,6 +15,12 @@ import {
|
||||
TAzureAppConfigurationConnectionInput,
|
||||
TValidateAzureAppConfigurationConnectionCredentialsSchema
|
||||
} from "./azure-app-configuration";
|
||||
import {
|
||||
TAzureClientSecretsConnection,
|
||||
TAzureClientSecretsConnectionConfig,
|
||||
TAzureClientSecretsConnectionInput,
|
||||
TValidateAzureClientSecretsConnectionCredentialsSchema
|
||||
} from "./azure-client-secrets";
|
||||
import {
|
||||
TAzureKeyVaultConnection,
|
||||
TAzureKeyVaultConnectionConfig,
|
||||
@@ -83,6 +89,7 @@ export type TAppConnection = { id: string } & (
|
||||
| TPostgresConnection
|
||||
| TMsSqlConnection
|
||||
| TCamundaConnection
|
||||
| TAzureClientSecretsConnection
|
||||
);
|
||||
|
||||
export type TAppConnectionRaw = NonNullable<Awaited<ReturnType<TAppConnectionDALFactory["findById"]>>>;
|
||||
@@ -102,6 +109,7 @@ export type TAppConnectionInput = { id: string } & (
|
||||
| TPostgresConnectionInput
|
||||
| TMsSqlConnectionInput
|
||||
| TCamundaConnectionInput
|
||||
| TAzureClientSecretsConnectionInput
|
||||
);
|
||||
|
||||
export type TSqlConnectionInput = TPostgresConnectionInput | TMsSqlConnectionInput;
|
||||
@@ -126,7 +134,8 @@ export type TAppConnectionConfig =
|
||||
| TTerraformCloudConnectionConfig
|
||||
| TVercelConnectionConfig
|
||||
| TSqlConnectionConfig
|
||||
| TCamundaConnectionConfig;
|
||||
| TCamundaConnectionConfig
|
||||
| TAzureClientSecretsConnectionConfig;
|
||||
|
||||
export type TValidateAppConnectionCredentialsSchema =
|
||||
| TValidateAwsConnectionCredentialsSchema
|
||||
@@ -140,7 +149,8 @@ export type TValidateAppConnectionCredentialsSchema =
|
||||
| TValidateMsSqlConnectionCredentialsSchema
|
||||
| TValidateCamundaConnectionCredentialsSchema
|
||||
| TValidateTerraformCloudConnectionCredentialsSchema
|
||||
| TValidateVercelConnectionCredentialsSchema;
|
||||
| TValidateVercelConnectionCredentialsSchema
|
||||
| TValidateAzureClientSecretsConnectionCredentialsSchema;
|
||||
|
||||
export type TListAwsConnectionKmsKeys = {
|
||||
connectionId: string;
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export enum AzureClientSecretsConnectionMethod {
|
||||
OAuth = "oauth"
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
import { AxiosError, AxiosResponse } from "axios";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
||||
import { getAppConnectionMethodName } from "@app/services/app-connection/app-connection-fns";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { AzureClientSecretsConnectionMethod } from "./azure-client-secrets-connection-enums";
|
||||
import {
|
||||
ExchangeCodeAzureResponse,
|
||||
TAzureClientSecretsConnectionConfig
|
||||
} from "./azure-client-secrets-connection-types";
|
||||
|
||||
export const getAzureClientSecretsConnectionListItem = () => {
|
||||
const { INF_APP_CONNECTION_AZURE_CLIENT_ID } = getConfig();
|
||||
|
||||
return {
|
||||
name: "Azure Client Secrets" as const,
|
||||
app: AppConnection.AzureClientSecrets as const,
|
||||
methods: Object.values(AzureClientSecretsConnectionMethod) as [AzureClientSecretsConnectionMethod.OAuth],
|
||||
oauthClientId: INF_APP_CONNECTION_AZURE_CLIENT_ID
|
||||
};
|
||||
};
|
||||
|
||||
export const validateAzureClientSecretsConnectionCredentials = async (config: TAzureClientSecretsConnectionConfig) => {
|
||||
const { credentials: inputCredentials, method } = config;
|
||||
|
||||
const { INF_APP_CONNECTION_AZURE_CLIENT_ID, INF_APP_CONNECTION_AZURE_CLIENT_SECRET, SITE_URL } = getConfig();
|
||||
|
||||
if (!INF_APP_CONNECTION_AZURE_CLIENT_ID || !INF_APP_CONNECTION_AZURE_CLIENT_SECRET) {
|
||||
throw new InternalServerError({
|
||||
message: `Azure ${getAppConnectionMethodName(method)} environment variables have not been configured`
|
||||
});
|
||||
}
|
||||
|
||||
let tokenResp: AxiosResponse<ExchangeCodeAzureResponse> | null = null;
|
||||
let tokenError: AxiosError | null = null;
|
||||
|
||||
try {
|
||||
tokenResp = await request.post<ExchangeCodeAzureResponse>(
|
||||
IntegrationUrls.AZURE_TOKEN_URL.replace("common", inputCredentials.tenantId || "common"),
|
||||
new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
code: inputCredentials.code,
|
||||
scope: `openid offline_access https://azconfig.io/.default`,
|
||||
client_id: INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
||||
client_secret: INF_APP_CONNECTION_AZURE_CLIENT_SECRET,
|
||||
redirect_uri: `${SITE_URL}/organization/app-connections/azure/oauth/callback`
|
||||
})
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof AxiosError) {
|
||||
tokenError = e;
|
||||
} else {
|
||||
throw new BadRequestError({
|
||||
message: `Unable to validate connection: verify credentials`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenError) {
|
||||
if (tokenError instanceof AxiosError) {
|
||||
throw new BadRequestError({
|
||||
message: `Failed to get access token: ${
|
||||
(tokenError?.response?.data as { error_description?: string })?.error_description || "Unknown error"
|
||||
}`
|
||||
});
|
||||
} else {
|
||||
throw new InternalServerError({
|
||||
message: "Failed to get access token"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!tokenResp) {
|
||||
throw new InternalServerError({
|
||||
message: `Failed to get access token: Token was empty with no error`
|
||||
});
|
||||
}
|
||||
|
||||
switch (method) {
|
||||
case AzureClientSecretsConnectionMethod.OAuth:
|
||||
return {
|
||||
tenantId: inputCredentials.tenantId,
|
||||
accessToken: tokenResp.data.access_token,
|
||||
refreshToken: tokenResp.data.refresh_token,
|
||||
expiresAt: Date.now() + tokenResp.data.expires_in * 1000
|
||||
};
|
||||
default:
|
||||
throw new InternalServerError({
|
||||
message: `Unhandled Azure connection method: ${method as AzureClientSecretsConnectionMethod}`
|
||||
});
|
||||
}
|
||||
};
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
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 { AzureClientSecretsConnectionMethod } from "./azure-client-secrets-connection-enums";
|
||||
|
||||
export const AzureClientSecretsConnectionOAuthInputCredentialsSchema = z.object({
|
||||
code: z.string().trim().min(1, "OAuth code required"),
|
||||
tenantId: z.string().trim().optional()
|
||||
});
|
||||
|
||||
export const AzureClientSecretsConnectionOAuthOutputCredentialsSchema = z.object({
|
||||
tenantId: z.string().optional(),
|
||||
accessToken: z.string(),
|
||||
refreshToken: z.string(),
|
||||
expiresAt: z.number()
|
||||
});
|
||||
|
||||
export const ValidateAzureClientSecretsConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z
|
||||
.literal(AzureClientSecretsConnectionMethod.OAuth)
|
||||
.describe(AppConnections.CREATE(AppConnection.AzureClientSecrets).method),
|
||||
credentials: AzureClientSecretsConnectionOAuthInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.AzureClientSecrets).credentials
|
||||
)
|
||||
})
|
||||
]);
|
||||
|
||||
export const CreateAzureClientSecretsConnectionSchema = ValidateAzureClientSecretsConnectionCredentialsSchema.and(
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.AzureClientSecrets)
|
||||
);
|
||||
|
||||
export const UpdateAzureClientSecretsConnectionSchema = z
|
||||
.object({
|
||||
credentials: AzureClientSecretsConnectionOAuthInputCredentialsSchema.optional().describe(
|
||||
AppConnections.UPDATE(AppConnection.AzureClientSecrets).credentials
|
||||
)
|
||||
})
|
||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.AzureClientSecrets));
|
||||
|
||||
const BaseAzureClientSecretsConnectionSchema = BaseAppConnectionSchema.extend({
|
||||
app: z.literal(AppConnection.AzureClientSecrets)
|
||||
});
|
||||
|
||||
export const AzureClientSecretsConnectionSchema = z.intersection(
|
||||
BaseAzureClientSecretsConnectionSchema,
|
||||
z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(AzureClientSecretsConnectionMethod.OAuth),
|
||||
credentials: AzureClientSecretsConnectionOAuthOutputCredentialsSchema
|
||||
})
|
||||
])
|
||||
);
|
||||
|
||||
export const SanitizedAzureClientSecretsConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseAzureClientSecretsConnectionSchema.extend({
|
||||
method: z.literal(AzureClientSecretsConnectionMethod.OAuth),
|
||||
credentials: AzureClientSecretsConnectionOAuthOutputCredentialsSchema.pick({
|
||||
tenantId: true
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
export const AzureClientSecretsConnectionListItemSchema = z.object({
|
||||
name: z.literal("Azure Client Secrets"),
|
||||
app: z.literal(AppConnection.AzureClientSecrets),
|
||||
methods: z.nativeEnum(AzureClientSecretsConnectionMethod).array(),
|
||||
oauthClientId: z.string().optional()
|
||||
});
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import z from "zod";
|
||||
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import {
|
||||
AzureClientSecretsConnectionOAuthOutputCredentialsSchema,
|
||||
AzureClientSecretsConnectionSchema,
|
||||
CreateAzureClientSecretsConnectionSchema,
|
||||
ValidateAzureClientSecretsConnectionCredentialsSchema
|
||||
} from "./azure-client-secrets-connection-schemas";
|
||||
|
||||
export type TAzureClientSecretsConnection = z.infer<typeof AzureClientSecretsConnectionSchema>;
|
||||
|
||||
export type TAzureClientSecretsConnectionInput = z.infer<typeof CreateAzureClientSecretsConnectionSchema> & {
|
||||
app: AppConnection.AzureClientSecrets;
|
||||
};
|
||||
|
||||
export type TValidateAzureClientSecretsConnectionCredentialsSchema =
|
||||
typeof ValidateAzureClientSecretsConnectionCredentialsSchema;
|
||||
|
||||
export type TAzureClientSecretsConnectionConfig = DiscriminativePick<
|
||||
TAzureClientSecretsConnectionInput,
|
||||
"method" | "app" | "credentials"
|
||||
> & {
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export type ExchangeCodeAzureResponse = {
|
||||
token_type: string;
|
||||
scope: string;
|
||||
expires_in: number;
|
||||
ext_expires_in: number;
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
id_token: string;
|
||||
};
|
||||
|
||||
export type TAzureClientSecretsConnectionCredentials = z.infer<
|
||||
typeof AzureClientSecretsConnectionOAuthOutputCredentialsSchema
|
||||
>;
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "./azure-client-secrets-connection-enums";
|
||||
export * from "./azure-client-secrets-connection-fns";
|
||||
export * from "./azure-client-secrets-connection-schemas";
|
||||
export * from "./azure-client-secrets-connection-types";
|
||||
Reference in New Issue
Block a user