Skip to content

Commit 9aa33c6

Browse files
authored
Do not include auth header when redirecting to other domain or when switching to insecure protocol (#334)
2 parents 0161a89 + 682edfa commit 9aa33c6

28 files changed

+280
-34
lines changed

src/main/java/land/oras/auth/HttpClient.java

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.HashMap;
3434
import java.util.List;
3535
import java.util.Map;
36+
import java.util.Objects;
3637
import java.util.regex.Matcher;
3738
import java.util.regex.Pattern;
3839
import java.util.stream.Collectors;
@@ -150,6 +151,7 @@ public ResponseWrapper<String> get(URI uri, Map<String, String> headers, Scopes
150151
return executeRequest(
151152
"GET",
152153
uri,
154+
true,
153155
headers,
154156
new byte[0],
155157
HttpResponse.BodyHandlers.ofString(),
@@ -172,6 +174,7 @@ public ResponseWrapper<Path> download(
172174
return executeRequest(
173175
"GET",
174176
uri,
177+
true,
175178
headers,
176179
new byte[0],
177180
HttpResponse.BodyHandlers.ofFile(file),
@@ -193,6 +196,7 @@ public ResponseWrapper<InputStream> download(
193196
return executeRequest(
194197
"GET",
195198
uri,
199+
true,
196200
headers,
197201
new byte[0],
198202
HttpResponse.BodyHandlers.ofInputStream(),
@@ -217,6 +221,7 @@ public ResponseWrapper<String> upload(
217221
return executeRequest(
218222
method,
219223
uri,
224+
true,
220225
headers,
221226
new byte[0],
222227
HttpResponse.BodyHandlers.ofString(),
@@ -241,6 +246,7 @@ public ResponseWrapper<String> head(
241246
return executeRequest(
242247
"HEAD",
243248
uri,
249+
true,
244250
headers,
245251
new byte[0],
246252
HttpResponse.BodyHandlers.ofString(),
@@ -262,6 +268,7 @@ public ResponseWrapper<String> delete(
262268
return executeRequest(
263269
"DELETE",
264270
uri,
271+
true,
265272
headers,
266273
new byte[0],
267274
HttpResponse.BodyHandlers.ofString(),
@@ -284,6 +291,7 @@ public ResponseWrapper<String> post(
284291
return executeRequest(
285292
"POST",
286293
uri,
294+
true,
287295
headers,
288296
body,
289297
HttpResponse.BodyHandlers.ofString(),
@@ -306,6 +314,7 @@ public ResponseWrapper<String> patch(
306314
return executeRequest(
307315
"PATCH",
308316
uri,
317+
true,
309318
headers,
310319
body,
311320
HttpResponse.BodyHandlers.ofString(),
@@ -328,6 +337,7 @@ public ResponseWrapper<String> put(
328337
return executeRequest(
329338
"PUT",
330339
uri,
340+
true,
331341
headers,
332342
body,
333343
HttpResponse.BodyHandlers.ofString(),
@@ -397,6 +407,22 @@ public <T> TokenResponse refreshToken(
397407
return JsonUtils.fromJson(responseWrapper.response(), TokenResponse.class);
398408
}
399409

410+
static boolean isSameOrigin(URI uri1, URI uri2) {
411+
return Objects.equals(uri1.getScheme(), uri2.getScheme())
412+
&& Objects.equals(uri1.getHost(), uri2.getHost())
413+
&& getPort(uri1) == getPort(uri2);
414+
}
415+
416+
static int getPort(URI uri) {
417+
return uri.getPort() != -1 ? uri.getPort() : ("https".equals(uri.getScheme()) ? 443 : 80);
418+
}
419+
420+
static <T> boolean shouldRedirect(HttpResponse<T> response) {
421+
return response.statusCode() == HttpURLConnection.HTTP_MOVED_PERM
422+
|| response.statusCode() == HttpURLConnection.HTTP_MOVED_TEMP
423+
|| response.statusCode() == 307;
424+
}
425+
400426
/**
401427
* Execute a request
402428
* @param method The method
@@ -411,6 +437,7 @@ public <T> TokenResponse refreshToken(
411437
private <T> ResponseWrapper<T> executeRequest(
412438
String method,
413439
URI uri,
440+
boolean includeAuthHeader,
414441
Map<String, String> headers,
415442
byte[] body,
416443
HttpResponse.BodyHandler<T> handler,
@@ -435,7 +462,8 @@ private <T> ResponseWrapper<T> executeRequest(
435462

436463
// Add authentication header if any
437464
if (authProvider.getAuthHeader(containerRef) != null
438-
&& !authProvider.getAuthScheme().equals(AuthScheme.NONE)) {
465+
&& !authProvider.getAuthScheme().equals(AuthScheme.NONE)
466+
&& includeAuthHeader) {
439467
builder = builder.header(Const.AUTHORIZATION_HEADER, authProvider.getAuthHeader(containerRef));
440468
}
441469
headers.forEach(builder::header);
@@ -450,9 +478,22 @@ private <T> ResponseWrapper<T> executeRequest(
450478
// Follow redirect
451479
if (shouldRedirect(response)) {
452480
String location = getLocationHeader(response);
453-
LOG.debug("Redirecting to {}", location);
481+
URI redirectUri = URI.create(location);
482+
LOG.debug("Redirecting to {} from domain {} to domain {}", location, uri, redirectUri);
483+
boolean includeAuthHeaderForRedirect = isSameOrigin(uri, redirectUri);
484+
if (!includeAuthHeaderForRedirect) {
485+
LOG.debug("Skipping auth header for redirect from {} to {}", uri, redirectUri);
486+
}
454487
return executeRequest(
455-
method, URI.create(location), headers, body, handler, bodyPublisher, newScopes, authProvider);
488+
method,
489+
redirectUri,
490+
includeAuthHeaderForRedirect,
491+
headers,
492+
body,
493+
handler,
494+
bodyPublisher,
495+
newScopes,
496+
authProvider);
456497
}
457498
return redoRequest(response, builder, handler, newScopes, authProvider);
458499
} catch (Exception e) {
@@ -461,12 +502,6 @@ private <T> ResponseWrapper<T> executeRequest(
461502
}
462503
}
463504

464-
private <T> boolean shouldRedirect(HttpResponse<T> response) {
465-
return response.statusCode() == HttpURLConnection.HTTP_MOVED_PERM
466-
|| response.statusCode() == HttpURLConnection.HTTP_MOVED_TEMP
467-
|| response.statusCode() == 307;
468-
}
469-
470505
private <T> String getLocationHeader(HttpResponse<T> response) {
471506
return response.headers()
472507
.firstValue("Location")

src/test/java/land/oras/AnnotationsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import org.junit.jupiter.api.parallel.ExecutionMode;
2929

3030
@Execution(ExecutionMode.CONCURRENT)
31-
public class AnnotationsTest {
31+
class AnnotationsTest {
3232

3333
@Test
3434
public void fromJson() {

src/test/java/land/oras/ArtifactTypeTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import org.junit.jupiter.api.parallel.ExecutionMode;
3131

3232
@Execution(ExecutionMode.CONCURRENT)
33-
public class ArtifactTypeTest {
33+
class ArtifactTypeTest {
3434

3535
@Test
3636
void validateArtifactType() {

src/test/java/land/oras/ClassAnnotationsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import org.junit.jupiter.api.parallel.ExecutionMode;
3535

3636
@Execution(ExecutionMode.CONCURRENT)
37-
public class ClassAnnotationsTest {
37+
class ClassAnnotationsTest {
3838

3939
@Test
4040
void shouldHaveAnnotationOnModel() {

src/test/java/land/oras/ConfigTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import org.junit.jupiter.api.parallel.ExecutionMode;
3030

3131
@Execution(ExecutionMode.CONCURRENT)
32-
public class ConfigTest {
32+
class ConfigTest {
3333

3434
@Test
3535
void shouldSerializeEmptyConfig() {

src/test/java/land/oras/DockerIoITCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import org.junit.jupiter.api.parallel.ExecutionMode;
3131

3232
@Execution(ExecutionMode.CONCURRENT)
33-
public class DockerIoITCase {
33+
class DockerIoITCase {
3434

3535
@TempDir
3636
Path tempDir;

src/test/java/land/oras/GitHubContainerRegistryITCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import org.junit.jupiter.api.parallel.ExecutionMode;
3131

3232
@Execution(ExecutionMode.CONCURRENT)
33-
public class GitHubContainerRegistryITCase {
33+
class GitHubContainerRegistryITCase {
3434

3535
@TempDir
3636
Path tempDir;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*-
2+
* =LICENSE=
3+
* ORAS Java SDK
4+
* ===
5+
* Copyright (C) 2024 - 2025 ORAS
6+
* ===
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* =LICENSEEND=
19+
*/
20+
21+
package land.oras;
22+
23+
import static org.junit.jupiter.api.Assertions.assertNotNull;
24+
25+
import java.nio.file.Path;
26+
import org.junit.jupiter.api.Disabled;
27+
import org.junit.jupiter.api.Test;
28+
import org.junit.jupiter.api.io.TempDir;
29+
import org.junit.jupiter.api.parallel.Execution;
30+
import org.junit.jupiter.api.parallel.ExecutionMode;
31+
32+
@Execution(ExecutionMode.CONCURRENT)
33+
class HarborS3ITCase {
34+
35+
@TempDir
36+
Path tempDir;
37+
38+
@Test
39+
@Disabled("Only to test with demo Harbor S3 instance")
40+
void shouldPullOneBlob() {
41+
Registry registry = Registry.builder().defaults().build();
42+
ContainerRef containerRef1 = ContainerRef.parse("demo.goharbor.io/oras/lib:foo");
43+
Manifest manifest = registry.getManifest(containerRef1);
44+
Layer oneLayer = manifest.getLayers().get(0);
45+
registry.fetchBlob(containerRef1.withDigest(oneLayer.getDigest()), tempDir.resolve("my-blob"));
46+
assertNotNull(tempDir.resolve("my-blob"));
47+
}
48+
}

src/test/java/land/oras/IndexTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import org.junit.jupiter.api.parallel.ExecutionMode;
3232

3333
@Execution(ExecutionMode.CONCURRENT)
34-
public class IndexTest {
34+
class IndexTest {
3535

3636
@Test
3737
void shouldReadAndWriteIndex() {

src/test/java/land/oras/JFrogArtifactoryITCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import org.junit.jupiter.api.parallel.ExecutionMode;
3131

3232
@Execution(ExecutionMode.CONCURRENT)
33-
public class JFrogArtifactoryITCase {
33+
class JFrogArtifactoryITCase {
3434

3535
@TempDir
3636
Path tempDir;

0 commit comments

Comments
 (0)