Skip to content

Commit 0b2735d

Browse files
committed
Fix empty jfrogPlatformURL when using oidc
1 parent 99043e7 commit 0b2735d

File tree

5 files changed

+201
-3
lines changed

5 files changed

+201
-3
lines changed

.eslintignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ distribution.js
1313
pipBuild.js
1414
tests.js
1515
testUtils.js
16-
docker.js
16+
docker.js
17+
utilsTests.js

jfrog-tasks-utils/utils.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,6 @@ declare module '@jfrog/tasks-utils' {
5252
cliPath: string,
5353
buildDir: string,
5454
): string;
55+
export function parsePlatformUrlFromServiceUrl(serviceUrl: string): string;
5556
export { taskSelectedCliVersionEnv };
5657
}

jfrog-tasks-utils/utils.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ module.exports = {
118118
jfrogCliToolName: jfrogCliToolName,
119119
isServerIdEnvSupported: isServerIdEnvSupported,
120120
setJdkHomeForJavaTasks: setJdkHomeForJavaTasks,
121+
parsePlatformUrlFromServiceUrl: parsePlatformUrlFromServiceUrl,
121122
};
122123

123124
/**
@@ -437,7 +438,15 @@ function configureSpecificCliServer(service, urlFlag, serverId, cliPath, buildDi
437438
// username and access token params for further use by the users.
438439
if (oidcProviderName) {
439440
// we need platform url for oidc token exchange
440-
let platformUrl = tl.getEndpointAuthorizationParameter(service, 'jfrogPlatformUrl', true);
441+
let platformUrl = "";
442+
try {
443+
platformUrl = tl.getEndpointAuthorizationParameter(service, 'jfrogPlatformUrl', true);
444+
} catch (error) {
445+
console.warn('Failed to get platform url from field: ' + error+"\nparsing from url instead");
446+
}
447+
if (!platformUrl || !platformUrl.trim()) {
448+
platformUrl = parsePlatformUrlFromServiceUrl(serviceUrl);
449+
}
441450
serviceAccessToken = exchangeOidcTokenAndSetStepVariables(service, platformUrl, oidcProviderName, cliPath, buildDir);
442451
}
443452

@@ -953,6 +962,26 @@ function stripTrailingSlash(str) {
953962
return str.endsWith('/') ? str.slice(0, -1) : str;
954963
}
955964

965+
/**
966+
* Parse platform URL from a service URL by stripping service-specific suffixes.
967+
* Handles URLs ending with /xray, /artifactory, or /distribution (case-insensitive).
968+
*
969+
* @param serviceUrl - The service URL to parse (e.g., 'https://example.jfrog.io/artifactory')
970+
* @returns The platform URL with the service suffix removed
971+
*/
972+
function parsePlatformUrlFromServiceUrl(serviceUrl) {
973+
const suffixes = ['/xray', '/artifactory', '/distribution'];
974+
const url = new URL(serviceUrl);
975+
for (const suffix of suffixes) {
976+
if (url.pathname.endsWith(suffix)) {
977+
url.pathname = url.pathname.replace(suffix, '');
978+
break;
979+
}
980+
}
981+
// Remove trailing slash
982+
return url.toString().replace(/\/$/, '');
983+
}
984+
956985
/**
957986
* Determines the required working directory for running the cli.
958987
* Decision is based on the default path to run, and the provided path by the user.

tests/.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
tests.js.map
33
tests.d.ts
44
tests.js
5+
56
testUtils.js.map
67
testUtils.d.ts
7-
testUtils.js
8+
testUtils.js
9+
10+
utilsTests.js.map
11+
utilsTests.d.ts
12+
utilsTests.js

tests/utilsTests.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/// <reference types="mocha" />
2+
import * as assert from 'assert';
3+
4+
// Use require to get the actual module with latest exports
5+
import * as jfrogUtils from '@jfrog/tasks-utils';
6+
7+
/**
8+
* Simulates the platformUrl resolution logic from utils.js lines 441-449:
9+
*
10+
* let platformUrl = "";
11+
* try {
12+
* platformUrl = tl.getEndpointAuthorizationParameter(service, 'jfrogPlatformUrl', true);
13+
* } catch (error) {
14+
* console.warn('Failed to get platform url from field: ' + error + "\nparsing from url instead");
15+
* }
16+
* if (!platformUrl || !platformUrl.trim()) {
17+
* platformUrl = parsePlatformUrlFromServiceUrl(serviceUrl);
18+
* }
19+
*
20+
* @param getEndpointResult - Simulated result from tl.getEndpointAuthorizationParameter
21+
* @param shouldThrow - Whether the call should throw an error
22+
* @param serviceUrl - The service URL to parse from if platformUrl is not available
23+
* @returns The resolved platform URL
24+
*/
25+
function simulatePlatformUrlResolution(
26+
getEndpointResult: string | undefined | null,
27+
shouldThrow: boolean,
28+
serviceUrl: string,
29+
): string {
30+
let platformUrl: string | undefined | null = '';
31+
try {
32+
if (shouldThrow) {
33+
throw new Error('Simulated error');
34+
}
35+
platformUrl = getEndpointResult;
36+
} catch {
37+
// Simulates: console.warn('Failed to get platform url from field...')
38+
}
39+
if (!platformUrl || !platformUrl.trim()) {
40+
platformUrl = jfrogUtils.parsePlatformUrlFromServiceUrl(serviceUrl);
41+
}
42+
return platformUrl;
43+
}
44+
45+
describe('Utils Unit Tests', (): void => {
46+
describe('platformUrl resolution (simulating tl.getEndpointAuthorizationParameter)', (): void => {
47+
const serviceUrl: string = 'https://example.jfrog.io/artifactory';
48+
49+
it('should use platformUrl directly when getEndpointAuthorizationParameter returns valid URL', (): void => {
50+
const result: string = simulatePlatformUrlResolution('https://my-platform.jfrog.io', false, serviceUrl);
51+
assert.strictEqual(result, 'https://my-platform.jfrog.io');
52+
});
53+
54+
it('should parse from serviceUrl when getEndpointAuthorizationParameter returns empty string', (): void => {
55+
const result: string = simulatePlatformUrlResolution('', false, serviceUrl);
56+
assert.strictEqual(result, 'https://example.jfrog.io');
57+
});
58+
59+
it('should parse from serviceUrl when getEndpointAuthorizationParameter returns undefined', (): void => {
60+
const result: string = simulatePlatformUrlResolution(undefined, false, serviceUrl);
61+
assert.strictEqual(result, 'https://example.jfrog.io');
62+
});
63+
64+
it('should parse from serviceUrl when getEndpointAuthorizationParameter returns null', (): void => {
65+
const result: string = simulatePlatformUrlResolution(null, false, serviceUrl);
66+
assert.strictEqual(result, 'https://example.jfrog.io');
67+
});
68+
69+
it('should parse from serviceUrl when getEndpointAuthorizationParameter returns whitespace only', (): void => {
70+
const result: string = simulatePlatformUrlResolution(' ', false, serviceUrl);
71+
assert.strictEqual(result, 'https://example.jfrog.io');
72+
});
73+
74+
it('should parse from serviceUrl when getEndpointAuthorizationParameter throws error', (): void => {
75+
const result: string = simulatePlatformUrlResolution('', true, serviceUrl);
76+
assert.strictEqual(result, 'https://example.jfrog.io');
77+
});
78+
79+
it('should parse from xray serviceUrl when platformUrl not available', (): void => {
80+
const result: string = simulatePlatformUrlResolution('', false, 'https://example.jfrog.io/xray');
81+
assert.strictEqual(result, 'https://example.jfrog.io');
82+
});
83+
84+
it('should parse from distribution serviceUrl when platformUrl not available', (): void => {
85+
const result: string = simulatePlatformUrlResolution('', false, 'https://example.jfrog.io/distribution');
86+
assert.strictEqual(result, 'https://example.jfrog.io');
87+
});
88+
});
89+
90+
describe('parsePlatformUrlFromServiceUrl', (): void => {
91+
it('should strip /artifactory suffix', (): void => {
92+
assert.strictEqual(
93+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.jfrog.io/artifactory'),
94+
'https://example.jfrog.io',
95+
);
96+
});
97+
98+
it('should strip /xray suffix', (): void => {
99+
assert.strictEqual(
100+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.jfrog.io/xray'),
101+
'https://example.jfrog.io',
102+
);
103+
});
104+
105+
it('should strip /distribution suffix', (): void => {
106+
assert.strictEqual(
107+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.jfrog.io/distribution'),
108+
'https://example.jfrog.io',
109+
);
110+
});
111+
112+
it('should handle case-insensitive matching for /Artifactory', (): void => {
113+
assert.strictEqual(
114+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.jfrog.io/Artifactory'),
115+
'https://example.jfrog.io',
116+
);
117+
});
118+
119+
it('should handle case-insensitive matching for /XRAY', (): void => {
120+
assert.strictEqual(
121+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.jfrog.io/XRAY'),
122+
'https://example.jfrog.io',
123+
);
124+
});
125+
126+
it('should return URL as-is when no known suffix', (): void => {
127+
assert.strictEqual(
128+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.jfrog.io/other'),
129+
'https://example.jfrog.io/other',
130+
);
131+
});
132+
133+
it('should handle URL with port', (): void => {
134+
assert.strictEqual(
135+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.jfrog.io:8080/artifactory'),
136+
'https://example.jfrog.io:8080',
137+
);
138+
});
139+
140+
it('should handle URL with trailing slash on suffix', (): void => {
141+
assert.strictEqual(
142+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.jfrog.io/artifactory/'),
143+
'https://example.jfrog.io',
144+
);
145+
});
146+
147+
it('should handle URL with path prefix', (): void => {
148+
assert.strictEqual(
149+
jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.com/jfrog/artifactory'),
150+
'https://example.com/jfrog',
151+
);
152+
});
153+
154+
it('should handle URL without any prefix', (): void => {
155+
assert.strictEqual(jfrogUtils.parsePlatformUrlFromServiceUrl('https://example.com/jfrog'), 'https://example.com/jfrog');
156+
});
157+
158+
it('should handle hostnames as well', (): void => {
159+
assert.strictEqual(jfrogUtils.parsePlatformUrlFromServiceUrl('https://artifactory.com/jfrog/artifactory'), 'https://artifactory.com/jfrog');
160+
});
161+
});
162+
});

0 commit comments

Comments
 (0)