mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 08:29:55 +00:00
feat(secret-sync): Add Azure Devops PR suggestions
This commit is contained in:
@@ -225,7 +225,6 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
||||
case TerraformCloudConnectionMethod.ApiToken:
|
||||
case VercelConnectionMethod.ApiToken:
|
||||
case OnePassConnectionMethod.ApiToken:
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
return "API Token";
|
||||
case PostgresConnectionMethod.UsernameAndPassword:
|
||||
case MsSqlConnectionMethod.UsernameAndPassword:
|
||||
@@ -234,6 +233,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
||||
case WindmillConnectionMethod.AccessToken:
|
||||
case HCVaultConnectionMethod.AccessToken:
|
||||
case TeamCityConnectionMethod.AccessToken:
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
return "Access Token";
|
||||
case Auth0ConnectionMethod.ClientCredentials:
|
||||
return "Client Credentials";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export enum AzureDevOpsConnectionMethod {
|
||||
OAuth = "oauth",
|
||||
ApiToken = "api-token"
|
||||
AccessToken = "access-token"
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ export const getAzureDevopsConnectionListItem = () => {
|
||||
app: AppConnection.AzureDevOps as const,
|
||||
methods: Object.values(AzureDevOpsConnectionMethod) as [
|
||||
AzureDevOpsConnectionMethod.OAuth,
|
||||
AzureDevOpsConnectionMethod.ApiToken
|
||||
AzureDevOpsConnectionMethod.AccessToken
|
||||
],
|
||||
oauthClientId: INF_APP_CONNECTION_AZURE_CLIENT_ID
|
||||
};
|
||||
};
|
||||
|
||||
export const getAzureDevopsConnectionAccessToken = async (
|
||||
export const getAzureDevopsConnection = async (
|
||||
connectionId: string,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
@@ -104,12 +104,12 @@ export const getAzureDevopsConnectionAccessToken = async (
|
||||
|
||||
return data.access_token;
|
||||
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
if (!("apiKey" in credentials)) {
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
if (!("accessToken" in credentials)) {
|
||||
throw new BadRequestError({ message: "Invalid API token credentials" });
|
||||
}
|
||||
// For API token, return the basic auth token directly
|
||||
return credentials.apiKey as string;
|
||||
return credentials.accessToken;
|
||||
|
||||
default:
|
||||
throw new BadRequestError({ message: `Unsupported connection method` });
|
||||
@@ -188,17 +188,17 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
expiresAt: Date.now() + tokenResp.data.expires_in * 1000
|
||||
};
|
||||
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
const apiTokenCredentials = inputCredentials as { apiKey: string; orgName?: string };
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
const apiTokenCredentials = inputCredentials as { accessToken: string; orgName?: string };
|
||||
|
||||
try {
|
||||
if (apiTokenCredentials.orgName) {
|
||||
// Validate against specific organization
|
||||
const response = await request.get(
|
||||
`${IntegrationUrls.AZURE_DEVOPS_API_URL}/${apiTokenCredentials.orgName}/_apis/projects?api-version=7.2-preview.2&$top=1`,
|
||||
`${IntegrationUrls.AZURE_DEVOPS_API_URL}/${encodeURIComponent(apiTokenCredentials.orgName)}/_apis/projects?api-version=7.2-preview.2&$top=1`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.apiKey}`).toString("base64")}`
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.accessToken}`).toString("base64")}`
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -210,7 +210,7 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
}
|
||||
|
||||
return {
|
||||
apiKey: apiTokenCredentials.apiKey,
|
||||
accessToken: apiTokenCredentials.accessToken,
|
||||
orgName: apiTokenCredentials.orgName
|
||||
};
|
||||
}
|
||||
@@ -219,7 +219,7 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
`https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.apiKey}`).toString("base64")}`
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.accessToken}`).toString("base64")}`
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -230,7 +230,7 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
value: Array<{ accountId: string; accountName: string; accountUri: string }>;
|
||||
}>(`https://app.vssps.visualstudio.com/_apis/accounts?api-version=7.1`, {
|
||||
headers: {
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.apiKey}`).toString("base64")}`
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.accessToken}`).toString("base64")}`
|
||||
}
|
||||
});
|
||||
organizations = orgsResponse.data.value || [];
|
||||
@@ -239,7 +239,7 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
}
|
||||
|
||||
return {
|
||||
apiKey: apiTokenCredentials.apiKey,
|
||||
accessToken: apiTokenCredentials.accessToken,
|
||||
userDisplayName: profileResponse.data.displayName,
|
||||
organizations: organizations.map((org) => ({
|
||||
accountId: org.accountId,
|
||||
|
||||
@@ -29,12 +29,12 @@ export const AzureDevOpsConnectionOAuthOutputCredentialsSchema = z.object({
|
||||
});
|
||||
|
||||
export const AzureDevOpsConnectionApiTokenInputCredentialsSchema = z.object({
|
||||
apiKey: z.string().trim().min(1, "API Key required"),
|
||||
accessToken: z.string().trim().min(1, "Access Token required"),
|
||||
orgName: z.string().trim().min(1, "Organization name required")
|
||||
});
|
||||
|
||||
export const AzureDevOpsConnectionApiTokenOutputCredentialsSchema = z.object({
|
||||
apiKey: z.string(),
|
||||
accessToken: z.string(),
|
||||
orgName: z.string()
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ export const ValidateAzureDevOpsConnectionCredentialsSchema = z.discriminatedUni
|
||||
}),
|
||||
z.object({
|
||||
method: z
|
||||
.literal(AzureDevOpsConnectionMethod.ApiToken)
|
||||
.literal(AzureDevOpsConnectionMethod.AccessToken)
|
||||
.describe(AppConnections.CREATE(AppConnection.AzureDevOps).method),
|
||||
credentials: AzureDevOpsConnectionApiTokenInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.AzureDevOps).credentials
|
||||
@@ -82,7 +82,7 @@ export const AzureDevOpsConnectionSchema = z.intersection(
|
||||
credentials: AzureDevOpsConnectionOAuthOutputCredentialsSchema
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(AzureDevOpsConnectionMethod.ApiToken),
|
||||
method: z.literal(AzureDevOpsConnectionMethod.AccessToken),
|
||||
credentials: AzureDevOpsConnectionApiTokenOutputCredentialsSchema
|
||||
})
|
||||
])
|
||||
@@ -97,7 +97,7 @@ export const SanitizedAzureDevOpsConnectionSchema = z.discriminatedUnion("method
|
||||
})
|
||||
}),
|
||||
BaseAzureDevOpsConnectionSchema.extend({
|
||||
method: z.literal(AzureDevOpsConnectionMethod.ApiToken),
|
||||
method: z.literal(AzureDevOpsConnectionMethod.AccessToken),
|
||||
credentials: AzureDevOpsConnectionApiTokenOutputCredentialsSchema.pick({
|
||||
orgName: true
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IntegrationUrls } from "@app/services/integration-auth/integration-list
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import { AzureDevOpsConnectionMethod } from "./azure-devops-enums";
|
||||
import { getAzureDevopsConnectionAccessToken } from "./azure-devops-fns";
|
||||
import { getAzureDevopsConnection } from "./azure-devops-fns";
|
||||
import { TAzureDevOpsConnection } from "./azure-devops-types";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
@@ -45,7 +45,7 @@ const getAuthHeaders = (appConnection: TAzureDevOpsConnection, accessToken: stri
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
};
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
// For API token, create Basic auth header
|
||||
const basicAuthToken = Buffer.from(`user:${accessToken}`).toString("base64");
|
||||
return {
|
||||
@@ -62,7 +62,7 @@ const listAzureDevOpsProjects = async (
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
): Promise<TAzureDevOpsProject[]> => {
|
||||
const accessToken = await getAzureDevopsConnectionAccessToken(appConnection.id, appConnectionDAL, kmsService);
|
||||
const accessToken = await getAzureDevopsConnection(appConnection.id, appConnectionDAL, kmsService);
|
||||
|
||||
// Both OAuth and API Token methods use organization name from credentials
|
||||
const credentials = appConnection.credentials as { orgName: string };
|
||||
|
||||
@@ -2,7 +2,7 @@ import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { AzureDevOpsConnectionMethod } from "@app/services/app-connection/azure-devops/azure-devops-enums";
|
||||
import { getAzureDevopsConnectionAccessToken } from "@app/services/app-connection/azure-devops/azure-devops-fns";
|
||||
import { getAzureDevopsConnection } from "@app/services/app-connection/azure-devops/azure-devops-fns";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
@@ -44,7 +44,7 @@ export const azureDevOpsSyncFactory = ({ kmsService, appConnectionDAL }: TAzureD
|
||||
});
|
||||
}
|
||||
|
||||
const accessToken = await getAzureDevopsConnectionAccessToken(
|
||||
const accessToken = await getAzureDevopsConnection(
|
||||
secretSync.connectionId,
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
@@ -115,7 +115,7 @@ export const azureDevOpsSyncFactory = ({ kmsService, appConnectionDAL }: TAzureD
|
||||
|
||||
if (!groupId) {
|
||||
// Create new variable group - API endpoint is organization-level
|
||||
const url = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${orgName}/_apis/distributedtask/variablegroups?api-version=7.1`;
|
||||
const url = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${encodeURIComponent(orgName)}/_apis/distributedtask/variablegroups?api-version=7.1`;
|
||||
|
||||
await request.post(
|
||||
url,
|
||||
@@ -143,7 +143,7 @@ export const azureDevOpsSyncFactory = ({ kmsService, appConnectionDAL }: TAzureD
|
||||
}
|
||||
);
|
||||
} else {
|
||||
const url = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${orgName}/_apis/distributedtask/variablegroups/${groupId}?api-version=7.1`;
|
||||
const url = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${encodeURIComponent(orgName)}/_apis/distributedtask/variablegroups/${groupId}?api-version=7.1`;
|
||||
|
||||
await request.put(
|
||||
url,
|
||||
@@ -186,7 +186,7 @@ export const azureDevOpsSyncFactory = ({ kmsService, appConnectionDAL }: TAzureD
|
||||
|
||||
if (groupId) {
|
||||
// Delete the variable group entirely using the DELETE API
|
||||
const deleteUrl = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${orgName}/_apis/distributedtask/variablegroups/${groupId}?projectIds=${secretSync.destinationConfig.devopsProjectId}&api-version=7.1`;
|
||||
const deleteUrl = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${encodeURIComponent(orgName)}/_apis/distributedtask/variablegroups/${groupId}?projectIds=${secretSync.destinationConfig.devopsProjectId}&api-version=7.1`;
|
||||
|
||||
await request.delete(deleteUrl, {
|
||||
headers: {
|
||||
|
||||
Reference in New Issue
Block a user