mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-04 20:06:27 +02:00
Ui request forwarding error (#4275)
* add ember-cli-content-security-policy * only enable client side CSP when not in production - the go side handles this otherwise * add service that handles and stores CSP violations via the securitypolicyviolation event * update auth form component to show a specialized message when there's a CSP error * move to computed prop for showing the CSP error message * fix typos
This commit is contained in:
parent
1a0901d15b
commit
e4144585bd
@ -1,12 +1,14 @@
|
||||
import Ember from 'ember';
|
||||
import { supportedAuthBackends } from 'vault/helpers/supported-auth-backends';
|
||||
const BACKENDS = supportedAuthBackends();
|
||||
const { computed, inject } = Ember;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['auth-form'],
|
||||
routing: Ember.inject.service('-routing'),
|
||||
auth: Ember.inject.service(),
|
||||
flashMessages: Ember.inject.service(),
|
||||
routing: inject.service('-routing'),
|
||||
auth: inject.service(),
|
||||
flashMessages: inject.service(),
|
||||
csp: inject.service('csp-event'),
|
||||
didRender() {
|
||||
// on very narrow viewports the active tab may be overflowed, so we scroll it into view here
|
||||
this.$('li.is-active').get(0).scrollIntoView();
|
||||
@ -25,9 +27,21 @@ export default Ember.Component.extend({
|
||||
return `auth-form/${type}`;
|
||||
}),
|
||||
|
||||
hasCSPError: computed.alias('csp.connectionViolations.firstObject'),
|
||||
|
||||
cspErrorText: `This is a standby Vault node but can't communicate with the active node via request forwarding. Sign in at the active node to use the Vault UI.`,
|
||||
|
||||
handleError(e) {
|
||||
this.set('loading', false);
|
||||
this.set('error', `Authentication failed: ${e.errors.join('.')}`);
|
||||
|
||||
let errors = e.errors.map(error => {
|
||||
if (error.detail) {
|
||||
return error.detail;
|
||||
}
|
||||
return error;
|
||||
});
|
||||
|
||||
this.set('error', `Authentication failed: ${errors.join('.')}`);
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: '',
|
||||
model: null,
|
||||
errors: [],
|
||||
errorMessage: null,
|
||||
|
||||
9
ui/app/instance-initializers/track-csp-event.js
Normal file
9
ui/app/instance-initializers/track-csp-event.js
Normal file
@ -0,0 +1,9 @@
|
||||
export function initialize(appInstance) {
|
||||
let service = appInstance.lookup('service:csp-event');
|
||||
service.attach();
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'track-csp-event',
|
||||
initialize,
|
||||
};
|
||||
@ -11,6 +11,7 @@ export default DS.Model.extend({
|
||||
nodes: hasMany('nodes', { async: false }),
|
||||
name: attr('string'),
|
||||
status: attr('string'),
|
||||
standby: attr('boolean'),
|
||||
|
||||
needsInit: computed('nodes', 'nodes.[]', function() {
|
||||
// needs init if no nodes are initialized
|
||||
|
||||
@ -28,9 +28,6 @@ export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
|
||||
},
|
||||
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
// payload looks like:
|
||||
// { "nodes": { "name": { "sealed": "true" }}}
|
||||
|
||||
const nodes = payload.nodes
|
||||
? Object.keys(payload.nodes).map(name => this.nodeFromObject(name, payload))
|
||||
: [Ember.assign(payload, { id: '1' })];
|
||||
|
||||
26
ui/app/services/csp-event.js
Normal file
26
ui/app/services/csp-event.js
Normal file
@ -0,0 +1,26 @@
|
||||
import Ember from 'ember';
|
||||
const { computed } = Ember;
|
||||
|
||||
export default Ember.Service.extend({
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.handleCSP = Ember.run.bind(this, '_handleCSP');
|
||||
},
|
||||
|
||||
events: [],
|
||||
|
||||
_handleCSP(event) {
|
||||
this.get('events').addObject(event);
|
||||
},
|
||||
|
||||
connectionViolations: computed.filterBy('events', 'violatedDirective', 'connect-src'),
|
||||
|
||||
attach() {
|
||||
this.get('events').clear();
|
||||
window.document.addEventListener('securitypolicyviolation', this.handleCSP, true);
|
||||
},
|
||||
|
||||
remove() {
|
||||
window.document.removeEventListener('securitypolicyviolation', this.handleCSP, true);
|
||||
},
|
||||
});
|
||||
@ -10,7 +10,11 @@
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="box is-marginless is-shadowless">
|
||||
{{message-error errorMessage=error}}
|
||||
{{#if (and cluster.standby hasCSPError)}}
|
||||
{{message-error errorMessage=cspErrorText data-test-auth-error=true}}
|
||||
{{else}}
|
||||
{{message-error errorMessage=error data-test-auth-error=true}}
|
||||
{{/if}}
|
||||
{{component providerComponentName onSubmit=(action 'doSubmit') }}
|
||||
<div class="box has-slim-padding is-shadowless">
|
||||
{{#unless (eq selectedAuthBackend.type "token")}}
|
||||
|
||||
@ -52,6 +52,10 @@ module.exports = function(environment) {
|
||||
enabled: false,
|
||||
};
|
||||
}
|
||||
if (environment !== 'production') {
|
||||
ENV.contentSecurityPolicyHeader = 'Content-Security-Policy';
|
||||
ENV.contentSecurityPolicyMeta = true;
|
||||
}
|
||||
|
||||
if (environment === 'production') {
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
"ember-cli": "~2.14.0",
|
||||
"ember-cli-babel": "^6.3.0",
|
||||
"ember-cli-clipboard": "^0.8.0",
|
||||
"ember-cli-content-security-policy": "^1.0.0",
|
||||
"ember-cli-dependency-checker": "^1.3.0",
|
||||
"ember-cli-eslint": "4",
|
||||
"ember-cli-favicon": "1.0.0-beta.4",
|
||||
|
||||
77
ui/tests/integration/components/auth-form-test.js
Normal file
77
ui/tests/integration/components/auth-form-test.js
Normal file
@ -0,0 +1,77 @@
|
||||
import { moduleForComponent, test } from 'ember-qunit';
|
||||
import Ember from 'ember';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
import Pretender from 'pretender';
|
||||
import { create } from 'ember-cli-page-object';
|
||||
import authForm from '../../pages/components/auth-form';
|
||||
|
||||
const component = create(authForm);
|
||||
|
||||
const authService = Ember.Service.extend({
|
||||
authenticate() {
|
||||
return Ember.$.getJSON('http://localhost:2000');
|
||||
},
|
||||
});
|
||||
|
||||
moduleForComponent('auth-form', 'Integration | Component | auth form', {
|
||||
integration: true,
|
||||
beforeEach() {
|
||||
Ember.getOwner(this).lookup('service:csp-event').attach();
|
||||
component.setContext(this);
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
Ember.getOwner(this).lookup('service:csp-event').remove();
|
||||
component.removeContext();
|
||||
},
|
||||
});
|
||||
|
||||
const CSP_ERR_TEXT = `Error This is a standby Vault node but can't communicate with the active node via request forwarding. Sign in at the active node to use the Vault UI.`;
|
||||
test('it renders error on CSP violation', function(assert) {
|
||||
this.register('service:auth', authService);
|
||||
this.inject.service('auth');
|
||||
this.set('cluster', Ember.Object.create({ standby: true }));
|
||||
this.render(hbs`{{auth-form cluster=cluster}}`);
|
||||
assert.equal(component.errorText, '');
|
||||
return component.login().then(() => wait()).then(() => {
|
||||
assert.equal(component.errorText, CSP_ERR_TEXT);
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders with vault style errors', function(assert) {
|
||||
let server = new Pretender(function() {
|
||||
this.get('/v1/auth/**', () => {
|
||||
return [
|
||||
400,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
errors: ['Not allowed'],
|
||||
}),
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
this.set('cluster', Ember.Object.create({}));
|
||||
this.render(hbs`{{auth-form cluster=cluster}}`);
|
||||
return component.login().then(() => {
|
||||
assert.equal(component.errorText, 'Error Authentication failed: Not allowed');
|
||||
server.shutdown();
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders AdapterError style errors', function(assert) {
|
||||
let server = new Pretender(function() {
|
||||
this.get('/v1/auth/**', () => {
|
||||
return [400, { 'Content-Type': 'application/json' }];
|
||||
});
|
||||
});
|
||||
|
||||
this.set('cluster', Ember.Object.create({}));
|
||||
this.render(hbs`{{auth-form cluster=cluster}}`);
|
||||
return component.login().then(() => {
|
||||
assert.equal(component.errorText, 'Error Authentication failed: Bad Request');
|
||||
server.shutdown();
|
||||
});
|
||||
});
|
||||
6
ui/tests/pages/components/auth-form.js
Normal file
6
ui/tests/pages/components/auth-form.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { clickable, text } from 'ember-cli-page-object';
|
||||
|
||||
export default {
|
||||
errorText: text('[data-test-auth-error]'),
|
||||
login: clickable('[data-test-auth-submit]'),
|
||||
};
|
||||
89
ui/yarn.lock
89
ui/yarn.lock
@ -1105,6 +1105,21 @@ bmp-js@0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.3.tgz#64113e9c7cf1202b376ed607bf30626ebe57b18a"
|
||||
|
||||
body-parser@^1.17.0:
|
||||
version "1.18.2"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454"
|
||||
dependencies:
|
||||
bytes "3.0.0"
|
||||
content-type "~1.0.4"
|
||||
debug "2.6.9"
|
||||
depd "~1.1.1"
|
||||
http-errors "~1.6.2"
|
||||
iconv-lite "0.4.19"
|
||||
on-finished "~2.3.0"
|
||||
qs "6.5.1"
|
||||
raw-body "2.3.2"
|
||||
type-is "~1.6.15"
|
||||
|
||||
body@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069"
|
||||
@ -1681,6 +1696,10 @@ bytes@2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.5.0.tgz#4c9423ea2d252c270c41b2bdefeff9bb6b62c06a"
|
||||
|
||||
bytes@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||
|
||||
calculate-cache-key-for-tree@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/calculate-cache-key-for-tree/-/calculate-cache-key-for-tree-1.1.0.tgz#0c3e42c9c134f3c9de5358c0f16793627ea976d6"
|
||||
@ -2091,6 +2110,10 @@ content-type@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed"
|
||||
|
||||
content-type@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
|
||||
continuable-cache@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f"
|
||||
@ -2243,6 +2266,12 @@ debug@2.6.8, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.4.
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
@ -2302,6 +2331,14 @@ depd@1.1.0, depd@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3"
|
||||
|
||||
depd@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
|
||||
|
||||
depd@~1.1.1, depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
|
||||
destroy@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
@ -2538,6 +2575,13 @@ ember-cli-clipboard@^0.8.0:
|
||||
ember-cli-htmlbars "^2.0.2"
|
||||
fastboot-transform "0.1.1"
|
||||
|
||||
ember-cli-content-security-policy@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-content-security-policy/-/ember-cli-content-security-policy-1.0.0.tgz#4f7d72997d4209cd59f10d3b0070fdb39593ed2d"
|
||||
dependencies:
|
||||
body-parser "^1.17.0"
|
||||
chalk "^2.0.0"
|
||||
|
||||
ember-cli-dependency-checker@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-dependency-checker/-/ember-cli-dependency-checker-1.4.0.tgz#2b13f977e1eea843fc1a21a001be6ca5d4ef1942"
|
||||
@ -4334,6 +4378,15 @@ htmlparser2@~3.8.1:
|
||||
entities "1.0"
|
||||
readable-stream "1.1"
|
||||
|
||||
http-errors@1.6.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
|
||||
dependencies:
|
||||
depd "1.1.1"
|
||||
inherits "2.0.3"
|
||||
setprototypeof "1.0.3"
|
||||
statuses ">= 1.3.1 < 2"
|
||||
|
||||
http-errors@~1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257"
|
||||
@ -4343,6 +4396,15 @@ http-errors@~1.6.1:
|
||||
setprototypeof "1.0.3"
|
||||
statuses ">= 1.3.1 < 2"
|
||||
|
||||
http-errors@~1.6.2:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.3"
|
||||
setprototypeof "1.1.0"
|
||||
statuses ">= 1.4.0 < 2"
|
||||
|
||||
http-proxy@^1.13.1, http-proxy@^1.9.0:
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742"
|
||||
@ -4358,6 +4420,10 @@ http-signature@~1.1.0:
|
||||
jsprim "^1.2.2"
|
||||
sshpk "^1.7.0"
|
||||
|
||||
iconv-lite@0.4.19:
|
||||
version "0.4.19"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
||||
|
||||
iconv-lite@^0.4.17, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
|
||||
version "0.4.18"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
|
||||
@ -6121,6 +6187,10 @@ qs@6.4.0, qs@^6.4.0, qs@~6.4.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
|
||||
|
||||
qs@6.5.1:
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
|
||||
|
||||
qs@~6.3.0:
|
||||
version "6.3.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c"
|
||||
@ -6164,6 +6234,15 @@ range-parser@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
|
||||
|
||||
raw-body@2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
|
||||
dependencies:
|
||||
bytes "3.0.0"
|
||||
http-errors "1.6.2"
|
||||
iconv-lite "0.4.19"
|
||||
unpipe "1.0.0"
|
||||
|
||||
raw-body@~1.1.0:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425"
|
||||
@ -6696,6 +6775,10 @@ setprototypeof@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
|
||||
|
||||
setprototypeof@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
@ -6927,6 +7010,10 @@ stack-trace@0.0.x:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
|
||||
|
||||
"statuses@>= 1.4.0 < 2":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
|
||||
stdout-stream@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b"
|
||||
@ -7371,7 +7458,7 @@ universalify@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.0.tgz#9eb1c4651debcc670cc94f1a75762332bb967778"
|
||||
|
||||
unpipe@~1.0.0:
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user