Skip to content

Commit

Permalink
refactor: use separate lib for creating presigned urls
Browse files Browse the repository at this point in the history
Based on the response on cloudydeno/deno-aws_api#29
we had to use another method of creating presigned urls.

We followed the recommendation on the above issue and pulled in a lib
specifically for that
  • Loading branch information
TillaTheHun0 committed Feb 11, 2022
1 parent d1ea3de commit 300dbd7
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 62 deletions.
11 changes: 8 additions & 3 deletions adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ const [META, CREATED_AT, DELETED_AT] = ["meta.json", "createdAt", "deletedAt"];
* @returns
*/
export default function (bucketPrefix, aws) {
const { s3 } = aws;
const { s3, getSignedUrl, credentialProvider } = aws;

const getCredentials = Async.fromPromise(credentialProvider.getCredentials);

const client = {
makeBucket: Async.fromPromise(lib.makeBucket(s3)),
Expand All @@ -77,7 +79,7 @@ export default function (bucketPrefix, aws) {
removeObject: Async.fromPromise(lib.removeObject(s3)),
removeObjects: Async.fromPromise(lib.removeObjects(s3)),
getObject: Async.fromPromise(lib.getObject(s3)),
getSignedUrl: Async.fromPromise(lib.getSignedUrl(s3)),
getSignedUrl: Async.fromPromise(lib.getSignedUrl({ getSignedUrl })),
listObjects: Async.fromPromise(lib.listObjects(s3)),
};

Expand Down Expand Up @@ -206,7 +208,10 @@ export default function (bucketPrefix, aws) {
}

return Async.of()
.chain(() => client.getSignedUrl({ bucket, key, method: "putObject" })) // 5 min expiration
.chain(getCredentials)
.chain((credentials) =>
client.getSignedUrl({ bucket, key, method: "PUT", credentials })
)
.map((url) => ({ ok: true, url }));
}

Expand Down
2 changes: 2 additions & 0 deletions deps.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export * as R from "https://cdn.skypack.dev/ramda@^0.28.0";
export { default as crocks } from "https://cdn.skypack.dev/crocks@^0.12.4";

export { getSignedUrl } from "https://deno.land/x/[email protected]/mod.ts";
export {
ApiFactory,
AwsEndpointResolver,
DefaultCredentialsProvider,
} from "https://deno.land/x/[email protected]/client/mod.ts";
// Use my fork until https://github.com/cloudydeno/deno-aws_api/pull/27 is merged
export { S3 } from "https://raw.githubusercontent.com/TillaTheHun0/deno-aws_api/fix/s3-delete-objects-missing-header/lib/services/s3/mod.ts";
Expand Down
2 changes: 2 additions & 0 deletions deps_lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@
"https://deno.land/[email protected]/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
"https://deno.land/[email protected]/path/win32.ts": "11549e8c6df8307a8efcfa47ad7b2a75da743eac7d4c89c9723a944661c8bd2e",
"https://deno.land/[email protected]/streams/conversion.ts": "7ff9af42540063fa72003ab31a377ba9dde8532d43b16329b933c37a6d7aac5f",
"https://deno.land/[email protected]/hash/sha256.ts": "2b524ce88f61b23f7340fba0e657c55e651b764c39c95564353d210b4a4df176",
"https://deno.land/x/[email protected]/client/client.ts": "3380542bb8da20b2cb8d1fbf04e183dab09c4cb80e3ee7be4e0eb69d053bd404",
"https://deno.land/x/[email protected]/client/common.ts": "e0cc6c3c4362716641c66e761c08b74112ba8f5c80fe1becb239ff4960dd9f61",
"https://deno.land/x/[email protected]/client/credentials.ts": "ca8e9d0687d9c52346e73b7b0cbbdb5783d9e78c5b8b38e8ed01dd4e4503a3f1",
Expand All @@ -337,6 +338,7 @@
"https://deno.land/x/[email protected]/client/mod.ts": "6faaf88ec3cf92ccf651d4f823425532fd1fb4a3e20870e94eb3df6d2c44c965",
"https://deno.land/x/[email protected]/client/signing.ts": "6465e0877e5cbbf467560fb7f666611689b2a15ce728f6f6286810c148e41063",
"https://deno.land/x/[email protected]/encoding/xml.ts": "0470b42ae0aada864f0fa829d74e6a68faa9e15df21987d74df7cc5150db6d94",
"https://deno.land/x/[email protected]/mod.ts": "a65be4547e61c496978c76879558564af47af4f1a0f9dd649558c728659f2ea5",
"https://raw.githubusercontent.com/TillaTheHun0/deno-aws_api/fix/s3-delete-objects-missing-header/lib/client/common.ts": "e0cc6c3c4362716641c66e761c08b74112ba8f5c80fe1becb239ff4960dd9f61",
"https://raw.githubusercontent.com/TillaTheHun0/deno-aws_api/fix/s3-delete-objects-missing-header/lib/encoding/common.ts": "cb57892b2adaf1ee6c2a99d65bdac98a748aadcff299b452e612c19b6149821a",
"https://raw.githubusercontent.com/TillaTheHun0/deno-aws_api/fix/s3-delete-objects-missing-header/lib/encoding/xml.ts": "0470b42ae0aada864f0fa829d74e6a68faa9e15df21987d74df7cc5150db6d94",
Expand Down
2 changes: 1 addition & 1 deletion dev_deps.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export {
assertEquals,
assertObjectMatch,
assertThrows,
} from "https://deno.land/std@0.122.0/testing/asserts.ts";
} from "https://deno.land/std@0.125.0/testing/asserts.ts";

