Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(client-sesv2): enable sigv4a code generation in sesv2 ahead of service availability #6719

Merged
merged 2 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -20360,6 +20360,7 @@ __metadata:
"@aws-sdk/middleware-recursion-detection": "npm:*"
"@aws-sdk/middleware-user-agent": "npm:*"
"@aws-sdk/region-config-resolver": "npm:*"
"@aws-sdk/signature-v4-multi-region": "npm:*"
"@aws-sdk/types": "npm:*"
"@aws-sdk/util-endpoints": "npm:*"
"@aws-sdk/util-user-agent-browser": "npm:*"
Expand Down
Loading