Compare commits

..

4 Commits

Author SHA1 Message Date
Richard Simpson 4ab6f6070f fix: release new action bundle 2020-03-11 14:02:39 -05:00
Richard Simpson bef2eb0b90 feat: add the ability to set extra headers (#27)
* feat: add the ability to set extra headers

* switch to more generic map solution for headers
2020-03-11 14:02:13 -05:00
dependabot-preview[bot] 0ece1da433 chore(deps-dev): bump @types/jest from 25.1.3 to 25.1.4 (#26)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 25.1.3 to 25.1.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-03-10 16:25:50 -05:00
dependabot-preview[bot] 7fb0d673d1 chore(deps): bump @actions/core from 1.2.2 to 1.2.3 (#25)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.2.2 to 1.2.3.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/master/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-04 10:20:55 -06:00
5 changed files with 166 additions and 18 deletions
+35 -1
View File
@@ -12,6 +12,7 @@ const VALID_KV_VERSION = [-1, 1, 2];
async function exportSecrets() { async function exportSecrets() {
const vaultUrl = core.getInput('url', { required: true }); const vaultUrl = core.getInput('url', { required: true });
const vaultNamespace = core.getInput('namespace', { required: false }); const vaultNamespace = core.getInput('namespace', { required: false });
const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
let enginePath = core.getInput('path', { required: false }); let enginePath = core.getInput('path', { required: false });
let kvVersion = core.getInput('kv-version', { required: false }); let kvVersion = core.getInput('kv-version', { required: false });
@@ -76,6 +77,10 @@ async function exportSecrets() {
}, },
}; };
for (const [headerName, headerValue] of extraHeaders) {
requestOptions.headers[headerName] = headerValue;
}
if (vaultNamespace != null) { if (vaultNamespace != null) {
requestOptions.headers["X-Vault-Namespace"] = vaultNamespace; requestOptions.headers["X-Vault-Namespace"] = vaultNamespace;
} }
@@ -216,6 +221,9 @@ function normalizeOutputKey(dataKey) {
} }
// @ts-ignore // @ts-ignore
/**
* @param {string} input
*/
function parseBoolInput(input) { function parseBoolInput(input) {
if (input === null || input === undefined || input.trim() === '') { if (input === null || input === undefined || input.trim() === '') {
return null; return null;
@@ -223,9 +231,35 @@ function parseBoolInput(input) {
return Boolean(input); return Boolean(input);
} }
/**
* @param {string} inputKey
* @param {any} inputOptions
*/
function parseHeadersInput(inputKey, inputOptions) {
/** @type {string}*/
const rawHeadersString = core.getInput(inputKey, inputOptions) || '';
const headerStrings = rawHeadersString
.split('\n')
.map(line => line.trim())
.filter(line => line !== '');
return headerStrings
.reduce((map, line) => {
const seperator = line.indexOf(':');
const key = line.substring(0, seperator).trim().toLowerCase();
const value = line.substring(seperator + 1).trim();
if (map.has(key)) {
map.set(key, [map.get(key), value].join(', '));
} else {
map.set(key, value);
}
return map;
}, new Map());
}
module.exports = { module.exports = {
exportSecrets, exportSecrets,
parseSecretsInput, parseSecretsInput,
parseResponse: getResponseData, parseResponse: getResponseData,
normalizeOutputKey normalizeOutputKey,
parseHeadersInput
}; };
+65 -1
View File
@@ -7,7 +7,8 @@ const got = require('got');
const { const {
exportSecrets, exportSecrets,
parseSecretsInput, parseSecretsInput,
parseResponse parseResponse,
parseHeadersInput
} = require('./action'); } = require('./action');
const { when } = require('jest-when'); const { when } = require('jest-when');
@@ -90,6 +91,46 @@ describe('parseSecretsInput', () => {
}) })
}); });
describe('parseHeaders', () => {
it('parses simple header', () => {
when(core.getInput)
.calledWith('extraHeaders')
.mockReturnValueOnce('TEST: 1');
const result = parseHeadersInput('extraHeaders');
expect(Array.from(result)).toContainEqual(['test', '1']);
});
it('parses simple header with whitespace', () => {
when(core.getInput)
.calledWith('extraHeaders')
.mockReturnValueOnce(`
TEST: 1
`);
const result = parseHeadersInput('extraHeaders');
expect(Array.from(result)).toContainEqual(['test', '1']);
});
it('parses multiple headers', () => {
when(core.getInput)
.calledWith('extraHeaders')
.mockReturnValueOnce(`
TEST: 1
FOO: bAr
`);
const result = parseHeadersInput('extraHeaders');
expect(Array.from(result)).toContainEqual(['test', '1']);
expect(Array.from(result)).toContainEqual(['foo', 'bAr']);
});
it('parses null response', () => {
when(core.getInput)
.calledWith('extraHeaders')
.mockReturnValueOnce(null);
const result = parseHeadersInput('extraHeaders');
expect(Array.from(result)).toHaveLength(0);
});
})
describe('parseResponse', () => { describe('parseResponse', () => {
// https://www.vaultproject.io/api/secret/kv/kv-v1.html#sample-response // https://www.vaultproject.io/api/secret/kv/kv-v1.html#sample-response
it('parses K/V version 1 response', () => { it('parses K/V version 1 response', () => {
@@ -148,6 +189,12 @@ describe('exportSecrets', () => {
.mockReturnValueOnce(version); .mockReturnValueOnce(version);
} }
function mockExtraHeaders(headerString) {
when(core.getInput)
.calledWith('extraHeaders')
.mockReturnValueOnce(headerString);
}
function mockVaultData(data, version='2') { function mockVaultData(data, version='2') {
switch(version) { switch(version) {
case '1': case '1':
@@ -196,6 +243,23 @@ describe('exportSecrets', () => {
it('simple secret retrieval from K/V v1', async () => { it('simple secret retrieval from K/V v1', async () => {
const version = '1'; const version = '1';
mockInput('test key');
mockExtraHeaders(`
TEST: 1
`);
mockVaultData({
key: 1
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY', '1');
expect(core.setOutput).toBeCalledWith('key', '1');
});
it('simple secret retrieval with extra headers', async () => {
const version = '1';
mockInput('test key'); mockInput('test key');
mockVersion(version); mockVersion(version);
mockVaultData({ mockVaultData({
+3
View File
@@ -13,6 +13,9 @@ inputs:
namespace: namespace:
description: 'The Vault namespace from which to query secrets. Vault Enterprise only, unset by default' description: 'The Vault namespace from which to query secrets. Vault Enterprise only, unset by default'
required: false required: false
extraHeaders:
description: 'A string of newline seperated extra headers to include on every request.'
required: false
runs: runs:
using: 'node12' using: 'node12'
main: 'dist/index.js' main: 'dist/index.js'
+51 -4
View File
@@ -648,6 +648,7 @@ const defaults = {
maxRedirects: 10, maxRedirects: 10,
prefixUrl: '', prefixUrl: '',
methodRewriting: true, methodRewriting: true,
allowGetBody: false,
ignoreInvalidCookies: false, ignoreInvalidCookies: false,
context: {}, context: {},
_pagination: { _pagination: {
@@ -1019,6 +1020,7 @@ exports.preNormalizeArguments = (options, defaults) => {
options.dnsCache = (_e = options.dnsCache, (_e !== null && _e !== void 0 ? _e : false)); options.dnsCache = (_e = options.dnsCache, (_e !== null && _e !== void 0 ? _e : false));
options.useElectronNet = Boolean(options.useElectronNet); options.useElectronNet = Boolean(options.useElectronNet);
options.methodRewriting = Boolean(options.methodRewriting); options.methodRewriting = Boolean(options.methodRewriting);
options.allowGetBody = Boolean(options.allowGetBody);
options.context = (_f = options.context, (_f !== null && _f !== void 0 ? _f : {})); options.context = (_f = options.context, (_f !== null && _f !== void 0 ? _f : {}));
return options; return options;
}; };
@@ -1131,7 +1133,8 @@ exports.normalizeArguments = (url, options, defaults) => {
} }
return normalizedOptions; return normalizedOptions;
}; };
const withoutBody = new Set(['GET', 'HEAD']); const withoutBody = new Set(['HEAD']);
const withoutBodyUnlessSpecified = 'GET';
exports.normalizeRequestArguments = async (options) => { exports.normalizeRequestArguments = async (options) => {
var _a, _b, _c; var _a, _b, _c;
options = exports.mergeOptions(options); options = exports.mergeOptions(options);
@@ -1146,6 +1149,9 @@ exports.normalizeRequestArguments = async (options) => {
if ((isBody || isForm || isJson) && withoutBody.has(options.method)) { if ((isBody || isForm || isJson) && withoutBody.has(options.method)) {
throw new TypeError(`The \`${options.method}\` method cannot be used with a body`); throw new TypeError(`The \`${options.method}\` method cannot be used with a body`);
} }
if (!options.allowGetBody && (isBody || isForm || isJson) && withoutBodyUnlessSpecified === options.method) {
throw new TypeError(`The \`${options.method}\` method cannot be used with a body`);
}
if ([isBody, isForm, isJson].filter(isTrue => isTrue).length > 1) { if ([isBody, isForm, isJson].filter(isTrue => isTrue).length > 1) {
throw new TypeError('The `body`, `json` and `form` options are mutually exclusive'); throw new TypeError('The `body`, `json` and `form` options are mutually exclusive');
} }
@@ -1192,7 +1198,7 @@ exports.normalizeRequestArguments = async (options) => {
// a payload body and the method semantics do not anticipate such a // a payload body and the method semantics do not anticipate such a
// body. // body.
if (is_1.default.undefined(headers['content-length']) && is_1.default.undefined(headers['transfer-encoding'])) { if (is_1.default.undefined(headers['content-length']) && is_1.default.undefined(headers['transfer-encoding'])) {
if ((options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH' || options.method === 'DELETE') && if ((options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH' || options.method === 'DELETE' || (options.allowGetBody && options.method === 'GET')) &&
!is_1.default.undefined(uploadBodySize)) { !is_1.default.undefined(uploadBodySize)) {
// @ts-ignore We assign if it is undefined, so this IS correct // @ts-ignore We assign if it is undefined, so this IS correct
headers['content-length'] = String(uploadBodySize); headers['content-length'] = String(uploadBodySize);
@@ -3146,7 +3152,7 @@ function asStream(options) {
throw new Error('Got\'s stream is not writable when the `body`, `json` or `form` option is used'); throw new Error('Got\'s stream is not writable when the `body`, `json` or `form` option is used');
}; };
} }
else if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') { else if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH' || (options.allowGetBody && options.method === 'GET')) {
options.body = input; options.body = input;
} }
else { else {
@@ -3551,6 +3557,13 @@ exports.setFailed = setFailed;
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Logging Commands // Logging Commands
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/**
* Gets whether Actions Step Debug is on or not
*/
function isDebug() {
return process.env['RUNNER_DEBUG'] === '1';
}
exports.isDebug = isDebug;
/** /**
* Writes debug message to user log * Writes debug message to user log
* @param message debug message * @param message debug message
@@ -4853,6 +4866,7 @@ const VALID_KV_VERSION = [-1, 1, 2];
async function exportSecrets() { async function exportSecrets() {
const vaultUrl = core.getInput('url', { required: true }); const vaultUrl = core.getInput('url', { required: true });
const vaultNamespace = core.getInput('namespace', { required: false }); const vaultNamespace = core.getInput('namespace', { required: false });
const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
let enginePath = core.getInput('path', { required: false }); let enginePath = core.getInput('path', { required: false });
let kvVersion = core.getInput('kv-version', { required: false }); let kvVersion = core.getInput('kv-version', { required: false });
@@ -4917,6 +4931,10 @@ async function exportSecrets() {
}, },
}; };
for (const [headerName, headerValue] of extraHeaders) {
requestOptions.headers[headerName] = headerValue;
}
if (vaultNamespace != null) { if (vaultNamespace != null) {
requestOptions.headers["X-Vault-Namespace"] = vaultNamespace; requestOptions.headers["X-Vault-Namespace"] = vaultNamespace;
} }
@@ -5057,6 +5075,9 @@ function normalizeOutputKey(dataKey) {
} }
// @ts-ignore // @ts-ignore
/**
* @param {string} input
*/
function parseBoolInput(input) { function parseBoolInput(input) {
if (input === null || input === undefined || input.trim() === '') { if (input === null || input === undefined || input.trim() === '') {
return null; return null;
@@ -5064,11 +5085,37 @@ function parseBoolInput(input) {
return Boolean(input); return Boolean(input);
} }
/**
* @param {string} inputKey
* @param {any} inputOptions
*/
function parseHeadersInput(inputKey, inputOptions) {
/** @type {string}*/
const rawHeadersString = core.getInput(inputKey, inputOptions) || '';
const headerStrings = rawHeadersString
.split('\n')
.map(line => line.trim())
.filter(line => line !== '');
return headerStrings
.reduce((map, line) => {
const seperator = line.indexOf(':');
const key = line.substring(0, seperator).trim().toLowerCase();
const value = line.substring(seperator + 1).trim();
if (map.has(key)) {
map.set(key, [map.get(key), value].join(', '));
} else {
map.set(key, value);
}
return map;
}, new Map());
}
module.exports = { module.exports = {
exportSecrets, exportSecrets,
parseSecretsInput, parseSecretsInput,
parseResponse: getResponseData, parseResponse: getResponseData,
normalizeOutputKey normalizeOutputKey,
parseHeadersInput
}; };
+12 -12
View File
@@ -5,9 +5,9 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@actions/core": { "@actions/core": {
"version": "1.2.2", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.2.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.3.tgz",
"integrity": "sha512-IbCx7oefq+Gi6FWbSs2Fnw8VkEI6Y4gvjrYprY3RV//ksq/KPMlClOerJ4jRosyal6zkUIc8R9fS/cpRMlGClg==" "integrity": "sha512-Wp4xnyokakM45Uuj4WLUxdsa8fJjKVl1fDTsPbTEcTcuu0Nb26IPQbOtjmnfaCPGcaoPOOqId8H9NapZ8gii4w=="
}, },
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.5.5", "version": "7.5.5",
@@ -1891,9 +1891,9 @@
} }
}, },
"@types/jest": { "@types/jest": {
"version": "25.1.3", "version": "25.1.4",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.3.tgz", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.4.tgz",
"integrity": "sha512-jqargqzyJWgWAJCXX96LBGR/Ei7wQcZBvRv0PLEu9ZByMfcs23keUJrKv9FMR6YZf9YCbfqDqgmY+JUBsnqhrg==", "integrity": "sha512-QDDY2uNAhCV7TMCITrxz+MRk1EizcsevzfeS6LykIlq2V1E5oO4wXG8V2ZEd9w7Snxeeagk46YbMgZ8ESHx3sw==",
"dev": true, "dev": true,
"requires": { "requires": {
"jest-diff": "^25.1.0", "jest-diff": "^25.1.0",
@@ -1913,9 +1913,9 @@
} }
}, },
"@types/yargs": { "@types/yargs": {
"version": "15.0.3", "version": "15.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz",
"integrity": "sha512-XCMQRK6kfpNBixHLyHUsGmXrpEmFFxzMrcnSXFMziHd8CoNJo8l16FkHyQq4x+xbM7E2XL83/O78OD8u+iZTdQ==", "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/yargs-parser": "*" "@types/yargs-parser": "*"
@@ -2005,9 +2005,9 @@
} }
}, },
"react-is": { "react-is": {
"version": "16.12.0", "version": "16.13.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz",
"integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==",
"dev": true "dev": true
}, },
"supports-color": { "supports-color": {