Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public enum AuthType {
this.value = value;
}

public String value() {
return value;
}

public static AuthType fromValue(String value) {
String normalizedValue = StringUtils.lowerCase(value);
return Arrays.stream(values())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import software.amazon.awssdk.codegen.model.service.AuthType;
import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
Expand All @@ -44,48 +47,6 @@ public final class AuthSchemeCodegenMetadata {
.build())
.build();

static final AuthSchemeCodegenMetadata SIGV4_UNSIGNED_BODY =
SIGV4.toBuilder()
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName("PAYLOAD_SIGNING_ENABLED")
.valueEmitter((spec, utils) -> spec.addCode("$L", false))
.build())
.build();

static final AuthSchemeCodegenMetadata S3 =
SIGV4.toBuilder()
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName("DOUBLE_URL_ENCODE")
.valueEmitter((spec, utils) -> spec.addCode("$L", "false"))
.build())
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName("NORMALIZE_PATH")
.valueEmitter((spec, utils) -> spec.addCode("$L", "false"))
.build())
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName("PAYLOAD_SIGNING_ENABLED")
.valueEmitter((spec, utils) -> spec.addCode("$L", false))
.build())
.build();

static final AuthSchemeCodegenMetadata S3V4 =
SIGV4.toBuilder()
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName("DOUBLE_URL_ENCODE")
.valueEmitter((spec, utils) -> spec.addCode("$L", "false"))
.build())
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName("NORMALIZE_PATH")
.valueEmitter((spec, utils) -> spec.addCode("$L", "false"))
.build())
.build();

static final AuthSchemeCodegenMetadata BEARER = builder()
.schemeId(BearerAuthScheme.SCHEME_ID)
.authSchemeClass(BearerAuthScheme.class)
Expand Down Expand Up @@ -126,26 +87,68 @@ private static Builder builder() {
return new Builder();
}


/**
* Transforms a {@link SigV4SignerDefaults} instance to an {@link AuthSchemeCodegenMetadata} instance.
*/
public static AuthSchemeCodegenMetadata fromConstants(SigV4SignerDefaults constants) {
AuthSchemeCodegenMetadata.Builder builder = SIGV4.toBuilder();
for (SignerPropertyValueProvider property : propertiesFromConstants(constants)) {
builder.addProperty(property);
}
return builder.build();
}

public static List<SignerPropertyValueProvider> propertiesFromConstants(SigV4SignerDefaults constants) {
List<SignerPropertyValueProvider> properties = new ArrayList<>();
if (constants.payloadSigningEnabled() != null) {
properties.add(from("PAYLOAD_SIGNING_ENABLED", constants::payloadSigningEnabled));
}
if (constants.doubleUrlEncode() != null) {
properties.add(from("DOUBLE_URL_ENCODE", constants::doubleUrlEncode));
}
if (constants.normalizePath() != null) {
properties.add(from("NORMALIZE_PATH", constants::normalizePath));
}
if (constants.chunkEncodingEnabled() != null) {
properties.add(from("CHUNK_ENCODING_ENABLED", constants::chunkEncodingEnabled));
}
return properties;
}

private static SignerPropertyValueProvider from(String name, Supplier<Object> valueSupplier) {
return SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName(name)
.constantValueSupplier(valueSupplier)
.build();
}

public static AuthSchemeCodegenMetadata fromAuthType(AuthType type) {
switch (type) {
case BEARER:
return BEARER;
case NONE:
return NO_AUTH;
case V4:
return SIGV4;
case V4_UNSIGNED_BODY:
return SIGV4_UNSIGNED_BODY;
case S3:
return S3;
case S3V4:
return S3V4;
default:
throw new IllegalArgumentException("Unknown auth type: " + type);
String authTypeName = type.value();
SigV4SignerDefaults defaults = AuthTypeToSigV4Default.authTypeToDefaults().get(authTypeName);
if (defaults == null) {
throw new IllegalArgumentException("Unknown auth type: " + type);
}
return fromConstants(defaults);
}
}

