Skip to content

Commit

Permalink
Support eventbridge MREP
Browse files Browse the repository at this point in the history
This commit adds support for multi-region endpoints in EventBridge.
  • Loading branch information
Bennett-Lynch authored and dagnir committed Apr 7, 2022
1 parent 89abf18 commit efb5655
Show file tree
Hide file tree
Showing 21 changed files with 898 additions and 76 deletions.
@@ -0,0 +1,63 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.auth.signer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import software.amazon.awssdk.annotations.SdkProtectedApi;
import software.amazon.awssdk.core.internal.util.ClassLoaderHelper;
import software.amazon.awssdk.core.signer.Signer;

/**
* Utility class for instantiating signers only if they're available on the class path.
*/
@SdkProtectedApi
public final class SignerLoader {

private static final Map<String, Signer> SIGNERS = new ConcurrentHashMap<>();

private SignerLoader() {
}

public static Signer getSigV4aSigner() {
return get("software.amazon.awssdk.authcrt.signer.AwsCrtV4aSigner");
}

public static Signer getS3SigV4aSigner() {
return get("software.amazon.awssdk.authcrt.signer.AwsCrtS3V4aSigner");
}

private static Signer get(String fqcn) {
return SIGNERS.computeIfAbsent(fqcn, SignerLoader::initializeV4aSigner);
}

private static Signer initializeV4aSigner(String fqcn) {
try {
Class<?> signerClass = ClassLoaderHelper.loadClass(fqcn, false, (Class) null);
Method m = signerClass.getDeclaredMethod("create");
Object o = m.invoke(null);
return (Signer) o;
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Cannot find the " + fqcn + " class."
+ " To invoke a request that requires a SigV4a signer, such as region independent " +
"signing, the 'auth-crt' core module must be on the class path. ", e);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException("Failed to create " + fqcn, e);
}
}
}
@@ -0,0 +1,83 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.awscore.util;

import java.util.Optional;
import java.util.function.Supplier;
import software.amazon.awssdk.annotations.SdkProtectedApi;
import software.amazon.awssdk.awscore.AwsRequest;
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
import software.amazon.awssdk.core.RequestOverrideConfiguration;
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
import software.amazon.awssdk.core.signer.Signer;

/**
* Utility to override a given {@link SdkRequest}'s {@link Signer}. Typically used by {@link ExecutionInterceptor}s that wish to
* dynamically enable particular signing methods, like SigV4a for multi-region endpoints.
*/
@SdkProtectedApi
public final class SignerOverrideUtils {
private SignerOverrideUtils() {
}

public static SdkRequest overrideSignerIfNotOverridden(SdkRequest request,
ExecutionAttributes executionAttributes,
Signer signer) {
return overrideSignerIfNotOverridden(request, executionAttributes, () -> signer);
}

public static SdkRequest overrideSignerIfNotOverridden(SdkRequest request,
ExecutionAttributes executionAttributes,
Supplier<Signer> signer) {
if (isSignerOverridden(request, executionAttributes)) {
return request;
}
return overrideSigner(request, signer.get());
}

private static boolean isSignerOverridden(SdkRequest request, ExecutionAttributes executionAttributes) {
Optional<Boolean> isClientSignerOverridden = Optional.ofNullable(
executionAttributes.getAttribute(SdkExecutionAttribute.SIGNER_OVERRIDDEN));
Optional<Signer> requestSigner = request.overrideConfiguration()
.flatMap(RequestOverrideConfiguration::signer);
return isClientSignerOverridden.isPresent() || requestSigner.isPresent();
}

private static SdkRequest overrideSigner(SdkRequest request, Signer signer) {
return request.overrideConfiguration()
.flatMap(config -> config.signer()
.map(existingOverrideSigner -> request))
.orElseGet(() -> createNewRequest(request, signer));
}

private static SdkRequest createNewRequest(SdkRequest request, Signer signer) {
AwsRequest awsRequest = (AwsRequest) request;

AwsRequestOverrideConfiguration modifiedOverride =
awsRequest.overrideConfiguration()
.map(AwsRequestOverrideConfiguration::toBuilder)
.orElseGet(AwsRequestOverrideConfiguration::builder)
.signer(signer)
.build();

return awsRequest.toBuilder()
.overrideConfiguration(modifiedOverride)
.build();
}
}
@@ -0,0 +1,118 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.awscore.util;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.awscore.AwsRequest;
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
import software.amazon.awssdk.awscore.client.http.NoopTestAwsRequest;
import software.amazon.awssdk.core.ApiName;
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.utils.Pair;

