Skip to content

Commit 8aaa591

Browse files
AlexVTorjmyersmsft
andauthored
Bugfix: Match service connection using feedId/ProjID (#19732)
* Bugfix: Parse service connection using feedId/ProjID * Bump task version * Add Type matching Co-authored-by: Jonathan Myers <[email protected]> * updated generated files * Verifying service connection matches feed * Limit search to subset of service connections * Bump task version * Removed unused var * Applied review suggestions * rebuild / removed unused + comment --------- Co-authored-by: Jonathan Myers <[email protected]>
1 parent 5f964db commit 8aaa591

File tree

10 files changed

+178
-46
lines changed

10 files changed

+178
-46
lines changed

Tasks/NuGetCommandV2/nugetpublisher.ts

+54-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import * as vstsNuGetPushToolRunner from "./Common/VstsNuGetPushToolRunner";
1515
import * as vstsNuGetPushToolUtilities from "./Common/VstsNuGetPushToolUtilities";
1616
import { getProjectAndFeedIdFromInputParam } from 'azure-pipelines-tasks-packaging-common/util';
1717
import { logError } from 'azure-pipelines-tasks-packaging-common/util';
18+
import { WebRequest, WebResponse, sendRequest } from 'azure-pipelines-tasks-utility-common/restutilities';
19+
1820

1921
class PublishOptions implements INuGetCommandOptions {
2022
constructor(
@@ -112,7 +114,7 @@ export async function run(nuGetPath: string): Promise<void> {
112114
let accessToken;
113115
let feed;
114116
const isInternalFeed: boolean = nugetFeedType === "internal";
115-
accessToken = getAccessToken(isInternalFeed);
117+
accessToken = await getAccessToken(isInternalFeed, urlPrefixes);
116118
const quirks = await ngToolRunner.getNuGetQuirksAsync(nuGetPath);
117119

118120
// Clauses ordered in this way to avoid short-circuit evaluation, so the debug info printed by the functions
@@ -414,9 +416,9 @@ function shouldUseVstsNuGetPush(isInternalFeed: boolean, conflictsAllowed: boole
414416
return false;
415417
}
416418

417-
function getAccessToken(isInternalFeed: boolean): string{
418-
let accessToken: string;
419+
async function getAccessToken(isInternalFeed: boolean, uriPrefixes: any): Promise<string>{
419420
let allowServiceConnection = tl.getVariable('PUBLISH_VIA_SERVICE_CONNECTION');
421+
let accessToken: string;
420422

421423
if(allowServiceConnection) {
422424
let endpoint = tl.getInput('externalEndpoint', false);
@@ -440,16 +442,25 @@ function getAccessToken(isInternalFeed: boolean): string{
440442
tl.debug("Checking for auth from Cred Provider.");
441443
const feed = getProjectAndFeedIdFromInputParam('feedPublish');
442444
const JsonEndpointsString = process.env["VSS_NUGET_EXTERNAL_FEED_ENDPOINTS"];
445+
443446
if (JsonEndpointsString) {
447+
tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`);
444448
tl.debug(`Endpoints found: ${JsonEndpointsString}`);
445449

446450
let endpointsArray: { endpointCredentials: EndpointCredentials[] } = JSON.parse(JsonEndpointsString);
447-
tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`);
448-
449-
for (let endpoint_in = 0; endpoint_in < endpointsArray.endpointCredentials.length; endpoint_in++) {
450-
if (endpointsArray.endpointCredentials[endpoint_in].endpoint.search(feed.feedName) != -1) {
451-
tl.debug(`Endpoint Credentials found for ${feed.feedName}`);
452-
accessToken = endpointsArray.endpointCredentials[endpoint_in].password;
451+
let matchingEndpoint: EndpointCredentials;
452+
for (const e of endpointsArray.endpointCredentials) {
453+
for (const prefix of uriPrefixes) {
454+
if (e.endpoint.toUpperCase().startsWith(prefix.toUpperCase())) {
455+
let isServiceConnectionValid = await tryServiceConnection(e, feed);
456+
if (isServiceConnectionValid) {
457+
matchingEndpoint = e;
458+
break;
459+
}
460+
}
461+
}
462+
if (matchingEndpoint) {
463+
accessToken = matchingEndpoint.password;
453464
break;
454465
}
455466
}
@@ -466,4 +477,37 @@ function getAccessToken(isInternalFeed: boolean): string{
466477
}
467478

468479
return accessToken;
469-
}
480+
}
481+
482+
async function tryServiceConnection(endpoint: EndpointCredentials, feed: any) : Promise<boolean>
483+
{
484+
// Create request
485+
const request = new WebRequest();
486+
const token64 = Buffer.from(`${endpoint.username}:${endpoint.password}`).toString('base64');
487+
request.uri = endpoint.endpoint;
488+
request.method = 'GET';
489+
request.headers = {
490+
"Content-Type": "application/json",
491+
"Authorization": "Basic " + token64
492+
};
493+
494+
const response = await sendRequest(request);
495+
496+
if(response.statusCode == 200) {
497+
if(response.body) {
498+
for (const entry of response.body.resources) {
499+
if (entry['@type'] === 'AzureDevOpsProjectId' && entry['label'].toUpperCase() !== feed.projectId.toUpperCase())
500+
{
501+
return false;
502+
}
503+
if (entry['@type'] === 'VssFeedId' && entry['label'].toUpperCase() !== feed.feedId.toUpperCase())
504+
{
505+
return false;
506+
}
507+
}
508+
// We found matches in feedId and projectId, return the service connection
509+
return true;
510+
}
511+
}
512+
return false;
513+
}

Tasks/NuGetCommandV2/task.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"version": {
1111
"Major": 2,
1212
"Minor": 238,
13-
"Patch": 0
13+
"Patch": 1
1414
},
1515
"runsOn": [
1616
"Agent",

Tasks/NuGetCommandV2/task.loc.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"version": {
1111
"Major": 2,
1212
"Minor": 238,
13-
"Patch": 0
13+
"Patch": 1
1414
},
1515
"runsOn": [
1616
"Agent",
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Default|2.238.0
2-
Node20_229_1|2.238.1
1+
Default|2.238.1
2+
Node20_229_1|2.238.2

_generated/NuGetCommandV2/nugetpublisher.ts

+54-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import * as vstsNuGetPushToolRunner from "./Common/VstsNuGetPushToolRunner";
1515
import * as vstsNuGetPushToolUtilities from "./Common/VstsNuGetPushToolUtilities";
1616
import { getProjectAndFeedIdFromInputParam } from 'azure-pipelines-tasks-packaging-common/util';
1717
import { logError } from 'azure-pipelines-tasks-packaging-common/util';
18+
import { WebRequest, WebResponse, sendRequest } from 'azure-pipelines-tasks-utility-common/restutilities';
19+
1820

1921
class PublishOptions implements INuGetCommandOptions {
2022
constructor(
@@ -112,7 +114,7 @@ export async function run(nuGetPath: string): Promise<void> {
112114
let accessToken;
113115
let feed;
114116
const isInternalFeed: boolean = nugetFeedType === "internal";
115-
accessToken = getAccessToken(isInternalFeed);
117+
accessToken = await getAccessToken(isInternalFeed, urlPrefixes);
116118
const quirks = await ngToolRunner.getNuGetQuirksAsync(nuGetPath);
117119

118120
// Clauses ordered in this way to avoid short-circuit evaluation, so the debug info printed by the functions
@@ -414,9 +416,9 @@ function shouldUseVstsNuGetPush(isInternalFeed: boolean, conflictsAllowed: boole
414416
return false;
415417
}
416418

417-
function getAccessToken(isInternalFeed: boolean): string{
418-
let accessToken: string;
419+
async function getAccessToken(isInternalFeed: boolean, uriPrefixes: any): Promise<string>{
419420
let allowServiceConnection = tl.getVariable('PUBLISH_VIA_SERVICE_CONNECTION');
421+
let accessToken: string;
420422

421423
if(allowServiceConnection) {
422424
let endpoint = tl.getInput('externalEndpoint', false);
@@ -440,16 +442,25 @@ function getAccessToken(isInternalFeed: boolean): string{
440442
tl.debug("Checking for auth from Cred Provider.");
441443
const feed = getProjectAndFeedIdFromInputParam('feedPublish');
442444
const JsonEndpointsString = process.env["VSS_NUGET_EXTERNAL_FEED_ENDPOINTS"];
445+
443446
if (JsonEndpointsString) {
447+
tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`);
444448
tl.debug(`Endpoints found: ${JsonEndpointsString}`);
445449

446450
let endpointsArray: { endpointCredentials: EndpointCredentials[] } = JSON.parse(JsonEndpointsString);
447-
tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`);
448-
449-
for (let endpoint_in = 0; endpoint_in < endpointsArray.endpointCredentials.length; endpoint_in++) {
450-
if (endpointsArray.endpointCredentials[endpoint_in].endpoint.search(feed.feedName) != -1) {
451-
tl.debug(`Endpoint Credentials found for ${feed.feedName}`);
452-
accessToken = endpointsArray.endpointCredentials[endpoint_in].password;
451+
let matchingEndpoint: EndpointCredentials;
452+
for (const e of endpointsArray.endpointCredentials) {
453+
for (const prefix of uriPrefixes) {
454+
if (e.endpoint.toUpperCase().startsWith(prefix.toUpperCase())) {
455+
let isServiceConnectionValid = await tryServiceConnection(e, feed);
456+
if (isServiceConnectionValid) {
457+
matchingEndpoint = e;
458+
break;
459+
}
460+
}
461+
}
462+
if (matchingEndpoint) {
463+
accessToken = matchingEndpoint.password;
453464
break;
454465
}
455466
}
@@ -466,4 +477,37 @@ function getAccessToken(isInternalFeed: boolean): string{
466477
}
467478

468479
return accessToken;
469-
}
480+
}
481+
482+
async function tryServiceConnection(endpoint: EndpointCredentials, feed: any) : Promise<boolean>
483+
{
484+
// Create request
485+
const request = new WebRequest();
486+
const token64 = Buffer.from(`${endpoint.username}:${endpoint.password}`).toString('base64');
487+
request.uri = endpoint.endpoint;
488+
request.method = 'GET';
489+
request.headers = {
490+
"Content-Type": "application/json",
491+
"Authorization": "Basic " + token64
492+
};
493+
494+
const response = await sendRequest(request);
495+
496+
if(response.statusCode == 200) {
497+
if(response.body) {
498+
for (const entry of response.body.resources) {
499+
if (entry['@type'] === 'AzureDevOpsProjectId' && entry['label'].toUpperCase() !== feed.projectId.toUpperCase())
500+
{
501+
return false;
502+
}
503+
if (entry['@type'] === 'VssFeedId' && entry['label'].toUpperCase() !== feed.feedId.toUpperCase())
504+
{
505+
return false;
506+
}
507+
}
508+
// We found matches in feedId and projectId, return the service connection
509+
return true;
510+
}
511+
}
512+
return false;
513+
}

_generated/NuGetCommandV2/task.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"version": {
1111
"Major": 2,
1212
"Minor": 238,
13-
"Patch": 0
13+
"Patch": 1
1414
},
1515
"runsOn": [
1616
"Agent",
@@ -546,7 +546,7 @@
546546
"Warning_UnsupportedServiceConnectionAuth": "The service connection does not use a supported authentication method. Please use a service connection with personal access token based auth."
547547
},
548548
"_buildConfigMapping": {
549-
"Default": "2.238.0",
550-
"Node20_229_1": "2.238.1"
549+
"Default": "2.238.1",
550+
"Node20_229_1": "2.238.2"
551551
}
552552
}

_generated/NuGetCommandV2/task.loc.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"version": {
1111
"Major": 2,
1212
"Minor": 238,
13-
"Patch": 0
13+
"Patch": 1
1414
},
1515
"runsOn": [
1616
"Agent",
@@ -546,7 +546,7 @@
546546
"Warning_UnsupportedServiceConnectionAuth": "ms-resource:loc.messages.Warning_UnsupportedServiceConnectionAuth"
547547
},
548548
"_buildConfigMapping": {
549-
"Default": "2.238.0",
550-
"Node20_229_1": "2.238.1"
549+
"Default": "2.238.1",
550+
"Node20_229_1": "2.238.2"
551551
}
552552
}

_generated/NuGetCommandV2_Node20/nugetpublisher.ts

+54-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import * as vstsNuGetPushToolRunner from "./Common/VstsNuGetPushToolRunner";
1515
import * as vstsNuGetPushToolUtilities from "./Common/VstsNuGetPushToolUtilities";
1616
import { getProjectAndFeedIdFromInputParam } from 'azure-pipelines-tasks-packaging-common/util';
1717
import { logError } from 'azure-pipelines-tasks-packaging-common/util';
18+
import { WebRequest, WebResponse, sendRequest } from 'azure-pipelines-tasks-utility-common/restutilities';
19+
1820

1921
class PublishOptions implements INuGetCommandOptions {
2022
constructor(
@@ -112,7 +114,7 @@ export async function run(nuGetPath: string): Promise<void> {
112114
let accessToken;
113115
let feed;
114116
const isInternalFeed: boolean = nugetFeedType === "internal";
115-
accessToken = getAccessToken(isInternalFeed);
117+
accessToken = await getAccessToken(isInternalFeed, urlPrefixes);
116118
const quirks = await ngToolRunner.getNuGetQuirksAsync(nuGetPath);
117119

118120
// Clauses ordered in this way to avoid short-circuit evaluation, so the debug info printed by the functions
@@ -414,9 +416,9 @@ function shouldUseVstsNuGetPush(isInternalFeed: boolean, conflictsAllowed: boole
414416
return false;
415417
}
416418

417-
function getAccessToken(isInternalFeed: boolean): string{
418-
let accessToken: string;
419+
async function getAccessToken(isInternalFeed: boolean, uriPrefixes: any): Promise<string>{
419420
let allowServiceConnection = tl.getVariable('PUBLISH_VIA_SERVICE_CONNECTION');
421+
let accessToken: string;
420422

421423
if(allowServiceConnection) {
422424
let endpoint = tl.getInput('externalEndpoint', false);
@@ -440,16 +442,25 @@ function getAccessToken(isInternalFeed: boolean): string{
440442
tl.debug("Checking for auth from Cred Provider.");
441443
const feed = getProjectAndFeedIdFromInputParam('feedPublish');
442444
const JsonEndpointsString = process.env["VSS_NUGET_EXTERNAL_FEED_ENDPOINTS"];
445+
443446
if (JsonEndpointsString) {
447+
tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`);
444448
tl.debug(`Endpoints found: ${JsonEndpointsString}`);
445449

446450
let endpointsArray: { endpointCredentials: EndpointCredentials[] } = JSON.parse(JsonEndpointsString);
447-
tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`);
448-
449-
for (let endpoint_in = 0; endpoint_in < endpointsArray.endpointCredentials.length; endpoint_in++) {
450-
if (endpointsArray.endpointCredentials[endpoint_in].endpoint.search(feed.feedName) != -1) {
451-
tl.debug(`Endpoint Credentials found for ${feed.feedName}`);
452-
accessToken = endpointsArray.endpointCredentials[endpoint_in].password;
451+
let matchingEndpoint: EndpointCredentials;
452+
for (const e of endpointsArray.endpointCredentials) {
453+
for (const prefix of uriPrefixes) {
454+
if (e.endpoint.toUpperCase().startsWith(prefix.toUpperCase())) {
455+
let isServiceConnectionValid = await tryServiceConnection(e, feed);
456+
if (isServiceConnectionValid) {
457+
matchingEndpoint = e;
458+
break;
459+
}
460+
}
461+
}
462+
if (matchingEndpoint) {
463+
accessToken = matchingEndpoint.password;
453464
break;
454465
}
455466
}
@@ -466,4 +477,37 @@ function getAccessToken(isInternalFeed: boolean): string{
466477
}
467478

468479
return accessToken;
469-
}
480+
}
481+
482+
async function tryServiceConnection(endpoint: EndpointCredentials, feed: any) : Promise<boolean>
483+
{
484+
// Create request
485+
const request = new WebRequest();
486+
const token64 = Buffer.from(`${endpoint.username}:${endpoint.password}`).toString('base64');
487+
request.uri = endpoint.endpoint;
488+
request.method = 'GET';
489+
request.headers = {
490+
"Content-Type": "application/json",
491+
"Authorization": "Basic " + token64
492+
};
493+
494+
const response = await sendRequest(request);
495+
496+
if(response.statusCode == 200) {
497+
if(response.body) {
498+
for (const entry of response.body.resources) {
499+
if (entry['@type'] === 'AzureDevOpsProjectId' && entry['label'].toUpperCase() !== feed.projectId.toUpperCase())
500+
{
501+
return false;
502+
}
503+
if (entry['@type'] === 'VssFeedId' && entry['label'].toUpperCase() !== feed.feedId.toUpperCase())
504+
{
505+
return false;
506+
}
507+
}
508+
// We found matches in feedId and projectId, return the service connection
509+
return true;
510+
}
511+
}
512+
return false;
513+
}

_generated/NuGetCommandV2_Node20/task.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"version": {
1111
"Major": 2,
1212
"Minor": 238,
13-
"Patch": 1
13+
"Patch": 2
1414
},
1515
"runsOn": [
1616
"Agent",
@@ -550,7 +550,7 @@
550550
"Warning_UnsupportedServiceConnectionAuth": "The service connection does not use a supported authentication method. Please use a service connection with personal access token based auth."
551551
},
552552
"_buildConfigMapping": {
553-
"Default": "2.238.0",
554-
"Node20_229_1": "2.238.1"
553+
"Default": "2.238.1",
554+
"Node20_229_1": "2.238.2"
555555
}
556556
}

0 commit comments

Comments
 (0)