Skip to content

Commit

Permalink
Fix basic HTTP authentication when resolving dependencies (smithy-lan…
Browse files Browse the repository at this point in the history
…g#1838)

Fixes smithy-lang#1837

Replace `MavenAuth` with existing aether builder to configure the repository auth context.
Modify the auth test to use a mock server to actually run the authentication flow.
  • Loading branch information
denisrosca authored and Steven Yuan committed Aug 11, 2023
1 parent 76f87ba commit b6ec195
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 62 deletions.
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 + "=****";
}
}
}

0 comments on commit b6ec195

Please sign in to comment.