Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix basic HTTP authentication when resolving dependencies #1838

Merged
merged 3 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions smithy-cli/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ dependencies {
implementation "org.apache.maven.resolver:maven-resolver-transport-file:1.9.2"
implementation "org.apache.maven.resolver:maven-resolver-transport-http:1.9.2"
implementation "org.slf4j:slf4j-jdk14:1.7.36" // Route slf4j used by Maven through JUL like the rest of Smithy.

testImplementation "org.mock-server:mockserver-netty:3.10.8"
}

// ------ Shade Maven dependency resolvers into the JAR. -------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.mockserver.integration.ClientAndServer.startClientAndServer;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.utils.ListUtils;
Expand Down Expand Up @@ -61,14 +65,46 @@ public void failsWhenBadVersionRequested() {
});
}

// TODO: This test could be better and actually test that auth works somehow.
@Test
public void usesCustomRepoWithAuth() {
IntegUtils.runWithEmptyCache("maven-auth", ListUtils.of("validate", "--debug"),
Collections.emptyMap(), result -> {
assertThat(result.getExitCode(), equalTo(1));
assertThat(result.getOutput(), containsString("with xxx=****"));
});
ClientAndServer mockServer = null;
try {
mockServer = startClientAndServer(1234);
mockServer.when(
HttpRequest
.request()
.withMethod("GET")
.withHeader("Authorization", "Basic eHh4Onl5eQ==")
.withPath("/maven/not/there/software/amazon/smithy/smithy-aws-iam-traits/.*\\.jar")
).respond(
HttpResponse
.response()
.withStatusCode(200)
.withBody("FAKE JAR CONTENT")
);
mockServer.when(
HttpRequest
.request()
.withMethod("GET")
.withPath("/maven/not/there/software/amazon/smithy/smithy-aws-iam-traits/.*")
).respond(
HttpResponse
.response()
.withStatusCode(401)
.withHeader("WWW-Authenticate", "Basic realm=\"Artifactory Realm\"")
);

IntegUtils.runWithEmptyCache("maven-auth", ListUtils.of("validate", "--debug"),
Collections.emptyMap(), result -> {
assertThat(result.getExitCode(), equalTo(1));
assertThat(result.getOutput(), containsString("HttpAuthenticator - Selected authentication options: [BASIC [complete=true]]"));
assertThat(result.getOutput(), containsString("HttpAuthenticator - Authentication succeeded"));
});
} finally {
if(mockServer!=null) {
mockServer.stop();
}
}
}

@Test
Expand Down Expand Up @@ -193,7 +229,7 @@ public void setSetMavenRepoWithEnvUsingAuth() {
ListUtils.of("validate", "--debug", "model"),
MapUtils.of(EnvironmentVariable.SMITHY_MAVEN_REPOS.toString(), repo),
result -> {
assertThat(result.getOutput(), containsString("with xxx=****"));
assertThat(result.getOutput(), containsString("username=xxx, password=***"));
assertThat(result.getExitCode(), equalTo(1));
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"maven": {
"repositories": [
{
"url": "https://localhost:1234/maven/not/there",
// Use HTTP instead of HTTPS because we're running a mock server during tests
"url": "http://localhost:1234/maven/not/there",
"httpCredentials": "xxx:yyy"
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Logger;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
Expand All @@ -36,9 +34,6 @@
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.AuthenticationContext;
import org.eclipse.aether.repository.AuthenticationDigest;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactResult;
Expand All @@ -49,8 +44,8 @@
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import software.amazon.smithy.build.model.MavenRepository;
import software.amazon.smithy.utils.StringUtils;

/**
* Resolves Maven dependencies for the Smithy CLI using Maven resolvers.
Expand Down Expand Up @@ -121,7 +116,12 @@ private void addUserInfoAuth(URI uri, String userInfo, RemoteRepository.Builder
if (parts.length != 2) {
throw new DependencyResolverException("Invalid credentials provided for " + uri);
}
builder.setAuthentication(new MavenAuth(parts[0], parts[1]));
builder.setAuthentication(
new AuthenticationBuilder()
.addUsername(parts[0])
.addPassword(parts[1])
.build()
);
}

@Override
Expand Down Expand Up @@ -190,51 +190,4 @@ private List<ArtifactResult> resolveMavenArtifacts() {
throw new DependencyResolverException(e);
}
}

/**
* Based on Maven's StringAuthentication. There doesn't appear to be another way to do this.
*/
private static final class MavenAuth implements Authentication {
private final String key;
private final String value;

private MavenAuth(String key, String value) {
if (StringUtils.isEmpty(key)) {
throw new IllegalArgumentException("Authentication key must be provided");
}
this.key = key;
this.value = value;
}

@Override
public void fill(AuthenticationContext context, String key, Map<String, String> data) {
context.put(this.key, value);
}

@Override
public void digest(AuthenticationDigest digest) {
digest.update(key, value);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
MavenAuth that = (MavenAuth) obj;
return Objects.equals(key, that.key) && Objects.equals(value, that.value);
}

@Override
public int hashCode() {
return Objects.hash(key, value);
}

@Override
public String toString() {
return key + "=****";
}
}
}