export { spy } from "https://deno.land/x/[email protected]/mod.ts";
export { cuid } from "https://deno.land/x/[email protected]/index.js";
32 changes: 16 additions & 16 deletions dev_deps_lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,20 @@
"https://deno.land/[email protected]/fmt/colors.ts": "8368ddf2d48dfe413ffd04cdbb7ae6a1009cf0dccc9c7ff1d76259d9c61a0621",
"https://deno.land/[email protected]/testing/_diff.ts": "e6a10d2aca8d6c27a9c5b8a2dbbf64353874730af539707b5b39d4128140642d",
"https://deno.land/[email protected]/testing/asserts.ts": "3ef8c26b2a1e8e71c8a39279ab98e5743d1131a719b13828c6cd8e29f211ddaa",
"https://deno.land/std@0.122.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
"https://deno.land/std@0.122.0/_util/os.ts": "dfb186cc4e968c770ab6cc3288bd65f4871be03b93beecae57d657232ecffcac",
"https://deno.land/std@0.122.0/fmt/colors.ts": "8368ddf2d48dfe413ffd04cdbb7ae6a1009cf0dccc9c7ff1d76259d9c61a0621",
"https://deno.land/std@0.122.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853",
"https://deno.land/std@0.122.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4",
"https://deno.land/std@0.122.0/path/_util.ts": "2e06a3b9e79beaf62687196bd4b60a4c391d862cfa007a20fc3a39f778ba073b",
"https://deno.land/std@0.122.0/path/common.ts": "f41a38a0719a1e85aa11c6ba3bea5e37c15dd009d705bd8873f94c833568cbc4",
"https://deno.land/std@0.122.0/path/glob.ts": "7bf2349e818e332a830f3d8874c3f45dd7580b6c742ed50dbf6282d84ab18405",
"https://deno.land/std@0.122.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12",
"https://deno.land/std@0.122.0/path/posix.ts": "34349174b9cd121625a2810837a82dd8b986bbaaad5ade690d1de75bbb4555b2",
"https://deno.land/std@0.122.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
"https://deno.land/std@0.122.0/path/win32.ts": "11549e8c6df8307a8efcfa47ad7b2a75da743eac7d4c89c9723a944661c8bd2e",
"https://deno.land/std@0.122.0/testing/_diff.ts": "e6a10d2aca8d6c27a9c5b8a2dbbf64353874730af539707b5b39d4128140642d",
"https://deno.land/std@0.122.0/testing/asserts.ts": "e1e7325b449cc9c747c309690b22d8cf464e401f79f494ddb106e4842fdb4dda",
"https://deno.land/std@0.123.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
"https://deno.land/std@0.123.0/_util/os.ts": "dfb186cc4e968c770ab6cc3288bd65f4871be03b93beecae57d657232ecffcac",
"https://deno.land/std@0.123.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853",
"https://deno.land/std@0.123.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4",
"https://deno.land/std@0.123.0/path/_util.ts": "2e06a3b9e79beaf62687196bd4b60a4c391d862cfa007a20fc3a39f778ba073b",
"https://deno.land/std@0.123.0/path/common.ts": "f41a38a0719a1e85aa11c6ba3bea5e37c15dd009d705bd8873f94c833568cbc4",
"https://deno.land/std@0.123.0/path/glob.ts": "7bf2349e818e332a830f3d8874c3f45dd7580b6c742ed50dbf6282d84ab18405",
"https://deno.land/std@0.123.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12",
"https://deno.land/std@0.123.0/path/posix.ts": "34349174b9cd121625a2810837a82dd8b986bbaaad5ade690d1de75bbb4555b2",
"https://deno.land/std@0.123.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
"https://deno.land/std@0.123.0/path/win32.ts": "11549e8c6df8307a8efcfa47ad7b2a75da743eac7d4c89c9723a944661c8bd2e",
"https://deno.land/std@0.125.0/fmt/colors.ts": "870f10d440af4c309ab7bff97266d8ddb2c8a69039bcbd33253c8bfc14502116",
"https://deno.land/std@0.125.0/testing/_diff.ts": "9d849cd6877694152e01775b2d93f9d6b7aef7e24bfe3bfafc4d7a1ac8e9f392",
"https://deno.land/std@0.125.0/testing/asserts.ts": "b7a2b7f80a12b9b2abe1bfeb7b84ead0e971671977c4d70390f43abd85c904ae",
"https://deno.land/[email protected]/_util/assert.ts": "e1f76e77c5ccb5a8e0dbbbe6cce3a56d2556c8cb5a9a8802fc9565af72462149",
"https://deno.land/[email protected]/async/deferred.ts": "ac95025f46580cf5197928ba90995d87f26e202c19ad961bc4e3177310894cdc",
"https://deno.land/[email protected]/async/delay.ts": "35957d585a6e3dd87706858fb1d6b551cb278271b03f52c5a2cb70e65e00c26a",
Expand Down Expand Up @@ -246,8 +246,8 @@
"https://deno.land/x/[email protected]/db.ts": "d5ecfa410b570874a648aa12bac90d9c6704dc75f1e8c03b8cac97dd02b003ce",
"https://deno.land/x/[email protected]/deps.ts": "adc4e59af148fe5a49087932536cdf24a7027daa16498c2983124e3ec4d90c20",
"https://deno.land/x/[email protected]/mod.ts": "d63583b978d32eff8b76e1ae5d83cba2fb27baa90cc1bcb0ad15a06122ea8c19",
"https://deno.land/x/[email protected].0/db.ts": "92e5f1735fd5896382e2cc6e94518cb57608777e2ac61c5f7bc1874b50df6f4d",
"https://deno.land/x/[email protected].0/mod.ts": "ccbecb6ecf551ca3c7f10011a534ef9e31b931df7c135d08c9469f435ba2e73b",
"https://deno.land/x/[email protected].1/db.ts": "92e5f1735fd5896382e2cc6e94518cb57608777e2ac61c5f7bc1874b50df6f4d",
"https://deno.land/x/[email protected].1/mod.ts": "36a25aca4ac10184f5a5254463c218b98ea32e6f468b95ee9af920fa104ba8b7",
"https://deno.land/x/[email protected]/db.ts": "22db9bd6eb24934105098e9fce27d2746ef3861cd2668edef749c83c60acd60b",
"https://deno.land/x/[email protected]/deps.ts": "2dd71d14adbe5f3c837bd79e010d9706895a2cebdcd50d13ab1debe42b32ff7f",
"https://deno.land/x/[email protected]/mod.ts": "d63583b978d32eff8b76e1ae5d83cba2fb27baa90cc1bcb0ad15a06122ea8c19",
Expand Down
23 changes: 14 additions & 9 deletions lib/s3.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,22 @@ export const getObject = (s3) =>
});
};

