Skip to content

Commit

Permalink
chore(client-sesv2): enable sigv4a flag in sesv2 ahead of service ava…
Browse files Browse the repository at this point in the history
…ilability
  • Loading branch information
kuhe committed Dec 6, 2024
1 parent 331273f commit d86bf3d
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 11 deletions.
1 change: 1 addition & 0 deletions clients/client-sesv2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@aws-sdk/middleware-recursion-detection": "*",
"@aws-sdk/middleware-user-agent": "*",
"@aws-sdk/region-config-resolver": "*",
"@aws-sdk/signature-v4-multi-region": "*",
"@aws-sdk/types": "*",
"@aws-sdk/util-endpoints": "*",
"@aws-sdk/util-user-agent-browser": "*",
Expand Down
1 change: 1 addition & 0 deletions clients/client-sesv2/src/SESv2Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ export class SESv2Client extends __Client<
identityProviderConfigProvider: async (config: SESv2ClientResolvedConfig) =>
new DefaultIdentityProviderConfig({
"aws.auth#sigv4": config.credentials,
"aws.auth#sigv4a": config.credentials,
}),
})
);
Expand Down
206 changes: 198 additions & 8 deletions clients/client-sesv2/src/auth/httpAuthSchemeProvider.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
// smithy-typescript generated code
import {
AwsSdkSigV4AAuthInputConfig,
AwsSdkSigV4AAuthResolvedConfig,
AwsSdkSigV4APreviouslyResolved,
AwsSdkSigV4AuthInputConfig,
AwsSdkSigV4AuthResolvedConfig,
AwsSdkSigV4PreviouslyResolved,
resolveAwsSdkSigV4AConfig,
resolveAwsSdkSigV4Config,
} from "@aws-sdk/core";
import { signatureV4CrtContainer } from "@aws-sdk/signature-v4-multi-region";
import { EndpointParameterInstructions, resolveParams } from "@smithy/middleware-endpoint";
import {
EndpointV2,
HandlerExecutionContext,
HttpAuthOption,
HttpAuthScheme,
HttpAuthSchemeId,
HttpAuthSchemeParameters,
HttpAuthSchemeParametersProvider,
HttpAuthSchemeProvider,
Logger,
} from "@smithy/types";
import { getSmithyContext, normalizeProvider } from "@smithy/util-middleware";

import { EndpointParameters } from "../endpoint/EndpointParameters";
import { defaultEndpointResolver } from "../endpoint/endpointResolver";
import { SESv2ClientConfig, SESv2ClientResolvedConfig } from "../SESv2Client";

