Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AWS KMS middleware #827

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/kms/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/**
lib/
2 changes: 2 additions & 0 deletions packages/kms/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
4 changes: 4 additions & 0 deletions packages/kms/.mocharc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const baseConfig = require('../../.mocharc');
module.exports = {
...baseConfig,
};
Empty file added packages/kms/CHANGELOG.md
Empty file.
4 changes: 4 additions & 0 deletions packages/kms/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# steveo-kms

A AWS KMS provider for [Steveo](https://github.com/ordermentum/steveo) pub/pub background processing
library.
39 changes: 39 additions & 0 deletions packages/kms/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@steveojs/kms",
"version": "7.1.3",
"main": "lib/index.js",
"repository": "[email protected]:ordermentum/steveo.git",
"license": "Apache-2.0",
"author": "Ordermentum <[email protected]>",
"scripts": {
"build": "yarn run tsc",
"test": "NODE_ENV=test nyc yarn spec",
"spec": "tsc && yarn mocha",
"lint": "yarn eslint 'src/**/*.{ts,js}'",
"lint:test": "yarn eslint 'test/**/*.{ts,js}'",
"autotest": "yarn run mocha --watch",
"prepublish": "yarn run build",
"typecheck": "yarn run tsc --noEmit"
},
"files": [
"lib"
],
"devDependencies": {
"@ordermentum/eslint-config-ordermentum": "^2.3.3",
"@types/chai": "^4.3.11",
"chai": "4.4.1",
"mocha": "^10.2.0",
"nyc": "^15.1.0",
"prettier": "2.8.8",
"source-map-support": "0.5.21",
"steveo": "*",
"ts-node": "10.9.2",
"typescript": "^5.3.3"
},
"dependencies": {
"aws-sdk": "2.1692.0"
},
"peerDependencies": {
"steveo": "*"
}
}
61 changes: 61 additions & 0 deletions packages/kms/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Middleware, MiddlewareCallback, MiddlewareContext } from 'steveo';
import AWS from 'aws-sdk';

export class KMSMiddleware implements Middleware {
client: AWS.KMS;

keyId: string;

constructor(keyId: string) {
this.client = new AWS.KMS();
this.keyId = keyId;
}

public async publish<Ctx = any, C = MiddlewareCallback>(
context: MiddlewareContext<Ctx>,
_next: C
) {
context.payload = await this.encrypt(context.payload);
}

private async encrypt<T = any>(data: T): Promise<string> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to move to banning 'any' and instead using the more idiomatic 'unknown' in this case.... but in this case i'm not sure why it's needed as a generic type is unknown

const value = JSON.stringify(data);

const params = {
KeyId: this.keyId,
Plaintext: Buffer.from(value),
};

const result = await this.client.encrypt(params).promise();

if (!result.CiphertextBlob) {
throw new Error('Failed to encrypt message');
}

return result.CiphertextBlob.toString('base64');
}

private async decrypt<T = any>(data: string): Promise<T> {
const params = {
KeyId: this.keyId,
CiphertextBlob: Buffer.from(data),
};

const result = await this.client.decrypt(params).promise();

if (!result.Plaintext) {
throw new Error('Failed to decrypt message');
}

return JSON.parse(result.Plaintext.toString());
}

public async consume<Ctx = any, C = MiddlewareCallback>(
context: MiddlewareContext<Ctx>,
_next: C
) {
context.payload = await this.decrypt(context.payload);
}
}

export default KMSMiddleware;
6 changes: 6 additions & 0 deletions packages/kms/test/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": [
"@ordermentum/ordermentum/mocha",
"@ordermentum/ordermentum/jest"
]
}
49 changes: 49 additions & 0 deletions packages/kms/test/index_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { expect } from 'chai';
import { Steveo } from 'steveo';

import sinon from 'sinon';
import KMSMiddleware from '../src';

describe('KMSMiddleware', () => {
let sandbox;
let middleware;

beforeEach(() => {
sandbox = sinon.createSandbox();
});

afterEach(() => {
sinon.restore();
});

it('add to steveo', () => {
middleware = new KMSMiddleware('keyId');

const steveo = new Steveo({
engine: 'dummy',
middleware: [middleware],
});

expect(steveo.middleware.length).to.equal(1);
});

describe('publish', () => {
it('should encrypt message', async () => {
sandbox.stub(middleware, 'encrypt').resolves('encrypted');
const context = { message: 'message' };
const next = sinon.stub().resolves();
await middleware.publish(context, next);
expect(context.message).to.equal('encrypted');
});
});

describe('consume', () => {
it('should decrypt message', async () => {
sandbox.stub(middleware, 'decrypt').resolves({ message: 'message' });
const context = { message: 'data' };
const next = sinon.stub().resolves();
await middleware.consume(context, next);
expect(context.message).to.deep.equal({ message: 'message' });
});
});
});
31 changes: 31 additions & 0 deletions packages/kms/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"compilerOptions": {
"lib": [
"es2022"
],
"target": "es2022",
"module": "commonjs",
"allowJs": false,
"declaration": true,
"outDir": "lib",
"strict": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noImplicitAny": false,
"noImplicitThis": false,
"resolveJsonModule": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"esModuleInterop": true
},
"include": [
"src"
],
"exclude": [
"lib",
"node_modules"
]
}
2 changes: 1 addition & 1 deletion packages/steveo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export {
DummyConfiguration,
} from './common';

export { Middleware };
export { Middleware, MiddlewareContext, MiddlewareCallback } from './common';

export class Steveo implements ISteveo {
config: KafkaConfiguration | RedisConfiguration | SQSConfiguration;
Expand Down
16 changes: 16 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3102,6 +3102,22 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"

[email protected]:
version "2.1692.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1692.0.tgz#9dac5f7bfcc5ab45825cc8591b12753aa7d2902c"
integrity sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==
dependencies:
buffer "4.9.2"
events "1.1.1"
ieee754 "1.1.13"
jmespath "0.16.0"
querystring "0.2.0"
sax "1.2.1"
url "0.10.3"
util "^0.12.4"
uuid "8.0.0"
xml2js "0.6.2"

aws-sdk@^2.1043.0:
version "2.1691.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1691.0.tgz#9d6ccdcbae03c806fc62667b76eb3e33e5294dcc"
Expand Down
Loading