/**
* https://deno.land/x/[email protected]#options
*/
export const getSignedUrl = (s3) =>
// expires in 5 min by default
({ bucket, key, method, expires = 60 * 5 }) => {
return new Promise((resolve) => {
// waiting on https://github.com/cloudydeno/deno-aws_api/issues/29 to be resolved
s3.getSignedUrl(
method,
{ Bucket: bucket, Key: key, Expires: expires },
resolve,
); // resolves to url
});
({ bucket, key, method, expires = 60 * 5, credentials }) => {
return Promise.resolve(s3.getSignedUrl({
accessKeyId: credentials.awsAccessKeyId,
secretAccessKey: credentials.awsSecretKey,
sessionToken: credentials.sessionToken,
region: credentials.region,
bucketName: bucket,
objectPath: key.startsWith("/") ? key : `/${key}`,
expiresIn: expires,
method,
}));
};

export const listObjects = (s3) =>
Expand Down
70 changes: 48 additions & 22 deletions mod.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { ApiFactory, AwsEndpointResolver, crocks, R, S3 } from "./deps.js";
import {
ApiFactory,
AwsEndpointResolver,
crocks,
DefaultCredentialsProvider,
getSignedUrl,
R,
S3,
} from "./deps.js";

import createAdapter from "./adapter.js";
import PORT_NAME from "./port_name.js";
Expand Down Expand Up @@ -40,36 +48,53 @@ export default (
{ region: "us-east-1" },
env,
);

const createCredentialProvider = (env) =>
over(
lensProp("credentialProvider"),
/**
* Either use provided credentials or use the DefaultCredentialsProvider
* from AWS deno sdk, merging in this adapters defualt region
*/
() =>
(env.awsAccessKeyId && env.awsSecretKey && env.region)
? { getCredentials: () => Promise.resolve(env) }
: {
...DefaultCredentialsProvider,
getCredentials: () =>
DefaultCredentialsProvider.getCredentials()
.then(mergeRight(
{ region: "us-east-1" }, // add default region if not provided
)),
},
env,
);

const createFactory = (env) =>
over(
lensProp("factory"),
/**
* Disable using Dualstack endpoints, so this adapter will use VPC Gateway endpoint when used within a VPC
* - For lib api, see https://github.com/cloudydeno/deno-aws_api/blob/3afef9fe3aaef842fd3a19245593494c3705a1dd/lib/client/endpoints.ts#L19
* - For Dualstack description https://docs.aws.amazon.com/AmazonS3/latest/userguide/dual-stack-endpoints.html#dual-stack-endpoints-description
*/
() =>
(env.awsAccessKeyId && env.awsSecretKey && env.region)
/**
* Disable using Dualstack endpoints, so this adapter will use VPC Gateway endpoint when used within a VPC
* - For lib api, see https://github.com/cloudydeno/deno-aws_api/blob/3afef9fe3aaef842fd3a19245593494c3705a1dd/lib/client/endpoints.ts#L19
* - For Dualstack description https://docs.aws.amazon.com/AmazonS3/latest/userguide/dual-stack-endpoints.html#dual-stack-endpoints-description
*/
? new ApiFactory({
credentials: env,
endpointResolver: new AwsEndpointResolver({ useDualstack: false }),
})
: /**
* ApiFactory attempts to pull credentials from multiple environment places
* If not provided via constructor
* See https://github.com/cloudydeno/deno-aws_api/blob/2b8605516802c1b790a2b112c03b790feb3bf58f/lib/client/credentials.ts#L50
*/
new ApiFactory({
endpointResolver: new AwsEndpointResolver({
useDualstack: false,
}),
}),
new ApiFactory({
credentialProvider: env.credentialProvider,
endpointResolver: new AwsEndpointResolver({ useDualstack: false }),
}),
env,
);

const setAws = (env) =>
over(
lensProp("aws"),
() => ({ factory: env.factory, s3: new S3(env.factory) }),
() => ({
factory: env.factory,
credentialProvider: env.credentialProvider,
s3: new S3(env.factory),
getSignedUrl,
}),
env,
);

Expand All @@ -85,6 +110,7 @@ export default (
notIsNil(bucketPrefix)
.map(setPrefixOn(env))
)
.map(createCredentialProvider)
.map(createFactory)
.map(setAws)
.either(
Expand Down
17 changes: 15 additions & 2 deletions test/adapter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,23 @@ const s3 = {
});
},
putObject: () => Promise.resolve(),
getSignedUrl: (_method, _params, resolve) => resolve("https://foo.bar"),
};