/**
* @internal
*/
export interface SESv2HttpAuthSchemeParameters extends HttpAuthSchemeParameters {
interface _SESv2HttpAuthSchemeParameters extends HttpAuthSchemeParameters {
region?: string;
}

/**
* @internal
*/
export interface SESv2HttpAuthSchemeParameters extends _SESv2HttpAuthSchemeParameters, EndpointParameters {
region?: string;
}

Expand All @@ -38,11 +56,71 @@ export interface SESv2HttpAuthSchemeParametersProvider
/**
* @internal
*/
export const defaultSESv2HttpAuthSchemeParametersProvider = async (
interface EndpointRuleSetSmithyContext {
commandInstance?: {
constructor?: {
getEndpointParameterInstructions(): EndpointParameterInstructions;
};
};
}
/**
* @internal
*/
interface EndpointRuleSetHttpAuthSchemeParametersProvider<
TConfig extends object,
TContext extends HandlerExecutionContext,
TParameters extends HttpAuthSchemeParameters & EndpointParameters,
TInput extends object
> extends HttpAuthSchemeParametersProvider<TConfig, TContext, TParameters, TInput> {}
/**
* @internal
*/
const createEndpointRuleSetHttpAuthSchemeParametersProvider =
<
TConfig extends object,
TContext extends HandlerExecutionContext,
THttpAuthSchemeParameters extends HttpAuthSchemeParameters,
TEndpointParameters extends EndpointParameters,
TParameters extends THttpAuthSchemeParameters & TEndpointParameters,
TInput extends object
>(
defaultHttpAuthSchemeParametersProvider: HttpAuthSchemeParametersProvider<
TConfig,
TContext,
THttpAuthSchemeParameters,
TInput
>
): EndpointRuleSetHttpAuthSchemeParametersProvider<
TConfig,
TContext,
THttpAuthSchemeParameters & TEndpointParameters,
TInput
> =>
async (config: TConfig, context: TContext, input: TInput): Promise<TParameters> => {
if (!input) {
throw new Error(`Could not find \`input\` for \`defaultEndpointRuleSetHttpAuthSchemeParametersProvider\``);
}
const defaultParameters = await defaultHttpAuthSchemeParametersProvider(config, context, input);
const instructionsFn = (getSmithyContext(context) as EndpointRuleSetSmithyContext)?.commandInstance?.constructor
?.getEndpointParameterInstructions;
if (!instructionsFn) {
throw new Error(`getEndpointParameterInstructions() is not defined on \`${context.commandName!}\``);
}
const endpointParameters = await resolveParams(
input as Record<string, unknown>,
{ getEndpointParameterInstructions: instructionsFn! },
config as Record<string, unknown>
);
return Object.assign(defaultParameters, endpointParameters) as TParameters;
};
/**
* @internal
*/
const _defaultSESv2HttpAuthSchemeParametersProvider = async (
config: SESv2ClientResolvedConfig,
context: HandlerExecutionContext,
input: object
): Promise<SESv2HttpAuthSchemeParameters> => {
): Promise<_SESv2HttpAuthSchemeParameters> => {
return {
operation: getSmithyContext(context).operation as string,
region:
Expand All @@ -52,6 +130,11 @@ export const defaultSESv2HttpAuthSchemeParametersProvider = async (
})(),
};
};
/**
* @internal
*/
export const defaultSESv2HttpAuthSchemeParametersProvider: SESv2HttpAuthSchemeParametersProvider =
createEndpointRuleSetHttpAuthSchemeParametersProvider(_defaultSESv2HttpAuthSchemeParametersProvider);

function createAwsAuthSigv4HttpAuthOption(authParameters: SESv2HttpAuthSchemeParameters): HttpAuthOption {
return {
Expand All @@ -72,6 +155,30 @@ function createAwsAuthSigv4HttpAuthOption(authParameters: SESv2HttpAuthSchemePar
};
}

function createAwsAuthSigv4aHttpAuthOption(authParameters: SESv2HttpAuthSchemeParameters): HttpAuthOption {
return {
schemeId: "aws.auth#sigv4a",
signingProperties: {
name: "ses",
region: authParameters.region,
},
propertiesExtractor: (config: Partial<SESv2ClientConfig>, context) => ({
/**
* @internal
*/
signingProperties: {
config,
context,
},
}),
};
}

/**
* @internal
*/
interface _SESv2HttpAuthSchemeProvider extends HttpAuthSchemeProvider<SESv2HttpAuthSchemeParameters> {}

/**
* @internal
*/
Expand All @@ -80,20 +187,102 @@ export interface SESv2HttpAuthSchemeProvider extends HttpAuthSchemeProvider<SESv
/**
* @internal
*/
export const defaultSESv2HttpAuthSchemeProvider: SESv2HttpAuthSchemeProvider = (authParameters) => {
interface EndpointRuleSetHttpAuthSchemeProvider<
EndpointParametersT extends EndpointParameters,
HttpAuthSchemeParametersT extends HttpAuthSchemeParameters
> extends HttpAuthSchemeProvider<EndpointParametersT & HttpAuthSchemeParametersT> {}
/**
* @internal
*/
interface DefaultEndpointResolver<EndpointParametersT extends EndpointParameters> {
(params: EndpointParametersT, context?: { logger?: Logger }): EndpointV2;
}
/**
* @internal
*/
const createEndpointRuleSetHttpAuthSchemeProvider = <
EndpointParametersT extends EndpointParameters,
HttpAuthSchemeParametersT extends HttpAuthSchemeParameters
>(
defaultEndpointResolver: DefaultEndpointResolver<EndpointParametersT>,
defaultHttpAuthSchemeResolver: HttpAuthSchemeProvider<HttpAuthSchemeParametersT>,
createHttpAuthOptionFunctions: Record<
HttpAuthSchemeId,
(authParameters: EndpointParametersT & HttpAuthSchemeParametersT) => HttpAuthOption
>
): EndpointRuleSetHttpAuthSchemeProvider<EndpointParametersT, HttpAuthSchemeParametersT> => {
const endpointRuleSetHttpAuthSchemeProvider: EndpointRuleSetHttpAuthSchemeProvider<
EndpointParametersT,
HttpAuthSchemeParametersT
> = (authParameters) => {
const endpoint: EndpointV2 = defaultEndpointResolver(authParameters);
const authSchemes = endpoint.properties?.authSchemes;
if (!authSchemes) {
return defaultHttpAuthSchemeResolver(authParameters);
}
const options: HttpAuthOption[] = [];
for (const scheme of authSchemes) {
const { name: resolvedName, properties = {}, ...rest } = scheme;
const name = resolvedName.toLowerCase();
if (resolvedName !== name) {
console.warn(`HttpAuthScheme has been normalized with lowercasing: \`${resolvedName}\` to \`${name}\``);
}
let schemeId;
if (name === "sigv4a") {
schemeId = "aws.auth#sigv4a";
const sigv4Present = authSchemes.find((s) => {
const name = s.name.toLowerCase();
return name !== "sigv4a" && name.startsWith("sigv4");
});
if (!signatureV4CrtContainer.CrtSignerV4 && sigv4Present) {
// sigv4a -> sigv4 fallback.
continue;
}
} else if (name.startsWith("sigv4")) {
schemeId = "aws.auth#sigv4";
} else {
throw new Error(`Unknown HttpAuthScheme found in \`@smithy.rules#endpointRuleSet\`: \`${name}\``);
}
const createOption = createHttpAuthOptionFunctions[schemeId];
if (!createOption) {
throw new Error(`Could not find HttpAuthOption create function for \`${schemeId}\``);
}
const option = createOption(authParameters);
option.schemeId = schemeId;
option.signingProperties = { ...(option.signingProperties || {}), ...rest, ...properties };
options.push(option);
}
return options;
};

return endpointRuleSetHttpAuthSchemeProvider;
};
/**
* @internal
*/
const _defaultSESv2HttpAuthSchemeProvider: _SESv2HttpAuthSchemeProvider = (authParameters) => {
const options: HttpAuthOption[] = [];
switch (authParameters.operation) {
default: {
options.push(createAwsAuthSigv4HttpAuthOption(authParameters));
options.push(createAwsAuthSigv4aHttpAuthOption(authParameters));
}
}
return options;
};
/**
* @internal
*/
export const defaultSESv2HttpAuthSchemeProvider: SESv2HttpAuthSchemeProvider =
createEndpointRuleSetHttpAuthSchemeProvider(defaultEndpointResolver, _defaultSESv2HttpAuthSchemeProvider, {
"aws.auth#sigv4": createAwsAuthSigv4HttpAuthOption,
"aws.auth#sigv4a": createAwsAuthSigv4aHttpAuthOption,
});

/**
* @internal
*/
export interface HttpAuthSchemeInputConfig extends AwsSdkSigV4AuthInputConfig {
export interface HttpAuthSchemeInputConfig extends AwsSdkSigV4AuthInputConfig, AwsSdkSigV4AAuthInputConfig {
/**
* Configuration of HttpAuthSchemes for a client which provides default identity providers and signers per auth scheme.
* @internal
Expand All @@ -110,7 +299,7 @@ export interface HttpAuthSchemeInputConfig extends AwsSdkSigV4AuthInputConfig {
/**
* @internal
*/
export interface HttpAuthSchemeResolvedConfig extends AwsSdkSigV4AuthResolvedConfig {
export interface HttpAuthSchemeResolvedConfig extends AwsSdkSigV4AuthResolvedConfig, AwsSdkSigV4AAuthResolvedConfig {
/**
* Configuration of HttpAuthSchemes for a client which provides default identity providers and signers per auth scheme.
* @internal
Expand All @@ -128,10 +317,11 @@ export interface HttpAuthSchemeResolvedConfig extends AwsSdkSigV4AuthResolvedCon
* @internal
*/
export const resolveHttpAuthSchemeConfig = <T>(
config: T & HttpAuthSchemeInputConfig & AwsSdkSigV4PreviouslyResolved
config: T & HttpAuthSchemeInputConfig & AwsSdkSigV4PreviouslyResolved & AwsSdkSigV4APreviouslyResolved
): T & HttpAuthSchemeResolvedConfig => {
const config_0 = resolveAwsSdkSigV4Config(config);
const config_1 = resolveAwsSdkSigV4AConfig(config_0);
return {
...config_0,
...config_1,
} as T & HttpAuthSchemeResolvedConfig;
};
9 changes: 8 additions & 1 deletion clients/client-sesv2/src/runtimeConfig.shared.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// smithy-typescript generated code
import { AwsSdkSigV4Signer } from "@aws-sdk/core";
import { AwsSdkSigV4ASigner, AwsSdkSigV4Signer } from "@aws-sdk/core";
import { SignatureV4MultiRegion } from "@aws-sdk/signature-v4-multi-region";
import { NoOpLogger } from "@smithy/smithy-client";
import { IdentityProviderConfig } from "@smithy/types";
import { parseUrl } from "@smithy/url-parser";
Expand Down Expand Up @@ -28,9 +29,15 @@ export const getRuntimeConfig = (config: SESv2ClientConfig) => {
identityProvider: (ipc: IdentityProviderConfig) => ipc.getIdentityProvider("aws.auth#sigv4"),
signer: new AwsSdkSigV4Signer(),
},
{
schemeId: "aws.auth#sigv4a",
identityProvider: (ipc: IdentityProviderConfig) => ipc.getIdentityProvider("aws.auth#sigv4a"),
signer: new AwsSdkSigV4ASigner(),
},
],
logger: config?.logger ?? new NoOpLogger(),
serviceId: config?.serviceId ?? "SESv2",
signerConstructor: config?.signerConstructor ?? SignatureV4MultiRegion,
urlParser: config?.urlParser ?? parseUrl,
utf8Decoder: config?.utf8Decoder ?? fromUtf8,
utf8Encoder: config?.utf8Encoder ?? toUtf8,
Expand Down
3 changes: 2 additions & 1 deletion clients/client-sesv2/src/runtimeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// @ts-ignore: package.json will be imported from dist folders
import packageInfo from "../package.json"; // eslint-disable-line

import { emitWarningIfUnsupportedVersion as awsCheckVersion } from "@aws-sdk/core";
import { NODE_SIGV4A_CONFIG_OPTIONS, emitWarningIfUnsupportedVersion as awsCheckVersion } from "@aws-sdk/core";
import { defaultProvider as credentialDefaultProvider } from "@aws-sdk/credential-provider-node";
import { NODE_APP_ID_CONFIG_OPTIONS, createDefaultUserAgentProvider } from "@aws-sdk/util-user-agent-node";
import {
Expand Down Expand Up @@ -52,6 +52,7 @@ export const getRuntimeConfig = (config: SESv2ClientConfig) => {
default: async () => (await defaultConfigProvider()).retryMode || DEFAULT_RETRY_MODE,
}),
sha256: config?.sha256 ?? Hash.bind(null, "sha256"),
sigv4aSigningRegionSet: config?.sigv4aSigningRegionSet ?? loadNodeConfig(NODE_SIGV4A_CONFIG_OPTIONS),
streamCollector: config?.streamCollector ?? streamCollector,
useDualstackEndpoint: config?.useDualstackEndpoint ?? loadNodeConfig(NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS),
useFipsEndpoint: config?.useFipsEndpoint ?? loadNodeConfig(NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public final class AwsTraitsUtils {
private static final Set<ShapeId> ENDPOINT_RULESET_HTTP_AUTH_SCHEME_SERVICES = Set.of(
ShapeId.from("com.amazonaws.s3#AmazonS3"),
ShapeId.from("com.amazonaws.eventbridge#AWSEvents"),
ShapeId.from("com.amazonaws.cloudfrontkeyvaluestore#CloudFrontKeyValueStore")
ShapeId.from("com.amazonaws.cloudfrontkeyvaluestore#CloudFrontKeyValueStore"),
ShapeId.from("com.amazonaws.sesv2#SimpleEmailService_v2")
);

private AwsTraitsUtils() {}
Expand Down

0 comments on commit d86bf3d

Please sign in to comment.