Skip to content

Commit

Permalink
Add codec server to encryption sample (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
bergundy committed Apr 29, 2022
1 parent d0d30b6 commit 3b9bae9
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 1 deletion.
1 change: 1 addition & 0 deletions .scripts/list-of-samples.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"production",
"protobufs",
"query-subscriptions",
"remote-codec",
"replay-history",
"search-attributes",
"signals-queries",
Expand Down
10 changes: 10 additions & 0 deletions encryption/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ Create a custom data converter that encrypts data with AES. See [encryption docs

- `EncryptionCodec`: [encryption-codec.ts](./src/encryption-codec.ts)
- Data Converter: [data-converter.ts](./src/data-converter.ts)
- Codec Server: [codec-server.ts](./src/codec-server.ts)

In order to view the decrypted payloads in the web UI:

- Run the Codec Server with `npm run codec-server`, it will listen to port 8888.
- Add this environment variable to the web UI server: `TEMPORAL_CODEC_ENDPOINT=http://localhost:8888`

In order to encrypt payloads with `tctl`, pass the following option when running the CLI:

- `tctl --codec_endpoint 'http://localhost:8888' ...`

The Payload Converter is supplied to the [client.ts](./src/client.ts) and [worker.ts](./src/worker.ts). When the Client sends `'Alice: Private message for Bob.'` to the Workflow, it gets encrypted on the Client and decrypted in the Worker. [`workflow.ts`](./src/workflow.ts) receives the decrypted message and appends another message. When it returns that longer string, the string gets encrypted by the Worker and decrypted by the Client.

Expand Down
7 changes: 6 additions & 1 deletion encryption/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"lint": "eslint .",
"start": "ts-node src/worker.ts",
"start.watch": "nodemon src/worker.ts",
"workflow": "ts-node src/client.ts"
"workflow": "ts-node src/client.ts",
"codec-server": "ts-node src/codec-server.ts"
},
"nodemonConfig": {
"execMap": {
Expand All @@ -21,13 +22,17 @@
},
"dependencies": {
"@ronomon/crypto-async": "^5.0.1",
"cors": "^2.8.5",
"ejson": "^2.2.2",
"express": "^4.18.0",
"temporalio": "0.21.x",
"uuid": "^8.3.2"
},
"devDependencies": {
"@tsconfig/node16": "^1.0.0",
"@types/cors": "^2.8.12",
"@types/ejson": "^2.1.3",
"@types/express": "^4.17.13",
"@types/ronomon__crypto-async": "^2.0.0",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.0.0",
Expand Down
91 changes: 91 additions & 0 deletions encryption/src/codec-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import express from 'express';
import cors from 'cors';
import * as proto from '@temporalio/proto';
import { EncryptionCodec } from './encryption-codec';

type Payload = proto.temporal.api.common.v1.IPayload;

interface JSONPayload {
metadata?: Record<string, string> | null;
data?: string | null;
}

interface Body {
payloads: JSONPayload[];
}

/**
* Helper function to convert a valid proto JSON to a payload object.
*
* This method will be part of the SDK when it supports proto JSON serialization.
*/
function fromJSON({ metadata, data }: JSONPayload): Payload {
return {
metadata:
metadata &&
Object.fromEntries(Object.entries(metadata).map(([k, v]): [string, Uint8Array] => [k, Buffer.from(v, 'base64')])),
data: data ? Buffer.from(data, 'base64') : undefined,
};
}

/**
* Helper function to convert a payload object to a valid proto JSON.
*
* This method will be part of the SDK when it supports proto JSON serialization.
*/
function toJSON({ metadata, data }: proto.temporal.api.common.v1.IPayload): JSONPayload {
return {
metadata:
metadata &&
Object.fromEntries(
Object.entries(metadata).map(([k, v]): [string, string] => [k, Buffer.from(v).toString('base64')])
),
data: data ? Buffer.from(data).toString('base64') : undefined,
};
}

async function main(port = 8888) {
const codec = await EncryptionCodec.create('test-key-id');

const app = express();
app.use(cors({ origin: 'http://localhost:8088', allowedHeaders: ['x-namespace', 'content-type'] }));
app.use(express.json());

app.post('/decode', async (req, res) => {
try {
const { payloads: raw } = req.body as Body;
const encoded = raw.map(fromJSON);
const decoded = await codec.decode(encoded);
const payloads = decoded.map(toJSON);
res.json({ payloads }).end();
} catch (err) {
console.error('Error in /decode', err);
res.status(500).end('Internal server error');
}
});

app.post('/encode', async (req, res) => {
try {
const { payloads: raw } = req.body as Body;
const decoded = raw.map(fromJSON);
const encoded = await codec.encode(decoded);
const payloads = encoded.map(toJSON);
res.json({ payloads }).end();
} catch (err) {
console.error('Error in /encode', err);
res.status(500).end('Internal server error');
}
});

await new Promise<void>((resolve, reject) => {
app.listen(port, resolve);
app.on('error', reject);
});

console.log('Codec server listening on port 8888');
}

main().catch((err) => {
console.error(err);
process.exit(1);
});

0 comments on commit 3b9bae9

Please sign in to comment.