Skip to content

Commit bd447d2

Browse files
author
Steven Yuan
committed
temp(temp): temp
1 parent 19fed28 commit bd447d2

File tree

3 files changed

+370
-0
lines changed

3 files changed

+370
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { HttpRequest } from "@smithy/protocol-http";
2+
import { ServiceException } from "@smithy/smithy-client";
3+
import {
4+
AuthScheme,
5+
AwsCredentialIdentity,
6+
HandlerExecutionContext,
7+
HttpRequest as IHttpRequest,
8+
HttpResponse,
9+
HttpSigner,
10+
RequestSigner,
11+
} from "@smithy/types";
12+
13+
import { getDateHeader, getSkewCorrectedDate, getUpdatedSystemClockOffset } from "../utils";
14+
15+
/**
16+
* @internal
17+
*/
18+
const throwSigningPropertyError = <T>(name: string, property: T | undefined): T | never => {
19+
if (!property) {
20+
throw new Error(`Property \`${name}\` is not resolved for AWS SDK SigV4Auth`);
21+
}
22+
return property;
23+
};
24+
25+
/**
26+
* @internal
27+
*/
28+
interface AwsSdkSigV4Config {
29+
systemClockOffset: number;
30+
signer: (authScheme?: AuthScheme) => Promise<RequestSigner>;
31+
}
32+
33+
/**
34+
* @internal
35+
*/
36+
interface AwsSdkSigV4AuthSigningProperties {
37+
config: AwsSdkSigV4Config;
38+
signer: RequestSigner;
39+
signingRegion?: string;
40+
signingName?: string;
41+
}
42+
43+
/**
44+
* @internal
45+
*/
46+
interface AwsSdkSigV4Exception extends ServiceException {
47+
ServerTime?: string;
48+
}
49+
50+
/**
51+
* @internal
52+
*/
53+
const validateSigningProperties = async (
54+
signingProperties: Record<string, unknown>
55+
): Promise<AwsSdkSigV4AuthSigningProperties> => {
56+
const context = throwSigningPropertyError(
57+
"context",
58+
signingProperties.context as HandlerExecutionContext | undefined
59+
);
60+
const config = throwSigningPropertyError("config", signingProperties.config as AwsSdkSigV4Config | undefined);
61+
const authScheme = context.endpointV2?.properties?.authSchemes?.[0];
62+
const signerFunction = throwSigningPropertyError(
63+
"signer",
64+
config.signer as ((authScheme?: AuthScheme) => Promise<RequestSigner>) | undefined
65+
);
66+
const signer = await signerFunction(authScheme);
67+
const signingRegion: string | undefined = signingProperties?.signingRegion as string | undefined;
68+
const signingName = signingProperties?.signingName as string | undefined;
69+
return {
70+
config,
71+
signer,
72+
signingRegion,
73+
signingName,
74+
};
75+
};
76+
77+
/**
78+
* @internal
79+
*/
80+
export class AwsSdkSigV4Signer implements HttpSigner {
81+
async sign(
82+
httpRequest: IHttpRequest,
83+
/**
84+
* `identity` is bound in {@link resolveAWSSDKSigV4Config}
85+
*/
86+
identity: AwsCredentialIdentity,
87+
signingProperties: Record<string, unknown>
88+
): Promise<IHttpRequest> {
89+
if (!HttpRequest.isInstance(httpRequest)) {
90+
throw new Error("The request is not an instance of `HttpRequest` and cannot be signed");
91+
}
92+
const { config, signer, signingRegion, signingName } = await validateSigningProperties(signingProperties);
93+
94+
const signedRequest = await signer.sign(httpRequest, {
95+
signingDate: getSkewCorrectedDate(config.systemClockOffset),
96+
signingRegion: signingRegion,
97+
signingService: signingName,
98+
});
99+
return signedRequest;
100+
}
101+
102+
errorHandler(signingProperties: Record<string, unknown>): (error: Error) => never {
103+
return (error: Error) => {
104+
const serverTime: string | undefined =
105+
(error as AwsSdkSigV4Exception).ServerTime ?? getDateHeader((error as AwsSdkSigV4Exception).$response);
106+
if (serverTime) {
107+
const config = throwSigningPropertyError(
108+
"config",
109+
signingProperties.config as AwsSdkSigV4Config | undefined
110+
);
111+
config.systemClockOffset = getUpdatedSystemClockOffset(serverTime, config.systemClockOffset);
112+
}
113+
throw error;
114+
};
115+
}
116+
117+
successHandler(httpResponse: HttpResponse | unknown, signingProperties: Record<string, unknown>): void {
118+
const dateHeader = getDateHeader(httpResponse);
119+
if (dateHeader) {
120+
const config = throwSigningPropertyError(
121+
"config",
122+
signingProperties.config as AwsSdkSigV4Config | undefined
123+
);
124+
config.systemClockOffset = getUpdatedSystemClockOffset(dateHeader, config.systemClockOffset);
125+
}
126+
}
127+
}
128+
129+
/**
130+
* @deprecated renamed to {@link AwsSdkSigV4Signer}
131+
*/
132+
export const AWSSDKSigV4Signer = AwsSdkSigV4Signer;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./AwsSdkSigV4Signer";
2+
export * from "./resolveAwsSdkSigV4Config";
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
import {
2+
doesIdentityRequireRefresh,
3+
isIdentityExpired,
4+
memoizeIdentityProvider,
5+
normalizeProvider,
6+
} from "@smithy/core";
7+
import { SignatureV4, SignatureV4CryptoInit, SignatureV4Init } from "@smithy/signature-v4";
8+
import {
9+
AuthScheme,
10+
AwsCredentialIdentity,
11+
AwsCredentialIdentityProvider,
12+
ChecksumConstructor,
13+
HashConstructor,
14+
MemoizedProvider,
15+
Provider,
16+
RegionInfo,
17+
RegionInfoProvider,
18+
RequestSigner,
19+
} from "@smithy/types";
20+
21+
/**
22+
* @internal
23+
*/
24+
export interface AwsSdkSigV4AuthInputConfig {
25+
/**
26+
* The credentials used to sign requests.
27+
*/
28+
credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;
29+
30+
/**
31+
* The signer to use when signing requests.
32+
*/
33+
signer?: RequestSigner | ((authScheme?: AuthScheme) => Promise<RequestSigner>);
34+
35+
/**
36+
* Whether to escape request path when signing the request.
37+
*/
38+
signingEscapePath?: boolean;
39+
40+
/**
41+
* An offset value in milliseconds to apply to all signing times.
42+
*/
43+
systemClockOffset?: number;
44+
45+
/**
46+
* The region where you want to sign your request against. This
47+
* can be different to the region in the endpoint.
48+
*/
49+
signingRegion?: string;
50+
51+
/**
52+
* The injectable SigV4-compatible signer class constructor. If not supplied,
53+
* regular SignatureV4 constructor will be used.
54+
*
55+
* @internal
56+
*/
57+
signerConstructor?: new (options: SignatureV4Init & SignatureV4CryptoInit) => RequestSigner;
58+
}
59+
60+
/**
61+
* @internal
62+
*/
63+
export interface AwsSdkSigV4PreviouslyResolved {
64+
credentialDefaultProvider?: (input: any) => MemoizedProvider<AwsCredentialIdentity>;
65+
region: string | Provider<string>;
66+
sha256: ChecksumConstructor | HashConstructor;
67+
signingName?: string;
68+
regionInfoProvider?: RegionInfoProvider;
69+
defaultSigningName?: string;
70+
serviceId: string;
71+
useFipsEndpoint: Provider<boolean>;
72+
useDualstackEndpoint: Provider<boolean>;
73+
}
74+
75+
/**
76+
* @internal
77+
*/
78+
export interface AwsSdkSigV4AuthResolvedConfig {
79+
/**
80+
* Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.credentials}
81+
* This provider MAY memoize the loaded credentials for certain period.
82+
* See {@link MemoizedProvider} for more information.
83+
*/
84+
credentials: AwsCredentialIdentityProvider;
85+
/**
86+
* Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.signer}
87+
*/
88+
signer: (authScheme?: AuthScheme) => Promise<RequestSigner>;
89+
/**
90+
* Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.signingEscapePath}
91+
*/
92+
signingEscapePath: boolean;
93+
/**
94+
* Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.systemClockOffset}
95+
*/
96+
systemClockOffset: number;
97+
}
98+
99+
/**
100+
* @internal
101+
*/
102+
export const resolveAwsSdkSigV4Config = <T>(
103+
config: T & AwsSdkSigV4AuthInputConfig & AwsSdkSigV4PreviouslyResolved
104+
): T & AwsSdkSigV4AuthResolvedConfig => {
105+
// Normalize credentials
106+
let normalizedCreds: AwsCredentialIdentityProvider | undefined;
107+
if (config.credentials) {
108+
normalizedCreds = memoizeIdentityProvider(config.credentials, isIdentityExpired, doesIdentityRequireRefresh);
109+
}
110+
if (!normalizedCreds) {
111+
// credentialDefaultProvider should always be populated, but in case
112+
// it isn't, set a default identity provider that throws an error
113+
if (config.credentialDefaultProvider) {
114+
normalizedCreds = normalizeProvider(config.credentialDefaultProvider(config as any));
115+
} else {
116+
normalizedCreds = async () => { throw new Error("`credentials` is missing") };
117+
}
118+
}
119+
120+
// Populate sigv4 arguments
121+
const {
122+
// Default for signingEscapePath
123+
signingEscapePath = true,
124+
// Default for systemClockOffset
125+
systemClockOffset = config.systemClockOffset || 0,
126+
// No default for sha256 since it is platform dependent
127+
sha256,
128+
} = config;
129+
130+
// Resolve signer
131+
let signer: (authScheme?: AuthScheme) => Promise<RequestSigner>;
132+
if (config.signer) {
133+
// if signer is supplied by user, normalize it to a function returning a promise for signer.
134+
signer = normalizeProvider(config.signer);
135+
} else if (config.regionInfoProvider) {
136+
// This branch is for endpoints V1.
137+
// construct a provider inferring signing from region.
138+
signer = () =>
139+
normalizeProvider(config.region)()
140+
.then(
141+
async (region) =>
142+
[
143+
(await config.regionInfoProvider!(region, {
144+
useFipsEndpoint: await config.useFipsEndpoint(),
145+
useDualstackEndpoint: await config.useDualstackEndpoint(),
146+
})) || {},
147+
region,
148+
] as [RegionInfo, string]
149+
)
150+
.then(([regionInfo, region]) => {
151+
const { signingRegion, signingService } = regionInfo;
152+
// update client's singing region and signing service config if they are resolved.
153+
// signing region resolving order: user supplied signingRegion -> endpoints.json inferred region -> client region
154+
config.signingRegion = config.signingRegion || signingRegion || region;
155+
// signing name resolving order:
156+
// user supplied signingName -> endpoints.json inferred (credential scope -> model arnNamespace) -> model service id
157+
config.signingName = config.signingName || signingService || config.serviceId;
158+
159+
const params: SignatureV4Init & SignatureV4CryptoInit = {
160+
...config,
161+
credentials: normalizedCreds!,
162+
region: config.signingRegion,
163+
service: config.signingName,
164+
sha256,
165+
uriEscapePath: signingEscapePath,
166+
};
167+
const SignerCtor = config.signerConstructor || SignatureV4;
168+
return new SignerCtor(params);
169+
});
170+
} else {
171+
// This branch is for endpoints V2.
172+
// Handle endpoints v2 that resolved per-command
173+
// TODO: need total refactor for reference auth architecture.
174+
signer = async (authScheme?: AuthScheme) => {
175+
authScheme = Object.assign(
176+
{},
177+
{
178+
name: "sigv4",
179+
signingName: config.signingName || config.defaultSigningName!,
180+
signingRegion: await normalizeProvider(config.region)(),
181+
properties: {},
182+
},
183+
authScheme
184+
);
185+
186+
const signingRegion = authScheme.signingRegion;
187+
const signingService = authScheme.signingName;
188+
// update client's singing region and signing service config if they are resolved.
189+
// signing region resolving order: user supplied signingRegion -> endpoints.json inferred region -> client region
190+
config.signingRegion = config.signingRegion || signingRegion;
191+
// signing name resolving order:
192+
// user supplied signingName -> endpoints.json inferred (credential scope -> model arnNamespace) -> model service id
193+
config.signingName = config.signingName || signingService || config.serviceId;
194+
195+
const params: SignatureV4Init & SignatureV4CryptoInit = {
196+
...config,
197+
credentials: normalizedCreds!,
198+
region: config.signingRegion,
199+
service: config.signingName,
200+
sha256,
201+
uriEscapePath: signingEscapePath,
202+
};
203+
204+
const SignerCtor = config.signerConstructor || SignatureV4;
205+
return new SignerCtor(params);
206+
};
207+
}
208+
209+
return {
210+
...config,
211+
systemClockOffset,
212+
signingEscapePath,
213+
credentials: normalizedCreds!,
214+
signer,
215+
};
216+
};
217+
218+
/**
219+
* @deprecated renamed to {@link AwsSdkSigV4AuthInputConfig}
220+
*/
221+
export interface AWSSDKSigV4AuthInputConfig extends AwsSdkSigV4AuthInputConfig {}
222+
223+
/**
224+
* @deprecated renamed to {@link AwsSdkSigV4PreviouslyResolved}
225+
*/
226+
export interface AWSSDKSigV4PreviouslyResolved extends AwsSdkSigV4PreviouslyResolved {}
227+
228+
/**
229+
* @deprecated renamed to {@link AwsSdkSigV4AuthResolvedConfig}
230+
*/
231+
export interface AWSSDKSigV4AuthResolvedConfig extends AwsSdkSigV4AuthResolvedConfig {}
232+
233+
/**
234+
* @deprecated renamed to {@link resolveAwsSdkSigV4Config}
235+
*/
236+
export const resolveAWSSDKSigV4Config = resolveAwsSdkSigV4Config;

0 commit comments

Comments
 (0)