Retrieve user-specific data from Dynamo, decrypt with KMS, send data to another app
@dylburger
code:
data:privatelast updated:5 years ago
today
Build integrations remarkably fast!
You're viewing a public workflow template.
Sign up to customize, add steps, modify code and more.
Join 1,000,000+ developers using the Pipedream platform
steps.
trigger
HTTP API
Deploy to generate unique URL
This workflow runs on Pipedream's servers and is triggered by HTTP / Webhook requests.
steps.
nodejs
auth
to use OAuth tokens and API keys in code via theauths object
code
Write any Node.jscodeand use anynpm package. You can alsoexport datafor use in later steps via return or this.key = 'value', pass input data to your code viaparams, and maintain state across executions with$checkpoint.
async (event, steps) => {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
}
111
const crypto = require("crypto")
const axios = require('axios')
const AWS = require("aws-sdk")

// Confirm the presence of appropriate env vars, exiting if absent
const { 
  DDB_KMS_AWS_ACCESS_KEY_ID, 
  DDB_KMS_AWS_SECRET_ACCESS_KEY, 
  USER_APP_AWS_REGION, 
  USER_APP_DDB_TABLE 
} = process.env

if (!DDB_KMS_AWS_ACCESS_KEY_ID) {
  $end("No DDB_KMS_AWS_ACCESS_KEY_ID environment variable defined")
}
if (!DDB_KMS_AWS_SECRET_ACCESS_KEY) {
  $end("No DDB_KMS_AWS_SECRET_ACCESS_KEY environment variable defined")
}
if (!USER_APP_AWS_REGION) {
  $end("No USER_APP_AWS_REGION environment variable defined")
}
if (!USER_APP_DDB_TABLE) {
  $end("No USER_APP_DDB_TABLE environment variable defined")
}

// Initialize AWS clients
const kmsClient = new AWS.KMS({
  accessKeyId: DDB_KMS_AWS_ACCESS_KEY_ID,
  secretAccessKey: DDB_KMS_AWS_SECRET_ACCESS_KEY,
  region: USER_APP_AWS_REGION,
})

const dynamoDocClient = new AWS.DynamoDB.DocumentClient({
  accessKeyId: DDB_KMS_AWS_ACCESS_KEY_ID,
  secretAccessKey: DDB_KMS_AWS_SECRET_ACCESS_KEY,
  region: USER_APP_AWS_REGION,
})

// $event.body contains the payload of the HTTP request sent to this pipeline.
// userId and app are both contained in the payload, so we can assign variables
// from $event.body accordingly
const { userId, app } = $event.body

// Retrieve the data from Dynamo and decrypt it
const { Item } = await dynamoDocClient
  .get({
    TableName: USER_APP_DDB_TABLE,
    Key: {
      userId,
      app
    }
  })
  .promise()

if (!Item) {
  $end(`No item found in Dynamo for user ${userId}, app ${app}`)
}

// Now we define a couple of helper functions for decryption to make the process
// clear. Since the data was originally encrypted with the data key and salt,
// we'll need to use both of these to decrypt the value using the master key
async function decryptWithMasterKey(CiphertextBlob) {
  const decryptedDataKey = await kmsClient
    .decrypt({
      CiphertextBlob
    })
    .promise();
  return decryptedDataKey.Plaintext;
}

async function decrypt(content, encryptedDataKey, salt) {
  const decryptedDataKey = await decryptWithMasterKey(encryptedDataKey);
  const decryptor = crypto.createDecipheriv(
    "AES-256-CBC",
    decryptedDataKey,
    salt
  );
  decryptor.write(content);
  decryptor.end();

  return decryptor.read().toString("utf8");
}

// Everything up to this point has been app-agnostic. Now, we decrypt
// specific attributes from the Dynamo object and set them as properties
// of $event we can reference later
switch(app) {
  case 'slack':
    const slackWebhookURL = await decrypt(
      Item.encryptedWebhookURL,
      Item.encryptedDataKey,
      Item.salt
    )
    
    // Send a message to the Slack webhook URL retrieved from Dynamo
    const { status, statusText, headers } = await axios.post(slackWebhookURL, {
      "text": ":tada: Test Slack Message!"
    })
    
    // Store the response so we can save to S3 for auditing
    $event.resp = {
      status,
      statusText,
      headers,
    }
    break
  default:
    $end("App not handled in switch statement")
}