Skip to content

Commit

Permalink
S3 should configure signer properties (#4856)
Browse files Browse the repository at this point in the history
* Configure modeled signer properties for endpoint based auth scheme resolver

* Refactor the logic to use Knowledge Indexes
  • Loading branch information
sugmanue authored Feb 8, 2024
1 parent f3da726 commit 2f2e6e1
Show file tree
Hide file tree
Showing 49 changed files with 2,233 additions and 356 deletions.
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
@@ -0,0 +1,80 @@
/*
* 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.codegen.poet.auth.scheme;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;

/**
* Knowledge index to get access to the configured service auth schemes and operations overrides. This index is optimized for
* code generation of switch statements therefore the data is grouped by operations that share the same auth schemes.
*/
public final class AuthSchemeCodegenKnowledgeIndex {
/**
* We delegate this value to {@link ModelAuthSchemeKnowledgeIndex#operationsToMetadata()}. We just wrap the results in an
* interface that easier to use for the layer that does the code generation.
*/
private final Map<List<String>, List<AuthSchemeCodegenMetadata>> operationsToAuthSchemes;

private AuthSchemeCodegenKnowledgeIndex(IntermediateModel intermediateModel) {
this.operationsToAuthSchemes = ModelAuthSchemeKnowledgeIndex.of(intermediateModel).operationsToMetadata();
}

/**
* Creates a new {@link AuthSchemeCodegenKnowledgeIndex} using the given {@code intermediateModel}..
*/
public static AuthSchemeCodegenKnowledgeIndex of(IntermediateModel intermediateModel) {
return new AuthSchemeCodegenKnowledgeIndex(intermediateModel);
}

/**
* Returns the service defaults auth schemes. These can be overridden by operation.
*
* @return the service defaults auth schemes.
*/
public List<AuthSchemeCodegenMetadata> serviceDefaultAuthSchemes() {
return operationsToAuthSchemes.get(Collections.emptyList());
}

/**
* Returns true if there are auth scheme overrides per operation.
*
* @return true if there are auth scheme overrides per operation
*/
public boolean hasPerOperationAuthSchemesOverrides() {
// The map at least contains one key-value pair (keyed with Collections.emptyList()).
// If we have more than that then we have at least one override.
return operationsToAuthSchemes.size() > 1;
}

/**
* Traverses each group of operations with the same set of auth schemes.
*
* @param consumer The consumer to call for each group of operations with the same set of auth schemes.
*/
public void forEachOperationsOverridesGroup(BiConsumer<List<String>, List<AuthSchemeCodegenMetadata>> consumer) {
for (Map.Entry<List<String>, List<AuthSchemeCodegenMetadata>> kvp : operationsToAuthSchemes.entrySet()) {
if (kvp.getKey().isEmpty()) {
// We are traversing operation groups, ignore service wide defaults.
continue;
}
consumer.accept(kvp.getKey(), kvp.getValue());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,87 +15,18 @@

package software.amazon.awssdk.codegen.poet.auth.scheme;

import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.CodeBlock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
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;
import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme;
import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme;
import java.util.function.Supplier;
import software.amazon.awssdk.utils.Validate;

/**
* Represents a modeled auth scheme option.
*/
public final class AuthSchemeCodegenMetadata {

static final AuthSchemeCodegenMetadata SIGV4 = builder()
.schemeId(AwsV4AuthScheme.SCHEME_ID)
.authSchemeClass(AwsV4AuthScheme.class)
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName("SERVICE_SIGNING_NAME")
.valueEmitter((spec, utils) -> spec.addCode("$S", utils.signingName()))
.build())
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4HttpSigner.class)
.fieldName("REGION_NAME")
.valueEmitter((spec, utils) -> spec.addCode("$L", "params.region().id()"))
.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)
.build();

static final AuthSchemeCodegenMetadata NO_AUTH = builder()
.schemeId(NoAuthAuthScheme.SCHEME_ID)
.authSchemeClass(NoAuthAuthScheme.class)
.build();

private final String schemeId;
private final List<SignerPropertyValueProvider> properties;
private final Class<?> authSchemeClass;
Expand All @@ -122,30 +53,11 @@ public Builder toBuilder() {
return new Builder(this);
}

private static Builder builder() {
public static Builder builder() {
return new Builder();
}

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);
}
}

private static class Builder {
public static class Builder {
private String schemeId;
private List<SignerPropertyValueProvider> properties = new ArrayList<>();
private Class<?> authSchemeClass;
Expand All @@ -169,6 +81,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 @@ -182,12 +100,14 @@ public AuthSchemeCodegenMetadata build() {
static class SignerPropertyValueProvider {
private final Class<?> containingClass;
private final String fieldName;
private final BiConsumer<MethodSpec.Builder, AuthSchemeSpecUtils> valueEmitter;
private final BiConsumer<CodeBlock.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,18 +118,28 @@ public String fieldName() {
return fieldName;
}

public void emitValue(MethodSpec.Builder spec, AuthSchemeSpecUtils utils) {
public boolean isConstant() {
return valueSupplier != null;
}

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

public void emitValue(CodeBlock.Builder spec, AuthSchemeSpecUtils utils) {
valueEmitter.accept(spec, utils);
}

private static Builder builder() {

public static Builder builder() {
return new Builder();
}

static class Builder {
private Class<?> containingClass;
private String fieldName;
private BiConsumer<MethodSpec.Builder, AuthSchemeSpecUtils> valueEmitter;
private BiConsumer<CodeBlock.Builder, AuthSchemeSpecUtils> valueEmitter;
private Supplier<Object> valueSupplier;

public Builder containingClass(Class<?> containingClass) {
this.containingClass = containingClass;
Expand All @@ -221,11 +151,19 @@ public Builder fieldName(String fieldName) {
return this;
}

public Builder valueEmitter(BiConsumer<MethodSpec.Builder, AuthSchemeSpecUtils> valueEmitter) {
public Builder valueEmitter(BiConsumer<CodeBlock.Builder, AuthSchemeSpecUtils> valueEmitter) {
this.valueEmitter = valueEmitter;
return this;
}

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

public SignerPropertyValueProvider build() {
return new SignerPropertyValueProvider(this);
}
Expand Down
Loading

0 comments on commit 2f2e6e1

Please sign in to comment.