Skip to content
This repository has been archived by the owner on Oct 18, 2023. It is now read-only.

Commit

Permalink
mocking up how using it in tests might work (#2)
Browse files Browse the repository at this point in the history
mocking up how using it in tests might work
  • Loading branch information
wheresrhys authored Jul 10, 2019
2 parents 178be75 + 07ca0eb commit 815a729
Show file tree
Hide file tree
Showing 16 changed files with 4,969 additions and 938 deletions.
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
command: echo "//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}" > ${HOME}/.npmrc
- run:
name: Publish to npm
command: make publish
command: make npm-publish

workflows:
version: 2
Expand All @@ -70,6 +70,7 @@ workflows:
filters:
<<: *only_version_tags
- test:
context: rel-eng-creds
requires:
- install
filters:
Expand Down
19 changes: 18 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
# Editor config configuration: https://editorconfig.org/
# Generated by rel-engage

root = true

[**{.js,.ts,.html,.hbs,.mustache,.xml,.xsl,.scss,.css,.sh,.vcl,.mk,Makefile,.json,.yml,.yaml,.md}]
[**{.js,.jsx,.ts,.tsx,.html,.hbs,.mustache,.xml,.xsl,.scss,.css,.sh,.vcl,.mk,Makefile}]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab

[**{.json,.yml,.yaml}]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_size = 2
indent_style = space

[*.md]
indent_size = unset
indent_style = unset
trim_trailing_whitespace = false
14 changes: 14 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Ignore file for eslint: https://eslint.org/docs/user-guide/configuring#eslintignore
# Generated by rel-engage

*.json

.serverless
.cache-loader
.webpack

build
dist
coverage

secret-squirrel.js
43 changes: 11 additions & 32 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,15 @@
/*
ESLint config file: https://eslint.org/docs/user-guide/configuring
Configures Javascript linting
Generated by rel-engage
*/

'use strict';

module.exports = {
extends: [
'origami-component',
'plugin:prettier/recommended',
// Cannot use `@financial-times/rel-engage/packages/dotfiles/eslint`
// as this is translated to `eslint-config-@financial-times/rel-engage/packages/dotfiles/eslint
'./node_modules/@financial-times/rel-engage/packages/dotfiles/eslint.js',
],
env: {
node: true,
jest: true,
},
parserOptions: {
ecmaVersion: 2017,
ecmaFeatures: {
jsx: true
},
},
globals: {
expect: true,
sinon: true,
tinymce: true
},
overrides: [
{
files: ['**/*.jsx'],
settings: {
react: {
pragma: 'h'
}
},
}
],
rules: {
'react/prop-types': 0,
'react/jsx-key': 0,
'new-cap': 0
}
};
10 changes: 10 additions & 0 deletions .huskyrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable import/no-unresolved, import/no-extraneous-dependencies */
/*
Husky config file: https://github.com/typicode/husky
Configures git hooks
Generated by rel-engage
*/

'use strict';

module.exports = require('@financial-times/rel-engage/packages/dotfiles/husky');
10 changes: 10 additions & 0 deletions .lintstagedrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable import/no-unresolved, import/no-extraneous-dependencies */
/*
Lint-staged config file: https://github.com/okonet/lint-staged
Configures per-file-extension tasks ran on pre-commit
Generated by rel-engage
*/

'use strict';

module.exports = require('@financial-times/rel-engage/packages/dotfiles/lint-staged');
1 change: 0 additions & 1 deletion .nvmrc

This file was deleted.

20 changes: 20 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Ignore file for prettier: https://prettier.io/docs/en/ignore.html
# Generated by rel-engage

.webpack
.cache-loader
.DS_Store
.serverless
*.mk
build
coverage
dist
Makefile
node_modules
bower_components
package-lock.json
package.json
bower.json
tslint.json
.vscode
.idea
6 changes: 0 additions & 6 deletions .prettierrc

This file was deleted.

10 changes: 10 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable import/no-unresolved, import/no-extraneous-dependencies */
/*
Prettier config file: https://prettier.io/docs/en/configuration.html
Configures file formatting behaviour
Generated by rel-engage
*/

'use strict';

module.exports = require('@financial-times/rel-engage/packages/dotfiles/prettier');
4 changes: 4 additions & 0 deletions .snyk
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.13.5
ignore: {}
patch: {}
31 changes: 19 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
export PATH := ./node_modules/.bin:$(PATH)
# ---------------------------
# Generated by rel-engage

lint:
ifneq ($(CIRCLECI),)
eslint .
else
eslint --cache --fix .
prettier --write *.md
endif
# This task tells make how to 'build' n-gage. It npm installs n-gage, and
# Once that's done it overwrites the file with its own contents - this
# ensures the timestamp on the file is recent, so make won't think the file
# is out of date and try to rebuild it every time
node_modules/@financial-times/rel-engage/index.mk:
@echo "Updating rel-engage"
@npm install --save-dev @financial-times/rel-engage
@touch $@

test: lint
# If, by the end of parsing your `Makefile`, `make` finds that any files
# referenced with `-include` don't exist or are out of date, it will run any
# tasks it finds that match the missing file. So if n-gage *is* installed
# it will just be included; if not, it will look for a task to run
-include node_modules/@financial-times/rel-engage/index.mk

publish:
npm version --no-git-tag-version ${CIRCLE_TAG}
npm publish --access public
# End generated by rel-engage
# ---------------------------

test: verify
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,50 @@

Use AWS STS to assume a role when using serverless-offline

## Why does this package exist

It's common practice in a lot of teams at the FT to keep credential mangement local to a project, i.e. no global environment variables needed in your development environment to run the project (with perhaps the exception of some global variables used to fetch others, such as vault login tokens).

We also try to follow best practice of least privilege when managing IAM resources. Our deploy _users_ (for which we mint access keys) have access to create and modify resources' configuration, whereas our application _roles_ have access to interact with (read, write etc) those resources once created.

However, AWS/serverless have a credentials model where allowing a user to assume a role in local dev involves storing keys and role assumption config in a global `~/.aws` directory which a) menas we have credentials living outside of the project root and b) we have another source of truth for the relation between roles and users, which must be kept in sync.

