Skip to content

Commit

Permalink
Multi-Auth Sigv4a Support (#5868)
Browse files Browse the repository at this point in the history
* Add support for Multi Auth Sigv4a for Service metadata (#5761)

* Add support for Multi Auth Sigv4a for Service metadata

* handled comments

* Sigv4aSigningRegionSetProvider and updating Execution Attributes (#5762)

* Sigv4aSigningRegionSetProvider and updating Execution Attributes

* Fixed typos

* Handled comments

* extra space

* Handled comments

* Handled comments to make RegionSet Provider as Internal API

* Update AuthScemeParams with RegionSet for Sigv4a auth Scheme (#5766)

* Codegen changes for AuthSchemeInterceptorSpecs to update the RegionSet from Execution Attributes (#5768)

* Update AuthScemeParams with RegionSet for Sigv4a auth Scheme

* Update Codegen AuthSchemeInterceptorSpec to update RegionSet for AuthSchemeParams

* updated method name

* Add sigv4aRegionSet to Client Builder for Multi-Auth Services Supporting Sigv4a (#5772)

* Update AuthScemeParams with RegionSet for Sigv4a auth Scheme

* Update Codegen AuthSchemeInterceptorSpec to update RegionSet for AuthSchemeParams

* updated method name

* Adding sigv4aResionSet client builder for services which has Sigv4a in Multi Auth trait

* Adding Codegen support for unsignedPayload model trait for multi-auth auth schemes (#5776)

* Update AuthScemeParams with RegionSet for Sigv4a auth Scheme

* Update Codegen AuthSchemeInterceptorSpec to update RegionSet for AuthSchemeParams

* updated method name

* Adding sigv4aResionSet client builder for services which has Sigv4a in Multi Auth trait

* Adding Codegen support for unsignedPayload model trait for multi-auth auth schemes

* Adding Codegen support for unsignedPayload model trait for multi-auth auth schemes

* Adding Codegen support for unsignedPayload model trait for multi-auth auth schemes

* Handled comments

* updated variable names

* Remove `useMultiAuth`  customization (#5800)

* Remove multiAuth customization

* Handle cases where AuthType gets updated from Legacy signature version

* Override RegionSet in EnpointResolverInterceptor after fetching the Signing Properties from Endpoint rules (#5825)

* Override RegionSet in EnpointResolverInterceptor after fetching the Signing Properties from Endpoint rules

* Endpoint Resolver Spec test added

* Add test case for service with MultiAuth only and not using Sigv4

* checkstyle fixed

* Handled comments

* Updated after Integ test failure and some comments

* Resolve Sigv4a regionset from Client configs if Endpoint-Based-Auth legacy modules doesnot have it defined in ruleset (#5860)

* Override RegionSet in EnpointResolverInterceptor after fetching the Signing Properties from Endpoint rules

* Endpoint Resolver Spec test added

* Add test case for service with MultiAuth only and not using Sigv4

* checkstyle fixed

* Handled comments

* Updated after Integ test failure and some comments

* Sigv4a Regionset updated for Endpoint-based-Auth Legacy services

* Added endpoint-resolve-interceptor-with-endpointsbasedauth.java

* Added test cases to check runtime resolution of RegionSet

* Updated test cases to make sure we check Empty set

* fixed test build

* Changed precedence after internal discussion

* Renamed API name `sigv4aRegionSet` to `sigv4aSigningRegionSet` to be conssistent with Environment variable and System Property name for this option (#5867)

* Changed API names sigv4aRegionSet to sigv4aSigningRegionSet to be consistent with Environmant variable and System Property name for this option

* Fixed checkstyle issues

* Added S3 junit with wiremock tests for Mult Auth Sigv4a (#5872)
  • Loading branch information
joviegas authored Feb 18, 2025
1 parent 15fe972 commit 36c89a0
Show file tree
Hide file tree
Showing 78 changed files with 4,558 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ public static Metadata constructMetadata(ServiceModel serviceModel,
.withJsonVersion(serviceMetadata.getJsonVersion())
.withEndpointPrefix(serviceMetadata.getEndpointPrefix())
.withSigningName(serviceMetadata.getSigningName())
.withAuthType(AuthType.fromValue(serviceMetadata.getSignatureVersion()))
.withAuthType(serviceMetadata.getSignatureVersion() != null ?
AuthType.fromValue(serviceMetadata.getSignatureVersion()) : null)
.withUid(serviceMetadata.getUid())
.withServiceId(serviceMetadata.getServiceId())
.withSupportsH2(supportsH2(serviceMetadata))
.withJsonVersion(getJsonVersion(metadata, serviceMetadata))
.withAwsQueryCompatible(serviceMetadata.getAwsQueryCompatible())
.withAuth(getAuthFromServiceMetadata(serviceMetadata, customizationConfig.useMultiAuth()));
.withAuth(getAuthFromServiceMetadata(serviceMetadata));

return metadata;
}
Expand Down Expand Up @@ -136,18 +137,14 @@ private static String getJsonVersion(Metadata metadata, ServiceMetadata serviceM
}

/**
* Converts service metadata into a list of AuthTypes. If useMultiAuth is enabled, then
* {@code metadata.auth} will be used in the conversion if present. Otherwise, use
* {@code metadata.signatureVersion}.
* Converts a list of authentication type strings from the given {@link ServiceMetadata} into a list of
* {@link AuthType} objects.
*/
private static List<AuthType> getAuthFromServiceMetadata(ServiceMetadata serviceMetadata,
boolean useMultiAuth) {
if (useMultiAuth) {
List<String> serviceAuth = serviceMetadata.getAuth();
if (serviceAuth != null) {
return serviceAuth.stream().map(AuthType::fromValue).collect(Collectors.toList());
}
private static List<AuthType> getAuthFromServiceMetadata(ServiceMetadata serviceMetadata) {
List<String> serviceAuth = serviceMetadata.getAuth();
if (serviceAuth != null) {
return serviceAuth.stream().map(AuthType::fromValue).collect(Collectors.toList());
}
return Collections.singletonList(AuthType.fromValue(serviceMetadata.getSignatureVersion()));
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,12 @@ final class AddOperations {
private final NamingStrategy namingStrategy;
private final Map<String, PaginatorDefinition> paginators;
private final List<String> deprecatedShapes;
private final boolean useMultiAuth;

AddOperations(IntermediateModelBuilder builder) {
this.serviceModel = builder.getService();
this.namingStrategy = builder.getNamingStrategy();
this.paginators = builder.getPaginators().getPagination();
this.deprecatedShapes = builder.getCustomConfig().getDeprecatedShapes();
this.useMultiAuth = builder.getCustomConfig().useMultiAuth();
}

private static boolean isAuthenticated(Operation op) {
Expand Down Expand Up @@ -182,6 +180,7 @@ public Map<String, OperationModel> constructOperations() {
operationModel.setStaticContextParams(op.getStaticContextParams());
operationModel.setOperationContextParams(op.getOperationContextParams());
operationModel.setAuth(getAuthFromOperation(op));
operationModel.setUnsignedPayload(op.isUnsignedPayload());

Input input = op.getInput();
if (input != null) {
Expand Down Expand Up @@ -237,21 +236,23 @@ public Map<String, OperationModel> constructOperations() {
}

/**
* Returns the list of authTypes defined for an operation. If useMultiAuth is enabled, then
* {@code operation.auth} will be used in the conversion if present. Otherwise, use
* {@code operation.authtype} if present.
* Retrieves the list of {@link AuthType} for the given operation.
* <p>
* If {@link Operation#getAuth()}is available, it is converted to a list of {@link AuthType}.
* Otherwise, {@link Operation#getAuthtype()} is returned as a single-element list if present.
* If neither is available, an empty list is returned.
*/
private List<AuthType> getAuthFromOperation(Operation op) {
if (useMultiAuth) {
List<String> opAuth = op.getAuth();
if (opAuth != null) {
return opAuth.stream().map(AuthType::fromValue).collect(Collectors.toList());
}
}

// First we check for legacy AuthType to support backward compatibility
AuthType legacyAuthType = op.getAuthtype();
if (legacyAuthType != null) {
return Collections.singletonList(legacyAuthType);
}
List<String> opAuth = op.getAuth();
if (opAuth != null) {
return opAuth.stream().map(AuthType::fromValue).collect(Collectors.toList());
}
return Collections.emptyList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,6 @@ public class CustomizationConfig {
*/
private String rootPackageName;

/**
* Set to true to read from c2j multi-auth values. Currently defaults to false.
*
* TODO(multi-auth): full multi-auth support is not implemented
*/
private boolean useMultiAuth;

/**
* Special case for a service where model changes for endpoint params were not updated .
* This should be removed once the service updates its models
Expand Down Expand Up @@ -891,14 +884,6 @@ public CustomizationConfig withRootPackageName(String packageName) {
return this;
}

public void setUseMultiAuth(boolean useMultiAuth) {
this.useMultiAuth = useMultiAuth;
}

public boolean useMultiAuth() {
return useMultiAuth;
}

public Map<String, ParameterModel> getEndpointParameters() {
return endpointParameters;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public class OperationModel extends DocumentationModel {
@JsonIgnore
private Map<String, OperationContextParam> operationContextParams;

private boolean unsignedPayload;

public String getOperationName() {
return operationName;
}
Expand Down Expand Up @@ -369,4 +371,12 @@ public Map<String, OperationContextParam> getOperationContextParams() {
public void setOperationContextParams(Map<String, OperationContextParam> operationContextParams) {
this.operationContextParams = operationContextParams;
}

public boolean isUnsignedPayload() {
return unsignedPayload;
}

public void setUnsignedPayload(boolean unsignedPayload) {
this.unsignedPayload = unsignedPayload;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public enum AuthType {
CUSTOM("custom"),
IAM("iam"),
V4("v4"),
V4A("v4a"),
V4_UNSIGNED_BODY("v4-unsigned-body"),
S3("s3"),
S3V4("s3v4"),
Expand All @@ -49,6 +50,8 @@ public static AuthType fromValue(String value) {
return NONE;
case "aws.auth#sigv4":
return V4;
case "aws.auth#sigv4a":
return V4A;
default:
String normalizedValue = StringUtils.lowerCase(value);
return Arrays.stream(values())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public class Operation {

private Map<String, OperationContextParam> operationContextParams;

private boolean unsignedPayload;

public String getName() {
return name;
}
Expand Down Expand Up @@ -227,4 +229,12 @@ public Map<String, OperationContextParam> getOperationContextParams() {
public void setOperationContextParams(Map<String, OperationContextParam> operationContextParams) {
this.operationContextParams = operationContextParams;
}

public boolean isUnsignedPayload() {
return unsignedPayload;
}

public void setUnsignedPayload(boolean unsignedPayload) {
this.unsignedPayload = unsignedPayload;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
import software.amazon.awssdk.codegen.model.service.AuthType;
import software.amazon.awssdk.codegen.poet.auth.scheme.AuthSchemeCodegenMetadata.SignerPropertyValueProvider;
import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme;
import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4FamilyHttpSigner;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner;
import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme;
import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme;
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
Expand Down Expand Up @@ -59,6 +62,24 @@ public final class AuthSchemeCodegenMetadataExt {
.authSchemeClass(BearerAuthScheme.class)
.build();

static final AuthSchemeCodegenMetadata SIGV4A =
builder()
.schemeId(AwsV4aAuthScheme.SCHEME_ID)
.authSchemeClass(AwsV4aAuthScheme.class)
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4aHttpSigner.class)
.fieldName(
"SERVICE_SIGNING_NAME")
.valueEmitter((spec, utils) -> spec.add("$S", utils.signingName()))
.build())
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4aHttpSigner.class)
.fieldName(
"REGION_SET")
.valueEmitter((spec, utils) -> spec.add("$L", "params.regionSet()"))
.build())
.build();

static final AuthSchemeCodegenMetadata NO_AUTH = builder()
.schemeId(NoAuthAuthScheme.SCHEME_ID)
.authSchemeClass(NoAuthAuthScheme.class)
Expand All @@ -71,20 +92,32 @@ private AuthSchemeCodegenMetadataExt() {
/**
* Creates a new auth scheme codegen metadata instance using the defaults for the given {@link AuthType} defaults.
*/
public static AuthSchemeCodegenMetadata fromAuthType(AuthType type) {
switch (type) {
public static AuthSchemeCodegenMetadata fromAuthType(AuthTrait authTrait) {
switch (authTrait.authType()) {
case BEARER:
return BEARER;
case NONE:
return NO_AUTH;
case V4A:
return getSigv4aAuthSchemeBuilder(authTrait).build();
default:
String authTypeName = type.value();
SigV4SignerDefaults defaults = AuthTypeToSigV4Default.authTypeToDefaults().get(authTypeName);
if (defaults == null) {
throw new IllegalArgumentException("Unknown auth type: " + type);
}
return fromConstants(defaults);
return resolveAuthSchemeForType(authTrait);
}
}

private static AuthSchemeCodegenMetadata resolveAuthSchemeForType(AuthTrait authTrait) {
String authTypeName = authTrait.authType().value();
SigV4SignerDefaults defaults = AuthTypeToSigV4Default.authTypeToDefaults().get(authTypeName);

if (defaults == null) {
throw new IllegalArgumentException("Unknown auth option: " + authTrait + " with type " + authTypeName);
}
if (authTrait.isUnsignedPayload()) {
defaults = defaults.toBuilder()
.payloadSigningEnabled(false)
.build();
}
return fromConstants(defaults);
}

/**
Expand Down Expand Up @@ -149,25 +182,54 @@ public static CodeBlock codegenSignerPropertiesIfAbsent(
private static List<SignerPropertyValueProvider> propertiesFromConstants(SigV4SignerDefaults constants) {
List<SignerPropertyValueProvider> properties = new ArrayList<>();
if (constants.payloadSigningEnabled() != null) {
properties.add(from("PAYLOAD_SIGNING_ENABLED", constants::payloadSigningEnabled));
properties.add(from("PAYLOAD_SIGNING_ENABLED", constants::payloadSigningEnabled, AwsV4HttpSigner.class));
}
if (constants.doubleUrlEncode() != null) {
properties.add(from("DOUBLE_URL_ENCODE", constants::doubleUrlEncode));
properties.add(from("DOUBLE_URL_ENCODE", constants::doubleUrlEncode, AwsV4HttpSigner.class));
}
if (constants.normalizePath() != null) {
properties.add(from("NORMALIZE_PATH", constants::normalizePath));
properties.add(from("NORMALIZE_PATH", constants::normalizePath, AwsV4HttpSigner.class));
}
if (constants.chunkEncodingEnabled() != null) {
properties.add(from("CHUNK_ENCODING_ENABLED", constants::chunkEncodingEnabled));
properties.add(from("CHUNK_ENCODING_ENABLED", constants::chunkEncodingEnabled, AwsV4HttpSigner.class));
}
return properties;
}

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

private static Builder getSigv4aAuthSchemeBuilder(AuthTrait authTrait) {
Builder sigv4aBuilder = builder()
.schemeId(AwsV4aAuthScheme.SCHEME_ID)
.authSchemeClass(AwsV4aAuthScheme.class);

addCommonSigv4aProperties(sigv4aBuilder);

if (authTrait.isUnsignedPayload()) {
sigv4aBuilder.addProperty(from("PAYLOAD_SIGNING_ENABLED", () -> false, AwsV4aHttpSigner.class));
}
return sigv4aBuilder;
}

private static void addCommonSigv4aProperties(Builder builder) {
builder.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4aHttpSigner.class)
.fieldName("SERVICE_SIGNING_NAME")
.valueEmitter((spec, utils) -> spec.add("$S", utils.signingName()))
.build())
.addProperty(SignerPropertyValueProvider.builder()
.containingClass(AwsV4aHttpSigner.class)
.fieldName("REGION_SET")
.valueEmitter((spec, utils) -> spec.add("$L", "params.regionSet()"))
.build());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import software.amazon.awssdk.core.internal.util.MetricUtils;
import software.amazon.awssdk.core.metrics.CoreMetric;
import software.amazon.awssdk.endpoints.EndpointProvider;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
Expand All @@ -62,6 +63,7 @@
import software.amazon.awssdk.metrics.MetricCollector;
import software.amazon.awssdk.metrics.SdkMetric;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.utils.CollectionUtils;
import software.amazon.awssdk.utils.Logger;
import software.amazon.awssdk.utils.Validate;

Expand Down Expand Up @@ -148,20 +150,17 @@ private MethodSpec generateAuthSchemeParams() {
if (!authSchemeSpecUtils.useEndpointBasedAuthProvider()) {
builder.addStatement("$T operation = executionAttributes.getAttribute($T.OPERATION_NAME)", String.class,
SdkExecutionAttribute.class);
builder.addStatement("$T.Builder builder = $T.builder().operation(operation)",
authSchemeSpecUtils.parametersInterfaceName(),
authSchemeSpecUtils.parametersInterfaceName());

if (authSchemeSpecUtils.usesSigV4()) {
builder.addStatement("$T region = executionAttributes.getAttribute($T.AWS_REGION)", Region.class,
AwsExecutionAttribute.class);
builder.addStatement("return $T.builder()"
+ ".operation(operation)"
+ ".region(region)"
+ ".build()",
authSchemeSpecUtils.parametersInterfaceName());
} else {
builder.addStatement("return $T.builder()"
+ ".operation(operation)"
+ ".build()",
authSchemeSpecUtils.parametersInterfaceName());
builder.addStatement("builder.region(region)");
}
generateSigv4aSigningRegionSet(builder);
builder.addStatement("return builder.build()");
return builder.build();
}

Expand All @@ -187,6 +186,7 @@ private MethodSpec generateAuthSchemeParams() {
AwsExecutionAttribute.class);
builder.addStatement("builder.region(region)");
}
generateSigv4aSigningRegionSet(builder);
ClassName paramsBuilderClass = authSchemeSpecUtils.parametersEndpointAwareDefaultImplName().nestedClass("Builder");
builder.beginControlFlow("if (builder instanceof $T)",
paramsBuilderClass);
Expand Down Expand Up @@ -449,4 +449,17 @@ private TypeName toTypeName(Object valueType) {
}
return result;
}

private void generateSigv4aSigningRegionSet(MethodSpec.Builder builder) {
if (authSchemeSpecUtils.hasSigV4aSupport()) {
builder.addStatement(
"executionAttributes.getOptionalAttribute($T.AWS_SIGV4A_SIGNING_REGION_SET)\n" +
" .filter(regionSet -> !$T.isNullOrEmpty(regionSet))\n" +
" .ifPresent(nonEmptyRegionSet -> builder.regionSet($T.create(nonEmptyRegionSet)))",
AwsExecutionAttribute.class,
CollectionUtils.class,
RegionSet.class
);
}
}
}
Loading

0 comments on commit 36c89a0

Please sign in to comment.