From c32a6e16224f512b67b9a1fb9f7b88e6f88fea32 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 6 Jan 2018 22:45:43 -0800 Subject: [PATCH 1/2] Added optional email cleanup on S3 at end of process --- CHANGELOG.md | 4 +++ README.md | 3 +- example/index.js | 3 +- index.js | 52 +++++++++++++++++++++++++++------ package.json | 2 +- test/emailCleanupOnS3.js | 62 ++++++++++++++++++++++++++++++++++++++++ test/handler.js | 6 +++- 7 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 test/emailCleanupOnS3.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bd3a30..20fdc66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log for aws-lambda-ses-forwarder +## 4.3.0 [2017/3/10] + +- Added optional `cleanupS3Email` step at the end of process + ## 4.2.0 [2017/6/20] - Removing `Message-ID` header from messages to fix `InvalidParameterValue: diff --git a/README.md b/README.md index e65f81d..dcfe313 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,8 @@ the email forwarding mapping from original destinations to new destination. "Effect": "Allow", "Action": [ "s3:GetObject", - "s3:PutObject" + "s3:PutObject", + "s3:DeleteObject" ], "Resource": "arn:aws:s3:::S3-BUCKET-NAME/*" } diff --git a/example/index.js b/example/index.js index 98acf98..dfc115d 100644 --- a/example/index.js +++ b/example/index.js @@ -16,7 +16,8 @@ exports.handler = function(event, context, callback) { "abuse@example.com": [ "example.jim@example.com" ] - } + }, + emailCleanupS3: false } }; LambdaForwarder.handler(event, context, callback, overrides); diff --git a/index.js b/index.js index ead0495..c4d311c 100644 --- a/index.js +++ b/index.js @@ -27,6 +27,8 @@ console.log("AWS Lambda SES Forwarder // @arithmetric // Version 4.2.0"); // // To match a mailbox name on all domains, use a key without the "at" symbol // and domain part of an email address (i.e. `info`). +// +// - emailCleanupOnS3: true to delete email from S3 bucket var defaultConfig = { fromEmail: "noreply@example.com", subjectPrefix: "", @@ -46,7 +48,8 @@ var defaultConfig = { "info": [ "info@example.com" ] - } + }, + emailCleanupOnS3: false }; /** @@ -280,6 +283,34 @@ exports.sendMessage = function(data) { }); }; +/** + * Clean up (delete) the S3 email object + * + * @param {object} data - Data bundle with context, email, etc. + * + * @return {object} - Promise resolved with data. + */ +exports.emailCleanupOnS3 = function(data) { + data.log({level: "info", message: "Deleting email at s3://" + + data.config.emailBucket + '/' + data.config.emailKeyPrefix + + data.email.messageId}); + return new Promise(function(resolve, reject) { + data.s3.deleteObject({ + Bucket: data.config.emailBucket, + Key: data.config.emailKeyPrefix + data.email.messageId + }, function(err, result) { + if (err) { + data.log({level: "error", message: "deleteObject() returned error:", + error: err, stack: err.stack}); + return reject(new Error('Error: Email cleanup on S3 failed.')); + } + data.log({level: "info", message: "emailCleanupOnS3() successful.", + result: result}); + resolve(data); + }); + }); +}; + /** * Handler function to be invoked by AWS Lambda with an inbound SES email as * the event. @@ -291,14 +322,6 @@ exports.sendMessage = function(data) { * configuration, SES object, and S3 object. */ exports.handler = function(event, context, callback, overrides) { - var steps = overrides && overrides.steps ? overrides.steps : - [ - exports.parseEvent, - exports.transformRecipients, - exports.fetchMessage, - exports.processMessage, - exports.sendMessage - ]; var data = { event: event, callback: callback, @@ -309,6 +332,17 @@ exports.handler = function(event, context, callback, overrides) { s3: overrides && overrides.s3 ? overrides.s3 : new AWS.S3({signatureVersion: 'v4'}) }; + var steps = overrides && overrides.steps ? overrides.steps : + [ + exports.parseEvent, + exports.transformRecipients, + exports.fetchMessage, + exports.processMessage, + exports.sendMessage + ]; + if (data.config.emailCleanupOnS3) { + steps.push(exports.emailCleanupOnS3); + } Promise.series(steps, data) .then(function(data) { data.log({level: "info", message: "Process finished successfully."}); diff --git a/package.json b/package.json index 40c2622..adcb129 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aws-lambda-ses-forwarder", - "version": "4.2.0", + "version": "4.3.0", "description": "Serverless email forwarding using AWS Lambda and SES", "main": "index.js", "scripts": { diff --git a/test/emailCleanupOnS3.js b/test/emailCleanupOnS3.js new file mode 100644 index 0000000..3d7d597 --- /dev/null +++ b/test/emailCleanupOnS3.js @@ -0,0 +1,62 @@ + +/* global describe, it */ + +var assert = require("assert"); + +var index = require("../index"); + +describe('index.js', function() { + describe('#emailCleanupOnS3()', function() { + it('should invoke the AWS S3 SDK to delete the email object', + function(done) { + var data = { + config: { + emailBucket: "bucket", + emailKeyPrefix: "prefix/", + emailCleanupOnS3: true + }, + context: {}, + email: { + messageId: "abc" + }, + log: console.log, + s3: { + deleteObject: function(options, callback) { + callback(null); + } + } + }; + index.emailCleanupOnS3(data) + .then(function() { + assert.ok(true, "emailCleanupOnS3 returned successfully"); + done(); + }); + }); + + it('should result in failure if the AWS S3 SDK cannot delete the object', + function(done) { + var data = { + config: { + emailBucket: "bucket", + emailKeyPrefix: "prefix/", + emailCleanupOnS3: true + }, + context: {}, + email: { + messageId: "abc" + }, + log: console.log, + s3: { + deleteObject: function(options, callback) { + callback(true); + } + } + }; + index.emailCleanupOnS3(data) + .catch(function(err) { + assert.ok(err, "emailCleanupOnS3 aborted operation"); + done(); + }); + }); + }); +}); diff --git a/test/handler.js b/test/handler.js index 167c644..9d5f740 100644 --- a/test/handler.js +++ b/test/handler.js @@ -21,6 +21,9 @@ describe('index.js', function() { }, getObject: function(options, callback) { callback(null, {Body: "email data"}); + }, + deleteObject: function(options, callback) { + callback(null, {}); } }, ses: { @@ -35,7 +38,8 @@ describe('index.js', function() { "info@example.com": [ "jim@example.com" ] - } + }, + emailCleanupOnS3: true } }; index.handler(event, context, callback, overrides); From ea702174be5e65ebaa9fafa1dfd57c4ce78d15f2 Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 8 Jan 2018 15:06:44 -0800 Subject: [PATCH 2/2] emailCleanupOnS3: move config assertion to cleanup method And completed documentation --- CHANGELOG.md | 4 ---- README.md | 8 ++++++-- index.js | 25 +++++++++++++------------ package.json | 2 +- test/emailCleanupOnS3.js | 14 ++++++++++++++ test/handler.js | 2 +- 6 files changed, 35 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20fdc66..3bd3a30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,5 @@ # Change Log for aws-lambda-ses-forwarder -## 4.3.0 [2017/3/10] - -- Added optional `cleanupS3Email` step at the end of process - ## 4.2.0 [2017/6/20] - Removing `Message-ID` header from messages to fix `InvalidParameterValue: diff --git a/README.md b/README.md index dcfe313..17e1129 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,9 @@ the email forwarding mapping from original destinations to new destination. - For Role, choose "Basic Execution Role" under Create New Role. In the popup, give the role a name (e.g., LambdaSesForwarder). Configure the role policy to - the following: + the following: +( Note: Action `s3:DeleteObject` is only needed when using the +`emailCleanupOnS3` option.) ``` { "Version": "2012-10-17", @@ -163,7 +165,9 @@ likely need to adjust the bucket policy statement with one like this: } ``` -8. Optionally set the S3 lifecycle for this bucket to delete/expire objects +8. Optionally, to clean up saved email files on the AWS S3 Bucket, you can either: +- Set the `emailCleanupOnS3` option in the script configuration to true (this will delete files on a succesful process). +- Set the S3 lifecycle for this bucket to delete/expire objects after a few days to clean up the saved emails. ## Extending diff --git a/index.js b/index.js index c4d311c..73f2552 100644 --- a/index.js +++ b/index.js @@ -284,13 +284,16 @@ exports.sendMessage = function(data) { }; /** - * Clean up (delete) the S3 email object + * Clean up (delete) the S3 email object if `data.config.emailCleanupOnS3` equals true * * @param {object} data - Data bundle with context, email, etc. * * @return {object} - Promise resolved with data. */ exports.emailCleanupOnS3 = function(data) { + if (!data.config.emailCleanupOnS3) { + return Promise.resolve(data); + } data.log({level: "info", message: "Deleting email at s3://" + data.config.emailBucket + '/' + data.config.emailKeyPrefix + data.email.messageId}); @@ -322,6 +325,15 @@ exports.emailCleanupOnS3 = function(data) { * configuration, SES object, and S3 object. */ exports.handler = function(event, context, callback, overrides) { + var steps = overrides && overrides.steps ? overrides.steps : + [ + exports.parseEvent, + exports.transformRecipients, + exports.fetchMessage, + exports.processMessage, + exports.sendMessage, + exports.emailCleanupOnS3 + ]; var data = { event: event, callback: callback, @@ -332,17 +344,6 @@ exports.handler = function(event, context, callback, overrides) { s3: overrides && overrides.s3 ? overrides.s3 : new AWS.S3({signatureVersion: 'v4'}) }; - var steps = overrides && overrides.steps ? overrides.steps : - [ - exports.parseEvent, - exports.transformRecipients, - exports.fetchMessage, - exports.processMessage, - exports.sendMessage - ]; - if (data.config.emailCleanupOnS3) { - steps.push(exports.emailCleanupOnS3); - } Promise.series(steps, data) .then(function(data) { data.log({level: "info", message: "Process finished successfully."}); diff --git a/package.json b/package.json index adcb129..40c2622 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aws-lambda-ses-forwarder", - "version": "4.3.0", + "version": "4.2.0", "description": "Serverless email forwarding using AWS Lambda and SES", "main": "index.js", "scripts": { diff --git a/test/emailCleanupOnS3.js b/test/emailCleanupOnS3.js index 3d7d597..8de942c 100644 --- a/test/emailCleanupOnS3.js +++ b/test/emailCleanupOnS3.js @@ -7,6 +7,20 @@ var index = require("../index"); describe('index.js', function() { describe('#emailCleanupOnS3()', function() { + it('should simply return data when emailCleanupOnS3 is false', + function(done) { + var data = { + config: { + emailCleanupOnS3: false + } + }; + index.emailCleanupOnS3(data) + .then(function() { + assert.ok(true, "emailCleanupOnS3 returned successfully"); + done(); + }); + }); + it('should invoke the AWS S3 SDK to delete the email object', function(done) { var data = { diff --git a/test/handler.js b/test/handler.js index 9d5f740..4b71f8f 100644 --- a/test/handler.js +++ b/test/handler.js @@ -39,7 +39,7 @@ describe('index.js', function() { "jim@example.com" ] }, - emailCleanupOnS3: true + emailCleanupOnS3: false } }; index.handler(event, context, callback, overrides);