const adapter = adapterBuilder("foo", { s3 });
const credentialProvider = {
getCredentials: () =>
Promise.resolve({
awsAccessKeyId: "foo",
awsSecretKey: "secret",
sessionToken: "token",
region: "us-east-1",
}),
};

const adapter = adapterBuilder("foo", {
s3,
credentialProvider,
getSignedUrl: () => Promise.resolve("https://foo.bar"),
});

const { test } = Deno;

Expand Down
35 changes: 27 additions & 8 deletions test/lib/s3.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,36 @@ test("getObject - should pass correct shape", async () => {
test("getSignedUrl - should pass correct shape", async () => {
const bucket = "buck";
const key = "foo.jpg";
const method = "putObject";
const method = "PUT";
const expires = 60 * 5;

s3.getSignedUrl = spy((_method, _params, resolve) => resolve());

await getSignedUrl(s3)({ bucket, key, method, expires }).catch(console.error);

const { args } = s3.getSignedUrl.calls.shift();
s3.getSignedUrl = spy(resolves());

await getSignedUrl(s3)({
bucket,
key,
method,
expires,
credentials: {
awsAccessKeyId: "foo",
awsSecretKey: "secret",
sessionToken: "token",
region: "us-east-1",
},
});

assertEquals(args[0], method);
assertObjectMatch(args[1], { Bucket: bucket, Key: key, Expires: expires });
assertObjectMatch(s3.getSignedUrl.calls.shift(), {
args: [{
accessKeyId: "foo",
secretAccessKey: "secret",
sessionToken: "token",
region: "us-east-1",
bucketName: bucket,
objectPath: `/${key}`,
expiresIn: expires,
method,
}],
});
});

test("listObjects - should pass correct shape", async () => {
Expand Down
11 changes: 10 additions & 1 deletion test/mod.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,16 @@ test("load - should default the region to us-east-1", async () => {

test("link - should return an adapter", () => {
const factory = createFactory("foo");
const adapter = factory.link({ prefix: "foo", aws: { s3: {} } })();
const adapter = factory.link({
prefix: "foo",
aws: {
s3: {},
credentialProvider: {
getCredentials: () => Promise.resolve(),
},
getSignedUrl: () => Promise.resolve(),
},
})();

assert(adapter);
assertEquals(typeof adapter, "object");
Expand Down

0 comments on commit 300dbd7

Please sign in to comment.