diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index 781c3aa087..2220472b85 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -13,6 +13,7 @@ module.exports = { rules: { 'no-unused-vars': ['error', { ignoreRestSiblings: true }], 'prettier/prettier': 'error', + 'ember/no-jquery': 'error', }, globals: { TextEncoderLite: true, diff --git a/ui/app/adapters/application.js b/ui/app/adapters/application.js index 6a7a15a7fc..6e54c834b6 100644 --- a/ui/app/adapters/application.js +++ b/ui/app/adapters/application.js @@ -3,13 +3,14 @@ import { assign } from '@ember/polyfills'; import { set } from '@ember/object'; import RSVP from 'rsvp'; import DS from 'ember-data'; +import AdapterFetch from 'ember-fetch/mixins/adapter-fetch'; import fetch from 'fetch'; import config from '../config/environment'; const { APP } = config; const { POLLING_URLS, NAMESPACE_ROOT_URLS } = APP; -export default DS.RESTAdapter.extend({ +export default DS.RESTAdapter.extend(AdapterFetch, { auth: service(), namespaceService: service('namespace'), controlGroup: service(), diff --git a/ui/app/components/auth-form.js b/ui/app/components/auth-form.js index fc8596404d..2884634302 100644 --- a/ui/app/components/auth-form.js +++ b/ui/app/components/auth-form.js @@ -175,15 +175,17 @@ export default Component.extend(DEFAULTS, { handleError(e, prefixMessage = true) { this.set('loading', false); - if (!e.errors) { - return e; + let errors; + if (e.errors) { + errors = e.errors.map(error => { + if (error.detail) { + return error.detail; + } + return error; + }); + } else { + errors = [e]; } - let errors = e.errors.map(error => { - if (error.detail) { - return error.detail; - } - return error; - }); let message = prefixMessage ? 'Authentication failed: ' : ''; this.set('error', `${message}${errors.join('.')}`); }, diff --git a/ui/app/components/linked-block.js b/ui/app/components/linked-block.js index b829b27548..1cd3e897f2 100644 --- a/ui/app/components/linked-block.js +++ b/ui/app/components/linked-block.js @@ -15,12 +15,12 @@ let LinkedBlockComponent = Component.extend({ encode: false, click(event) { - const $target = this.$(event.target); + const $target = event.target; const isAnchorOrButton = - $target.is('a') || - $target.is('button') || - $target.closest('button', event.currentTarget).length > 0 || - $target.closest('a', event.currentTarget).length > 0; + $target.tagName === 'A' || + $target.tagName === 'BUTTON' || + $target.closest('button') || + $target.closest('a'); if (!isAnchorOrButton) { let params = this.get('params'); if (this.encode) { diff --git a/ui/app/components/role-edit.js b/ui/app/components/role-edit.js index 9291276444..fb9bc80b6b 100644 --- a/ui/app/components/role-edit.js +++ b/ui/app/components/role-edit.js @@ -1,7 +1,7 @@ import { inject as service } from '@ember/service'; import { or } from '@ember/object/computed'; import { isBlank } from '@ember/utils'; -import $ from 'jquery'; +import { task, waitForEvent } from 'ember-concurrency'; import Component from '@ember/component'; import { set, get } from '@ember/object'; import FocusOnInsertMixin from 'vault/mixins/focus-on-insert'; @@ -42,20 +42,22 @@ export default Component.extend(FocusOnInsertMixin, { } }, - didInsertElement() { - this._super(...arguments); - $(document).on('keyup.keyEdit', this.onEscape.bind(this)); - }, - willDestroyElement() { this._super(...arguments); - const model = this.get('model'); - if (get(model, 'isError')) { - model.rollbackAttributes(); + if (this.model && this.model.isError) { + this.model.rollbackAttributes(); } - $(document).off('keyup.keyEdit'); }, + waitForKeyUp: task(function*() { + while (true) { + let event = yield waitForEvent(document.body, 'keyup'); + this.onEscape(event); + } + }) + .on('didInsertElement') + .cancelOn('willDestroyElement'), + transitionToRoute() { this.get('router').transitionTo(...arguments); }, @@ -84,18 +86,6 @@ export default Component.extend(FocusOnInsertMixin, { }, actions: { - handleKeyDown(_, e) { - e.stopPropagation(); - if (!(e.keyCode === keys.ENTER && e.metaKey)) { - return; - } - let $form = this.$('form'); - if ($form.length) { - $form.submit(); - } - $form = null; - }, - createOrUpdate(type, event) { event.preventDefault(); @@ -112,10 +102,6 @@ export default Component.extend(FocusOnInsertMixin, { }); }, - handleChange() { - this.hasDataChanges(); - }, - setValue(key, event) { set(get(this, 'model'), key, event.target.checked); }, diff --git a/ui/app/components/secret-edit.js b/ui/app/components/secret-edit.js index c2634900a3..c9dd40041b 100644 --- a/ui/app/components/secret-edit.js +++ b/ui/app/components/secret-edit.js @@ -95,7 +95,7 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { updatePath: maybeQueryRecord( 'capabilities', context => { - if (context.mode === 'create') { + if (!context.model || context.mode === 'create') { return; } let backend = context.isV2 ? context.get('model.engine.id') : context.model.backend; @@ -116,7 +116,7 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { v2UpdatePath: maybeQueryRecord( 'capabilities', context => { - if (context.mode === 'create' || context.isV2 === false) { + if (!context.model || context.mode === 'create' || context.isV2 === false) { return; } let backend = context.get('model.engine.id'); @@ -137,7 +137,9 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { buttonDisabled: or('requestInFlight', 'model.isFolder', 'model.flagsIsInvalid', 'hasLintError', 'error'), modelForData: computed('isV2', 'model', function() { - return this.isV2 ? this.model.belongsTo('selectedVersion').value() : this.model; + let { model } = this; + if (!model) return null; + return this.isV2 ? model.belongsTo('selectedVersion').value() : model; }), basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function() { @@ -160,6 +162,7 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { 'model.{failedServerRead,selectedVersion.failedServerRead}', 'isV2', function() { + if (!this.model) return; // if the version couldn't be read from the server if (this.isV2 && this.model.selectedVersion.failedServerRead) { return true; diff --git a/ui/app/components/transit-edit.js b/ui/app/components/transit-edit.js index 9c465c472e..d5258086e1 100644 --- a/ui/app/components/transit-edit.js +++ b/ui/app/components/transit-edit.js @@ -1,9 +1,10 @@ import { inject as service } from '@ember/service'; import { or } from '@ember/object/computed'; import { isBlank } from '@ember/utils'; -import $ from 'jquery'; import Component from '@ember/component'; +import { task, waitForEvent } from 'ember-concurrency'; import { set, get } from '@ember/object'; + import FocusOnInsertMixin from 'vault/mixins/focus-on-insert'; import keys from 'vault/lib/keycodes'; @@ -19,24 +20,22 @@ export default Component.extend(FocusOnInsertMixin, { key: null, requestInFlight: or('key.isLoading', 'key.isReloading', 'key.isSaving'), - init() { - this._super(...arguments); - }, - - didInsertElement() { - this._super(...arguments); - $(document).on('keyup.keyEdit', this.onEscape.bind(this)); - }, - willDestroyElement() { this._super(...arguments); - const key = this.get('key'); - if (get(key, 'isError')) { - key.rollbackAttributes(); + if (this.key && this.key.isError) { + this.key.rollbackAttributes(); } - $(document).off('keyup.keyEdit'); }, + waitForKeyUp: task(function*() { + while (true) { + let event = yield waitForEvent(document.body, 'keyup'); + this.onEscape(event); + } + }) + .on('didInsertElement') + .cancelOn('willDestroyElement'), + transitionToRoute() { this.get('router').transitionTo(...arguments); }, @@ -69,18 +68,6 @@ export default Component.extend(FocusOnInsertMixin, { }, actions: { - handleKeyDown(_, e) { - e.stopPropagation(); - if (!(e.keyCode === keys.ENTER && e.metaKey)) { - return; - } - let $form = this.$('form'); - if ($form.length) { - $form.submit(); - } - $form = null; - }, - createOrUpdateKey(type, event) { event.preventDefault(); @@ -101,10 +88,6 @@ export default Component.extend(FocusOnInsertMixin, { ); }, - handleChange() { - this.hasDataChanges(); - }, - setValueOnKey(key, event) { set(get(this, 'key'), key, event.target.checked); }, diff --git a/ui/app/controllers/vault/cluster/settings/seal.js b/ui/app/controllers/vault/cluster/settings/seal.js index d0afb3673b..721a2fee6d 100644 --- a/ui/app/controllers/vault/cluster/settings/seal.js +++ b/ui/app/controllers/vault/cluster/settings/seal.js @@ -12,7 +12,7 @@ export default Controller.extend({ .then(() => { this.model.cluster.get('leaderNode').set('sealed', true); this.get('auth').deleteCurrentToken(); - return this.transitionToRoute('vault.cluster'); + return this.transitionToRoute('vault.cluster.unseal'); }); }, }, diff --git a/ui/app/mixins/unload-model-route.js b/ui/app/mixins/unload-model-route.js index 2440798953..b1f10d111c 100644 --- a/ui/app/mixins/unload-model-route.js +++ b/ui/app/mixins/unload-model-route.js @@ -5,12 +5,15 @@ import Mixin from '@ember/object/mixin'; export default Mixin.create({ modelPath: 'model', unloadModel() { - const model = this.controller.get(this.get('modelPath')); + let { modelPath } = this; + let model = this.controller.get(modelPath); if (!model || !model.unloadRecord) { return; } this.store.unloadRecord(model); model.destroy(); + // it's important to unset the model on the controller since controllers are singletons + this.controller.set(modelPath, null); }, actions: { diff --git a/ui/app/routes/vault/cluster.js b/ui/app/routes/vault/cluster.js index 46a41eb3b8..ab2b78b07b 100644 --- a/ui/app/routes/vault/cluster.js +++ b/ui/app/routes/vault/cluster.js @@ -52,14 +52,14 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { } }, - beforeModel() { + async beforeModel() { const params = this.paramsFor(this.routeName); this.clearNonGlobalModels(); this.get('namespaceService').setNamespace(params.namespaceQueryParam); const id = this.getClusterId(params); if (id) { this.get('auth').setCluster(id); - this.get('permissions').getPaths.perform(); + await this.get('permissions').getPaths.perform(); return this.get('version').fetchFeatures(); } else { return reject({ httpStatus: 404, message: 'not found', path: params.cluster_name }); diff --git a/ui/app/services/auth.js b/ui/app/services/auth.js index f9704de339..c757b0c7da 100644 --- a/ui/app/services/auth.js +++ b/ui/app/services/auth.js @@ -1,9 +1,10 @@ import Ember from 'ember'; -import { resolve } from 'rsvp'; +import { resolve, reject } from 'rsvp'; import { assign } from '@ember/polyfills'; -import $ from 'jquery'; import { isArray } from '@ember/array'; import { computed, get } from '@ember/object'; + +import fetch from 'fetch'; import { getOwner } from '@ember/application'; import Service, { inject as service } from '@ember/service'; import getStorage from '../lib/token-storage'; @@ -86,7 +87,18 @@ export default Service.extend({ if (namespace) { defaults.headers['X-Vault-Namespace'] = namespace; } - return $.ajax(assign(defaults, options)); + let opts = assign(defaults, options); + + return fetch(url, { + method: opts.method || 'GET', + headers: opts.headers || {}, + }).then(response => { + if (response.status >= 200 && response.status < 300) { + return resolve(response.json()); + } else { + return reject(); + } + }); }, renewCurrentToken() { diff --git a/ui/app/services/csp-event.js b/ui/app/services/csp-event.js index df1a5779d2..21a3947779 100644 --- a/ui/app/services/csp-event.js +++ b/ui/app/services/csp-event.js @@ -1,6 +1,5 @@ /*eslint-disable no-constant-condition*/ import { computed } from '@ember/object'; -import { filterBy } from '@ember/object/computed'; import Service from '@ember/service'; import { task, waitForEvent } from 'ember-concurrency'; @@ -9,7 +8,9 @@ export default Service.extend({ events: computed(function() { return []; }), - connectionViolations: filterBy('events', 'violatedDirective', 'connect-src'), + connectionViolations: computed('events.[].violatedDirective', function() { + return this.get('events').filter(e => e.violatedDirective.startsWith('connect-src')); + }), attach() { this.monitor.perform(); diff --git a/ui/app/templates/partials/role-aws/form.hbs b/ui/app/templates/partials/role-aws/form.hbs index 73f2a126c5..de75c1dc31 100644 --- a/ui/app/templates/partials/role-aws/form.hbs +++ b/ui/app/templates/partials/role-aws/form.hbs @@ -1,4 +1,4 @@ -