dotenv-keyvault
Version:
Works with dotenv to resolve dotenv config and retrieve the value of secrets from Azure Key Vault.
138 lines (128 loc) • 5.46 kB
JavaScript
const rewire = require('rewire');
const dotenvKeyvault = rewire('../index');
const sinon = require('sinon');
const { expect } = require('chai');
var ServerMock = require('mock-http-server');
function createFakeDotenv(parsed) {
return {
parsed,
};
}
describe('dotenv-keyvault', () => {
describe('unit', () => {
let requestSpy = sinon.stub().returns(Promise.resolve({}));
let revert;
beforeEach(() => {
revert = dotenvKeyvault.__set__({
request: requestSpy,
});
});
afterEach(() => {
revert();
});
it('takes an Azure Active Directory token as a function', (done) => {
const fakeADToken = 'SOME_TOKEN';
const configSpy = sinon.stub().returns(fakeADToken);
const keyVaultGetter = dotenvKeyvault.config({ aadAccessToken: configSpy });
keyVaultGetter({})
.then(() => {
expect(configSpy.called).to.equal(true);
done();
})
.catch(done);
});
it('calls keyvault with the AD access token', (done) => {
const keyVaultGetter = dotenvKeyvault.config({ aadAccessToken: 'SOME_TOKEN' });
keyVaultGetter(createFakeDotenv({ MYPLAIN: 'PLAINTEXT', MYSECRET: 'kv:myfakekeyvault.com' }))
.then(() => {
expect(requestSpy.called).to.equal(true);
expect(requestSpy.args[0][0]).to.have.nested.property('headers.Authorization').that.contains('SOME_TOKEN');
done();
})
.catch(done);
});
it('does nothing with non-secret variables', (done) => {
const myEnv = createFakeDotenv({ MYTRUTH: 'THETRUTH' });
const keyVaultGetter = dotenvKeyvault.config({ aadAccessToken: () => 'SOME_TOKEN' });
keyVaultGetter(myEnv)
.then((nonSecretEnv) => {
expect(nonSecretEnv).to.have.property('MYTRUTH', 'THETRUTH');
done();
})
.catch(done);
});
it('returns a promise representing open fetches', () => {
const keyVaultGetter = dotenvKeyvault.config({ aadAccessToken: 'SOME_TOKEN' });
expect(keyVaultGetter()).to.respondTo('then');
});
});
describe('integration', () => {
const msiServer = new ServerMock({ host: 'localhost', port: 9000 });
const kvServer = new ServerMock({ host: 'localhost', port: 9001 });
const { MSI_ENDPOINT, MSI_SECRET } = process.env;
beforeEach((done) => {
process.env.MSI_ENDPOINT = 'http://localhost:9000';
process.env.MSI_SECRET = 'MY_SECRET_KEY';
msiServer.start(() => {
kvServer.start(done);
});
});
afterEach((done) => {
process.env.MSI_ENDPOINT = MSI_ENDPOINT;
process.env.MSI_SECRET = MSI_SECRET;
kvServer.stop(() => {
msiServer.stop(done);
});
});
it('succesfully works all up', (done) => {
msiServer.on({
method: 'GET',
path: '*',
reply: {
status: 200,
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
access_token: 'eyJ0eXAiblahblah',
expires_on: '09/14/2018 00:00:00 PM +00:00',
resource: 'https://vault.azure.net',
token_type: 'Bearer',
}),
},
});
kvServer.on({
method: 'GET',
path: '*',
reply: {
status: 200,
body: JSON.stringify({
attributes: 'attr',
contentType: 'thing',
id: 'id',
kid: 'string',
managed: 'true',
value: 'MYSECRETVALUE',
}),
},
});
const dotEnvConfig = createFakeDotenv({
MYTRUTH: 'TRUTH',
MYSECRET: 'kv:http://localhost:9001/secrets/MYSECRET',
});
const keyVaultEnvGetter = dotenvKeyvault.config();
keyVaultEnvGetter(dotEnvConfig)
.then((keyVaultConfigWithSecrets) => {
// calls with the MSI_SECRET
expect(msiServer.requests()).to.have.nested.property('[0].headers.secret', process.env.MSI_SECRET);
// api-version must be specified
expect(msiServer.requests()).to.have.nested.property('[0].url').that.contains('api-version=');
// calls with the access token received
expect(kvServer.requests()).to.have.nested.property('[0].headers.authorization', 'Bearer eyJ0eXAiblahblah');
// api-version must be specified
expect(kvServer.requests()).to.have.nested.property('[0].url').that.contains('api-version=');
expect(keyVaultConfigWithSecrets).to.have.property('MYSECRET', 'MYSECRETVALUE');
done();
})
.catch(done);
});
});
});