class SignerOverrideUtilsTest {

@Test
@DisplayName("If signer is already overridden, assert that it is not modified")
void overrideSignerIfNotOverridden() {
Pair<SdkRequest, ExecutionAttributes> stubs = stubInitialSigner(new FooSigner());
SdkRequest request = stubs.left();
ExecutionAttributes executionAttributes = stubs.right();

SdkRequest result = SignerOverrideUtils.overrideSignerIfNotOverridden(request, executionAttributes, BarSigner::new);

assertThat(result.overrideConfiguration()).isPresent();
assertThat(result.overrideConfiguration().get().signer().get()).isInstanceOf(FooSigner.class);
}

@Test
@DisplayName("If signer is not already overridden, assert that it is overridden with the new signer")
void overrideSignerIfNotOverridden2() {
Pair<SdkRequest, ExecutionAttributes> stubs = stubInitialSigner(null);
SdkRequest request = stubs.left();
ExecutionAttributes executionAttributes = stubs.right();

SdkRequest result = SignerOverrideUtils.overrideSignerIfNotOverridden(request, executionAttributes, BarSigner::new);

assertThat(result.overrideConfiguration()).isPresent();
assertThat(result.overrideConfiguration().get().signer().get()).isInstanceOf(BarSigner.class);
}

@Test
@DisplayName("If signer will be overridden, assert that the other existing override configuration properties are preserved")
public void overrideSignerOriginalConfigPreserved() {
AwsRequestOverrideConfiguration originalOverride =
AwsRequestOverrideConfiguration.builder()
.putHeader("Header1", "HeaderValue1")
.putRawQueryParameter("QueryParam1", "QueryValue1")
.addApiName(ApiName.builder().name("foo").version("bar").build())
.build();

Pair<SdkRequest, ExecutionAttributes> stubs = stubInitialSigner(null);
SdkRequest request = ((AwsRequest) stubs.left()).toBuilder()
.overrideConfiguration(originalOverride)
.build();
ExecutionAttributes executionAttributes = stubs.right();

BarSigner overrideSigner = new BarSigner();
SdkRequest result = SignerOverrideUtils.overrideSignerIfNotOverridden(request, executionAttributes, () -> overrideSigner);
AwsRequestOverrideConfiguration originalOverrideWithNewSigner = originalOverride.toBuilder()
.signer(overrideSigner)
.build();

assertThat(result.overrideConfiguration().get()).isEqualTo(originalOverrideWithNewSigner);
}

private static Pair<SdkRequest, ExecutionAttributes> stubInitialSigner(Signer signer) {
AwsRequest request;
if (signer != null) {
AwsRequestOverrideConfiguration config = AwsRequestOverrideConfiguration.builder()
.signer(signer)
.build();
request = NoopTestAwsRequest.builder()
.overrideConfiguration(config)
.build();
} else {
request = NoopTestAwsRequest.builder().build();
}
ExecutionAttributes executionAttributes = new ExecutionAttributes();
if (signer != null) {
executionAttributes.putAttribute(SdkExecutionAttribute.SIGNER_OVERRIDDEN, true);
}
return Pair.of(request, executionAttributes);
}

private static class FooSigner implements Signer {
@Override
public SdkHttpFullRequest sign(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
throw new UnsupportedOperationException();
}
}

private static class BarSigner implements Signer {
@Override
public SdkHttpFullRequest sign(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
throw new UnsupportedOperationException();
}
}
}
Expand Up @@ -29,6 +29,9 @@ private static Class<?> loadClassViaClasses(String fqcn, Class<?>[] classes) {
}

for (Class<?> clzz: classes) {
if (clzz == null) {
continue;
}
ClassLoader loader = clzz.getClassLoader();
if (loader != null) {
try {
Expand Down
13 changes: 13 additions & 0 deletions services/eventbridge/pom.xml
Expand Up @@ -56,5 +56,18 @@
<artifactId>aws-json-protocol</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>auth-crt</artifactId>
<version>${awsjavasdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>route53</artifactId>
<version>${awsjavasdk.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
38 changes: 38 additions & 0 deletions services/eventbridge/src/it/java/resources/log4j2.properties
@@ -0,0 +1,38 @@
#
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at
#
# http://aws.amazon.com/apache2.0
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.
#

status = warn

appender.console.type = Console
appender.console.name = ConsoleAppender
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n%throwable

rootLogger.level = info
rootLogger.appenderRef.stdout.ref = ConsoleAppender

# Uncomment below to enable more specific logging
#
#logger.sdk.name = software.amazon.awssdk
#logger.sdk.level = debug
#
#logger.request.name = software.amazon.awssdk.request
#logger.request.level = debug
#
#logger.apache.name = org.apache.http.wire
#logger.apache.level = debug
#
#logger.netty.name = io.netty.handler.logging
#logger.netty.level = debug

0 comments on commit efb5655

Please sign in to comment.