This package gets around that by providing a serverless plugin that assumes the application role before starting serverless/integration tests, thus removing the need to maintain any credentials/config outside the project.

## Usage

Add `serverless-offline-sts` to your plugins list in `serverless.yaml` (after `serverless-offline`)

It requires that your `serverless.yaml`'s `provider` section contains a `region` and a `role`, and that you have ([via a method of your choice](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/configuring-the-jssdk.html)) AWS credentials that allow assuming that role.

### Usage in tests

If you have integration tests that do not use serverless directly, you may use a static method exported by the library to explictly pass in the same settings as serverless would. This offers very little over and above using sts directly, but reduces boilerplate a little.

#### With Jest

```js
const { assumeRole } = require('serverless-offline-sts');

const rolePromise = assumeRole();

describe('My integration tests', () => {
beforeAll(() => rolePromise);
});
```

#### With Mocha

Use the following options `--delay --require ./sts.js`

sts.js

```js
const { assumeRole } = require('serverless-offline-sts');

assumeRole().then(() => {
// need to wrap run() as it is not defined on require() - mocha adds it globally
// at some later point
run();
});
```
65 changes: 39 additions & 26 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
const execa = require('execa');
const yaml = require('js-yaml');

const readServerless = async () => {
const serverlessYaml = await execa('serverless', ['print']);
return yaml.load(serverlessYaml.stdout);
};

const assumeRole = async config => {
const AWS = require('aws-sdk'); // eslint-disable-line global-require
AWS.config.region = config.provider.region;
const sts = new AWS.STS({ apiVersion: '2011-06-15' });

return sts
.assumeRole({
RoleArn: config.provider.role,
RoleSessionName: `${config.provider.service}-${process.env.USER ||
'unknown'}-${Date.now()}`,
})
.promise()
.then(data => {
AWS.config.update({
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken,
});
})
.catch(err => {
console.error(`Failed to assume ${config.provider.role}`, err);
throw err;
});
};

class OfflineSTS {
constructor(serverless) {
this.commands = {
Expand All @@ -12,32 +45,12 @@ class OfflineSTS {
}

async assumeRole(serverless) {
const AWS = require('aws-sdk');

AWS.config.region = serverless.service.provider.region;
const sts = new AWS.STS({ apiVersion: '2011-06-15' });

return sts
.assumeRole({
RoleArn: serverless.service.provider.role,
RoleSessionName: `${serverless.service.service}-${process.env.USER ||
'unknown'}-${Date.now()}`,
})
.promise()
.then(data => {
AWS.config.update({
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken,
});
})
.catch(err => {
console.error(
`Failed to assume ${serverless.service.provider.role}`,
err,
);
throw err;
});
return assumeRole(serverless.service);
}

static async assumeRole() {
const serverlessYaml = await readServerless();
return assumeRole(serverlessYaml);
}
}

Expand Down
Loading

0 comments on commit 815a729

Please sign in to comment.