private static class Builder {
public static Map<String, Object> constantProperties(AuthSchemeCodegenMetadata metadata) {
return metadata
.properties()
.stream()
.filter(SignerPropertyValueProvider::isConstant)
.collect(Collectors.toMap(SignerPropertyValueProvider::fieldName, SignerPropertyValueProvider::value));
}

public static class Builder {
private String schemeId;
private List<SignerPropertyValueProvider> properties = new ArrayList<>();
private Class<?> authSchemeClass;
Expand All @@ -169,6 +172,12 @@ public Builder addProperty(SignerPropertyValueProvider property) {
return this;
}

public Builder properties(List<SignerPropertyValueProvider> properties) {
this.properties.clear();
this.properties.addAll(properties);
return this;
}

public Builder authSchemeClass(Class<?> authSchemeClass) {
this.authSchemeClass = authSchemeClass;
return this;
Expand All @@ -183,11 +192,13 @@ static class SignerPropertyValueProvider {
private final Class<?> containingClass;
private final String fieldName;
private final BiConsumer<MethodSpec.Builder, AuthSchemeSpecUtils> valueEmitter;
private final Supplier<Object> valueSupplier;

SignerPropertyValueProvider(Builder builder) {
this.containingClass = Validate.paramNotNull(builder.containingClass, "containingClass");
this.valueEmitter = Validate.paramNotNull(builder.valueEmitter, "valueEmitter");
this.fieldName = Validate.paramNotNull(builder.fieldName, "fieldName");
this.valueSupplier = builder.valueSupplier;
}

public Class<?> containingClass() {
Expand All @@ -198,6 +209,14 @@ public String fieldName() {
return fieldName;
}

public boolean isConstant() {
return valueSupplier != null;
}

public Object value() {
return valueSupplier.get();
}

public void emitValue(MethodSpec.Builder spec, AuthSchemeSpecUtils utils) {
valueEmitter.accept(spec, utils);
}
Expand All @@ -210,6 +229,7 @@ static class Builder {
private Class<?> containingClass;
private String fieldName;
private BiConsumer<MethodSpec.Builder, AuthSchemeSpecUtils> valueEmitter;
private Supplier<Object> valueSupplier;

public Builder containingClass(Class<?> containingClass) {
this.containingClass = containingClass;
Expand All @@ -226,6 +246,14 @@ public Builder valueEmitter(BiConsumer<MethodSpec.Builder, AuthSchemeSpecUtils>
return this;
}

public Builder constantValueSupplier(Supplier<Object> valueSupplier) {
this.valueSupplier = valueSupplier;
if (valueEmitter == null) {
valueEmitter = (spec, utils) -> spec.addCode("$L", valueSupplier.get());
}
return this;
}

public SignerPropertyValueProvider build() {
return new SignerPropertyValueProvider(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,25 @@
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
import software.amazon.awssdk.codegen.model.service.AuthType;
import software.amazon.awssdk.codegen.utils.AuthUtils;
import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme;
import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme;
import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme;
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
Expand Down Expand Up @@ -188,6 +191,133 @@ public Map<List<String>, List<AuthType>> operationsToAuthType() {
return operationsToAuthType;
}

/**
* Computes a map from operations to codegen metadata objects. The intermediate model is used to compute mappings to
* {@link AuthType} values for the service and for each operation that has an override. Then we group all the operations
* that share the same set of auth types together and finally convert the auth types to their corresponding codegen
* metadata instances that then we can use to codegen switch statements. The service wide codegen metadata instances are
* keyed using {@link Collections#emptyList()}.
*
* @see #computeServiceWideDefaults
*/
private Map<List<String>, List<AuthSchemeCodegenMetadata>> operationsToModeledMetadata() {
Map<List<String>, List<AuthType>> operationsToAuthType = operationsToAuthType();
Map<List<String>, List<AuthSchemeCodegenMetadata>> operationsToMetadata = new LinkedHashMap<>();
operationsToAuthType.forEach((k, v) -> operationsToMetadata.put(k, authTypeToCodegenMetadata(v)));
return operationsToMetadata;
}

public Map<List<String>, List<AuthSchemeCodegenMetadata>> operationsToMetadata() {
Comment thread
cenedhryn marked this conversation as resolved.
Outdated
List<AuthType> serviceDefaults = serviceDefaultAuthTypes();
if (serviceDefaults.size() == 1) {
String authTypeName = serviceDefaults.get(0).value();
SigV4SignerDefaults defaults = AuthTypeToSigV4Default.authTypeToDefaults().get(authTypeName);
if (areServiceWide(defaults)) {
return computeServiceWideDefaults(defaults);
}
}
return operationsToModeledMetadata();
}

/**
* Similar to {@link #operationsToModeledMetadata()} computes a map from operations to codegen metadata objects. The
* service default list of codegen metadata is keyed with {@link Collections#emptyList()}.
*
* This map is used to codegen switch statements.
*/
Comment thread
cenedhryn marked this conversation as resolved.
Outdated
private Map<List<String>, List<AuthSchemeCodegenMetadata>> computeServiceWideDefaults(SigV4SignerDefaults defaults) {
Map<SigV4SignerDefaults, List<String>> defaultsToOperations =
defaults.operations()
.entrySet()
.stream()
.map(kvp -> new AbstractMap.SimpleEntry<>(kvp.getKey(), kvp.getValue()))
.collect(Collectors.groupingBy(AbstractMap.SimpleEntry::getValue,
Collectors.mapping(AbstractMap.SimpleEntry::getKey,
Collectors.toList())));

Map<List<String>, SigV4SignerDefaults> operationsToDefaults =
defaultsToOperations.entrySet()
.stream()
.sorted(Comparator.comparing(left -> left.getValue().get(0)))
.collect(Collectors.toMap(Map.Entry::getValue,
Map.Entry::getKey, (a, b) -> b,
LinkedHashMap::new));

Map<List<String>, List<AuthSchemeCodegenMetadata>> result = new LinkedHashMap<>();
for (Map.Entry<List<String>, SigV4SignerDefaults> kvp : operationsToDefaults.entrySet()) {
result.put(kvp.getKey(),
Arrays.asList(AuthSchemeCodegenMetadata.fromConstants(kvp.getValue())));
}
result.put(Collections.emptyList(), Arrays.asList(AuthSchemeCodegenMetadata.fromConstants(defaults)));
return result;
}

public boolean areServiceWide(SigV4SignerDefaults defaults) {
Comment thread
cenedhryn marked this conversation as resolved.
Outdated
return defaults != null
&& defaults.service() != null
&& Objects.equals(intermediateModel.getMetadata().getServiceName(), defaults.service());
}
Comment thread
cenedhryn marked this conversation as resolved.
Outdated

public Map<List<String>, AuthSchemeCodegenMetadata> operationsToNonStandardSigv4Metadata() {
Map<List<String>, AuthSchemeCodegenMetadata> result =
operationsToMetadata()
.entrySet()
.stream()
.filter(kvp -> containsNonStandardSigV4(kvp.getValue()))
.map(kvp -> new AbstractMap.SimpleEntry<>(
kvp.getKey(),
findNonStandardSigV4(kvp.getValue())))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: is there a more efficient way to do this than calling findNonStandardSigV4 twice?

.collect(
Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue,
(a, b) -> b,
LinkedHashMap::new));
return result;
}

private boolean containsNonStandardSigV4(List<AuthSchemeCodegenMetadata> options) {
return findNonStandardSigV4(options) != null;
}

private AuthSchemeCodegenMetadata findNonStandardSigV4(List<AuthSchemeCodegenMetadata> options) {
Comment thread
cenedhryn marked this conversation as resolved.
Outdated
Map<String, Object> defaultSigv4Properties =
AuthSchemeCodegenMetadata.constantProperties(AuthSchemeCodegenMetadata.SIGV4);
for (AuthSchemeCodegenMetadata metadata : options) {
if (metadata.authSchemeClass() != AwsV4AuthScheme.class) {
continue;
}
Map<String, Object> sigv4Properties = AuthSchemeCodegenMetadata.constantProperties(metadata);
if (defaultSigv4Properties.equals(sigv4Properties)) {
return null;
}
List<AuthSchemeCodegenMetadata.SignerPropertyValueProvider> properties =
metadata
.properties()
.stream()
.filter(AuthSchemeSpecUtils::isNonDefaultSigv4Property)
.collect(Collectors.toList());
if (!properties.isEmpty()) {
return metadata.toBuilder().properties(properties).build();
Comment thread
cenedhryn marked this conversation as resolved.
Outdated
}
return null;
}
return null;
}

private static boolean isNonDefaultSigv4Property(AuthSchemeCodegenMetadata.SignerPropertyValueProvider provider) {
Comment thread
cenedhryn marked this conversation as resolved.
Outdated
switch (provider.fieldName()) {
case "SERVICE_SIGNING_NAME":
case "REGION_NAME":
case "DOUBLE_URL_ENCODE":
return false;
default:
return true;
}
}

private List<AuthSchemeCodegenMetadata> authTypeToCodegenMetadata(List<AuthType> authTypes) {
return authTypes.stream().map(AuthSchemeCodegenMetadata::fromAuthType).collect(Collectors.toList());
}

public List<AuthType> serviceDefaultAuthTypes() {
List<AuthType> modeled = intermediateModel.getMetadata().getAuth();
if (!modeled.isEmpty()) {
Expand All @@ -197,16 +327,12 @@ public List<AuthType> serviceDefaultAuthTypes() {
}

public Set<Class<?>> allServiceConcreteAuthSchemeClasses() {
Set<Class<?>> result =
Stream.concat(intermediateModel.getOperations()
.values()
.stream()
.map(OperationModel::getAuth)
.flatMap(List::stream),
intermediateModel.getMetadata().getAuth().stream())
.map(AuthSchemeCodegenMetadata::fromAuthType)
.map(AuthSchemeCodegenMetadata::authSchemeClass)
.collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Class::getSimpleName))));
Set<Class<?>> result = operationsToMetadata()
.values()
.stream()
.flatMap(Collection::stream)
.map(AuthSchemeCodegenMetadata::authSchemeClass)
.collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Class::getSimpleName))));

if (useEndpointBasedAuthProvider()) {
// sigv4a is not modeled but needed for the endpoints based auth-scheme cases.
Expand Down
Loading