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

initial DynamoDB implementation #36

Merged
merged 3 commits into from
Jan 24, 2018

Conversation

alamarre
Copy link
Contributor

No description provided.


// .....
// to expose public keys:
publicKeyRouter.get('/auth/.well-known/jwks', /* @this publicKeyRouter */ function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

AuthProvisionerDynamo = require('brightspace-auth-keys-dynamodb-store'),
publicKeyRouter = require('koa-router')();

const provisioner = new AuthProvisionerDynamo({
Copy link
Contributor

Choose a reason for hiding this comment

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

Right, so I don't love this wrappery pattern.

AbstractProvisioningCache = require('brightspace-auth-provisioning').AbstractProvisioningCache,
clock = require('./clock');

class DynamoProvisioningCache extends AbstractProvisioningCache {
Copy link
Contributor

Choose a reason for hiding this comment

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

Mm, sorry if I misinterpreted.

Would rather keep this separate, maybe giving the node-auth-provisioning repo the same treatment

Copy link
Contributor

Choose a reason for hiding this comment

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

(or maaaybe bundling all together more, but, babier steps)

"main": "src/public-key-store.js",
"scripts": {
"test": "nyc --all mocha --timeout 15000 --recursive ./test && npm run check-style",
"check-style": "eslint ."
Copy link
Contributor

Choose a reason for hiding this comment

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

eslint is taken care of at the top level

Copy link
Contributor Author

Choose a reason for hiding this comment

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

working on cleaning this up

},
"author": "D2L Corporation",
"license": "Apache-2.0",
"devDependencies": {
Copy link
Contributor

Choose a reason for hiding this comment

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

Most of these should already installed at the top level.

Trying to keep most things there, since they'll be mostly re-used, and greenkeeper will manage them that way.

I did the the peerDep for "redis" at this level, but i dunno if that was the right move. The scripts don't handle that yet though, so definitely keep that here.

same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2017 D2L Corporation
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it's techincally 2018 now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updating all the licenses

"type": "git",
"url": "https://github.com/Brightspace/node-auth-keys.git"
},
"files": [
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 files is strictly better than npmignore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

just copied it from your PR

function setTtl(db, tablename) {
var params = {
TableName: tablename,
TimeToLiveSpecification: {
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't find the dynamo documentation that actually describes what the value of the TTL column is/interpreted as...?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It shows up in the popup in the console "TTL is a mechanism to set a specific timestamp for expiring items from your table. The timestamp should be expressed as an attribute on the items in the table. The attribute should be a Number data type containing time in epoch format. Once the timestamp expires, the corresponding item is deleted from the table in the background."

return db.createTable(params).promise().then(() => {
return setTtl(db, tablename);
}).catch((e) => {
// ResourceInUseException indicates the table already exists
Copy link
Contributor

Choose a reason for hiding this comment

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

Assuming that the ttl is also set if the table already exists?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or that if they manually created that they decided they didn't want it for whatever reason

}
};
return db.updateTimeToLive(params).promise().catch(e => {
// UnknownOperationException will be thrown for Dynamo Local
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a way to "detect" dynamo local, so we can only ignore this for dynamo local?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The UnknownOperationException exception won't happen unless it doesn't support TTL so even if it was production that threw it I would want to ignore it.

@@ -0,0 +1,138 @@
'use strict';

const
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I avoid mixing up "local" requires with package requires

Copy link
Contributor Author

Choose a reason for hiding this comment

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

change incoming

before(() => {
return DynamoDbLocal.launch(dynamoLocalPort, null, ['-sharedDb']).then(() => {
db = new AWS.DynamoDB({
endpoint: new AWS.Endpoint('http://localhost:8000'),
Copy link
Contributor

Choose a reason for hiding this comment

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

http://localhost:${dynamoLocalPort}?

})
.then(retrievedKeys => {
// Rather than checking for equality, assert that expectedPublicKey is in the returned array.
// The contents of the array are not guaranteed, and may contain actual keys. The same applies for the test below.
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably better to just have the comment again rather than positionally referring to another test :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Just realized I did it first ;)

assert.deepInclude(retrievedKeys, expectedPublicKey);
})
.then(() => {
return provisioner._storePublicKey(expectedPublicKey, oldExpiryTime);
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't get why this is happening?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To "clean up" after itself. Storing an expired key is functionally the same as deleting the key, but there's no API for deleting. Otherwise there's potential for tests to affect each other.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess re: my other comment then, is there a method to wipe the table, and just do that in an afterEach?


before(() => {
return DynamoDbLocal.launch(dynamoLocalPort, null, ['-sharedDb']).then(() => {
db = new AWS.DynamoDB({
Copy link
Contributor

Choose a reason for hiding this comment

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

Any way to clear the table between tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not that I could find. Tests should generally clean up after themselves.

Copy link
Contributor

Choose a reason for hiding this comment

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

just .stop() and then start a new one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possible, but heavy-handed and may slow down test execution noticeably. Similarly it's possible to call deleteTable followed by createTable, but other than writing fewer lines of code there doesn't seem to be a good reason to take a heavy handed approach between tests instead of expecting the tests to remove the data they add.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I decided to just do it since it's what we're doing elsewhere

omsmith added a commit that referenced this pull request Jan 17, 2018
.eslintrc.json Outdated
"extends": "brightspace/node-config"
"extends": "brightspace/node-config",
"env": {
"mocha":true
Copy link
Contributor

Choose a reason for hiding this comment

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

@@ -0,0 +1,47 @@
# brightspace-auth-keys-dynamodb-store

[![Build Status](https://travis-ci.org/Brightspace/node-auth-keys.svg?branch=master)](https://travis-ci.org/Brightspace/node-auth-keys)
Copy link
Contributor

Choose a reason for hiding this comment

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

'tis just node-auth now

"README.md",
"LICENSE"
],
"devDependencies": {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you define these top-level? The peer dep is fine to stay here

Copy link
Contributor

Choose a reason for hiding this comment

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

(The tests failed because I removed the script that runs install within the packages)

@@ -14,7 +14,7 @@
"LICENSE"
],
"scripts": {
"test": "istanbul cover --root src mocha -- --recursive ./test"
"test": "istanbul cover --root src ../../../node_modules/mocha/bin/mocha -- --recursive ./test"
Copy link
Contributor

Choose a reason for hiding this comment

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

Did it really need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To work in Windows it seems to be necessary. gotwarlost/istanbul#90

Copy link
Contributor

Choose a reason for hiding this comment

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

Alright cool. nyc may not have the same problem, so will look at fixing it afterward

@omsmith
Copy link
Contributor

omsmith commented Jan 17, 2018

Add to the package list in README.md?

@omsmith
Copy link
Contributor

omsmith commented Jan 19, 2018

Would be nice to have it rebased / squashed down before merge

_lookupPublicKeys() {
const parameters = {
TableName: this._tableName,
FilterExpression: '#e > :expires_at',
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a predicate on inclusion, not eviction, correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct. Keys are evicted by the table TTL

Copy link
Contributor

Choose a reason for hiding this comment

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

I just meant more like, doublechecking it should be a > and not a <

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, but the name is bad, I'm going to change that

return this._scanUntilComplete(null, parameters, []);
}

_scanUntilComplete(response, parameters, items) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Thoughts on:?

	_scanUntilComplete(response, parameters, items) {
		if (response) {
			if (response.Items) {
				items = items.concat(response.Items.map(row => row.PublicKey.S));
			}

			if (response.LastEvaluatedKey) {
				parameters.ExclusiveStartKey = response.LastEvaluatedKey;
			} else {
				return Promise.resolve(items);
			}
		}

		return this
			._client
			.scan(parameters)
			.promise()
			.then(response => this._scanUntilComplete(response, parameters, items));
	}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that's cleaner, I like it

@omsmith omsmith merged commit 5d104b6 into Brightspace:master Jan 24, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants