Skip to content

Commit 89a6d8b

Browse files
Uses PAT on shared provider library requests from Azure (#4272)
* Uses PAT on shared provider library requests from Azure * Uses our base64 function to avoid btoa issues * Also updates local Azure queries --------- Co-authored-by: Eric Amodio <[email protected]>
1 parent 8f61e02 commit 89a6d8b

File tree

3 files changed

+42
-15
lines changed

3 files changed

+42
-15
lines changed

src/plus/integrations/providers/azure/azure.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { Logger } from '../../../../system/logger';
2424
import type { LogScope } from '../../../../system/logger.scope';
2525
import { getLogScope } from '../../../../system/logger.scope';
2626
import { maybeStopWatch } from '../../../../system/stopwatch';
27+
import { base64 } from '../../../../system/string';
2728
import type {
2829
AzureProjectDescriptor,
2930
AzurePullRequest,
@@ -334,7 +335,10 @@ export class AzureDevOpsApi implements Disposable {
334335

335336
rsp = await wrapForForcedInsecureSSL(provider.getIgnoreSSLErrors(), () =>
336337
fetch(url, {
337-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
338+
headers: {
339+
Authorization: `Basic ${base64(`PAT:${token}`)}`,
340+
'Content-Type': 'application/json',
341+
},
338342
agent: agent,
339343
signal: aborter?.signal,
340344
...options,

src/plus/integrations/providers/azureDevOps.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { IssueOrPullRequest } from '../../../git/models/issueOrPullRequest'
88
import type { PullRequest, PullRequestMergeMethod, PullRequestState } from '../../../git/models/pullRequest';
99
import type { RepositoryMetadata } from '../../../git/models/repositoryMetadata';
1010
import { getSettledValue } from '../../../system/promise';
11+
import { base64 } from '../../../system/string';
1112
import type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider';
1213
import { HostingIntegration } from '../integration';
1314
import type {
@@ -81,7 +82,10 @@ export class AzureDevOpsIntegration extends HostingIntegration<
8182
const account = await this.getProviderCurrentAccount(session);
8283
if (account?.id == null) return undefined;
8384

84-
const resources = await api.getAzureResourcesForUser(account.id, { accessToken: accessToken });
85+
const resources = await api.getAzureResourcesForUser(account.id, {
86+
accessToken: convertTokentoPAT(accessToken),
87+
isPAT: true,
88+
});
8589
this._organizations.set(
8690
accessToken,
8791
resources != null ? resources.map(r => ({ ...r, key: r.id })) : undefined,
@@ -117,7 +121,10 @@ export class AzureDevOpsIntegration extends HostingIntegration<
117121
const azureProjects = (
118122
await Promise.allSettled(
119123
resourcesWithoutProjects.map(resource =>
120-
api.getAzureProjectsForResource(resource.name, { accessToken: accessToken }),
124+
api.getAzureProjectsForResource(resource.name, {
125+
accessToken: convertTokentoPAT(accessToken),
126+
isPAT: true,
127+
}),
121128
),
122129
)
123130
)
@@ -164,7 +171,8 @@ export class AzureDevOpsIntegration extends HostingIntegration<
164171
projects.map(async project => {
165172
const repos = (
166173
await api.getReposForAzureProject(project.resourceName, project.name, {
167-
accessToken: accessToken,
174+
accessToken: convertTokentoPAT(accessToken),
175+
isPAT: true,
168176
})
169177
)?.values;
170178
if (repos != null && repos.length > 0) {
@@ -202,7 +210,8 @@ export class AzureDevOpsIntegration extends HostingIntegration<
202210
try {
203211
const merged = await api.mergePullRequest(this.id, pr, {
204212
...options,
205-
accessToken: accessToken,
213+
accessToken: convertTokentoPAT(accessToken),
214+
isPAT: true,
206215
});
207216
return merged;
208217
} catch (ex) {
@@ -306,8 +315,11 @@ export class AzureDevOpsIntegration extends HostingIntegration<
306315
project: string;
307316
}): Promise<ProviderRepository | undefined> {
308317
const api = await this.getProvidersApi();
318+
if (this._session == null) return undefined;
319+
309320
return api.getRepo(this.id, repo.owner, repo.name, repo.project, {
310-
accessToken: this._session?.accessToken,
321+
accessToken: convertTokentoPAT(this._session.accessToken),
322+
isPAT: true,
311323
});
312324
}
313325

@@ -347,13 +359,15 @@ export class AzureDevOpsIntegration extends HostingIntegration<
347359
const projectInputs = projects.map(p => ({ namespace: p.resourceName, project: p.name }));
348360
const assignedPrs = (
349361
await api.getPullRequestsForAzureProjects(projectInputs, {
350-
accessToken: session.accessToken,
362+
accessToken: convertTokentoPAT(session.accessToken),
363+
isPAT: true,
351364
assigneeLogins: [user.username],
352365
})
353366
)?.map(pr => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects));
354367
const authoredPrs = (
355368
await api.getPullRequestsForAzureProjects(projectInputs, {
356-
accessToken: session.accessToken,
369+
accessToken: convertTokentoPAT(session.accessToken),
370+
isPAT: true,
357371
authorLogin: user.username,
358372
})
359373
)?.map(pr => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects));
@@ -392,7 +406,8 @@ export class AzureDevOpsIntegration extends HostingIntegration<
392406
projects.map(async p => {
393407
const issuesResponse = (
394408
await api.getIssuesForAzureProject(p.resourceName, p.name, {
395-
accessToken: session.accessToken,
409+
accessToken: convertTokentoPAT(session.accessToken),
410+
isPAT: true,
396411
assigneeLogins: [user.username!],
397412
})
398413
).values;
@@ -547,3 +562,7 @@ const azureCloudDomainRegex = /^dev\.azure\.com$|\bvisualstudio\.com$/i;
547562
export function isAzureCloudDomain(domain: string | undefined): boolean {
548563
return domain != null && azureCloudDomainRegex.test(domain);
549564
}
565+
566+
function convertTokentoPAT(accessToken: string): string {
567+
return base64(`PAT:${accessToken}`);
568+
}

src/plus/integrations/providers/providersApi.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ export class ProvidersApi {
611611

612612
async getAzureResourcesForUser(
613613
userId: string,
614-
options?: { accessToken?: string },
614+
options?: { accessToken?: string; isPAT?: boolean },
615615
): Promise<ProviderAzureResource[] | undefined> {
616616
const { provider, token } = await this.ensureProviderTokenAndFunction(
617617
HostingIntegrationId.AzureDevOps,
@@ -620,7 +620,9 @@ export class ProvidersApi {
620620
);
621621

622622
try {
623-
return (await provider.getAzureResourcesForUserFn?.({ userId: userId }, { token: token }))?.data;
623+
return (
624+
await provider.getAzureResourcesForUserFn?.({ userId: userId }, { token: token, isPAT: options?.isPAT })
625+
)?.data;
624626
} catch (e) {
625627
return this.handleProviderError<ProviderAzureResource[] | undefined>(
626628
HostingIntegrationId.AzureDevOps,
@@ -732,7 +734,7 @@ export class ProvidersApi {
732734
provider.getAzureProjectsForResourceFn,
733735
azureToken,
734736
options?.cursor,
735-
true,
737+
options?.isPAT,
736738
);
737739
} catch (e) {
738740
return this.handleProviderError<PagedResult<ProviderAzureProject>>(
@@ -746,7 +748,7 @@ export class ProvidersApi {
746748
async getReposForAzureProject(
747749
namespace: string,
748750
project: string,
749-
options?: GetReposOptions & { accessToken?: string },
751+
options?: GetReposOptions & { accessToken?: string; isPAT?: boolean },
750752
): Promise<PagedResult<ProviderRepository>> {
751753
const { provider, token } = await this.ensureProviderTokenAndFunction(
752754
HostingIntegrationId.AzureDevOps,
@@ -760,6 +762,7 @@ export class ProvidersApi {
760762
provider.getReposForAzureProjectFn,
761763
token,
762764
options?.cursor,
765+
options?.isPAT,
763766
);
764767
}
765768

@@ -864,7 +867,7 @@ export class ProvidersApi {
864867
return (
865868
await provider.getPullRequestsForAzureProjectsFn?.(
866869
{ projects: projects, ...options },
867-
{ token: azureToken, isPAT: true },
870+
{ token: azureToken, isPAT: options?.isPAT },
868871
)
869872
)?.data;
870873
} catch (e) {
@@ -971,7 +974,7 @@ export class ProvidersApi {
971974
async getIssuesForAzureProject(
972975
namespace: string,
973976
project: string,
974-
options?: GetIssuesOptions & { accessToken?: string },
977+
options?: GetIssuesOptions & { accessToken?: string; isPAT?: boolean },
975978
): Promise<PagedResult<ProviderIssue>> {
976979
const { provider, token } = await this.ensureProviderTokenAndFunction(
977980
HostingIntegrationId.AzureDevOps,
@@ -985,6 +988,7 @@ export class ProvidersApi {
985988
provider.getIssuesForAzureProjectFn,
986989
token,
987990
options?.cursor,
991+
options?.isPAT,
988992
);
989993
}
990994

0 commit comments

Comments
 (0)