Unverified Commit 11377636 authored by Matthew Irish's avatar Matthew Irish Committed by GitHub
Browse files

UI - don't coerce JSON input to an Object (#5271)

* have fromJSON throw if trying to convert non-object to a KVObject

* catch the fromJSON error in secret-edit, display an error, and disabled the submit button
parent 51eea745
Showing with 68 additions and 7 deletions
+68 -7
...@@ -30,6 +30,7 @@ export default Ember.Component.extend(FocusOnInsertMixin, { ...@@ -30,6 +30,7 @@ export default Ember.Component.extend(FocusOnInsertMixin, {
// use a named action here so we don't have to pass one in // use a named action here so we don't have to pass one in
// this will bubble to the route // this will bubble to the route
toggleAdvancedEdit: 'toggleAdvancedEdit', toggleAdvancedEdit: 'toggleAdvancedEdit',
error: null,
codemirrorString: null, codemirrorString: null,
...@@ -79,7 +80,8 @@ export default Ember.Component.extend(FocusOnInsertMixin, { ...@@ -79,7 +80,8 @@ export default Ember.Component.extend(FocusOnInsertMixin, {
'key.isFolder', 'key.isFolder',
'key.isError', 'key.isError',
'key.flagsIsInvalid', 'key.flagsIsInvalid',
'hasLintError' 'hasLintError',
'error'
), ),
basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function() { basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function() {
...@@ -242,10 +244,15 @@ export default Ember.Component.extend(FocusOnInsertMixin, { ...@@ -242,10 +244,15 @@ export default Ember.Component.extend(FocusOnInsertMixin, {
}, },
codemirrorUpdated(val, codemirror) { codemirrorUpdated(val, codemirror) {
this.set('error', null);
codemirror.performLint(); codemirror.performLint();
const noErrors = codemirror.state.lint.marked.length === 0; const noErrors = codemirror.state.lint.marked.length === 0;
if (noErrors) { if (noErrors) {
this.get('secretData').fromJSONString(val); try {
this.get('secretData').fromJSONString(val);
} catch (e) {
this.set('error', e.message);
}
} }
this.set('hasLintError', !noErrors); this.set('hasLintError', !noErrors);
this.set('codemirrorString', val); this.set('codemirrorString', val);
......
import Ember from 'ember'; import Ember from 'ember';
const { typeOf, guidFor } = Ember;
export default Ember.ArrayProxy.extend({ export default Ember.ArrayProxy.extend({
fromJSON(json) { fromJSON(json) {
const contents = Object.keys(json || []).map(key => { if (json && typeOf(json) !== 'object') {
throw new Error('Vault expects data to be formatted as an JSON object.');
}
let contents = Object.keys(json || []).map(key => {
let obj = { let obj = {
name: key, name: key,
value: json[key], value: json[key],
}; };
Ember.guidFor(obj); guidFor(obj);
return obj; return obj;
}); });
this.setObjects( this.setObjects(
......
<form class="{{if showAdvancedMode 'advanced-edit' 'simple-edit'}}" onsubmit={{action "createOrUpdateKey" "create"}} onchange={{action "handleChange"}}> <form class="{{if showAdvancedMode 'advanced-edit' 'simple-edit'}}" onsubmit={{action "createOrUpdateKey" "create"}} onchange={{action "handleChange"}}>
<div class="field box is-fullwidth is-sideless is-marginless"> <div class="field box is-fullwidth is-sideless is-marginless">
<NamespaceReminder @mode="create" @noun="secret" /> <NamespaceReminder @mode="create" @noun="secret" />
{{message-error model=key}} {{message-error model=key errorMessage=error}}
<label class="label is-font-weight-normal" for="kv-key">Path for this secret</label> <label class="label is-font-weight-normal" for="kv-key">Path for this secret</label>
<div class="field has-addons"> <div class="field has-addons">
{{#if (not-eq key.initialParentKey '') }} {{#if (not-eq key.initialParentKey '') }}
......
<form onsubmit={{action "createOrUpdateKey" "update"}} onchange={{action "handleChange"}}> <form onsubmit={{action "createOrUpdateKey" "update"}} onchange={{action "handleChange"}}>
<div class="box is-sideless is-fullwidth is-marginless"> <div class="box is-sideless is-fullwidth is-marginless">
{{message-error model=key}} {{message-error model=key errorMessage=error}}
<NamespaceReminder @mode="edit" @noun="secret" /> <NamespaceReminder @mode="edit" @noun="secret" />
{{#unless showAdvancedMode}} {{#unless showAdvancedMode}}
<div class="table info-table-row-header"> <div class="table info-table-row-header">
......
...@@ -3,6 +3,9 @@ import hbs from 'htmlbars-inline-precompile'; ...@@ -3,6 +3,9 @@ import hbs from 'htmlbars-inline-precompile';
moduleForComponent('secret-edit', 'Integration | Component | secret edit', { moduleForComponent('secret-edit', 'Integration | Component | secret edit', {
integration: true, integration: true,
beforeEach() {
this.inject.service('code-mirror', { as: 'codeMirror' });
},
}); });
test('it disables JSON toggle in show mode when is an advanced format', function(assert) { test('it disables JSON toggle in show mode when is an advanced format', function(assert) {
...@@ -19,7 +22,7 @@ test('it disables JSON toggle in show mode when is an advanced format', function ...@@ -19,7 +22,7 @@ test('it disables JSON toggle in show mode when is an advanced format', function
assert.dom('[data-test-secret-json-toggle]').isDisabled(); assert.dom('[data-test-secret-json-toggle]').isDisabled();
}); });
test('it does JSON toggle in show mode when is', function(assert) { test('it does JSON toggle in show mode when showing string data', function(assert) {
this.set('mode', 'show'); this.set('mode', 'show');
this.set('key', { this.set('key', {
secretData: { secretData: {
...@@ -32,3 +35,38 @@ test('it does JSON toggle in show mode when is', function(assert) { ...@@ -32,3 +35,38 @@ test('it does JSON toggle in show mode when is', function(assert) {
this.render(hbs`{{secret-edit mode=mode key=key }}`); this.render(hbs`{{secret-edit mode=mode key=key }}`);
assert.dom('[data-test-secret-json-toggle]').isNotDisabled(); assert.dom('[data-test-secret-json-toggle]').isNotDisabled();
}); });
test('it shows an error when creating and data is not an object', function(assert) {
this.set('mode', 'create');
this.set('key', {
secretData: {
int: '2',
null: 'null',
float: '1.234',
},
});
this.render(hbs`{{secret-edit mode=mode key=key preferAdvancedEdit=true }}`);
let instance = this.codeMirror.instanceFor(this.$('[data-test-component=json-editor]').attr('id'));
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
});
test('it shows an error when editing and the data is not an object', function(assert) {
this.set('mode', 'edit');
this.set('capabilities', {
canUpdate: true,
});
this.set('key', {
secretData: {
int: '2',
null: 'null',
float: '1.234',
},
});
this.render(hbs`{{secret-edit capabilities=capabilities mode=mode key=key preferAdvancedEdit=true }}`);
let instance = this.codeMirror.instanceFor(this.$('[data-test-component=json-editor]').attr('id'));
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
});
...@@ -37,6 +37,17 @@ fromJSONTests.forEach(function([name, input, content]) { ...@@ -37,6 +37,17 @@ fromJSONTests.forEach(function([name, input, content]) {
}); });
}); });
test(`fromJSON: non-object input`, function(assert) {
let input = [{ foo: 'bar' }];
assert.throws(
() => {
KVObject.create({ content: [] }).fromJSON(input);
},
/Vault expects data to be formatted as an JSON object/,
'throws when non-object input is used to construct the KVObject'
);
});
fromJSONTests.forEach(function([name, input, content]) { fromJSONTests.forEach(function([name, input, content]) {
test(`fromJSONString: ${name}`, function(assert) { test(`fromJSONString: ${name}`, function(assert) {
let inputString = JSON.stringify(input, null, 2); let inputString = JSON.stringify(input, null, 2);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment