mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 20:25:43 +00:00
merge conflicts
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -23,6 +23,11 @@ import {
|
||||
getAzureAppConfigurationConnectionListItem,
|
||||
validateAzureAppConfigurationConnectionCredentials
|
||||
} from "./azure-app-configuration";
|
||||
import {
|
||||
AzureClientSecretsConnectionMethod,
|
||||
getAzureClientSecretsConnectionListItem,
|
||||
validateAzureClientSecretsConnectionCredentials
|
||||
} from "./azure-client-secrets";
|
||||
import {
|
||||
AzureKeyVaultConnectionMethod,
|
||||
getAzureKeyVaultConnectionListItem,
|
||||
@@ -81,6 +86,7 @@ export const listAppConnectionOptions = () => {
|
||||
getPostgresConnectionListItem(),
|
||||
getMsSqlConnectionListItem(),
|
||||
getCamundaConnectionListItem(),
|
||||
getAzureClientSecretsConnectionListItem(),
|
||||
getWindmillConnectionListItem(),
|
||||
getAuth0ConnectionListItem(),
|
||||
getHCVaultConnectionListItem(),
|
||||
@@ -142,6 +148,8 @@ export const validateAppConnectionCredentials = async (
|
||||
[AppConnection.AzureKeyVault]: validateAzureKeyVaultConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.AzureAppConfiguration]:
|
||||
validateAzureAppConfigurationConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.AzureClientSecrets]:
|
||||
validateAzureClientSecretsConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.Humanitec]: validateHumanitecConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.Postgres]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.MsSql]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
@@ -164,6 +172,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:
|
||||
@@ -236,6 +245,7 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
|
||||
[AppConnection.TerraformCloud]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.Camunda]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.Vercel]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.AzureClientSecrets]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.Windmill]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.Auth0]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.HCVault]: 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",
|
||||
|
||||
@@ -32,6 +32,8 @@ import { ValidateAuth0ConnectionCredentialsSchema } from "./auth0";
|
||||
import { ValidateAwsConnectionCredentialsSchema } from "./aws";
|
||||
import { awsConnectionService } from "./aws/aws-connection-service";
|
||||
import { ValidateAzureAppConfigurationConnectionCredentialsSchema } from "./azure-app-configuration";
|
||||
import { ValidateAzureClientSecretsConnectionCredentialsSchema } from "./azure-client-secrets";
|
||||
import { azureClientSecretsConnectionService } from "./azure-client-secrets/azure-client-secrets-service";
|
||||
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
|
||||
import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
|
||||
import { camundaConnectionService } from "./camunda/camunda-connection-service";
|
||||
@@ -78,6 +80,7 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
||||
[AppConnection.Postgres]: ValidatePostgresConnectionCredentialsSchema,
|
||||
[AppConnection.MsSql]: ValidateMsSqlConnectionCredentialsSchema,
|
||||
[AppConnection.Camunda]: ValidateCamundaConnectionCredentialsSchema,
|
||||
[AppConnection.AzureClientSecrets]: ValidateAzureClientSecretsConnectionCredentialsSchema,
|
||||
[AppConnection.Windmill]: ValidateWindmillConnectionCredentialsSchema,
|
||||
[AppConnection.Auth0]: ValidateAuth0ConnectionCredentialsSchema,
|
||||
[AppConnection.HCVault]: ValidateHCVaultConnectionCredentialsSchema,
|
||||
@@ -457,9 +460,10 @@ export const appConnectionServiceFactory = ({
|
||||
terraformCloud: terraformCloudConnectionService(connectAppConnectionById),
|
||||
camunda: camundaConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
||||
vercel: vercelConnectionService(connectAppConnectionById),
|
||||
windmill: windmillConnectionService(connectAppConnectionById),
|
||||
azureClientSecrets: azureClientSecretsConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
||||
auth0: auth0ConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
||||
hcvault: hcVaultConnectionService(connectAppConnectionById),
|
||||
windmill: windmillConnectionService(connectAppConnectionById),
|
||||
teamcity: teamcityConnectionService(connectAppConnectionById)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -21,6 +21,12 @@ import {
|
||||
TAzureAppConfigurationConnectionInput,
|
||||
TValidateAzureAppConfigurationConnectionCredentialsSchema
|
||||
} from "./azure-app-configuration";
|
||||
import {
|
||||
TAzureClientSecretsConnection,
|
||||
TAzureClientSecretsConnectionConfig,
|
||||
TAzureClientSecretsConnectionInput,
|
||||
TValidateAzureClientSecretsConnectionCredentialsSchema
|
||||
} from "./azure-client-secrets";
|
||||
import {
|
||||
TAzureKeyVaultConnection,
|
||||
TAzureKeyVaultConnectionConfig,
|
||||
@@ -113,6 +119,7 @@ export type TAppConnection = { id: string } & (
|
||||
| TPostgresConnection
|
||||
| TMsSqlConnection
|
||||
| TCamundaConnection
|
||||
| TAzureClientSecretsConnection
|
||||
| TWindmillConnection
|
||||
| TAuth0Connection
|
||||
| THCVaultConnection
|
||||
@@ -137,6 +144,7 @@ export type TAppConnectionInput = { id: string } & (
|
||||
| TPostgresConnectionInput
|
||||
| TMsSqlConnectionInput
|
||||
| TCamundaConnectionInput
|
||||
| TAzureClientSecretsConnectionInput
|
||||
| TWindmillConnectionInput
|
||||
| TAuth0ConnectionInput
|
||||
| THCVaultConnectionInput
|
||||
@@ -161,12 +169,13 @@ export type TAppConnectionConfig =
|
||||
| TGcpConnectionConfig
|
||||
| TAzureKeyVaultConnectionConfig
|
||||
| TAzureAppConfigurationConnectionConfig
|
||||
| TAzureClientSecretsConnectionConfig
|
||||
| TDatabricksConnectionConfig
|
||||
| THumanitecConnectionConfig
|
||||
| TTerraformCloudConnectionConfig
|
||||
| TVercelConnectionConfig
|
||||
| TSqlConnectionConfig
|
||||
| TCamundaConnectionConfig
|
||||
| TVercelConnectionConfig
|
||||
| TWindmillConnectionConfig
|
||||
| TAuth0ConnectionConfig
|
||||
| THCVaultConnectionConfig
|
||||
@@ -179,13 +188,14 @@ export type TValidateAppConnectionCredentialsSchema =
|
||||
| TValidateGcpConnectionCredentialsSchema
|
||||
| TValidateAzureKeyVaultConnectionCredentialsSchema
|
||||
| TValidateAzureAppConfigurationConnectionCredentialsSchema
|
||||
| TValidateAzureClientSecretsConnectionCredentialsSchema
|
||||
| TValidateDatabricksConnectionCredentialsSchema
|
||||
| TValidateHumanitecConnectionCredentialsSchema
|
||||
| TValidatePostgresConnectionCredentialsSchema
|
||||
| TValidateMsSqlConnectionCredentialsSchema
|
||||
| TValidateCamundaConnectionCredentialsSchema
|
||||
| TValidateTerraformCloudConnectionCredentialsSchema
|
||||
| TValidateVercelConnectionCredentialsSchema
|
||||
| TValidateTerraformCloudConnectionCredentialsSchema
|
||||
| TValidateWindmillConnectionCredentialsSchema
|
||||
| TValidateAuth0ConnectionCredentialsSchema
|
||||
| TValidateHCVaultConnectionCredentialsSchema
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export enum AzureClientSecretsConnectionMethod {
|
||||
OAuth = "oauth"
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
import { AxiosError, AxiosResponse } from "axios";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||
import {
|
||||
decryptAppConnectionCredentials,
|
||||
encryptAppConnectionCredentials,
|
||||
getAppConnectionMethodName
|
||||
} from "@app/services/app-connection/app-connection-fns";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import { TAppConnectionDALFactory } from "../app-connection-dal";
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { AzureClientSecretsConnectionMethod } from "./azure-client-secrets-connection-enums";
|
||||
import {
|
||||
ExchangeCodeAzureResponse,
|
||||
TAzureClientSecretsConnectionConfig,
|
||||
TAzureClientSecretsConnectionCredentials
|
||||
} 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 getAzureConnectionAccessToken = async (
|
||||
connectionId: string,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const appCfg = getConfig();
|
||||
if (!appCfg.INF_APP_CONNECTION_AZURE_CLIENT_ID || !appCfg.INF_APP_CONNECTION_AZURE_CLIENT_SECRET) {
|
||||
throw new BadRequestError({
|
||||
message: `Azure environment variables have not been configured`
|
||||
});
|
||||
}
|
||||
|
||||
const appConnection = await appConnectionDAL.findById(connectionId);
|
||||
|
||||
if (!appConnection) {
|
||||
throw new NotFoundError({ message: `Connection with ID '${connectionId}' not found` });
|
||||
}
|
||||
|
||||
if (appConnection.app !== AppConnection.AzureClientSecrets) {
|
||||
throw new BadRequestError({
|
||||
message: `Connection with ID '${connectionId}' is not an Azure Client Secrets connection`
|
||||
});
|
||||
}
|
||||
|
||||
const credentials = (await decryptAppConnectionCredentials({
|
||||
orgId: appConnection.orgId,
|
||||
kmsService,
|
||||
encryptedCredentials: appConnection.encryptedCredentials
|
||||
})) as TAzureClientSecretsConnectionCredentials;
|
||||
|
||||
const { refreshToken } = credentials;
|
||||
const currentTime = Date.now();
|
||||
|
||||
const { data } = await request.post<ExchangeCodeAzureResponse>(
|
||||
IntegrationUrls.AZURE_TOKEN_URL.replace("common", credentials.tenantId || "common"),
|
||||
new URLSearchParams({
|
||||
grant_type: "refresh_token",
|
||||
scope: `openid offline_access https://graph.microsoft.com/.default`,
|
||||
client_id: appCfg.INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
||||
client_secret: appCfg.INF_APP_CONNECTION_AZURE_CLIENT_SECRET,
|
||||
refresh_token: refreshToken
|
||||
})
|
||||
);
|
||||
|
||||
const updatedCredentials = {
|
||||
...credentials,
|
||||
accessToken: data.access_token,
|
||||
expiresAt: currentTime + data.expires_in * 1000,
|
||||
refreshToken: data.refresh_token
|
||||
};
|
||||
|
||||
const encryptedCredentials = await encryptAppConnectionCredentials({
|
||||
credentials: updatedCredentials,
|
||||
orgId: appConnection.orgId,
|
||||
kmsService
|
||||
});
|
||||
|
||||
await appConnectionDAL.updateById(appConnection.id, { encryptedCredentials });
|
||||
|
||||
return data.access_token;
|
||||
};
|
||||
|
||||
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 (!SITE_URL) {
|
||||
throw new InternalServerError({ message: "SITE_URL env var is required to complete Azure OAuth flow" });
|
||||
}
|
||||
|
||||
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://graph.microsoft.com/.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}`
|
||||
});
|
||||
}
|
||||
};
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
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").describe(AppConnections.CREDENTIALS.AZURE_CLIENT_SECRETS.code),
|
||||
tenantId: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Tenant ID required")
|
||||
.describe(AppConnections.CREDENTIALS.AZURE_CLIENT_SECRETS.tenantId)
|
||||
});
|
||||
|
||||
export const AzureClientSecretsConnectionOAuthOutputCredentialsSchema = z.object({
|
||||
tenantId: z.string(),
|
||||
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()
|
||||
});
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
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 TAzureClientSecretsConnectionCredentials = z.infer<
|
||||
typeof AzureClientSecretsConnectionOAuthOutputCredentialsSchema
|
||||
>;
|
||||
|
||||
export interface ExchangeCodeAzureResponse {
|
||||
token_type: string;
|
||||
scope: string;
|
||||
expires_in: number;
|
||||
ext_expires_in: number;
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
id_token: string;
|
||||
}
|
||||
|
||||
export interface TAzureRegisteredApp {
|
||||
id: string;
|
||||
appId: string;
|
||||
displayName: string;
|
||||
description?: string;
|
||||
createdDateTime: string;
|
||||
identifierUris?: string[];
|
||||
signInAudience?: string;
|
||||
}
|
||||
|
||||
export interface TAzureListRegisteredAppsResponse {
|
||||
"@odata.context": string;
|
||||
"@odata.nextLink"?: string;
|
||||
value: TAzureRegisteredApp[];
|
||||
}
|
||||
|
||||
export interface TAzureClientSecret {
|
||||
keyId: string;
|
||||
displayName?: string;
|
||||
startDateTime: string;
|
||||
endDateTime: string;
|
||||
secretText?: string;
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-client-secrets/azure-client-secrets-connection-fns";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import {
|
||||
TAzureClientSecretsConnection,
|
||||
TAzureListRegisteredAppsResponse,
|
||||
TAzureRegisteredApp
|
||||
} from "./azure-client-secrets-connection-types";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
app: AppConnection,
|
||||
connectionId: string,
|
||||
actor: OrgServiceActor
|
||||
) => Promise<TAzureClientSecretsConnection>;
|
||||
|
||||
const listAzureRegisteredApps = async (
|
||||
appConnection: TAzureClientSecretsConnection,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const accessToken = await getAzureConnectionAccessToken(appConnection.id, appConnectionDAL, kmsService);
|
||||
|
||||
const graphEndpoint = `https://graph.microsoft.com/v1.0/applications`;
|
||||
|
||||
const apps: TAzureRegisteredApp[] = [];
|
||||
let nextLink = graphEndpoint;
|
||||
|
||||
while (nextLink) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { data: appsPage } = await request.get<TAzureListRegisteredAppsResponse>(nextLink, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
apps.push(...appsPage.value);
|
||||
nextLink = appsPage["@odata.nextLink"] || "";
|
||||
}
|
||||
|
||||
return apps;
|
||||
};
|
||||
|
||||
export const azureClientSecretsConnectionService = (
|
||||
getAppConnection: TGetAppConnectionFunc,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const listApps = async (connectionId: string, actor: OrgServiceActor) => {
|
||||
const appConnection = await getAppConnection(AppConnection.AzureClientSecrets, connectionId, actor);
|
||||
|
||||
const apps = await listAzureRegisteredApps(appConnection, appConnectionDAL, kmsService);
|
||||
|
||||
return apps.map((app) => ({
|
||||
id: app.id,
|
||||
name: app.displayName,
|
||||
appId: app.appId
|
||||
}));
|
||||
};
|
||||
|
||||
return {
|
||||
listApps
|
||||
};
|
||||
};
|
||||
@@ -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";
|
||||
+6
-2
@@ -38,8 +38,12 @@ export const getAzureConnectionAccessToken = async (
|
||||
throw new NotFoundError({ message: `Connection with ID '${connectionId}' not found` });
|
||||
}
|
||||
|
||||
if (appConnection.app !== AppConnection.AzureKeyVault && appConnection.app !== AppConnection.AzureAppConfiguration) {
|
||||
throw new BadRequestError({ message: `Connection with ID '${connectionId}' is not an Azure Key Vault connection` });
|
||||
if (
|
||||
appConnection.app !== AppConnection.AzureKeyVault &&
|
||||
appConnection.app !== AppConnection.AzureAppConfiguration &&
|
||||
appConnection.app !== AppConnection.AzureClientSecrets
|
||||
) {
|
||||
throw new BadRequestError({ message: `Connection with ID '${connectionId}' is not a valid Azure connection` });
|
||||
}
|
||||
|
||||
const credentials = (await decryptAppConnectionCredentials({
|
||||
|
||||
@@ -69,6 +69,5 @@ export const listTeamCityProjects = async (appConnection: TTeamCityConnection) =
|
||||
}
|
||||
);
|
||||
|
||||
// Filter out the root project. Should not be seen by users.
|
||||
return resp.data.project.filter((proj) => proj.id !== "_Root");
|
||||
return resp.data.project;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user