Skip to content

Commit 8408c64

Browse files
committed
Try anonymous first, retry with credentials on 4xx
Mirrors Apache Maven Resolver's DeferredCredentialsProvider behavior: issue an unauthenticated request first, then send credentials only when the server challenges with a 4xx. This matches what users get from running Maven directly, and avoids leaking credentials to public artifacts.
1 parent 03b7ac4 commit 8408c64

2 files changed

Lines changed: 18 additions & 16 deletions

File tree

rewrite-maven/src/main/java/org/openrewrite/maven/utilities/MavenArtifactDownloader.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,21 @@ public MavenArtifactDownloader(MavenArtifactCache mavenArtifactCache,
109109
} else if ("file".equals(URI.create(uri).getScheme())) {
110110
bodyStream = Files.newInputStream(Paths.get(URI.create(uri)));
111111
} else {
112-
HttpSender.Request.Builder request = applyAuthentication(dependency.getRepository(), httpSender.get(uri));
113112
try {
113+
// Try anonymously first, mirroring Apache Maven's DeferredCredentialsProvider behavior
114114
byte[] responseBytes = null;
115115
int responseCode;
116-
try (HttpSender.Response response = Failsafe.with(retryPolicy).get(() -> httpSender.send(request.build()));
116+
try (HttpSender.Response response = Failsafe.with(retryPolicy).get(() -> httpSender.send(httpSender.get(uri).build()));
117117
InputStream body = response.getBody()) {
118118
responseCode = response.getCode();
119119
if (response.isSuccessful() && body != null) {
120120
responseBytes = readAllBytes(body);
121121
}
122122
}
123-
// Fall back to anonymous if authenticated request fails with a 4xx client error
124-
if (responseBytes == null && hasCredentials(dependency.getRepository()) && responseCode >= 400 && responseCode < 500) {
125-
try (HttpSender.Response response = Failsafe.with(retryPolicy).get(() -> httpSender.send(httpSender.get(uri).build()));
123+
// Retry with credentials if the anonymous request failed with a 4xx and we have credentials
124+
if (responseBytes == null && responseCode >= 400 && responseCode < 500 && hasCredentials(dependency.getRepository())) {
125+
HttpSender.Request.Builder request = applyAuthentication(dependency.getRepository(), httpSender.get(uri));
126+
try (HttpSender.Response response = Failsafe.with(retryPolicy).get(() -> httpSender.send(request.build()));
126127
InputStream body = response.getBody()) {
127128
responseCode = response.getCode();
128129
if (response.isSuccessful() && body != null) {

rewrite-maven/src/test/java/org/openrewrite/maven/utilities/MavenArtifactDownloaderTest.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ void downloadDependenciesWithClassifier(@TempDir Path tempDir) {
133133
}
134134

135135
@Test
136-
void fallsBackToAnonymousWhenServerReturns401(@TempDir Path tempDir) throws Exception {
137-
byte[] jarBytes = {0x50, 0x4B, 0x03, 0x04};
136+
void publicArtifactsResolveAnonymouslyEvenWhenCredentialsAreInvalid(@TempDir Path tempDir) throws Exception {
137+
byte[] jarBytes = {0x50, 0x4B, 0x03, 0x04}; // minimal ZIP magic bytes
138138

139139
try (MockWebServer mockRepo = new MockWebServer()) {
140140
mockRepo.setDispatcher(new Dispatcher() {
@@ -183,22 +183,21 @@ public MockResponse dispatch(RecordedRequest request) {
183183

184184
assertThat(artifact).isNotNull();
185185
assertThat(error.get()).isNull();
186-
assertThat(mockRepo.getRequestCount()).isEqualTo(2);
187-
assertThat(mockRepo.takeRequest().getHeader("Authorization")).isNotNull();
186+
assertThat(mockRepo.getRequestCount()).isEqualTo(1);
188187
assertThat(mockRepo.takeRequest().getHeader("Authorization")).isNull();
189188
}
190189
}
191190

192191
@Test
193-
void fallsBackToAnonymousWhenCredentialsRejected(@TempDir Path tempDir) throws Exception {
194-
byte[] jarBytes = {0x50, 0x4B, 0x03, 0x04}; // minimal ZIP magic bytes
192+
void retriesWithCredentialsWhenAnonymousReturns401(@TempDir Path tempDir) throws Exception {
193+
byte[] jarBytes = {0x50, 0x4B, 0x03, 0x04};
195194

196195
try (MockWebServer mockRepo = new MockWebServer()) {
197196
mockRepo.setDispatcher(new Dispatcher() {
198197
@Override
199198
public MockResponse dispatch(RecordedRequest request) {
200-
if (request.getHeader("Authorization") != null) {
201-
return new MockResponse().setResponseCode(403); // Throw if used; it should not be called at all
199+
if (request.getHeader("Authorization") == null) {
200+
return new MockResponse().setResponseCode(401);
202201
}
203202
return new MockResponse().setResponseCode(200)
204203
.setBody(new okio.Buffer().write(jarBytes));
@@ -215,8 +214,8 @@ public MockResponse dispatch(RecordedRequest request) {
215214
<servers>
216215
<server>
217216
<id>mock-repo</id>
218-
<username>${placeholder}</username>
219-
<password>${placeholder}</password>
217+
<username>good-user</username>
218+
<password>good-password</password>
220219
</server>
221220
</servers>
222221
</settings>
@@ -240,7 +239,9 @@ public MockResponse dispatch(RecordedRequest request) {
240239

241240
assertThat(artifact).isNotNull();
242241
assertThat(error.get()).isNull();
243-
assertThat(mockRepo.getRequestCount()).isEqualTo(1);
242+
assertThat(mockRepo.getRequestCount()).isEqualTo(2);
243+
assertThat(mockRepo.takeRequest().getHeader("Authorization")).isNull();
244+
assertThat(mockRepo.takeRequest().getHeader("Authorization")).isNotNull();
244245
}
245246
}
246247
}

0 commit comments

Comments
 (0)