Skip to content

Commit

Permalink
chore: pass fips/dualstack config to regionInfoProvider (#2982)
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr committed Nov 4, 2021
1 parent 447580b commit 45365ac
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 22 deletions.
Expand Up @@ -57,13 +57,17 @@ export interface EndpointsResolvedConfig extends Required<EndpointsInputConfig>

export const resolveEndpointsConfig = <T>(
input: T & EndpointsInputConfig & PreviouslyResolved
): T & EndpointsResolvedConfig => ({
...input,
tls: input.tls ?? true,
endpoint: input.endpoint
? normalizeEndpoint({ ...input, endpoint: input.endpoint })
: () => getEndpointFromRegion(input),
isCustomEndpoint: input.endpoint ? true : false,
useDualstackEndpoint: normalizeBoolean(input.useDualstackEndpoint!),
useFipsEndpoint: normalizeBoolean(input.useFipsEndpoint!),
});
): T & EndpointsResolvedConfig => {
const useDualstackEndpoint = normalizeBoolean(input.useDualstackEndpoint!);
const useFipsEndpoint = normalizeBoolean(input.useFipsEndpoint!);
return {
...input,
tls: input.tls ?? true,
endpoint: input.endpoint
? normalizeEndpoint({ ...input, endpoint: input.endpoint })
: () => getEndpointFromRegion({ ...input, useDualstackEndpoint, useFipsEndpoint }),
isCustomEndpoint: input.endpoint ? true : false,
useDualstackEndpoint,
useFipsEndpoint,
};
};
Expand Up @@ -4,8 +4,16 @@ describe(getEndpointFromRegion.name, () => {
const mockRegion = jest.fn();
const mockUrlParser = jest.fn();
const mockRegionInfoProvider = jest.fn();
const mockUseFipsEndpoint = jest.fn();
const mockUseDualstackEndpoint = jest.fn();

const mockInput = { region: mockRegion, urlParser: mockUrlParser, regionInfoProvider: mockRegionInfoProvider };
const mockInput = {
region: mockRegion,
urlParser: mockUrlParser,
regionInfoProvider: mockRegionInfoProvider,
useDualstackEndpoint: mockUseDualstackEndpoint,
useFipsEndpoint: mockUseFipsEndpoint,
};

const mockRegionValue = "mockRegion";
const mockEndpoint = {
Expand All @@ -19,6 +27,8 @@ describe(getEndpointFromRegion.name, () => {
mockRegion.mockResolvedValue(mockRegionValue);
mockUrlParser.mockResolvedValue(mockEndpoint);
mockRegionInfoProvider.mockResolvedValue(mockRegionInfo);
mockUseFipsEndpoint.mockResolvedValue(false);
mockUseDualstackEndpoint.mockResolvedValue(false);
});

afterEach(() => {
Expand All @@ -28,7 +38,10 @@ describe(getEndpointFromRegion.name, () => {

describe("tls", () => {
afterEach(() => {
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue);
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue, {
useDualstackEndpoint: false,
useFipsEndpoint: false,
});
});

it("uses protocol https when not defined", async () => {
Expand Down Expand Up @@ -81,14 +94,20 @@ describe(getEndpointFromRegion.name, () => {
} catch (error) {
expect(error.message).toEqual(errorMsg);
}
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue);
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue, {
useDualstackEndpoint: false,
useFipsEndpoint: false,
});
expect(mockUrlParser).not.toHaveBeenCalled();
});

it("returns parsed endpoint", async () => {
const endpoint = await getEndpointFromRegion(mockInput);
expect(endpoint).toEqual(mockEndpoint);
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue);
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue, {
useDualstackEndpoint: false,
useFipsEndpoint: false,
});
expect(mockUrlParser).toHaveBeenCalledWith(`https://${mockRegionInfo.hostname}`);
});
});
Expand Up @@ -5,6 +5,8 @@ interface GetEndpointFromRegionOptions {
tls?: boolean;
regionInfoProvider: RegionInfoProvider;
urlParser: UrlParser;
useDualstackEndpoint: Provider<boolean>;
useFipsEndpoint: Provider<boolean>;
}

export const getEndpointFromRegion = async (input: GetEndpointFromRegionOptions) => {
Expand All @@ -16,7 +18,9 @@ export const getEndpointFromRegion = async (input: GetEndpointFromRegionOptions)
throw new Error("Invalid region in client config");
}

const { hostname } = (await input.regionInfoProvider(region)) ?? {};
const useDualstackEndpoint = await input.useDualstackEndpoint();
const useFipsEndpoint = await input.useFipsEndpoint();
const { hostname } = (await input.regionInfoProvider(region, { useDualstackEndpoint, useFipsEndpoint })) ?? {};
if (!hostname) {
throw new Error("Cannot resolve hostname from client config");
}
Expand Down
Expand Up @@ -34,6 +34,7 @@ describe("bucketEndpointMiddleware", () => {
.fn()
.mockResolvedValue({ hostname: "foo.us-foo-2.amazonaws.com", partition: "aws-foo", signingRegion: mockRegion }),
useArnRegion: jest.fn().mockResolvedValue(false),
useFipsEndpoint: () => Promise.resolve(false),
useDualstackEndpoint: () => Promise.resolve(false),
};

Expand Down
Expand Up @@ -31,9 +31,11 @@ export const bucketEndpointMiddleware =
} else if (validateArn(bucketName)) {
const bucketArn = parseArn(bucketName);
const clientRegion = getPseudoRegion(await options.region());
const { partition, signingRegion = clientRegion } = (await options.regionInfoProvider(clientRegion)) || {};
const useDualstackEndpoint = await options.useDualstackEndpoint();
const useFipsEndpoint = await options.useFipsEndpoint();
const { partition, signingRegion = clientRegion } =
(await options.regionInfoProvider(clientRegion, { useDualstackEndpoint, useFipsEndpoint })) || {};
const useArnRegion = await options.useArnRegion();
const dualstackEndpoint = await options.useDualstackEndpoint();
const {
hostname,
bucketEndpoint,
Expand All @@ -43,7 +45,7 @@ export const bucketEndpointMiddleware =
bucketName: bucketArn,
baseHostname: request.hostname,
accelerateEndpoint: options.useAccelerateEndpoint,
dualstackEndpoint,
dualstackEndpoint: useDualstackEndpoint,
pathStyleEndpoint: options.forcePathStyle,
tlsCompatible: request.protocol === "https:",
useArnRegion,
Expand Down
5 changes: 5 additions & 0 deletions packages/middleware-bucket-endpoint/src/configurations.ts
Expand Up @@ -28,6 +28,7 @@ interface PreviouslyResolved {
isCustomEndpoint: boolean;
region: Provider<string>;
regionInfoProvider: RegionInfoProvider;
useFipsEndpoint: Provider<boolean>;
useDualstackEndpoint: Provider<boolean>;
}

Expand All @@ -49,6 +50,10 @@ export interface BucketEndpointResolvedConfig {
* Resolved value for input config {@link BucketEndpointInputConfig.useAccelerateEndpoint}
*/
useAccelerateEndpoint: boolean;
/**
* Enables FIPS compatible endpoints.
*/
useFipsEndpoint: Provider<boolean>;
/**
* Enables IPv6/IPv4 dualstack endpoint.
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/middleware-sdk-s3-control/src/configurations.ts
Expand Up @@ -12,6 +12,7 @@ interface PreviouslyResolved {
isCustomEndpoint: boolean;
region: Provider<string>;
regionInfoProvider: RegionInfoProvider;
useFipsEndpoint: Provider<boolean>;
useDualstackEndpoint: Provider<boolean>;
}

Expand All @@ -21,6 +22,10 @@ export interface S3ControlResolvedConfig {
* @internal
*/
isCustomEndpoint: boolean;
/**
* Enables FIPS compatible endpoints.
*/
useFipsEndpoint: Provider<boolean>;
/**
* Enables IPv6/IPv4 dualstack endpoint.
*/
Expand Down
Expand Up @@ -36,11 +36,14 @@ export const parseOutpostArnablesMiddleaware =
if (!parameter) return next(args);

const clientRegion = await options.region();
const { regionInfoProvider } = options;
const useArnRegion = await options.useArnRegion();
const useFipsEndpoint = await options.useFipsEndpoint();
const useDualstackEndpoint = await options.useDualstackEndpoint();
const baseRegion = getPseudoRegion(clientRegion);
const { partition: clientPartition, signingRegion = baseRegion } = (await regionInfoProvider(baseRegion))!;
const { partition: clientPartition, signingRegion = baseRegion } = (await options.regionInfoProvider(baseRegion, {
useFipsEndpoint,
useDualstackEndpoint,
}))!;
const validatorOptions: ValidateOutpostsArnOptions = {
useDualstackEndpoint,
clientRegion,
Expand Down
Expand Up @@ -10,11 +10,13 @@ describe("getProcessArnablesMiddleware", () => {
region: string;
regionInfoProvider?: Provider<RegionInfo>;
useAccelerateEndpoint?: boolean;
useFipsEndpoint?: Provider<boolean>;
useDualstackEndpoint?: Provider<boolean>;
useArnRegion?: boolean;
};
const setupPluginOptions = (options: FakeOptions): S3ControlResolvedConfig => {
return {
useFipsEndpoint: () => Promise.resolve(false),
useDualstackEndpoint: () => Promise.resolve(false),
...options,
regionInfoProvider: options.regionInfoProvider ?? jest.fn().mockResolvedValue({ partition: "aws" }),
Expand Down
4 changes: 4 additions & 0 deletions packages/middleware-sdk-sts/src/index.ts
Expand Up @@ -2,14 +2,18 @@ import { AwsAuthInputConfig, AwsAuthResolvedConfig, resolveAwsAuthConfig } from
import { Client, Credentials, HashConstructor, Pluggable, Provider, RegionInfoProvider } from "@aws-sdk/types";

export interface StsAuthInputConfig extends AwsAuthInputConfig {}

interface PreviouslyResolved {
credentialDefaultProvider: (input: any) => Provider<Credentials>;
region: string | Provider<string>;
regionInfoProvider: RegionInfoProvider;
signingName?: string;
serviceId: string;
sha256: HashConstructor;
useFipsEndpoint: Provider<boolean>;
useDualstackEndpoint: Provider<boolean>;
}

export interface StsAuthResolvedConfig extends AwsAuthResolvedConfig {
/**
* Reference to STSClient class constructor.
Expand Down
3 changes: 2 additions & 1 deletion packages/middleware-signing/src/configuration.spec.ts
Expand Up @@ -3,7 +3,6 @@ import { HttpRequest } from "@aws-sdk/protocol-http";
import { resolveAwsAuthConfig, resolveSigV4AuthConfig } from "./configurations";

describe("AuthConfig", () => {

describe("resolveAwsAuthConfig", () => {
const inputParams = {
credentialDefaultProvider: () => () => Promise.resolve({ accessKeyId: "key", secretAccessKey: "secret" }),
Expand All @@ -15,6 +14,8 @@ describe("AuthConfig", () => {
digest: jest.fn().mockReturnValue("SHA256 hash"),
}),
credentials: jest.fn().mockResolvedValue({ accessKeyId: "key", secretAccessKey: "secret" }),
useFipsEndpoint: () => Promise.resolve(false),
useDualstackEndpoint: () => Promise.resolve(false),
};

beforeEach(() => {
Expand Down
14 changes: 13 additions & 1 deletion packages/middleware-signing/src/configurations.ts
@@ -1,6 +1,7 @@
import { memoize } from "@aws-sdk/property-provider";
import { SignatureV4, SignatureV4CryptoInit, SignatureV4Init } from "@aws-sdk/signature-v4";
import { Credentials, HashConstructor, Provider, RegionInfo, RegionInfoProvider, RequestSigner } from "@aws-sdk/types";
import { options } from "yargs";

// 5 minutes buffer time the refresh the credential before it really expires
const CREDENTIAL_EXPIRE_WINDOW = 300000;
Expand Down Expand Up @@ -73,6 +74,8 @@ interface PreviouslyResolved {
signingName?: string;
serviceId: string;
sha256: HashConstructor;
useFipsEndpoint: Provider<boolean>;
useDualstackEndpoint: Provider<boolean>;
}

interface SigV4PreviouslyResolved {
Expand Down Expand Up @@ -118,7 +121,16 @@ export const resolveAwsAuthConfig = <T>(
//construct a provider inferring signing from region.
signer = () =>
normalizeProvider(input.region)()
.then(async (region) => [(await input.regionInfoProvider(region)) || {}, region] as [RegionInfo, string])
.then(
async (region) =>
[
(await input.regionInfoProvider(region, {
useFipsEndpoint: await input.useFipsEndpoint(),
useDualstackEndpoint: await input.useDualstackEndpoint(),
})) || {},
region,
] as [RegionInfo, string]
)
.then(([regionInfo, region]) => {
const { signingRegion, signingService } = regionInfo;
//update client's singing region and signing service config if they are resolved.
Expand Down

0 comments on commit 45365ac

Please sign in to comment.