From 8f0770d78083d7ba371edc9405c866f3f9e595fa Mon Sep 17 00:00:00 2001 From: Matthew Irish Date: Tue, 4 Sep 2018 16:15:42 -0500 Subject: [PATCH 1/2] have fromJSON throw if trying to convert non-object to a KVObject --- ui/app/lib/kv-object.js | 9 +++++++-- ui/tests/unit/lib/kv-object-test.js | 11 +++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ui/app/lib/kv-object.js b/ui/app/lib/kv-object.js index 4ca3ed136c44..4c6ec8a3b959 100644 --- a/ui/app/lib/kv-object.js +++ b/ui/app/lib/kv-object.js @@ -1,13 +1,18 @@ import Ember from 'ember'; +const { typeOf, guidFor } = Ember; + export default Ember.ArrayProxy.extend({ 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 = { name: key, value: json[key], }; - Ember.guidFor(obj); + guidFor(obj); return obj; }); this.setObjects( diff --git a/ui/tests/unit/lib/kv-object-test.js b/ui/tests/unit/lib/kv-object-test.js index 1962829fd63c..6192bcd5992e 100644 --- a/ui/tests/unit/lib/kv-object-test.js +++ b/ui/tests/unit/lib/kv-object-test.js @@ -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]) { test(`fromJSONString: ${name}`, function(assert) { let inputString = JSON.stringify(input, null, 2); From b5403e230f41a1288684aa9b88ce5b1888a06d93 Mon Sep 17 00:00:00 2001 From: Matthew Irish Date: Tue, 4 Sep 2018 16:16:36 -0500 Subject: [PATCH 2/2] catch the fromJSON error in secret-edit, display an error, and disabled the submit button --- ui/app/components/secret-edit.js | 11 ++++- .../templates/partials/secret-form-create.hbs | 2 +- .../templates/partials/secret-form-edit.hbs | 2 +- .../components/secret-edit-test.js | 40 ++++++++++++++++++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/ui/app/components/secret-edit.js b/ui/app/components/secret-edit.js index ff115506a379..8d673dc7bef3 100644 --- a/ui/app/components/secret-edit.js +++ b/ui/app/components/secret-edit.js @@ -30,6 +30,7 @@ export default Ember.Component.extend(FocusOnInsertMixin, { // use a named action here so we don't have to pass one in // this will bubble to the route toggleAdvancedEdit: 'toggleAdvancedEdit', + error: null, codemirrorString: null, @@ -79,7 +80,8 @@ export default Ember.Component.extend(FocusOnInsertMixin, { 'key.isFolder', 'key.isError', 'key.flagsIsInvalid', - 'hasLintError' + 'hasLintError', + 'error' ), basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function() { @@ -242,10 +244,15 @@ export default Ember.Component.extend(FocusOnInsertMixin, { }, codemirrorUpdated(val, codemirror) { + this.set('error', null); codemirror.performLint(); const noErrors = codemirror.state.lint.marked.length === 0; 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('codemirrorString', val); diff --git a/ui/app/templates/partials/secret-form-create.hbs b/ui/app/templates/partials/secret-form-create.hbs index 535e956cfc26..852e1d68321d 100644 --- a/ui/app/templates/partials/secret-form-create.hbs +++ b/ui/app/templates/partials/secret-form-create.hbs @@ -1,7 +1,7 @@
- {{message-error model=key}} + {{message-error model=key errorMessage=error}}
{{#if (not-eq key.initialParentKey '') }} diff --git a/ui/app/templates/partials/secret-form-edit.hbs b/ui/app/templates/partials/secret-form-edit.hbs index efd9ce378da8..5b1495fb423d 100644 --- a/ui/app/templates/partials/secret-form-edit.hbs +++ b/ui/app/templates/partials/secret-form-edit.hbs @@ -1,6 +1,6 @@
- {{message-error model=key}} + {{message-error model=key errorMessage=error}} {{#unless showAdvancedMode}}
diff --git a/ui/tests/integration/components/secret-edit-test.js b/ui/tests/integration/components/secret-edit-test.js index 13d069d7a9b3..4459162038ee 100644 --- a/ui/tests/integration/components/secret-edit-test.js +++ b/ui/tests/integration/components/secret-edit-test.js @@ -3,6 +3,9 @@ import hbs from 'htmlbars-inline-precompile'; moduleForComponent('secret-edit', 'Integration | Component | secret edit', { 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) { @@ -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(); }); -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('key', { secretData: { @@ -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 }}`); 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'); +});