Ember Upgrade to 3.24 (#13443)

* Update browserslist

* Add browserslistrc

* ember-cli-update --to 3.26, fix conflicts

* Run codemodes that start with ember-*

* More codemods - before cp*

* More codemods (curly data-test-*)

* WIP ember-basic-dropdown template errors

* updates ember-basic-dropdown and related deps to fix build issues

* updates basic dropdown instances to new version API

* updates more deps -- ember-template-lint is working again

* runs no-implicit-this codemod

* creates and runs no-quoteless-attributes codemod

* runs angle brackets codemod

* updates lint:hbs globs to only touch hbs files

* removes yield only templates

* creates and runs deprecated args transform

* supresses lint error for invokeAction on LinkTo component

* resolves remaining ambiguous path lint errors

* resolves simple-unless lint errors

* adds warnings for deprecated tagName arg on LinkTo components

* adds warnings for remaining curly component invocation

* updates global template lint rules

* resolves remaining template lint errors

* disables some ember specfic lint rules that target pre octane patterns

* js lint fix run

* resolves remaining js lint errors

* fixes test run

* adds npm-run-all dep

* fixes test attribute issues

* fixes console acceptance tests

* fixes tests

* adds yield only wizard/tutorial-active template

* fixes more tests

* attempts to fix more flaky tests

* removes commented out settled in transit test

* updates deprecations workflow and adds initializer to filter by version

* updates flaky policies acl old test

* updates to flaky transit test

* bumps ember deps down to LTS version

* runs linters after main merge

* fixes client count tests after bad merge conflict fixes

* fixes client count history test

* more updates to lint config

* another round of hbs lint fixes after extending stylistic rule

* updates lint-staged commands

* removes indent eslint rule since it seems to break things

* fixes bad attribute in transform-edit-form template

* test fixes

* fixes enterprise tests

* adds changelog

* removes deprecated ember-concurrency-test-waiters dep and adds @ember/test-waiters

* flaky test fix

Co-authored-by: hashishaw <cshaw@hashicorp.com>
This commit is contained in:
Jordan Reimer 2021-12-16 20:44:29 -07:00 committed by GitHub
parent 5e316537f2
commit 5c2a08de6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1093 changed files with 14704 additions and 12316 deletions

3
changelog/13443.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:change
ui: Upgrade Ember to version 3.24
```

3
ui/.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
defaults
not IE 11
maintained node versions

View File

@ -16,6 +16,8 @@
# misc # misc
/coverage/ /coverage/
!.*
.eslintcache
# ember-try # ember-try
/.node_modules.ember-try/ /.node_modules.ember-try/

View File

@ -12,21 +12,28 @@ module.exports = {
legacyDecorators: true, legacyDecorators: true,
}, },
}, },
plugins: ['ember', 'prettier'], plugins: ['ember'],
extends: ['eslint:recommended', 'plugin:ember/recommended', 'prettier'], extends: ['eslint:recommended', 'plugin:ember/recommended', 'plugin:prettier/recommended'],
env: { env: {
browser: true, browser: true,
es6: true,
}, },
rules: { rules: {
// TODO revisit once figure out how to replace, added during upgrade to 3.20 'no-console': 'warn',
'ember/no-new-mixins': 'off', 'ember/no-mixins': 'warn',
'ember/no-mixins': 'off', 'ember/no-new-mixins': 'off', // should be warn but then every line of the mixin is green
// need to be fully glimmerized before these rules can be turned on
'ember/no-classic-classes': 'off',
'ember/no-classic-components': 'off',
'ember/no-actions-hash': 'off',
'ember/require-tagless-components': 'off',
'ember/no-component-lifecycle-hooks': 'off',
}, },
overrides: [ overrides: [
// node files // node files
{ {
files: [ files: [
'.eslintrc.js',
'.prettierrc.js',
'.template-lintrc.js', '.template-lintrc.js',
'ember-cli-build.js', 'ember-cli-build.js',
'testem.js', 'testem.js',
@ -34,10 +41,10 @@ module.exports = {
'config/**/*.js', 'config/**/*.js',
'lib/*/index.js', 'lib/*/index.js',
'scripts/start-vault.js', 'scripts/start-vault.js',
'server/**/*.js',
], ],
parserOptions: { parserOptions: {
sourceType: 'script', sourceType: 'script',
ecmaVersion: 2018,
}, },
env: { env: {
browser: false, browser: false,

1
ui/.gitignore vendored
View File

@ -11,6 +11,7 @@
# misc # misc
/.sass-cache /.sass-cache
/.eslintcache
/connect.lock /connect.lock
/coverage/ /coverage/
/libpeerconnection.log /libpeerconnection.log

21
ui/.prettierignore Normal file
View File

@ -0,0 +1,21 @@
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/coverage/
!.*
.eslintcache
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

View File

@ -1,5 +0,0 @@
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 110
}

16
ui/.prettierrc.js Normal file
View File

@ -0,0 +1,16 @@
'use strict';
module.exports = {
singleQuote: true,
trailingComma: 'es5',
printWidth: 110,
overrides: [
{
files: '*.hbs',
options: {
singleQuote: false,
printWidth: 125,
},
},
],
};

View File

@ -7,8 +7,8 @@ function loadStories() {
// automatically import all files ending in *.stories.js // automatically import all files ending in *.stories.js
const appStories = require.context('../stories', true, /.stories.js$/); const appStories = require.context('../stories', true, /.stories.js$/);
const addonAndRepoStories = require.context('../lib', true, /.stories.js$/); const addonAndRepoStories = require.context('../lib', true, /.stories.js$/);
appStories.keys().forEach(filename => appStories(filename)); appStories.keys().forEach((filename) => appStories(filename));
addonAndRepoStories.keys().forEach(filename => addonAndRepoStories(filename)); addonAndRepoStories.keys().forEach((filename) => addonAndRepoStories(filename));
} }
addParameters({ addParameters({
@ -16,7 +16,7 @@ addParameters({
options: { theme }, options: { theme },
}); });
addDecorator(storyFn => { addDecorator((storyFn) => {
const { template, context } = storyFn(); const { template, context } = storyFn();
// flight icon sprite must be inserted into dom for icon lookup via use element // flight icon sprite must be inserted into dom for icon lookup via use element

View File

@ -1,27 +1,17 @@
'use strict'; 'use strict';
module.exports = { module.exports = {
extends: 'recommended', extends: ['octane', 'stylistic'],
rules: { rules: {
// should definitely move to template only 'no-bare-strings': 'off',
// glimmer components for this one 'no-action': 'off',
'no-partial': false, 'no-duplicate-landmark-elements': 'warn',
'no-implicit-this': {
// these need to be looked into, but allow: ['supported-auth-backends'],
// may be a bigger change },
'no-invalid-interactive': false, 'require-input-label': 'off',
'simple-unless': false, 'no-down-event-binding': 'warn',
'self-closing-void-elements': 'off',
'self-closing-void-elements': false,
'no-unnecessary-concat': false,
'no-quoteless-attributes': false,
'no-nested-interactive': false,
// not sure we'll ever want these on,
// would be nice but if prettier isn't doing
// it for us, then not sure it's worth it
'attribute-indentation': false,
'block-indentation': false,
quotes: false,
}, },
ignore: ['lib/story-md', 'tests/**'],
}; };

View File

@ -1,6 +1,90 @@
## Module Report ## Module Report
### Unknown Global ### Unknown Global
**Global**: `Ember.testing`
**Location**: `app/components/auth-jwt.js` at line 9
```js
/* eslint-disable ember/no-ember-testing-in-module-scope */
const WAIT_TIME = Ember.testing ? 0 : 500;
const ERROR_WINDOW_CLOSED =
'The provider window was closed before authentication was complete. Please click Sign In to try again.';
```
### Unknown Global
**Global**: `Ember.testing`
**Location**: `app/components/auth-form.js` at line 252
```js
delayAuthMessageReminder: task(function*() {
if (Ember.testing) {
this.showLoading = true;
yield timeout(0);
```
### Unknown Global
**Global**: `Ember.testing`
**Location**: `app/routes/vault/cluster/logout.js` at line 30
```js
this.flashMessages.clearMessages();
this.permissions.reset();
if (Ember.testing) {
// Don't redirect on the test
this.replaceWith('vault.cluster.auth', { queryParams: { with: authType } });
```
### Unknown Global
**Global**: `Ember.testing`
**Location**: `app/components/mount-backend-form.js` at line 100
```js
capabilities = yield this.store.findRecord('capabilities', `${path}/config`);
} catch (err) {
if (Ember.testing) {
//captures mount-backend-form component test
yield mountModel.save();
```
### Unknown Global
**Global**: `Ember.testing`
**Location**: `app/components/oidc-consent-block.js` at line 47
```js
let { redirect, ...params } = this.args;
let redirectUrl = this.buildUrl(redirect, params);
if (Ember.testing) {
this.args.testRedirect(redirectUrl.toString());
} else {
```
### Unknown Global
**Global**: `Ember.testing`
**Location**: `lib/core/addon/components/ttl-form.js` at line 82
```js
this.set('time', parsedTime);
this.handleChange();
if (Ember.testing) {
return;
}
```
### Unknown Global
**Global**: `Ember.onerror` **Global**: `Ember.onerror`
**Location**: `tests/helpers/wait-for-error.js` at line 5 **Location**: `tests/helpers/wait-for-error.js` at line 5
@ -143,34 +227,6 @@ export default function waitForError(opts) {
**Global**: `Ember.testing` **Global**: `Ember.testing`
**Location**: `app/components/auth-jwt.js` at line 9
```js
/* eslint-disable ember/no-ember-testing-in-module-scope */
const WAIT_TIME = Ember.testing ? 0 : 500;
const ERROR_WINDOW_CLOSED =
'The provider window was closed before authentication was complete. Please click Sign In to try again.';
```
### Unknown Global
**Global**: `Ember.testing`
**Location**: `app/components/auth-jwt.js` at line 119
```js
exchangeOIDC: task(function*(event, oidcWindow) {
// in non-incognito mode we need to use a timeout because it takes time before oidcState is written to local storage.
let oidcState = Ember.testing
? event.storageArea.getItem('oidcState')
: yield timeout(1000).then(() => event.storageArea.getItem('oidcState'));
```
### Unknown Global
**Global**: `Ember.testing`
**Location**: `app/routes/vault.js` at line 7 **Location**: `app/routes/vault.js` at line 7
```js ```js
@ -185,7 +241,7 @@ export default Route.extend({
**Global**: `Ember.testing` **Global**: `Ember.testing`
**Location**: `app/services/auth.js` at line 267 **Location**: `app/services/auth.js` at line 268
```js ```js
checkShouldRenew: task(function*() { checkShouldRenew: task(function*() {
@ -194,17 +250,3 @@ export default Route.extend({
return; return;
} }
``` ```
### Unknown Global
**Global**: `Ember.testing`
**Location**: `lib/core/addon/components/ttl-form.js` at line 82
```js
this.set('time', parsedTime);
this.handleChange();
if (Ember.testing) {
return;
}
```

View File

@ -111,9 +111,8 @@ To run the tests locally in a browser other than IE11, swap out `launch_in_ci: [
### Linting ### Linting
- `yarn lint:hbs` * `yarn lint`
- `yarn lint:js` * `yarn lint:fix`
- `yarn lint:js -- --fix`
### Building Vault UI into a Vault Binary ### Building Vault UI into a Vault Binary

View File

@ -41,7 +41,7 @@ export default RESTAdapter.extend({
headers['X-Vault-Wrap-TTL'] = options.wrapTTL; headers['X-Vault-Wrap-TTL'] = options.wrapTTL;
} }
let namespace = typeof options.namespace === 'undefined' ? this.namespaceService.path : options.namespace; let namespace = typeof options.namespace === 'undefined' ? this.namespaceService.path : options.namespace;
if (namespace && !NAMESPACE_ROOT_URLS.some(str => url.includes(str))) { if (namespace && !NAMESPACE_ROOT_URLS.some((str) => url.includes(str))) {
headers['X-Vault-Namespace'] = namespace; headers['X-Vault-Namespace'] = namespace;
} }
options.headers = assign(options.headers || {}, headers); options.headers = assign(options.headers || {}, headers);
@ -49,7 +49,7 @@ export default RESTAdapter.extend({
_preRequest(url, options) { _preRequest(url, options) {
this.addHeaders(url, options); this.addHeaders(url, options);
const isPolling = POLLING_URLS.some(str => url.includes(str)); const isPolling = POLLING_URLS.some((str) => url.includes(str));
if (!isPolling) { if (!isPolling) {
this.auth.setLastFetch(Date.now()); this.auth.setLastFetch(Date.now());
} }
@ -86,7 +86,7 @@ export default RESTAdapter.extend({
const [resp] = args; const [resp] = args;
if (resp && resp.warnings) { if (resp && resp.warnings) {
let flash = this.flashMessages; let flash = this.flashMessages;
resp.warnings.forEach(message => { resp.warnings.forEach((message) => {
flash.info(message); flash.info(message);
}); });
} }
@ -102,7 +102,7 @@ export default RESTAdapter.extend({
headers: opts.headers || {}, headers: opts.headers || {},
body: opts.body, body: opts.body,
signal: opts.signal, signal: opts.signal,
}).then(response => { }).then((response) => {
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
return RSVP.resolve(response); return RSVP.resolve(response);
} else { } else {

View File

@ -22,7 +22,7 @@ export default ApplicationAdapter.extend({
return this.ajax(url, 'GET', { return this.ajax(url, 'GET', {
unauthenticated: true, unauthenticated: true,
}) })
.then(result => { .then((result) => {
return { return {
data: result.data.auth, data: result.data.auth,
}; };
@ -33,7 +33,7 @@ export default ApplicationAdapter.extend({
}; };
}); });
} }
return this.ajax(this.url(), 'GET').catch(e => { return this.ajax(this.url(), 'GET').catch((e) => {
if (e instanceof AdapterError) { if (e instanceof AdapterError) {
set(e, 'policyPath', 'sys/auth'); set(e, 'policyPath', 'sys/auth');
} }

View File

@ -22,7 +22,7 @@ export default ApplicationAdapter.extend({
let role = snapshot.attr('role'); let role = snapshot.attr('role');
let url = `/v1/${role.backend}/creds/${role.name}`; let url = `/v1/${role.backend}/creds/${role.name}`;
return this.ajax(url, method, options).then(response => { return this.ajax(url, method, options).then((response) => {
response.id = snapshot.id; response.id = snapshot.id;
response.modelName = type.modelName; response.modelName = type.modelName;
store.pushPayload(type.modelName, response); store.pushPayload(type.modelName, response);

View File

@ -8,7 +8,7 @@ export default ApplicationAdapter.extend({
}, },
findRecord(store, type, id) { findRecord(store, type, id) {
return this.ajax(this.buildURL(type), 'POST', { data: { paths: [id] } }).catch(e => { return this.ajax(this.buildURL(type), 'POST', { data: { paths: [id] } }).catch((e) => {
if (e instanceof AdapterError) { if (e instanceof AdapterError) {
set(e, 'policyPath', 'sys/capabilities-self'); set(e, 'policyPath', 'sys/capabilities-self');
} }
@ -21,7 +21,7 @@ export default ApplicationAdapter.extend({
if (!id) { if (!id) {
return; return;
} }
return this.findRecord(store, type, id).then(resp => { return this.findRecord(store, type, id).then((resp) => {
resp.path = id; resp.path = id;
return resp; return resp;
}); });

View File

@ -11,7 +11,7 @@ export default Application.extend({
query = null; query = null;
} }
// API accepts start and end as query params // API accepts start and end as query params
return this.ajax(url, 'GET', { data: query }).then(resp => { return this.ajax(url, 'GET', { data: query }).then((resp) => {
let response = resp || {}; let response = resp || {};
// if the response is a 204 it has no request id // if the response is a 204 it has no request id
response.id = response.request_id || 'no-data'; response.id = response.request_id || 'no-data';

View File

@ -2,7 +2,7 @@ import Application from '../application';
export default Application.extend({ export default Application.extend({
queryRecord() { queryRecord() {
return this.ajax(this.urlForQuery(), 'GET').then(resp => { return this.ajax(this.urlForQuery(), 'GET').then((resp) => {
resp.id = resp.request_id; resp.id = resp.request_id;
return resp; return resp;
}); });

View File

@ -40,10 +40,10 @@ export default ApplicationAdapter.extend({
findRecord(store, type, id, snapshot) { findRecord(store, type, id, snapshot) {
let fetches = { let fetches = {
health: this.health(), health: this.health(),
sealStatus: this.sealStatus().catch(e => e), sealStatus: this.sealStatus().catch((e) => e),
}; };
if (this.version.isEnterprise && this.namespaceService.inRootNamespace) { if (this.version.isEnterprise && this.namespaceService.inRootNamespace) {
fetches.replicationStatus = this.replicationStatus().catch(e => e); fetches.replicationStatus = this.replicationStatus().catch((e) => e);
} }
return hash(fetches).then(({ health, sealStatus, replicationStatus }) => { return hash(fetches).then(({ health, sealStatus, replicationStatus }) => {
let ret = { let ret = {

View File

@ -11,7 +11,7 @@ export default ApplicationAdapter.extend({
data: { data: {
accessor: id, accessor: id,
}, },
}).then(response => { }).then((response) => {
response.id = id; response.id = id;
return response; return response;
}); });

View File

@ -24,7 +24,7 @@ export default ApplicationAdapter.extend({
}, },
fetchByQuery(store, query) { fetchByQuery(store, query) {
const { backend, id } = query; const { backend, id } = query;
return this.ajax(this.urlFor(backend, id), 'GET', this.optionsForQuery(id)).then(resp => { return this.ajax(this.urlFor(backend, id), 'GET', this.optionsForQuery(id)).then((resp) => {
resp.backend = backend; resp.backend = backend;
if (id) { if (id) {
resp.id = id; resp.id = id;

View File

@ -9,14 +9,14 @@ export default ApplicationAdapter.extend({
return this.ajax( return this.ajax(
`${this.buildURL()}/${encodeURIComponent(backend)}/static-creds/${encodeURIComponent(secret)}`, `${this.buildURL()}/${encodeURIComponent(backend)}/static-creds/${encodeURIComponent(secret)}`,
'GET' 'GET'
).then(resp => ({ ...resp, roleType: 'static' })); ).then((resp) => ({ ...resp, roleType: 'static' }));
}, },
_dynamicCreds(backend, secret) { _dynamicCreds(backend, secret) {
return this.ajax( return this.ajax(
`${this.buildURL()}/${encodeURIComponent(backend)}/creds/${encodeURIComponent(secret)}`, `${this.buildURL()}/${encodeURIComponent(backend)}/creds/${encodeURIComponent(secret)}`,
'GET' 'GET'
).then(resp => ({ ...resp, roleType: 'dynamic' })); ).then((resp) => ({ ...resp, roleType: 'dynamic' }));
}, },
fetchByQuery(store, query) { fetchByQuery(store, query) {

View File

@ -25,7 +25,7 @@ export default ApplicationAdapter.extend({
}, },
staticRoles(backend, id) { staticRoles(backend, id) {
return this.ajax(this.urlFor(backend, id, 'static'), 'GET', this.optionsForQuery(id)).then(resp => { return this.ajax(this.urlFor(backend, id, 'static'), 'GET', this.optionsForQuery(id)).then((resp) => {
if (id) { if (id) {
return { return {
...resp, ...resp,
@ -39,7 +39,7 @@ export default ApplicationAdapter.extend({
}, },
dynamicRoles(backend, id) { dynamicRoles(backend, id) {
return this.ajax(this.urlFor(backend, id), 'GET', this.optionsForQuery(id)).then(resp => { return this.ajax(this.urlFor(backend, id), 'GET', this.optionsForQuery(id)).then((resp) => {
if (id) { if (id) {
return { return {
...resp, ...resp,

View File

@ -6,14 +6,14 @@ export default ApplicationAdapter.extend({
namespace: 'v1', namespace: 'v1',
urlForItem() {}, urlForItem() {},
dynamicApiPath: '', dynamicApiPath: '',
getDynamicApiPath: task(function*(id) { getDynamicApiPath: task(function* (id) {
// TODO: remove yield at some point. // TODO: remove yield at some point.
let result = yield this.store.peekRecord('auth-method', id); let result = yield this.store.peekRecord('auth-method', id);
this.dynamicApiPath = result.apiPath; this.dynamicApiPath = result.apiPath;
return; return;
}), }),
fetchByQuery: task(function*(store, query, isList) { fetchByQuery: task(function* (store, query, isList) {
const { id } = query; const { id } = query;
let data = {}; let data = {};
if (isList) { if (isList) {
@ -21,7 +21,7 @@ export default ApplicationAdapter.extend({
yield this.getDynamicApiPath.perform(id); yield this.getDynamicApiPath.perform(id);
} }
return this.ajax(this.urlForItem(id, isList, this.dynamicApiPath), 'GET', { data }).then(resp => { return this.ajax(this.urlForItem(id, isList, this.dynamicApiPath), 'GET', { data }).then((resp) => {
const data = { const data = {
id, id,
method: id, method: id,

View File

@ -3,7 +3,7 @@ import IdentityAdapter from './base';
export default IdentityAdapter.extend({ export default IdentityAdapter.extend({
lookup(store, data) { lookup(store, data) {
let url = `/${this.urlPrefix()}/identity/lookup/entity`; let url = `/${this.urlPrefix()}/identity/lookup/entity`;
return this.ajax(url, 'POST', { data }).then(response => { return this.ajax(url, 'POST', { data }).then((response) => {
// unsuccessful lookup is a 204 // unsuccessful lookup is a 204
if (!response) return; if (!response) return;
let modelName = 'identity/entity'; let modelName = 'identity/entity';

View File

@ -3,7 +3,7 @@ import IdentityAdapter from './base';
export default IdentityAdapter.extend({ export default IdentityAdapter.extend({
lookup(store, data) { lookup(store, data) {
let url = `/${this.urlPrefix()}/identity/lookup/group`; let url = `/${this.urlPrefix()}/identity/lookup/group`;
return this.ajax(url, 'POST', { data }).then(response => { return this.ajax(url, 'POST', { data }).then((response) => {
// unsuccessful lookup is a 204 // unsuccessful lookup is a 204
if (!response) return; if (!response) return;
let modelName = 'identity/group'; let modelName = 'identity/group';

View File

@ -38,7 +38,7 @@ export default ApplicationAdapter.extend({
}, },
query(store, type, query) { query(store, type, query) {
return this.ajax(this.urlForQuery(query, type.modelName), 'GET').then(resp => { return this.ajax(this.urlForQuery(query, type.modelName), 'GET').then((resp) => {
// remove pagination query items here // remove pagination query items here
const { ...modelAttrs } = query; const { ...modelAttrs } = query;
resp._requestQuery = modelAttrs; resp._requestQuery = modelAttrs;
@ -49,7 +49,7 @@ export default ApplicationAdapter.extend({
queryRecord(store, type, query) { queryRecord(store, type, query) {
let id = query.id; let id = query.id;
delete query.id; delete query.id;
return this.ajax(this._url(type.modelName, query, id), 'GET').then(resp => { return this.ajax(this._url(type.modelName, query, id), 'GET').then((resp) => {
resp.id = id; resp.id = id;
resp = { ...resp, ...query }; resp = { ...resp, ...query };
return resp; return resp;

View File

@ -8,7 +8,7 @@ export default BaseAdapter.extend({
role: snapshot.record.role, role: snapshot.record.role,
}); });
url = `${url}/generate`; url = `${url}/generate`;
return this.ajax(url, 'POST', { data: snapshot.serialize() }).then(model => { return this.ajax(url, 'POST', { data: snapshot.serialize() }).then((model) => {
model.data.id = model.data.serial_number; model.data.id = model.data.serial_number;
return model; return model;
}); });

View File

@ -48,7 +48,7 @@ export default ApplicationAdapter.extend({
data: { data: {
list: true, list: true,
}, },
}).then(resp => { }).then((resp) => {
if (prefix) { if (prefix) {
resp.prefix = prefix; resp.prefix = prefix;
} }

View File

@ -6,7 +6,7 @@ export default ApplicationAdapter.extend({
}, },
findRecord(store, type, id) { findRecord(store, type, id) {
return this.ajax(this.url(id), 'GET').then(resp => { return this.ajax(this.url(id), 'GET').then((resp) => {
resp.id = id; resp.id = id;
return resp; return resp;
}); });

View File

@ -34,7 +34,7 @@ export default ApplicationAdapter.extend({
data = serializer.serialize(snapshot, requestType); data = serializer.serialize(snapshot, requestType);
} }
return this.ajax(this.url(snapshot, action), 'POST', { data }).then(response => { return this.ajax(this.url(snapshot, action), 'POST', { data }).then((response) => {
// uploading CA, setting signed intermediate cert, and attempting to generate // uploading CA, setting signed intermediate cert, and attempting to generate
// a new CA if one exists, all return a 204 // a new CA if one exists, all return a 204
if (!response) { if (!response) {

View File

@ -23,7 +23,7 @@ export default Adapter.extend({
fetchByQuery(store, query) { fetchByQuery(store, query) {
const { backend, id } = query; const { backend, id } = query;
return this.ajax(this.urlFor(backend, id), 'GET', this.optionsForQuery(id)).then(resp => { return this.ajax(this.urlFor(backend, id), 'GET', this.optionsForQuery(id)).then((resp) => {
const data = { const data = {
backend, backend,
}; };
@ -53,7 +53,7 @@ export default Adapter.extend({
const data = { const data = {
serial_number: id, serial_number: id,
}; };
return this.ajax(`${this.buildURL()}/${backend}/revoke`, 'POST', { data }).then(resp => { return this.ajax(`${this.buildURL()}/${backend}/revoke`, 'POST', { data }).then((resp) => {
const data = { const data = {
id, id,
serial_number: id, serial_number: id,

View File

@ -31,7 +31,7 @@ export default ApplicationAdapter.extend({
} }
return data; return data;
}, {}); }, {});
return this.ajax(url, 'POST', { data }).then(resp => { return this.ajax(url, 'POST', { data }).then((resp) => {
let response = resp || {}; let response = resp || {};
response.id = `${snapshot.record.get('backend')}-${snapshot.adapterOptions.method}`; response.id = `${snapshot.record.get('backend')}-${snapshot.adapterOptions.method}`;
return response; return response;
@ -69,9 +69,11 @@ export default ApplicationAdapter.extend({
return hash({ return hash({
backend: backendPath, backend: backendPath,
id: this.id(backendPath), id: this.id(backendPath),
der: this.rawRequest(derURL, 'GET', { unauthenticated: true }).then(response => response.blob()), der: this.rawRequest(derURL, 'GET', { unauthenticated: true }).then((response) => response.blob()),
pem: this.rawRequest(pemURL, 'GET', { unauthenticated: true }).then(response => response.text()), pem: this.rawRequest(pemURL, 'GET', { unauthenticated: true }).then((response) => response.text()),
ca_chain: this.rawRequest(chainURL, 'GET', { unauthenticated: true }).then(response => response.text()), ca_chain: this.rawRequest(chainURL, 'GET', { unauthenticated: true }).then((response) =>
response.text()
),
}); });
}, },
@ -79,12 +81,12 @@ export default ApplicationAdapter.extend({
const url = `/v1/${backendPath}/config/urls`; const url = `/v1/${backendPath}/config/urls`;
const id = this.id(backendPath); const id = this.id(backendPath);
return this.ajax(url, 'GET') return this.ajax(url, 'GET')
.then(resp => { .then((resp) => {
resp.id = id; resp.id = id;
resp.backend = backendPath; resp.backend = backendPath;
return resp; return resp;
}) })
.catch(e => { .catch((e) => {
if (e.httpStatus === 404) { if (e.httpStatus === 404) {
return resolve({ id }); return resolve({ id });
} else { } else {
@ -97,12 +99,12 @@ export default ApplicationAdapter.extend({
const url = `/v1/${backendPath}/config/crl`; const url = `/v1/${backendPath}/config/crl`;
const id = this.id(backendPath); const id = this.id(backendPath);
return this.ajax(url, 'GET') return this.ajax(url, 'GET')
.then(resp => { .then((resp) => {
resp.id = id; resp.id = id;
resp.backend = backendPath; resp.backend = backendPath;
return resp; return resp;
}) })
.catch(e => { .catch((e) => {
if (e.httpStatus === 404) { if (e.httpStatus === 404) {
return { id }; return { id };
} else { } else {
@ -118,7 +120,7 @@ export default ApplicationAdapter.extend({
queryRecord(store, type, query) { queryRecord(store, type, query) {
const { backend, section } = query; const { backend, section } = query;
return this.fetchSection(backend, section).then(resp => { return this.fetchSection(backend, section).then((resp) => {
resp.backend = backend; resp.backend = backend;
return resp; return resp;
}); });

View File

@ -13,7 +13,7 @@ export default ApplicationAdapter.extend({
const data = serializer.serialize(snapshot, requestType); const data = serializer.serialize(snapshot, requestType);
const role = snapshot.attr('role'); const role = snapshot.attr('role');
return this.ajax(this.url(role, snapshot), 'POST', { data }).then(response => { return this.ajax(this.url(role, snapshot), 'POST', { data }).then((response) => {
response.id = snapshot.id; response.id = snapshot.id;
response.modelName = type.modelName; response.modelName = type.modelName;
store.pushPayload(type.modelName, response); store.pushPayload(type.modelName, response);

View File

@ -7,7 +7,7 @@ export default ApplicationAdapter.extend({
fetchStatus(mode) { fetchStatus(mode) {
let url = this.getStatusUrl(mode); let url = this.getStatusUrl(mode);
return this.ajax(url, 'GET', { unauthenticated: true }).then(resp => { return this.ajax(url, 'GET', { unauthenticated: true }).then((resp) => {
return resp.data; return resp.data;
}); });
}, },

View File

@ -49,7 +49,7 @@ export default ApplicationAdapter.extend({
fetchByQuery(store, query) { fetchByQuery(store, query) {
const { id, backend } = query; const { id, backend } = query;
return this.ajax(this.urlForRole(backend, id), 'GET', this.optionsForQuery(id)).then(resp => { return this.ajax(this.urlForRole(backend, id), 'GET', this.optionsForQuery(id)).then((resp) => {
const data = { const data = {
id, id,
name: id, name: id,

View File

@ -49,7 +49,7 @@ export default ApplicationAdapter.extend({
fetchByQuery(store, query) { fetchByQuery(store, query) {
const { id, backend } = query; const { id, backend } = query;
return this.ajax(this.urlForRole(backend, id), 'GET', this.optionsForQuery(id)).then(resp => { return this.ajax(this.urlForRole(backend, id), 'GET', this.optionsForQuery(id)).then((resp) => {
const data = { const data = {
id, id,
name: id, name: id,

View File

@ -58,7 +58,7 @@ export default ApplicationAdapter.extend({
zeroAddressAjax = this.findAllZeroAddress(store, query); zeroAddressAjax = this.findAllZeroAddress(store, query);
} }
return allSettled([queryAjax, zeroAddressAjax]).then(results => { return allSettled([queryAjax, zeroAddressAjax]).then((results) => {
// query result 404d, so throw the adapterError // query result 404d, so throw the adapterError
if (!results[0].value) { if (!results[0].value) {
throw results[0].reason; throw results[0].reason;
@ -70,7 +70,7 @@ export default ApplicationAdapter.extend({
data: {}, data: {},
}; };
results.forEach(result => { results.forEach((result) => {
if (result.value) { if (result.value) {
if (result.value.data.roles) { if (result.value.data.roles) {
resp.data = assign({}, resp.data, { zero_address_roles: result.value.data.roles }); resp.data = assign({}, resp.data, { zero_address_roles: result.value.data.roles });

View File

@ -100,7 +100,7 @@ export default ApplicationAdapter.extend({
queryRecord(store, type, query) { queryRecord(store, type, query) {
if (query.type === 'aws') { if (query.type === 'aws') {
return this.ajax(`/v1/${encodePath(query.backend)}/config/lease`, 'GET').then(resp => { return this.ajax(`/v1/${encodePath(query.backend)}/config/lease`, 'GET').then((resp) => {
resp.path = query.backend + '/'; resp.path = query.backend + '/';
return resp; return resp;
}); });
@ -135,11 +135,7 @@ export default ApplicationAdapter.extend({
saveZeroAddressConfig(store, type, snapshot) { saveZeroAddressConfig(store, type, snapshot) {
const path = encodePath(snapshot.id); const path = encodePath(snapshot.id);
const roles = store const roles = store.peekAll('role-ssh').filterBy('zeroAddress').mapBy('id').join(',');
.peekAll('role-ssh')
.filterBy('zeroAddress')
.mapBy('id')
.join(',');
const url = `/v1/${path}/config/zeroaddress`; const url = `/v1/${path}/config/zeroaddress`;
const data = { roles }; const data = { roles };
if (roles === '') { if (roles === '') {

View File

@ -26,7 +26,7 @@ export default ApplicationAdapter.extend({
}, },
findRecord() { findRecord() {
return this._super(...arguments).catch(errorOrModel => { return this._super(...arguments).catch((errorOrModel) => {
// if the response is a real 404 or if the secret is gated by a control group this will be an error, // if the response is a real 404 or if the secret is gated by a control group this will be an error,
// otherwise the response will be the body of a deleted / destroyed version // otherwise the response will be the body of a deleted / destroyed version
if (errorOrModel instanceof AdapterError) { if (errorOrModel instanceof AdapterError) {
@ -44,7 +44,7 @@ export default ApplicationAdapter.extend({
}, },
queryRecord(id, options) { queryRecord(id, options) {
return this.ajax(this.urlForQueryRecord(id), 'GET', options).then(resp => { return this.ajax(this.urlForQueryRecord(id), 'GET', options).then((resp) => {
if (options.wrapTTL) { if (options.wrapTTL) {
return resp; return resp;
} }
@ -56,10 +56,10 @@ export default ApplicationAdapter.extend({
querySecretDataByVersion(id) { querySecretDataByVersion(id) {
return this.ajax(this.urlForQueryRecord(id), 'GET') return this.ajax(this.urlForQueryRecord(id), 'GET')
.then(resp => { .then((resp) => {
return resp.data; return resp.data;
}) })
.catch(error => { .catch((error) => {
return error.data; return error.data;
}); });
}, },
@ -73,7 +73,7 @@ export default ApplicationAdapter.extend({
createRecord(store, modelName, snapshot) { createRecord(store, modelName, snapshot) {
let backend = snapshot.belongsTo('secret').belongsTo('engine').id; let backend = snapshot.belongsTo('secret').belongsTo('engine').id;
let path = snapshot.attr('path'); let path = snapshot.attr('path');
return this._super(...arguments).then(resp => { return this._super(...arguments).then((resp) => {
resp.id = JSON.stringify([backend, path, resp.version]); resp.id = JSON.stringify([backend, path, resp.version]);
return resp; return resp;
}); });

View File

@ -56,7 +56,7 @@ export default ApplicationAdapter.extend({
fetchByQuery(query, action) { fetchByQuery(query, action) {
const { id, backend, wrapTTL } = query; const { id, backend, wrapTTL } = query;
return this.ajax(this.urlForSecret(backend, id), 'GET', this.optionsForQuery(id, action, wrapTTL)).then( return this.ajax(this.urlForSecret(backend, id), 'GET', this.optionsForQuery(id, action, wrapTTL)).then(
resp => { (resp) => {
if (wrapTTL) { if (wrapTTL) {
return resp; return resp;
} }

View File

@ -15,7 +15,7 @@ export default ApplicationAdapter.extend({
const data = serializer.serialize(snapshot, requestType); const data = serializer.serialize(snapshot, requestType);
const role = snapshot.attr('role'); const role = snapshot.attr('role');
return this.ajax(this.url(role), 'POST', { data }).then(response => { return this.ajax(this.url(role), 'POST', { data }).then((response) => {
response.id = snapshot.id; response.id = snapshot.id;
response.modelName = type.modelName; response.modelName = type.modelName;
store.pushPayload(type.modelName, response); store.pushPayload(type.modelName, response);

View File

@ -52,7 +52,7 @@ export default ApplicationAdapter.extend({
const { id, backend } = query; const { id, backend } = query;
const queryAjax = this.ajax(this.urlForTransformations(backend, id), 'GET', this.optionsForQuery(id)); const queryAjax = this.ajax(this.urlForTransformations(backend, id), 'GET', this.optionsForQuery(id));
return allSettled([queryAjax]).then(results => { return allSettled([queryAjax]).then((results) => {
// query result 404d, so throw the adapterError // query result 404d, so throw the adapterError
if (!results[0].value) { if (!results[0].value) {
throw results[0].reason; throw results[0].reason;
@ -64,7 +64,7 @@ export default ApplicationAdapter.extend({
data: {}, data: {},
}; };
results.forEach(result => { results.forEach((result) => {
if (result.value) { if (result.value) {
let d = result.value.data; let d = result.value.data;
if (d.templates) { if (d.templates) {

View File

@ -41,7 +41,7 @@ export default ApplicationAdapter.extend({
fetchByQuery(query) { fetchByQuery(query) {
const { backend, modelName, id } = query; const { backend, modelName, id } = query;
return this.ajax(this.url(backend, modelName, id), 'GET').then(resp => { return this.ajax(this.url(backend, modelName, id), 'GET').then((resp) => {
return { return {
...resp, ...resp,
backend, backend,
@ -54,7 +54,7 @@ export default ApplicationAdapter.extend({
}, },
queryRecord(store, type, query) { queryRecord(store, type, query) {
return this.ajax(this.url(query.backend, type.modelName, query.id), 'GET').then(result => { return this.ajax(this.url(query.backend, type.modelName, query.id), 'GET').then((result) => {
// CBS TODO: Add name to response and unmap name <> id on models // CBS TODO: Add name to response and unmap name <> id on models
return { return {
id: query.id, id: query.id,

View File

@ -14,7 +14,7 @@ export default ApplicationAdapter.extend({
url = url + '/config'; url = url + '/config';
} }
return this.ajax(url, 'POST', { data }).then(resp => { return this.ajax(url, 'POST', { data }).then((resp) => {
let response = resp || {}; let response = resp || {};
response.id = name; response.id = name;
return response; return response;
@ -86,7 +86,7 @@ export default ApplicationAdapter.extend({
fetchByQuery(query) { fetchByQuery(query) {
const { id, backend } = query; const { id, backend } = query;
return this.ajax(this.urlForSecret(backend, id), 'GET', this.optionsForQuery(id)).then(resp => { return this.ajax(this.urlForSecret(backend, id), 'GET', this.optionsForQuery(id)).then((resp) => {
resp.id = id; resp.id = id;
resp.backend = backend; resp.backend = backend;
return resp; return resp;

View File

@ -2,9 +2,7 @@ import Application from '@ember/application';
import Resolver from 'ember-resolver'; import Resolver from 'ember-resolver';
import loadInitializers from 'ember-load-initializers'; import loadInitializers from 'ember-load-initializers';
import config from 'vault/config/environment'; import config from 'vault/config/environment';
import defineModifier from 'ember-concurrency-test-waiter/define-modifier';
defineModifier();
export default class App extends Application { export default class App extends Application {
modulePrefix = config.modulePrefix; modulePrefix = config.modulePrefix;
podModulePrefix = config.podModulePrefix; podModulePrefix = config.podModulePrefix;

View File

@ -2,6 +2,7 @@ import AdapterError from '@ember-data/adapter/error';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Component from '@ember/component'; import Component from '@ember/component';
import { task } from 'ember-concurrency'; import { task } from 'ember-concurrency';
import { waitFor } from '@ember/test-waiters';
/** /**
* @module AuthConfigForm/Config * @module AuthConfigForm/Config
@ -23,23 +24,25 @@ const AuthConfigBase = Component.extend({
flashMessages: service(), flashMessages: service(),
router: service(), router: service(),
wizard: service(), wizard: service(),
saveModel: task(function*() { saveModel: task(
try { waitFor(function* () {
yield this.model.save(); try {
} catch (err) { yield this.model.save();
// AdapterErrors are handled by the error-message component } catch (err) {
// in the form // AdapterErrors are handled by the error-message component
if (err instanceof AdapterError === false) { // in the form
throw err; if (err instanceof AdapterError === false) {
throw err;
}
return;
} }
return; if (this.wizard.currentMachine === 'authentication' && this.wizard.featureState === 'config') {
} this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE');
if (this.wizard.currentMachine === 'authentication' && this.wizard.featureState === 'config') { }
this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE'); this.router.transitionTo('vault.cluster.access.methods').followRedirects();
} this.flashMessages.success('The configuration was saved successfully.');
this.router.transitionTo('vault.cluster.access.methods').followRedirects(); })
this.flashMessages.success('The configuration was saved successfully.'); ),
}).withTestWaiter(),
}); });
AuthConfigBase.reopenClass({ AuthConfigBase.reopenClass({

View File

@ -2,6 +2,7 @@ import AdapterError from '@ember-data/adapter/error';
import AuthConfigComponent from './config'; import AuthConfigComponent from './config';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { task } from 'ember-concurrency'; import { task } from 'ember-concurrency';
import { waitFor } from '@ember/test-waiters';
/** /**
* @module AuthConfigForm/Options * @module AuthConfigForm/Options
@ -19,36 +20,38 @@ import { task } from 'ember-concurrency';
export default AuthConfigComponent.extend({ export default AuthConfigComponent.extend({
router: service(), router: service(),
wizard: service(), wizard: service(),
saveModel: task(function*() { saveModel: task(
let data = this.model.config.serialize(); waitFor(function* () {
data.description = this.model.description; let data = this.model.config.serialize();
data.description = this.model.description;
// token_type should not be tuneable for the token auth method, default is 'default-service' // token_type should not be tuneable for the token auth method, default is 'default-service'
if (this.model.type === 'token') { if (this.model.type === 'token') {
delete data.token_type; delete data.token_type;
}
try {
yield this.model.tune(data);
} catch (err) {
// AdapterErrors are handled by the error-message component
// in the form
if (err instanceof AdapterError === false) {
throw err;
} }
// because we're not calling model.save the model never updates with
// the error. Forcing the error message by manually setting the errorMessage
try { try {
this.model.set('errorMessage', err.errors.firstObject); yield this.model.tune(data);
} catch { } catch (err) {
// do nothing // AdapterErrors are handled by the error-message component
// in the form
if (err instanceof AdapterError === false) {
throw err;
}
// because we're not calling model.save the model never updates with
// the error. Forcing the error message by manually setting the errorMessage
try {
this.model.set('errorMessage', err.errors.firstObject);
} catch {
// do nothing
}
return;
} }
return; if (this.wizard.currentMachine === 'authentication' && this.wizard.featureState === 'config') {
} this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE');
if (this.wizard.currentMachine === 'authentication' && this.wizard.featureState === 'config') { }
this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE'); this.router.transitionTo('vault.cluster.access.methods').followRedirects();
} this.flashMessages.success('The configuration was saved successfully.');
this.router.transitionTo('vault.cluster.access.methods').followRedirects(); })
this.flashMessages.success('The configuration was saved successfully.'); ),
}).withTestWaiter(),
}); });

View File

@ -8,6 +8,8 @@ import Component from '@ember/component';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { supportedAuthBackends } from 'vault/helpers/supported-auth-backends'; import { supportedAuthBackends } from 'vault/helpers/supported-auth-backends';
import { task, timeout } from 'ember-concurrency'; import { task, timeout } from 'ember-concurrency';
import { waitFor } from '@ember/test-waiters';
const BACKENDS = supportedAuthBackends(); const BACKENDS = supportedAuthBackends();
/** /**
@ -116,7 +118,7 @@ export default Component.extend(DEFAULTS, {
'methods.[]', 'methods.[]',
'selectedAuth', 'selectedAuth',
'selectedAuthIsPath', 'selectedAuthIsPath',
function() { function () {
let { wrappedToken, methods, selectedAuth, selectedAuthIsPath: keyIsPath } = this; let { wrappedToken, methods, selectedAuth, selectedAuthIsPath: keyIsPath } = this;
if (!methods && !wrappedToken) { if (!methods && !wrappedToken) {
return {}; return {};
@ -128,7 +130,7 @@ export default Component.extend(DEFAULTS, {
} }
), ),
providerName: computed('selectedAuthBackend.type', function() { providerName: computed('selectedAuthBackend.type', function () {
if (!this.selectedAuthBackend) { if (!this.selectedAuthBackend) {
return; return;
} }
@ -142,59 +144,65 @@ export default Component.extend(DEFAULTS, {
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.`, 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.`,
allSupportedMethods: computed('methodsToShow', 'hasMethodsWithPath', function() { allSupportedMethods: computed('methodsToShow', 'hasMethodsWithPath', function () {
let hasMethodsWithPath = this.hasMethodsWithPath; let hasMethodsWithPath = this.hasMethodsWithPath;
let methodsToShow = this.methodsToShow; let methodsToShow = this.methodsToShow;
return hasMethodsWithPath ? methodsToShow.concat(BACKENDS) : methodsToShow; return hasMethodsWithPath ? methodsToShow.concat(BACKENDS) : methodsToShow;
}), }),
hasMethodsWithPath: computed('methodsToShow', function() { hasMethodsWithPath: computed('methodsToShow', function () {
return this.methodsToShow.isAny('path'); return this.methodsToShow.isAny('path');
}), }),
methodsToShow: computed('methods', function() { methodsToShow: computed('methods', function () {
let methods = this.methods || []; let methods = this.methods || [];
let shownMethods = methods.filter(m => BACKENDS.find(b => b.type.toLowerCase() === m.type.toLowerCase())); let shownMethods = methods.filter((m) =>
BACKENDS.find((b) => b.type.toLowerCase() === m.type.toLowerCase())
);
return shownMethods.length ? shownMethods : BACKENDS; return shownMethods.length ? shownMethods : BACKENDS;
}), }),
unwrapToken: task(function*(token) { unwrapToken: task(
// will be using the Token Auth Method, so set it here waitFor(function* (token) {
this.set('selectedAuth', 'token'); // will be using the Token Auth Method, so set it here
let adapter = this.store.adapterFor('tools'); this.set('selectedAuth', 'token');
try { let adapter = this.store.adapterFor('tools');
let response = yield adapter.toolAction('unwrap', null, { clientToken: token }); try {
this.set('token', response.auth.client_token); let response = yield adapter.toolAction('unwrap', null, { clientToken: token });
this.send('doSubmit'); this.set('token', response.auth.client_token);
} catch (e) { this.send('doSubmit');
this.set('error', `Token unwrap failed: ${e.errors[0]}`); } catch (e) {
} this.set('error', `Token unwrap failed: ${e.errors[0]}`);
}).withTestWaiter(), }
})
),
fetchMethods: task(function*() { fetchMethods: task(
let store = this.store; waitFor(function* () {
try { let store = this.store;
let methods = yield store.findAll('auth-method', { try {
adapterOptions: { let methods = yield store.findAll('auth-method', {
unauthenticated: true, adapterOptions: {
}, unauthenticated: true,
}); },
this.set( });
'methods', this.set(
methods.map(m => { 'methods',
const method = m.serialize({ includeId: true }); methods.map((m) => {
return { const method = m.serialize({ includeId: true });
...method, return {
mountDescription: method.description, ...method,
}; mountDescription: method.description,
}) };
); })
next(() => { );
store.unloadAll('auth-method'); next(() => {
}); store.unloadAll('auth-method');
} catch (e) { });
this.set('error', `There was an error fetching Auth Methods: ${e.errors[0]}`); } catch (e) {
} this.set('error', `There was an error fetching Auth Methods: ${e.errors[0]}`);
}).withTestWaiter(), }
})
),
showLoading: or('isLoading', 'authenticate.isRunning', 'fetchMethods.isRunning', 'unwrapToken.isRunning'), showLoading: or('isLoading', 'authenticate.isRunning', 'fetchMethods.isRunning', 'unwrapToken.isRunning'),
@ -202,7 +210,7 @@ export default Component.extend(DEFAULTS, {
this.set('loading', false); this.set('loading', false);
let errors; let errors;
if (e.errors) { if (e.errors) {
errors = e.errors.map(error => { errors = e.errors.map((error) => {
if (error.detail) { if (error.detail) {
return error.detail; return error.detail;
} }
@ -215,40 +223,42 @@ export default Component.extend(DEFAULTS, {
this.set('error', `${message}${errors.join('.')}`); this.set('error', `${message}${errors.join('.')}`);
}, },
authenticate: task(function*(backendType, data) { authenticate: task(
let clusterId = this.cluster.id; waitFor(function* (backendType, data) {
try { let clusterId = this.cluster.id;
if (backendType === 'okta') { try {
this.delayAuthMessageReminder.perform(); if (backendType === 'okta') {
} this.delayAuthMessageReminder.perform();
let authResponse = yield this.auth.authenticate({ clusterId, backend: backendType, data });
let { isRoot, namespace } = authResponse;
let transition;
let { redirectTo } = this;
if (redirectTo) {
// reset the value on the controller because it's bound here
this.set('redirectTo', '');
// here we don't need the namespace because it will be encoded in redirectTo
transition = this.router.transitionTo(redirectTo);
} else {
transition = this.router.transitionTo('vault.cluster', { queryParams: { namespace } });
}
// returning this w/then because if we keep it
// in the task, it will get cancelled when the component in un-rendered
yield transition.followRedirects().then(() => {
if (isRoot) {
this.flashMessages.warning(
'You have logged in with a root token. As a security precaution, this root token will not be stored by your browser and you will need to re-authenticate after the window is closed or refreshed.'
);
} }
}); let authResponse = yield this.auth.authenticate({ clusterId, backend: backendType, data });
} catch (e) {
this.handleError(e);
}
}).withTestWaiter(),
delayAuthMessageReminder: task(function*() { let { isRoot, namespace } = authResponse;
let transition;
let { redirectTo } = this;
if (redirectTo) {
// reset the value on the controller because it's bound here
this.set('redirectTo', '');
// here we don't need the namespace because it will be encoded in redirectTo
transition = this.router.transitionTo(redirectTo);
} else {
transition = this.router.transitionTo('vault.cluster', { queryParams: { namespace } });
}
// returning this w/then because if we keep it
// in the task, it will get cancelled when the component in un-rendered
yield transition.followRedirects().then(() => {
if (isRoot) {
this.flashMessages.warning(
'You have logged in with a root token. As a security precaution, this root token will not be stored by your browser and you will need to re-authenticate after the window is closed or refreshed.'
);
}
});
} catch (e) {
this.handleError(e);
}
})
),
delayAuthMessageReminder: task(function* () {
if (Ember.testing) { if (Ember.testing) {
this.showLoading = true; this.showLoading = true;
yield timeout(0); yield timeout(0);
@ -274,7 +284,7 @@ export default Component.extend(DEFAULTS, {
}); });
let backend = this.selectedAuthBackend || {}; let backend = this.selectedAuthBackend || {};
let backendMeta = BACKENDS.find( let backendMeta = BACKENDS.find(
b => (b.type || '').toLowerCase() === (backend.type || '').toLowerCase() (b) => (b.type || '').toLowerCase() === (backend.type || '').toLowerCase()
); );
let attributes = (backendMeta || {}).formAttributes || []; let attributes = (backendMeta || {}).formAttributes || [];

View File

@ -4,9 +4,9 @@ import Component from './outer-html';
import { later } from '@ember/runloop'; import { later } from '@ember/runloop';
import { task, timeout, waitForEvent } from 'ember-concurrency'; import { task, timeout, waitForEvent } from 'ember-concurrency';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { waitFor } from '@ember/test-waiters';
/* eslint-disable ember/no-ember-testing-in-module-scope */ const WAIT_TIME = 500;
const WAIT_TIME = Ember.testing ? 0 : 500;
const ERROR_WINDOW_CLOSED = const ERROR_WINDOW_CLOSED =
'The provider window was closed before authentication was complete. Please click Sign In to try again.'; 'The provider window was closed before authentication was complete. Please click Sign In to try again.';
const ERROR_MISSING_PARAMS = const ERROR_MISSING_PARAMS =
@ -29,6 +29,7 @@ export default Component.extend({
onNamespace() {}, onNamespace() {},
didReceiveAttrs() { didReceiveAttrs() {
this._super();
let { oldSelectedAuthPath, selectedAuthPath } = this; let { oldSelectedAuthPath, selectedAuthPath } = this;
let shouldDebounce = !oldSelectedAuthPath && !selectedAuthPath; let shouldDebounce = !oldSelectedAuthPath && !selectedAuthPath;
if (oldSelectedAuthPath !== selectedAuthPath) { if (oldSelectedAuthPath !== selectedAuthPath) {
@ -44,7 +45,7 @@ export default Component.extend({
// Assumes authentication using OIDC until it's known that the mount is // Assumes authentication using OIDC until it's known that the mount is
// configured for JWT authentication via static keys, JWKS, or OIDC discovery. // configured for JWT authentication via static keys, JWKS, or OIDC discovery.
isOIDC: computed('errorMessage', function() { isOIDC: computed('errorMessage', function () {
return this.errorMessage !== ERROR_JWT_LOGIN; return this.errorMessage !== ERROR_JWT_LOGIN;
}), }),
@ -52,29 +53,30 @@ export default Component.extend({
return this.window || window; return this.window || window;
}, },
fetchRole: task(function*(roleName, options = { debounce: true }) { fetchRole: task(
if (options.debounce) { waitFor(function* (roleName, options = { debounce: true }) {
this.onRoleName(roleName); if (options.debounce) {
// debounce this.onRoleName(roleName);
yield timeout(WAIT_TIME); // debounce
} yield timeout(Ember.testing ? 0 : WAIT_TIME);
let path = this.selectedAuthPath || this.selectedAuthType;
let id = JSON.stringify([path, roleName]);
let role = null;
try {
role = yield this.store.findRecord('role-jwt', id, { adapterOptions: { namespace: this.namespace } });
} catch (e) {
if (!e.httpStatus || e.httpStatus !== 400) {
throw e;
} }
if (e.errors && e.errors.length > 0) { let path = this.selectedAuthPath || this.selectedAuthType;
this.set('errorMessage', e.errors[0]); let id = JSON.stringify([path, roleName]);
let role = null;
try {
role = yield this.store.findRecord('role-jwt', id, { adapterOptions: { namespace: this.namespace } });
} catch (e) {
// throwing here causes failures in tests
if ((!e.httpStatus || e.httpStatus !== 400) && !Ember.testing) {
throw e;
}
if (e.errors && e.errors.length > 0) {
this.set('errorMessage', e.errors[0]);
}
} }
} this.set('role', role);
this.set('role', role); })
}) ).restartable(),
.restartable()
.withTestWaiter(),
handleOIDCError(err) { handleOIDCError(err) {
this.onLoading(false); this.onLoading(false);
@ -82,7 +84,7 @@ export default Component.extend({
this.onError(err); this.onError(err);
}, },
prepareForOIDC: task(function*(oidcWindow) { prepareForOIDC: task(function* (oidcWindow) {
const thisWindow = this.getWindow(); const thisWindow = this.getWindow();
// show the loading animation in the parent // show the loading animation in the parent
this.onLoading(true); this.onLoading(true);
@ -104,7 +106,7 @@ export default Component.extend({
} }
}), }),
watchPopup: task(function*(oidcWindow) { watchPopup: task(function* (oidcWindow) {
while (true) { while (true) {
yield timeout(WAIT_TIME); yield timeout(WAIT_TIME);
if (!oidcWindow || oidcWindow.closed) { if (!oidcWindow || oidcWindow.closed) {
@ -113,7 +115,7 @@ export default Component.extend({
} }
}), }),
watchCurrent: task(function*(oidcWindow) { watchCurrent: task(function* (oidcWindow) {
// when user is about to change pages, close the popup window // when user is about to change pages, close the popup window
yield waitForEvent(this.getWindow(), 'beforeunload'); yield waitForEvent(this.getWindow(), 'beforeunload');
oidcWindow.close(); oidcWindow.close();
@ -125,7 +127,7 @@ export default Component.extend({
oidcWindow.close(); oidcWindow.close();
}, },
exchangeOIDC: task(function*(oidcState, oidcWindow) { exchangeOIDC: task(function* (oidcState, oidcWindow) {
if (oidcState === null || oidcState === undefined) { if (oidcState === null || oidcState === undefined) {
return; return;
} }

View File

@ -82,7 +82,7 @@ export default Component.extend({
* @private * @private
* @type boolean * @type boolean
*/ */
valuesMatch: computed('value', '_value', function() { valuesMatch: computed('value', '_value', function () {
const { value, _value } = this; const { value, _value } = this;
const anyBlank = isBlank(value) || isBlank(_value); const anyBlank = isBlank(value) || isBlank(_value);
return !anyBlank && value === _value; return !anyBlank && value === _value;
@ -99,6 +99,7 @@ export default Component.extend({
}, },
didReceiveAttrs() { didReceiveAttrs() {
this._super();
// if there's no value, reset encoding // if there's no value, reset encoding
if (this.value === '') { if (this.value === '') {
set(this, 'currentEncoding', UTF8); set(this, 'currentEncoding', UTF8);

View File

@ -50,7 +50,7 @@ export default class ConfigComponent extends Component {
return content; return content;
} }
@(task(function*() { @(task(function* () {
try { try {
yield this.args.model.save(); yield this.args.model.save();
} catch (err) { } catch (err) {

View File

@ -34,7 +34,7 @@ export default class HistoryComponent extends Component {
return null; return null;
} }
let dataList = this.args.model.activity.byNamespace; let dataList = this.args.model.activity.byNamespace;
return dataList.map(d => { return dataList.map((d) => {
return { return {
name: d['namespace_id'], name: d['namespace_id'],
id: d['namespace_path'] === '' ? 'root' : d['namespace_path'], id: d['namespace_path'] === '' ? 'root' : d['namespace_path'],
@ -48,7 +48,7 @@ export default class HistoryComponent extends Component {
return null; return null;
} }
let dataset = this.args.model.activity.byNamespace.slice(0, this.max_namespaces); let dataset = this.args.model.activity.byNamespace.slice(0, this.max_namespaces);
return dataset.map(d => { return dataset.map((d) => {
return { return {
label: d['namespace_path'] === '' ? 'root' : d['namespace_path'], label: d['namespace_path'] === '' ? 'root' : d['namespace_path'],
non_entity_tokens: d['counts']['non_entity_tokens'], non_entity_tokens: d['counts']['non_entity_tokens'],
@ -69,7 +69,7 @@ export default class HistoryComponent extends Component {
results = fields.join(',') + '\n'; results = fields.join(',') + '\n';
namespaces.forEach(function(item) { namespaces.forEach(function (item) {
let path = item.namespace_path !== '' ? item.namespace_path : 'root', let path = item.namespace_path !== '' ? item.namespace_path : 'root',
total = item.counts.clients, total = item.counts.clients,
unique = item.counts.distinct_entities, unique = item.counts.distinct_entities,
@ -95,7 +95,7 @@ export default class HistoryComponent extends Component {
// Get the namespace by matching the path from the namespace list // Get the namespace by matching the path from the namespace list
getNamespace(path) { getNamespace(path) {
return this.args.model.activity.byNamespace.find(ns => { return this.args.model.activity.byNamespace.find((ns) => {
if (path === 'root') { if (path === 'root') {
return ns.namespace_path === ''; return ns.namespace_path === '';
} }

View File

@ -100,7 +100,7 @@ export default Component.extend({
* This array provides the text and download hrefs for those links. * This array provides the text and download hrefs for those links.
* *
*/ */
downloadHrefs: computed('config', 'config.{backend,pem,caChain,der}', function() { downloadHrefs: computed('config', 'config.{backend,pem,caChain,der}', function () {
const config = this.config; const config = this.config;
const { backend, pem, caChain, der } = config; const { backend, pem, caChain, der } = config;
@ -139,7 +139,7 @@ export default Component.extend({
const isUpload = this.model.uploadPemBundle; const isUpload = this.model.uploadPemBundle;
model model
.save({ adapterOptions: { method } }) .save({ adapterOptions: { method } })
.then(m => { .then((m) => {
if (method === 'setSignedIntermediate' || isUpload) { if (method === 'setSignedIntermediate' || isUpload) {
this.send('refresh'); this.send('refresh');
this.flashMessages.success('The certificate for this backend has been updated.'); this.flashMessages.success('The certificate for this backend has been updated.');

View File

@ -44,7 +44,7 @@ export default Component.extend({
.save({ .save({
adapterOptions: { adapterOptions: {
method: section, method: section,
fields: get(config, `${section}Attrs`).map(attr => attr.name), fields: get(config, `${section}Attrs`).map((attr) => attr.name),
}, },
}) })
.then(() => { .then(() => {

View File

@ -1,3 +0,0 @@
import Component from '@ember/component';
export default Component.extend({});

View File

@ -1,3 +0,0 @@
import Component from '@ember/component';
export default Component.extend({});

View File

@ -1,3 +0,0 @@
import Component from '@ember/component';
export default Component.extend({});

View File

@ -4,7 +4,7 @@ import { computed } from '@ember/object';
import columnify from 'columnify'; import columnify from 'columnify';
export function stringifyObjectValues(data) { export function stringifyObjectValues(data) {
Object.keys(data).forEach(item => { Object.keys(data).forEach((item) => {
let val = data[item]; let val = data[item];
if (typeof val !== 'string') { if (typeof val !== 'string') {
val = JSON.stringify(val); val = JSON.stringify(val);
@ -15,13 +15,13 @@ export function stringifyObjectValues(data) {
export default Component.extend({ export default Component.extend({
content: null, content: null,
columns: computed('content', function() { columns: computed('content', function () {
let data = this.content; let data = this.content;
stringifyObjectValues(data); stringifyObjectValues(data);
return columnify(data, { return columnify(data, {
preserveNewLines: true, preserveNewLines: true,
headingTransform: function(heading) { headingTransform: function (heading) {
return capitalize(heading); return capitalize(heading);
}, },
}); });

View File

@ -1,3 +0,0 @@
import Component from '@ember/component';
export default Component.extend({});

View File

@ -39,13 +39,13 @@ export default Component.extend({
isRunning: or('executeCommand.isRunning', 'refreshRoute.isRunning'), isRunning: or('executeCommand.isRunning', 'refreshRoute.isRunning'),
executeCommand: task(function*(command, shouldThrow = false) { executeCommand: task(function* (command, shouldThrow = false) {
this.set('inputValue', ''); this.set('inputValue', '');
let service = this.console; let service = this.console;
let serviceArgs; let serviceArgs;
if ( if (
executeUICommand(command, args => this.logAndOutput(args), { executeUICommand(command, (args) => this.logAndOutput(args), {
api: () => this.routeToExplore.perform(command), api: () => this.routeToExplore.perform(command),
clearall: () => service.clearLog(true), clearall: () => service.clearLog(true),
clear: () => service.clearLog(), clear: () => service.clearLog(),
@ -90,7 +90,7 @@ export default Component.extend({
} }
}), }),
refreshRoute: task(function*() { refreshRoute: task(function* () {
let owner = getOwner(this); let owner = getOwner(this);
let currentRoute = owner.lookup(`router:main`).get('currentRouteName'); let currentRoute = owner.lookup(`router:main`).get('currentRouteName');
@ -103,7 +103,7 @@ export default Component.extend({
} }
}), }),
routeToExplore: task(function*(command) { routeToExplore: task(function* (command) {
let filter = command.replace('api', '').trim(); let filter = command.replace('api', '').trim();
let content = let content =
'Welcome to the Vault API explorer! \nYou can search for endpoints, see what parameters they accept, and even execute requests with your current token.'; 'Welcome to the Vault API explorer! \nYou can search for endpoints, see what parameters they accept, and even execute requests with your current token.';
@ -134,7 +134,7 @@ export default Component.extend({
}), }),
shiftCommandIndex(keyCode) { shiftCommandIndex(keyCode) {
this.console.shiftCommandIndex(keyCode, val => { this.console.shiftCommandIndex(keyCode, (val) => {
this.set('inputValue', val); this.set('inputValue', val);
}); });
}, },

View File

@ -15,7 +15,7 @@ export default Component.extend({
error: null, error: null,
unwrapData: null, unwrapData: null,
unwrap: task(function*(token) { unwrap: task(function* (token) {
let adapter = this.store.adapterFor('tools'); let adapter = this.store.adapterFor('tools');
this.set('error', null); this.set('error', null);
try { try {
@ -27,7 +27,7 @@ export default Component.extend({
} }
}).drop(), }).drop(),
markAndNavigate: task(function*() { markAndNavigate: task(function* () {
this.controlGroup.markTokenForUnwrap(this.model.id); this.controlGroup.markTokenForUnwrap(this.model.id);
let { url } = this.controlGroupResponse.uiParams; let { url } = this.controlGroupResponse.uiParams;
yield this.router.transitionTo(url); yield this.router.transitionTo(url);

View File

@ -21,18 +21,18 @@ export default Component.extend({
currentUserEntityId: alias('auth.authData.entity_id'), currentUserEntityId: alias('auth.authData.entity_id'),
currentUserIsRequesting: computed('currentUserEntityId', 'model.requestEntity.id', function() { currentUserIsRequesting: computed('currentUserEntityId', 'model.requestEntity.id', function () {
if (!this.model.requestEntity) return false; if (!this.model.requestEntity) return false;
return this.currentUserEntityId === this.model.requestEntity.id; return this.currentUserEntityId === this.model.requestEntity.id;
}), }),
currentUserHasAuthorized: computed('currentUserEntityId', 'model.authorizations.@each.id', function() { currentUserHasAuthorized: computed('currentUserEntityId', 'model.authorizations.@each.id', function () {
let authorizations = this.model.authorizations || []; let authorizations = this.model.authorizations || [];
return Boolean(authorizations.findBy('id', this.currentUserEntityId)); return Boolean(authorizations.findBy('id', this.currentUserEntityId));
}), }),
isSuccess: or('currentUserHasAuthorized', 'model.approved'), isSuccess: or('currentUserHasAuthorized', 'model.approved'),
requestorName: computed('currentUserIsRequesting', 'model.requestEntity', function() { requestorName: computed('currentUserIsRequesting', 'model.requestEntity', function () {
let entity = this.model.requestEntity; let entity = this.model.requestEntity;
if (this.currentUserIsRequesting) { if (this.currentUserIsRequesting) {
@ -44,7 +44,7 @@ export default Component.extend({
return 'Someone'; return 'Someone';
}), }),
bannerPrefix: computed('model.approved', 'currentUserHasAuthorized', function() { bannerPrefix: computed('model.approved', 'currentUserHasAuthorized', function () {
if (this.currentUserHasAuthorized) { if (this.currentUserHasAuthorized) {
return 'Thanks!'; return 'Thanks!';
} }
@ -54,7 +54,7 @@ export default Component.extend({
return 'Locked'; return 'Locked';
}), }),
bannerText: computed('model.approved', 'currentUserIsRequesting', 'currentUserHasAuthorized', function() { bannerText: computed('model.approved', 'currentUserIsRequesting', 'currentUserHasAuthorized', function () {
let isApproved = this.model.approved; let isApproved = this.model.approved;
let { currentUserHasAuthorized, currentUserIsRequesting } = this; let { currentUserHasAuthorized, currentUserIsRequesting } = this;
if (currentUserHasAuthorized) { if (currentUserHasAuthorized) {
@ -72,7 +72,7 @@ export default Component.extend({
return 'Someone is requesting access to a path locked by a Control Group'; return 'Someone is requesting access to a path locked by a Control Group';
}), }),
refresh: task(function*() { refresh: task(function* () {
try { try {
yield this.model.reload(); yield this.model.reload();
} catch (e) { } catch (e) {
@ -80,7 +80,7 @@ export default Component.extend({
} }
}).drop(), }).drop(),
authorize: task(function*() { authorize: task(function* () {
try { try {
yield this.model.save(); yield this.model.save();
yield this.refresh.perform(); yield this.refresh.perform();

View File

@ -6,7 +6,7 @@ import { action } from '@ember/object';
const LIST_ROOT_ROUTE = 'vault.cluster.secrets.backend.list-root'; const LIST_ROOT_ROUTE = 'vault.cluster.secrets.backend.list-root';
const SHOW_ROUTE = 'vault.cluster.secrets.backend.show'; const SHOW_ROUTE = 'vault.cluster.secrets.backend.show';
const getErrorMessage = errors => { const getErrorMessage = (errors) => {
let errorMessage = errors?.join('. ') || 'Something went wrong. Check the Vault logs for more information.'; let errorMessage = errors?.join('. ') || 'Something went wrong. Check the Vault logs for more information.';
if (errorMessage.indexOf('failed to verify') >= 0) { if (errorMessage.indexOf('failed to verify') >= 0) {
errorMessage = errorMessage =
@ -69,7 +69,7 @@ export default class DatabaseConnectionEdit extends Component {
.then(() => { .then(() => {
this.showSaveModal = true; this.showSaveModal = true;
}) })
.catch(e => { .catch((e) => {
const errorMessage = getErrorMessage(e.errors); const errorMessage = getErrorMessage(e.errors);
this.flashMessages.danger(errorMessage); this.flashMessages.danger(errorMessage);
}); });
@ -91,7 +91,7 @@ export default class DatabaseConnectionEdit extends Component {
this.flashMessages.success(`Successfully rotated root credentials for connection "${name}"`); this.flashMessages.success(`Successfully rotated root credentials for connection "${name}"`);
this.transitionToRoute(SHOW_ROUTE, name); this.transitionToRoute(SHOW_ROUTE, name);
}) })
.catch(e => { .catch((e) => {
this.flashMessages.danger(`Error rotating root credentials: ${e.errors}`); this.flashMessages.danger(`Error rotating root credentials: ${e.errors}`);
this.transitionToRoute(SHOW_ROUTE, name); this.transitionToRoute(SHOW_ROUTE, name);
}); });
@ -107,7 +107,7 @@ export default class DatabaseConnectionEdit extends Component {
.then(() => { .then(() => {
this.transitionToRoute(SHOW_ROUTE, secretId); this.transitionToRoute(SHOW_ROUTE, secretId);
}) })
.catch(e => { .catch((e) => {
const errorMessage = getErrorMessage(e.errors); const errorMessage = getErrorMessage(e.errors);
this.flashMessages.danger(errorMessage); this.flashMessages.danger(errorMessage);
}); });
@ -133,7 +133,7 @@ export default class DatabaseConnectionEdit extends Component {
// TODO: Why isn't the confirmAction closing? // TODO: Why isn't the confirmAction closing?
this.flashMessages.success('Successfully reset connection'); this.flashMessages.success('Successfully reset connection');
}) })
.catch(e => { .catch((e) => {
const errorMessage = getErrorMessage(e.errors); const errorMessage = getErrorMessage(e.errors);
this.flashMessages.danger(errorMessage); this.flashMessages.danger(errorMessage);
}); });
@ -147,7 +147,7 @@ export default class DatabaseConnectionEdit extends Component {
// TODO: Why isn't the confirmAction closing? // TODO: Why isn't the confirmAction closing?
this.flashMessages.success('Successfully rotated credentials'); this.flashMessages.success('Successfully rotated credentials');
}) })
.catch(e => { .catch((e) => {
const errorMessage = getErrorMessage(e.errors); const errorMessage = getErrorMessage(e.errors);
this.flashMessages.danger(errorMessage); this.flashMessages.danger(errorMessage);
}); });

View File

@ -49,7 +49,7 @@ export default class DatabaseRoleEdit extends Component {
} }
return this.store return this.store
.queryRecord('database/connection', { id: dbs[0], backend }) .queryRecord('database/connection', { id: dbs[0], backend })
.then(record => record.plugin_name) .then((record) => record.plugin_name)
.catch(() => null); .catch(() => null);
} }
@ -73,7 +73,7 @@ export default class DatabaseRoleEdit extends Component {
console.debug(e); console.debug(e);
} }
}) })
.catch(e => { .catch((e) => {
this.flashMessages.danger(e.errors?.join('. ')); this.flashMessages.danger(e.errors?.join('. '));
}); });
} }
@ -100,7 +100,7 @@ export default class DatabaseRoleEdit extends Component {
console.debug(e); console.debug(e);
} }
}) })
.catch(e => { .catch((e) => {
const errorMessage = e.errors?.join('. ') || e.message; const errorMessage = e.errors?.join('. ') || e.message;
this.flashMessages.danger( this.flashMessages.danger(
errorMessage || 'Could not save the role. Please check Vault logs for more information.' errorMessage || 'Could not save the role. Please check Vault logs for more information.'

View File

@ -20,7 +20,7 @@ export default class DatabaseRoleSettingForm extends Component {
get settingFields() { get settingFields() {
if (!this.args.roleType) return null; if (!this.args.roleType) return null;
let dbValidFields = getRoleFields(this.args.roleType); let dbValidFields = getRoleFields(this.args.roleType);
return this.args.attrs.filter(a => { return this.args.attrs.filter((a) => {
return dbValidFields.includes(a.name); return dbValidFields.includes(a.name);
}); });
} }
@ -30,7 +30,7 @@ export default class DatabaseRoleSettingForm extends Component {
const plugin = this.args.dbType; const plugin = this.args.dbType;
if (!type) return null; if (!type) return null;
let dbValidFields = getStatementFields(type, plugin); let dbValidFields = getStatementFields(type, plugin);
return this.args.attrs.filter(a => { return this.args.attrs.filter((a) => {
return dbValidFields.includes(a.name); return dbValidFields.includes(a.name);
}); });
} }

View File

@ -35,14 +35,14 @@ export default class DiffVersionSelector extends Component {
let string = `["${this.args.model.engineId}", "${this.args.model.id}", "${this.args.model.currentVersion}"]`; let string = `["${this.args.model.engineId}", "${this.args.model.id}", "${this.args.model.currentVersion}"]`;
return this.adapter return this.adapter
.querySecretDataByVersion(string) .querySecretDataByVersion(string)
.then(response => response.data) .then((response) => response.data)
.catch(() => null); .catch(() => null);
} }
get rightSideDataInit() { get rightSideDataInit() {
let string = `["${this.args.model.engineId}", "${this.args.model.id}", "${this.rightSideVersionInit}"]`; let string = `["${this.args.model.engineId}", "${this.args.model.id}", "${this.rightSideVersionInit}"]`;
return this.adapter return this.adapter
.querySecretDataByVersion(string) .querySecretDataByVersion(string)
.then(response => response.data) .then((response) => response.data)
.catch(() => null); .catch(() => null);
} }
get rightSideVersionInit() { get rightSideVersionInit() {

View File

@ -1,15 +0,0 @@
/**
* @module FormFieldGroupsLoop
* FormFieldGroupsLoop components are used to show optional form fields, generally when setting up a secret engine.
*
* @example
* ```js
* <FormFieldGroupsLoop @model={{model}} @mode={{mode}}/>
* ```
* @param {object} model - the data model of the parent component
* @param {object} model - the mode: create show or edit.
*/
import Component from '@glimmer/component';
export default class FormFieldGroupsLoop extends Component {}

View File

@ -48,7 +48,7 @@ export default Component.extend({
this.router.transitionTo('vault.cluster.secrets.backend.list-root', this.backendPath); this.router.transitionTo('vault.cluster.secrets.backend.list-root', this.backendPath);
}, },
options: computed('action', 'backendType', function() { options: computed('action', 'backendType', function () {
const action = this.action || 'creds'; const action = this.action || 'creds';
return MODEL_TYPES[`${this.backendType}-${action}`]; return MODEL_TYPES[`${this.backendType}-${action}`];
}), }),
@ -59,6 +59,7 @@ export default Component.extend({
}, },
didReceiveAttrs() { didReceiveAttrs() {
this._super();
if (this.wizard.featureState === 'displayRole') { if (this.wizard.featureState === 'displayRole') {
this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', this.backendType); this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', this.backendType);
} }

View File

@ -3,6 +3,7 @@ import { inject as service } from '@ember/service';
import Component from '@ember/component'; import Component from '@ember/component';
import { computed, set } from '@ember/object'; import { computed, set } from '@ember/object';
import { task } from 'ember-concurrency'; import { task } from 'ember-concurrency';
import { waitFor } from '@ember/test-waiters';
/** /**
* @module GeneratedItem * @module GeneratedItem
@ -26,32 +27,34 @@ export default Component.extend({
router: service(), router: service(),
validationMessages: null, validationMessages: null,
isFormInvalid: false, isFormInvalid: false,
props: computed('model', function() { props: computed('model', function () {
return this.model.serialize(); return this.model.serialize();
}), }),
saveModel: task(function*() { saveModel: task(
try { waitFor(function* () {
yield this.model.save(); try {
} catch (err) { yield this.model.save();
// AdapterErrors are handled by the error-message component } catch (err) {
// in the form // AdapterErrors are handled by the error-message component
if (err instanceof AdapterError === false) { // in the form
throw err; if (err instanceof AdapterError === false) {
throw err;
}
return;
} }
return; this.router.transitionTo('vault.cluster.access.method.item.list').followRedirects();
} this.flashMessages.success(`Successfully saved ${this.itemType} ${this.model.id}.`);
this.router.transitionTo('vault.cluster.access.method.item.list').followRedirects(); })
this.flashMessages.success(`Successfully saved ${this.itemType} ${this.model.id}.`); ),
}).withTestWaiter(),
init() { init() {
this._super(...arguments); this._super(...arguments);
this.set('validationMessages', {}); this.set('validationMessages', {});
if (this.mode === 'edit') { if (this.mode === 'edit') {
// For validation to work in edit mode, // For validation to work in edit mode,
// reconstruct the model values from field group // reconstruct the model values from field group
this.model.fieldGroups.forEach(element => { this.model.fieldGroups.forEach((element) => {
if (element.default) { if (element.default) {
element.default.forEach(attr => { element.default.forEach((attr) => {
let fieldValue = attr.options && attr.options.fieldValue; let fieldValue = attr.options && attr.options.fieldValue;
if (fieldValue) { if (fieldValue) {
this.model[attr.name] = this.model[fieldValue]; this.model[attr.name] = this.model[fieldValue];

View File

@ -19,11 +19,11 @@ import { computed } from '@ember/object';
export default Component.extend({ export default Component.extend({
tagName: '', tagName: '',
text: computed(function() { text: computed(function () {
return 'home'; return 'home';
}), }),
computedClasses: computed('classNames', function() { computedClasses: computed('classNames', function () {
return this.classNames.join(' '); return this.classNames.join(' ');
}), }),
}); });

View File

@ -32,7 +32,7 @@ export default Component.extend({
this.onSuccess(); this.onSuccess();
this.flashMessages.success(this.successMessage(...messageArgs)); this.flashMessages.success(this.successMessage(...messageArgs));
}) })
.catch(e => { .catch((e) => {
this.onError(...messageArgs); this.onError(...messageArgs);
this.flashMessages.success(this.errorMessage(e, ...messageArgs)); this.flashMessages.success(this.errorMessage(e, ...messageArgs));
}); });

View File

@ -3,6 +3,7 @@ import Component from '@ember/component';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { task } from 'ember-concurrency'; import { task } from 'ember-concurrency';
import { humanize } from 'vault/helpers/humanize'; import { humanize } from 'vault/helpers/humanize';
import { waitFor } from '@ember/test-waiters';
export default Component.extend({ export default Component.extend({
flashMessages: service(), flashMessages: service(),
@ -19,7 +20,7 @@ export default Component.extend({
*/ */
onSave: () => {}, onSave: () => {},
cancelLink: computed('mode', 'model.identityType', function() { cancelLink: computed('mode', 'model.identityType', function () {
let { model, mode } = this; let { model, mode } = this;
let routes = { let routes = {
'create-entity': 'vault.cluster.access.identity', 'create-entity': 'vault.cluster.access.identity',
@ -49,23 +50,24 @@ export default Component.extend({
return `Successfully ${action} ${typeDisplay}.`; return `Successfully ${action} ${typeDisplay}.`;
}, },
save: task(function*() { save: task(
let model = this.model; waitFor(function* () {
let message = this.getMessage(model); let model = this.model;
let message = this.getMessage(model);
try { try {
yield model.save(); yield model.save();
} catch (err) { } catch (err) {
// err will display via model state // err will display via model state
return; return;
} }
this.flashMessages.success(message); this.flashMessages.success(message);
yield this.onSave({ saveType: 'save', model }); yield this.onSave({ saveType: 'save', model });
}) })
.drop() ).drop(),
.withTestWaiter(),
willDestroy() { willDestroy() {
this._super(...arguments);
let model = this.model; let model = this.model;
if (!model) return; if (!model) return;
if ((model.get('isDirty') && !model.isDestroyed) || !model.isDestroying) { if ((model.get('isDirty') && !model.isDestroyed) || !model.isDestroying) {

View File

@ -13,7 +13,7 @@ export default Component.extend({
.then(() => { .then(() => {
this.flashMessages.success(`Successfully enabled entity: ${model.id}`); this.flashMessages.success(`Successfully enabled entity: ${model.id}`);
}) })
.catch(e => { .catch((e) => {
this.flashMessages.success( this.flashMessages.success(
`There was a problem enabling the entity: ${model.id} - ${e.errors.join(' ') || e.message}` `There was a problem enabling the entity: ${model.id} - ${e.errors.join(' ') || e.message}`
); );

View File

@ -20,7 +20,7 @@ export default Component.extend({
init() { init() {
this._super(...arguments); this._super(...arguments);
this.store.findAll('auth-method').then(methods => { this.store.findAll('auth-method').then((methods) => {
this.set('authMethods', methods); this.set('authMethods', methods);
this.set('aliasMountAccessor', methods.get('firstObject.accessor')); this.set('aliasMountAccessor', methods.get('firstObject.accessor'));
}); });
@ -43,7 +43,7 @@ export default Component.extend({
return data; return data;
}, },
lookup: task(function*() { lookup: task(function* () {
let flash = this.flashMessages; let flash = this.flashMessages;
let type = this.type; let type = this.type;
let store = this.store; let store = this.store;

View File

@ -5,11 +5,11 @@ import Base from './_popup-base';
export default Base.extend({ export default Base.extend({
model: alias('params.firstObject'), model: alias('params.firstObject'),
groupArray: computed('params', function() { groupArray: computed('params', function () {
return this.params.objectAt(1); return this.params.objectAt(1);
}), }),
memberId: computed('params', function() { memberId: computed('params', function () {
return this.params.objectAt(2); return this.params.objectAt(2);
}), }),

View File

@ -4,7 +4,7 @@ import { alias } from '@ember/object/computed';
export default Base.extend({ export default Base.extend({
model: alias('params.firstObject'), model: alias('params.firstObject'),
key: computed('params', function() { key: computed('params', function () {
return this.params.objectAt(1); return this.params.objectAt(1);
}), }),

View File

@ -4,7 +4,7 @@ import Base from './_popup-base';
export default Base.extend({ export default Base.extend({
model: alias('params.firstObject'), model: alias('params.firstObject'),
policyName: computed('params', function() { policyName: computed('params', function () {
return this.params.objectAt(1); return this.params.objectAt(1);
}), }),

View File

@ -51,15 +51,15 @@ export default Component.extend({
kvData: null, kvData: null,
kvDataAsJSON: computed('kvData', 'kvData.[]', function() { kvDataAsJSON: computed('kvData', 'kvData.[]', function () {
return this.kvData.toJSON(); return this.kvData.toJSON();
}), }),
kvDataIsAdvanced: computed('kvData', 'kvData.[]', function() { kvDataIsAdvanced: computed('kvData', 'kvData.[]', function () {
return this.kvData.isAdvanced(); return this.kvData.isAdvanced();
}), }),
kvHasDuplicateKeys: computed('kvData', 'kvData.@each.name', function() { kvHasDuplicateKeys: computed('kvData', 'kvData.@each.name', function () {
let data = this.kvData; let data = this.kvData;
return data.uniqBy('name').length !== data.get('length'); return data.uniqBy('name').length !== data.get('length');
}), }),

View File

@ -23,7 +23,7 @@ import { allFeatures } from 'vault/helpers/all-features';
*/ */
export default class LicenseInfoComponent extends Component { export default class LicenseInfoComponent extends Component {
get featuresInfo() { get featuresInfo() {
return allFeatures().map(feature => { return allFeatures().map((feature) => {
let active = this.args.features.includes(feature); let active = this.args.features.includes(feature);
if (active && feature === 'Performance Standby') { if (active && feature === 'Performance Standby') {
let count = this.args.performanceStandbyCount; let count = this.args.performanceStandbyCount;

View File

@ -15,7 +15,7 @@ export default Component.extend({
this.authMethods.perform(); this.authMethods.perform();
}, },
authMethods: task(function*() { authMethods: task(function* () {
let methods = yield this.store.findAll('auth-method'); let methods = yield this.store.findAll('auth-method');
if (!this.value) { if (!this.value) {
this.set('value', methods.get('firstObject.accessor')); this.set('value', methods.get('firstObject.accessor'));

View File

@ -5,6 +5,7 @@ import Component from '@ember/component';
import { task } from 'ember-concurrency'; import { task } from 'ember-concurrency';
import { methods } from 'vault/helpers/mountable-auth-methods'; import { methods } from 'vault/helpers/mountable-auth-methods';
import { engines, KMIP, TRANSFORM } from 'vault/helpers/mountable-secret-engines'; import { engines, KMIP, TRANSFORM } from 'vault/helpers/mountable-secret-engines';
import { waitFor } from '@ember/test-waiters';
const METHODS = methods(); const METHODS = methods();
const ENGINES = engines(); const ENGINES = engines();
@ -62,11 +63,11 @@ export default Component.extend({
}); });
}, },
mountTypes: computed('engines', 'mountType', function() { mountTypes: computed('engines', 'mountType', function () {
return this.mountType === 'secret' ? this.engines : METHODS; return this.mountType === 'secret' ? this.engines : METHODS;
}), }),
engines: computed('version.{features[],isEnterprise}', function() { engines: computed('version.{features[],isEnterprise}', function () {
if (this.version.isEnterprise) { if (this.version.isEnterprise) {
return ENGINES.concat([KMIP, TRANSFORM]); return ENGINES.concat([KMIP, TRANSFORM]);
} }
@ -74,6 +75,7 @@ export default Component.extend({
}), }),
willDestroy() { willDestroy() {
this._super(...arguments);
// if unsaved, we want to unload so it doesn't show up in the auth mount list // if unsaved, we want to unload so it doesn't show up in the auth mount list
this.mountModel.rollbackAttributes(); this.mountModel.rollbackAttributes();
}, },
@ -90,60 +92,60 @@ export default Component.extend({
} }
}, },
mountBackend: task(function*() { mountBackend: task(
const mountModel = this.mountModel; waitFor(function* () {
const { type, path } = mountModel; const mountModel = this.mountModel;
let capabilities = null; const { type, path } = mountModel;
try { let capabilities = null;
capabilities = yield this.store.findRecord('capabilities', `${path}/config`); try {
} catch (err) { capabilities = yield this.store.findRecord('capabilities', `${path}/config`);
if (Ember.testing) { } catch (err) {
//captures mount-backend-form component test if (Ember.testing) {
yield mountModel.save(); //captures mount-backend-form component test
let mountType = this.mountType; yield mountModel.save();
mountType = mountType === 'secret' ? `${mountType}s engine` : `${mountType} method`; let mountType = this.mountType;
this.flashMessages.success(`Successfully mounted the ${type} ${mountType} at ${path}.`); mountType = mountType === 'secret' ? `${mountType}s engine` : `${mountType} method`;
yield this.onMountSuccess(type, path); this.flashMessages.success(`Successfully mounted the ${type} ${mountType} at ${path}.`);
return; yield this.onMountSuccess(type, path);
} else { return;
throw err; } else {
throw err;
}
} }
}
if (!capabilities.get('canUpdate')) { if (!capabilities.get('canUpdate')) {
// if there is no sys/mount issue then error is config endpoint. // if there is no sys/mount issue then error is config endpoint.
this.flashMessages.warning( this.flashMessages.warning(
'You do not have access to the config endpoint. The secret engine was mounted, but the configuration settings were not saved.' 'You do not have access to the config endpoint. The secret engine was mounted, but the configuration settings were not saved.'
);
// remove the config data from the model otherwise it will save it even if the network request failed.
[this.mountModel.maxVersions, this.mountModel.casRequired, this.mountModel.deleteVersionAfter] = [
0,
false,
0,
];
}
try {
yield mountModel.save();
} catch (err) {
if (err.message === 'mountIssue') {
this.mountIssue = true;
this.set('isFormInvalid', this.mountIssue);
this.flashMessages.danger(
'You do not have access to the sys/mounts endpoint. The secret engine was not mounted.'
); );
// remove the config data from the model otherwise it will save it even if the network request failed.
[this.mountModel.maxVersions, this.mountModel.casRequired, this.mountModel.deleteVersionAfter] = [
0,
false,
0,
];
}
try {
yield mountModel.save();
} catch (err) {
if (err.message === 'mountIssue') {
this.mountIssue = true;
this.set('isFormInvalid', this.mountIssue);
this.flashMessages.danger(
'You do not have access to the sys/mounts endpoint. The secret engine was not mounted.'
);
return;
}
this.set('errorMessage', 'This mount path already exist.');
return; return;
} }
this.set('errorMessage', 'This mount path already exist.'); let mountType = this.mountType;
mountType = mountType === 'secret' ? `${mountType}s engine` : `${mountType} method`;
this.flashMessages.success(`Successfully mounted the ${type} ${mountType} at ${path}.`);
yield this.onMountSuccess(type, path);
return; return;
} })
let mountType = this.mountType; ).drop(),
mountType = mountType === 'secret' ? `${mountType}s engine` : `${mountType} method`;
this.flashMessages.success(`Successfully mounted the ${type} ${mountType} at ${path}.`);
yield this.onMountSuccess(type, path);
return;
})
.drop()
.withTestWaiter(),
actions: { actions: {
onKeyUp(name, value) { onKeyUp(name, value) {

View File

@ -12,12 +12,12 @@ export default Component.extend({
targetNamespace: null, targetNamespace: null,
showLastSegment: false, showLastSegment: false,
normalizedNamespace: computed('targetNamespace', function() { normalizedNamespace: computed('targetNamespace', function () {
let ns = this.targetNamespace; let ns = this.targetNamespace;
return (ns || '').replace(/\.+/g, '/').replace(/☃/g, '.'); return (ns || '').replace(/\.+/g, '/').replace(/☃/g, '.');
}), }),
namespaceDisplay: computed('normalizedNamespace', 'showLastSegment', function() { namespaceDisplay: computed('normalizedNamespace', 'showLastSegment', function () {
let ns = this.normalizedNamespace; let ns = this.normalizedNamespace;
if (!ns) return 'root'; if (!ns) return 'root';
let showLastSegment = this.showLastSegment; let showLastSegment = this.showLastSegment;
@ -25,7 +25,7 @@ export default Component.extend({
return showLastSegment ? parts[parts.length - 1] : ns; return showLastSegment ? parts[parts.length - 1] : ns;
}), }),
isCurrentNamespace: computed('targetNamespace', 'currentNamespace', function() { isCurrentNamespace: computed('targetNamespace', 'currentNamespace', function () {
return this.currentNamespace === this.targetNamespace; return this.currentNamespace === this.targetNamespace;
}), }),

View File

@ -36,7 +36,7 @@ export default Component.extend({
this.set('oldNamespace', ns); this.set('oldNamespace', ns);
}, },
fetchListCapability: task(function*() { fetchListCapability: task(function* () {
try { try {
let capability = yield this.store.findRecord('capabilities', 'sys/namespaces/'); let capability = yield this.store.findRecord('capabilities', 'sys/namespaces/');
this.set('listCapability', capability); this.set('listCapability', capability);
@ -47,7 +47,7 @@ export default Component.extend({
this.set('canList', false); this.set('canList', false);
} }
}), }),
setForAnimation: task(function*() { setForAnimation: task(function* () {
let leaves = this.menuLeaves; let leaves = this.menuLeaves;
let lastLeaves = this.lastMenuLeaves; let lastLeaves = this.lastMenuLeaves;
if (!lastLeaves) { if (!lastLeaves) {
@ -81,7 +81,7 @@ export default Component.extend({
accessibleNamespaces: alias('namespaceService.accessibleNamespaces'), accessibleNamespaces: alias('namespaceService.accessibleNamespaces'),
inRootNamespace: alias('namespaceService.inRootNamespace'), inRootNamespace: alias('namespaceService.inRootNamespace'),
namespaceTree: computed('accessibleNamespaces', function() { namespaceTree: computed('accessibleNamespaces', function () {
let nsList = this.accessibleNamespaces; let nsList = this.accessibleNamespaces;
if (!nsList) { if (!nsList) {
@ -123,7 +123,7 @@ export default Component.extend({
// to render the nodes of each leaf // to render the nodes of each leaf
// gets set as 'lastMenuLeaves' in the ember concurrency task above // gets set as 'lastMenuLeaves' in the ember concurrency task above
menuLeaves: computed('namespacePath', 'namespaceTree', 'pathToLeaf', function() { menuLeaves: computed('namespacePath', 'namespaceTree', 'pathToLeaf', function () {
let ns = this.namespacePath; let ns = this.namespacePath;
ns = (ns || '').replace(/^\//, ''); ns = (ns || '').replace(/^\//, '');
let leaves = ancestorKeysForKey(ns); let leaves = ancestorKeysForKey(ns);
@ -136,7 +136,7 @@ export default Component.extend({
// the nodes at the root of the namespace tree // the nodes at the root of the namespace tree
// these will get rendered as the bottom layer // these will get rendered as the bottom layer
rootLeaves: computed('namespaceTree', function() { rootLeaves: computed('namespaceTree', function () {
let tree = this.namespaceTree; let tree = this.namespaceTree;
let leaves = Object.keys(tree); let leaves = Object.keys(tree);
return leaves; return leaves;
@ -144,11 +144,11 @@ export default Component.extend({
currentLeaf: alias('lastMenuLeaves.lastObject'), currentLeaf: alias('lastMenuLeaves.lastObject'),
canAccessMultipleNamespaces: gt('accessibleNamespaces.length', 1), canAccessMultipleNamespaces: gt('accessibleNamespaces.length', 1),
isUserRootNamespace: computed('auth.authData.userRootNamespace', 'namespacePath', function() { isUserRootNamespace: computed('auth.authData.userRootNamespace', 'namespacePath', function () {
return this.auth.authData.userRootNamespace === this.namespacePath; return this.auth.authData.userRootNamespace === this.namespacePath;
}), }),
namespaceDisplay: computed('namespacePath', 'accessibleNamespaces', 'accessibleNamespaces.[]', function() { namespaceDisplay: computed('namespacePath', 'accessibleNamespaces', 'accessibleNamespaces.[]', function () {
let namespace = this.namespacePath; let namespace = this.namespacePath;
if (!namespace) return ''; if (!namespace) return '';
let parts = namespace?.split('/'); let parts = namespace?.split('/');

View File

@ -9,7 +9,7 @@ export default Component.extend({
tagName: 'header', tagName: 'header',
navDrawerOpen: false, navDrawerOpen: false,
consoleFullscreen: false, consoleFullscreen: false,
hideLinks: computed('router.currentRouteName', function() { hideLinks: computed('router.currentRouteName', function () {
let currentRoute = this.router.currentRouteName; let currentRoute = this.router.currentRouteName;
if ('vault.cluster.oidc-provider' === currentRoute) { if ('vault.cluster.oidc-provider' === currentRoute) {
return true; return true;

View File

@ -27,7 +27,7 @@ export default class OidcConsentBlockComponent extends Component {
buildUrl(urlString, params) { buildUrl(urlString, params) {
try { try {
let url = new URL(urlString); let url = new URL(urlString);
Object.keys(params).forEach(key => { Object.keys(params).forEach((key) => {
if (params[key] && validParameters.includes(key)) { if (params[key] && validParameters.includes(key)) {
url.searchParams.append(key, params[key]); url.searchParams.append(key, params[key]);
} }

View File

@ -1,6 +1,8 @@
import Component from '@ember/component'; import Component from '@ember/component';
import { set } from '@ember/object'; import { set } from '@ember/object';
import { task } from 'ember-concurrency'; import { task } from 'ember-concurrency';
import { waitFor } from '@ember/test-waiters';
const BASE_64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gi; const BASE_64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gi;
export default Component.extend({ export default Component.extend({
@ -41,17 +43,19 @@ export default Component.extend({
reader.readAsDataURL(file); reader.readAsDataURL(file);
}, },
setPGPKey: task(function*(dataURL, filename) { setPGPKey: task(
const b64File = dataURL.split(',')[1].trim(); waitFor(function* (dataURL, filename) {
const decoded = atob(b64File).trim(); const b64File = dataURL.split(',')[1].trim();
const decoded = atob(b64File).trim();
// If a b64-encoded file was uploaded, then after decoding, it // If a b64-encoded file was uploaded, then after decoding, it
// will still be b64. // will still be b64.
// If after decoding it's not b64, we want // If after decoding it's not b64, we want
// the original as it was only encoded when we used `readAsDataURL`. // the original as it was only encoded when we used `readAsDataURL`.
const fileData = decoded.match(BASE_64_REGEX) ? decoded : b64File; const fileData = decoded.match(BASE_64_REGEX) ? decoded : b64File;
yield this.onChange(this.index, { value: fileData, fileName: filename }); yield this.onChange(this.index, { value: fileData, fileName: filename });
}).withTestWaiter(), })
),
actions: { actions: {
pickedFile(e) { pickedFile(e) {

View File

@ -34,7 +34,7 @@ export default Component.extend({
list = [...this.listData, ...this.newList(this.listLength - this.listData.length)]; list = [...this.listData, ...this.newList(this.listLength - this.listData.length)];
} }
this.set('listData', list || this.listData); this.set('listData', list || this.listData);
this.onDataUpdate((list || this.listData).compact().map(k => k.value)); this.onDataUpdate((list || this.listData).compact().map((k) => k.value));
}, },
newList(length) { newList(length) {
@ -47,7 +47,7 @@ export default Component.extend({
setKey(index, key) { setKey(index, key) {
let { listData } = this; let { listData } = this;
listData.splice(index, 1, key); listData.splice(index, 1, key);
this.onDataUpdate(listData.compact().map(k => k.value)); this.onDataUpdate(listData.compact().map((k) => k.value));
}, },
}, },
}); });

View File

@ -37,7 +37,7 @@ export default Component.extend({
defaultSpan: 12, defaultSpan: 12,
retentionMonths: 24, retentionMonths: 24,
startDate: computed('start', function() { startDate: computed('start', function () {
if (!this.start) return null; if (!this.start) return null;
let date; let date;
try { try {
@ -48,7 +48,7 @@ export default Component.extend({
return null; return null;
} }
}), }),
endDate: computed('end', function() { endDate: computed('end', function () {
if (!this.end) return null; if (!this.end) return null;
let date; let date;
try { try {
@ -60,7 +60,7 @@ export default Component.extend({
} }
}), }),
error: computed('end', 'endDate', 'retentionMonths', 'start', 'startDate', function() { error: computed('end', 'endDate', 'retentionMonths', 'start', 'startDate', function () {
if (!this.startDate) { if (!this.startDate) {
return 'Start date is invalid. Please use format MM/yyyy'; return 'Start date is invalid. Please use format MM/yyyy';
} }

View File

@ -10,20 +10,20 @@ export default Component.extend({
size: 20, size: 20,
strokeWidth: 1, strokeWidth: 1,
viewBox: computed('size', function() { viewBox: computed('size', function () {
let s = this.size; let s = this.size;
return `0 0 ${s} ${s}`; return `0 0 ${s} ${s}`;
}), }),
centerValue: computed('size', function() { centerValue: computed('size', function () {
return this.size / 2; return this.size / 2;
}), }),
r: computed('size', 'strokeWidth', function() { r: computed('size', 'strokeWidth', function () {
return (this.size - this.strokeWidth) / 2; return (this.size - this.strokeWidth) / 2;
}), }),
c: computed('r', function() { c: computed('r', function () {
return 2 * Math.PI * this.r; return 2 * Math.PI * this.r;
}), }),
dashArrayOffset: computed('c', 'progressDecimal', function() { dashArrayOffset: computed('c', 'progressDecimal', function () {
return this.c * (1 - this.progressDecimal); return this.c * (1 - this.progressDecimal);
}), }),
}); });

View File

@ -12,7 +12,7 @@ export default Component.extend({
flashMessages: service(), flashMessages: service(),
isUploading: alias('restore.isRunning'), isUploading: alias('restore.isRunning'),
abortController: null, abortController: null,
restore: task(function*() { restore: task(function* () {
this.set('errors', null); this.set('errors', null);
let adapter = getOwner(this).lookup('adapter:application'); let adapter = getOwner(this).lookup('adapter:application');
try { try {

View File

@ -1,18 +1,26 @@
<div class="field"> <div class="field">
<div class="regex-label-wrapper"> <div class="regex-label-wrapper">
<div class="regex-label"> <div class="regex-label">
<label for="{{@attr.name}}" class="is-label"> <label for={{@attr.name}} class="is-label">
{{@labelString}} {{@labelString}}
{{#if @attr.options.helpText}} {{#if @attr.options.helpText}}
{{#info-tooltip}} <InfoTooltip>
<span data-test-help-text> <span data-test-help-text>
{{@attr.options.helpText}} {{@attr.options.helpText}}
</span> </span>
{{/info-tooltip}} </InfoTooltip>
{{/if}} {{/if}}
</label> </label>
{{#if @attr.options.subText}} {{#if @attr.options.subText}}
<p class="sub-text">{{@attr.options.subText}} {{#if @attr.options.docLink}}<a href="{{@attr.options.docLink}}" target="_blank" rel="noopener noreferrer">See our documentation</a> for help.{{/if}}</p> <p class="sub-text">
{{@attr.options.subText}}
{{#if @attr.options.docLink}}
<a href={{@attr.options.docLink}} target="_blank" rel="noopener noreferrer">
See our documentation
</a>
for help.
{{/if}}
</p>
{{/if}} {{/if}}
</div> </div>
<div> <div>
@ -22,7 +30,7 @@
@size="small" @size="small"
@checked={{this.showTestValue}} @checked={{this.showTestValue}}
@onChange={{this.toggleTestValue}} @onChange={{this.toggleTestValue}}
> >
<span class="has-text-grey">Validation</span> <span class="has-text-grey">Validation</span>
</Toggle> </Toggle>
</div> </div>
@ -32,37 +40,37 @@
data-test-input={{@attr.name}} data-test-input={{@attr.name}}
autocomplete="off" autocomplete="off"
spellcheck="false" spellcheck="false"
{{on 'change' @onChange}} {{on "change" @onChange}}
value={{@value}} value={{@value}}
class="input" class="input"
/> />
</div> </div>
{{#if this.showTestValue}} {{#if this.showTestValue}}
<div data-test-regex-validator-test-string> <div data-test-regex-validator-test-string>
<label for="{{@attr.name}}" class="is-label"> <label for={{@attr.name}} class="is-label">
Test string Test string
</label> </label>
<input <input
data-test-input={{concat @attr.name "-testval"}} data-test-input={{concat @attr.name "-testval"}}
id={{concat @attr.name "-testval"}} id={{concat @attr.name "-testval"}}
autocomplete="off" autocomplete="off"
spellcheck="false" spellcheck="false"
value={{this.testValue}} value={{this.testValue}}
{{on 'change' this.updateTestValue}} {{on "change" this.updateTestValue}}
class="input {{if this.regexError 'has-error'}}" /> class="input {{if this.regexError "has-error"}}"
/>
{{#if (and this.testValue @value)}} {{#if (and this.testValue @value)}}
<div data-test-regex-validation-message> <div data-test-regex-validation-message>
{{#if this.regexError}} {{#if this.regexError}}
<AlertInline @type="danger" @message="Your regex doesn't match the subject string" /> <AlertInline @type="danger" @message="Your regex doesn't match the subject string" />
{{else}} {{else}}
<div class="message-inline"> <div class="message-inline">
<Icon @name="check-circle-fill" class="has-text-success" /> <Icon @name="check-circle-fill" class="has-text-success" />
<p data-test-inline-success-message>Your regex matches the subject string</p> <p data-test-inline-success-message>Your regex matches the subject string</p>
</div> </div>
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}

View File

@ -41,7 +41,7 @@ export default Component.extend(FocusOnInsertMixin, {
} }
}, },
waitForKeyUp: task(function*() { waitForKeyUp: task(function* () {
while (true) { while (true) {
let event = yield waitForEvent(document.body, 'keyup'); let event = yield waitForEvent(document.body, 'keyup');
this.onEscape(event); this.onEscape(event);

View File

@ -28,9 +28,8 @@ import ControlGroupError from 'vault/lib/control-group-error';
import Ember from 'ember'; import Ember from 'ember';
import keys from 'vault/lib/keycodes'; import keys from 'vault/lib/keycodes';
import { action } from '@ember/object'; import { action, set } from '@ember/object';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { set } from '@ember/object';
import { tracked } from '@glimmer/tracking'; import { tracked } from '@glimmer/tracking';
import { isBlank, isNone } from '@ember/utils'; import { isBlank, isNone } from '@ember/utils';
@ -66,7 +65,7 @@ export default class SecretCreateOrUpdate extends Component {
let adapter = this.store.adapterFor('secret-v2'); let adapter = this.store.adapterFor('secret-v2');
let type = { modelName: 'secret-v2' }; let type = { modelName: 'secret-v2' };
let query = { backend: this.args.model.backend }; let query = { backend: this.args.model.backend };
adapter.query(this.store, type, query).then(result => { adapter.query(this.store, type, query).then((result) => {
this.secretPaths = result.data.keys; this.secretPaths = result.data.keys;
}); });
} }
@ -143,7 +142,7 @@ export default class SecretCreateOrUpdate extends Component {
.then(() => { .then(() => {
this.saveComplete(successCallback, key); this.saveComplete(successCallback, key);
}) })
.catch(e => { .catch((e) => {
// when mode is not create the metadata error is handled in secret-edit-metadata // when mode is not create the metadata error is handled in secret-edit-metadata
if (this.args.mode === 'create') { if (this.args.mode === 'create') {
this.error = e.errors.join(' '); this.error = e.errors.join(' ');
@ -155,7 +154,7 @@ export default class SecretCreateOrUpdate extends Component {
} }
} }
}) })
.catch(error => { .catch((error) => {
if (error instanceof ControlGroupError) { if (error instanceof ControlGroupError) {
let errorMessage = this.controlGroup.logFromError(error); let errorMessage = this.controlGroup.logFromError(error);
this.error = errorMessage.content; this.error = errorMessage.content;
@ -188,7 +187,7 @@ export default class SecretCreateOrUpdate extends Component {
return false; return false;
} }
@(task(function*(name, value) { @(task(function* (name, value) {
this.checkValidation(name, value); this.checkValidation(name, value);
while (true) { while (true) {
let event = yield waitForEvent(document.body, 'keyup'); let event = yield waitForEvent(document.body, 'keyup');

View File

@ -1,3 +1,4 @@
/* eslint ember/no-computed-properties-in-native-classes: 'warn' */
import Ember from 'ember'; import Ember from 'ember';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Component from '@glimmer/component'; import Component from '@glimmer/component';
@ -6,7 +7,7 @@ import { action } from '@ember/object';
import { alias } from '@ember/object/computed'; import { alias } from '@ember/object/computed';
import { maybeQueryRecord } from 'vault/macros/maybe-query-record'; import { maybeQueryRecord } from 'vault/macros/maybe-query-record';
const getErrorMessage = errors => { const getErrorMessage = (errors) => {
let errorMessage = errors?.join('. ') || 'Something went wrong. Check the Vault logs for more information.'; let errorMessage = errors?.join('. ') || 'Something went wrong. Check the Vault logs for more information.';
return errorMessage; return errorMessage;
}; };
@ -19,7 +20,7 @@ export default class SecretDeleteMenu extends Component {
@maybeQueryRecord( @maybeQueryRecord(
'capabilities', 'capabilities',
context => { (context) => {
if (!context.args || !context.args.modelForData || !context.args.modelForData.id) return; if (!context.args || !context.args.modelForData || !context.args.modelForData.id) return;
let [backend, id] = JSON.parse(context.args.modelForData.id); let [backend, id] = JSON.parse(context.args.modelForData.id);
return { return {
@ -33,7 +34,7 @@ export default class SecretDeleteMenu extends Component {
@maybeQueryRecord( @maybeQueryRecord(
'capabilities', 'capabilities',
context => { (context) => {
if (!context.args || !context.args.modelForData || !context.args.modelForData.id) return; if (!context.args || !context.args.modelForData || !context.args.modelForData.id) return;
let [backend, id] = JSON.parse(context.args.modelForData.id); let [backend, id] = JSON.parse(context.args.modelForData.id);
return { return {
@ -47,7 +48,7 @@ export default class SecretDeleteMenu extends Component {
@maybeQueryRecord( @maybeQueryRecord(
'capabilities', 'capabilities',
context => { (context) => {
if (!context.args.model || !context.args.model.engine || !context.args.model.id) return; if (!context.args.model || !context.args.model.engine || !context.args.model.id) return;
let backend = context.args.model.engine.id; let backend = context.args.model.engine.id;
let id = context.args.model.id; let id = context.args.model.id;
@ -64,7 +65,7 @@ export default class SecretDeleteMenu extends Component {
@maybeQueryRecord( @maybeQueryRecord(
'capabilities', 'capabilities',
context => { (context) => {
if (!context.args.model || context.args.mode === 'create') { if (!context.args.model || context.args.mode === 'create') {
return; return;
} }
@ -85,7 +86,7 @@ export default class SecretDeleteMenu extends Component {
@maybeQueryRecord( @maybeQueryRecord(
'capabilities', 'capabilities',
context => { (context) => {
if (!context.args.model || context.args.mode === 'create') { if (!context.args.model || context.args.mode === 'create') {
return; return;
} }
@ -136,7 +137,7 @@ export default class SecretDeleteMenu extends Component {
return this.store return this.store
.adapterFor('secret-v2-version') .adapterFor('secret-v2-version')
.v2DeleteOperation(this.store, this.args.modelForData.id, deleteType, currentVersionForNoReadMetadata) .v2DeleteOperation(this.store, this.args.modelForData.id, deleteType, currentVersionForNoReadMetadata)
.then(resp => { .then((resp) => {
if (Ember.testing) { if (Ember.testing) {
this.showDeleteModal = false; this.showDeleteModal = false;
// we don't want a refresh otherwise test loop will rerun in a loop // we don't want a refresh otherwise test loop will rerun in a loop

View File

@ -16,9 +16,8 @@
*/ */
import Component from '@glimmer/component'; import Component from '@glimmer/component';
import { action } from '@ember/object'; import { action, set } from '@ember/object';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { set } from '@ember/object';
import { tracked } from '@glimmer/tracking'; import { tracked } from '@glimmer/tracking';
export default class SecretEditMetadata extends Component { export default class SecretEditMetadata extends Component {

View File

@ -36,7 +36,7 @@
* @param {object} wrappedData - when copy the data it's the token of the secret returned. * @param {object} wrappedData - when copy the data it's the token of the secret returned.
* @param {object} editActions - actions passed from parent to child * @param {object} editActions - actions passed from parent to child
*/ */
/* eslint ember/no-computed-properties-in-native-classes: 'warn' */
import Component from '@glimmer/component'; import Component from '@glimmer/component';
import { action } from '@ember/object'; import { action } from '@ember/object';
import { not } from '@ember/object/computed'; import { not } from '@ember/object/computed';
@ -75,7 +75,7 @@ export default class SecretEditToolbar extends Component {
this.store this.store
.adapterFor('secret-v2-version') .adapterFor('secret-v2-version')
.queryRecord(this.args.modelForData.id, { wrapTTL: 1800 }) .queryRecord(this.args.modelForData.id, { wrapTTL: 1800 })
.then(resp => { .then((resp) => {
this.wrappedData = resp.wrap_info.token; this.wrappedData = resp.wrap_info.token;
this.flashMessages.success('Secret Successfully Wrapped!'); this.flashMessages.success('Secret Successfully Wrapped!');
}) })
@ -93,7 +93,7 @@ export default class SecretEditToolbar extends Component {
id: this.args.modelForData.id, id: this.args.modelForData.id,
wrapTTL: 1800, wrapTTL: 1800,
}) })
.then(resp => { .then((resp) => {
this.wrappedData = resp.wrap_info.token; this.wrappedData = resp.wrap_info.token;
this.flashMessages.success('Secret Successfully Wrapped!'); this.flashMessages.success('Secret Successfully Wrapped!');
}) })

View File

@ -73,7 +73,7 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, {
checkSecretCapabilities: maybeQueryRecord( checkSecretCapabilities: maybeQueryRecord(
'capabilities', 'capabilities',
context => { (context) => {
if (!context.model || context.mode === 'create') { if (!context.model || context.mode === 'create') {
return; return;
} }
@ -94,7 +94,7 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, {
checkMetadataCapabilities: maybeQueryRecord( checkMetadataCapabilities: maybeQueryRecord(
'capabilities', 'capabilities',
context => { (context) => {
if (!context.model || !context.isV2) { if (!context.model || !context.isV2) {
return; return;
} }
@ -117,38 +117,43 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, {
buttonDisabled: or('requestInFlight', 'model.isFolder', 'model.flagsIsInvalid'), buttonDisabled: or('requestInFlight', 'model.isFolder', 'model.flagsIsInvalid'),
modelForData: computed('isV2', 'model', function() { modelForData: computed('isV2', 'model', function () {
let { model } = this; let { model } = this;
if (!model) return null; if (!model) return null;
return this.isV2 ? model.belongsTo('selectedVersion').value() : model; return this.isV2 ? model.belongsTo('selectedVersion').value() : model;
}), }),
basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function() { basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function () {
return this.secretDataIsAdvanced || this.showAdvancedMode === false; return this.secretDataIsAdvanced || this.showAdvancedMode === false;
}), }),
secretDataAsJSON: computed('secretData', 'secretData.[]', function() { secretDataAsJSON: computed('secretData', 'secretData.[]', function () {
return this.secretData.toJSON(); return this.secretData.toJSON();
}), }),
secretDataIsAdvanced: computed('secretData', 'secretData.[]', function() { secretDataIsAdvanced: computed('secretData', 'secretData.[]', function () {
return this.secretData.isAdvanced(); return this.secretData.isAdvanced();
}), }),
showAdvancedMode: or('secretDataIsAdvanced', 'preferAdvancedEdit'), showAdvancedMode: or('secretDataIsAdvanced', 'preferAdvancedEdit'),
isWriteWithoutRead: computed('model.failedServerRead', 'modelForData.failedServerRead', 'isV2', function() { isWriteWithoutRead: computed(
if (!this.model) return; 'model.failedServerRead',
// if the version couldn't be read from the server 'modelForData.failedServerRead',
if (this.isV2 && this.modelForData.failedServerRead) { 'isV2',
return true; function () {
if (!this.model) return;
// if the version couldn't be read from the server
if (this.isV2 && this.modelForData.failedServerRead) {
return true;
}
// if the model couldn't be read from the server
if (!this.isV2 && this.model.failedServerRead) {
return true;
}
return false;
} }
// if the model couldn't be read from the server ),
if (!this.isV2 && this.model.failedServerRead) {
return true;
}
return false;
}),
actions: { actions: {
refresh() { refresh() {

View File

@ -30,7 +30,7 @@ export default Component.extend({
queryParams: null, queryParams: null,
ariaLabel: null, ariaLabel: null,
linkParams: computed('mode', 'secret', 'queryParams', function() { linkParams: computed('mode', 'secret', 'queryParams', function () {
let data = { mode: this.mode, secret: this.secret, queryParams: this.queryParams }; let data = { mode: this.mode, secret: this.secret, queryParams: this.queryParams };
return linkParams(data); return linkParams(data);
}), }),

View File

@ -36,10 +36,10 @@ export default class SecretListHeaderTab extends Component {
async fetchCapabilities() { async fetchCapabilities() {
let capabilitiesArray = ['canList', 'canCreate', 'canUpdate']; let capabilitiesArray = ['canList', 'canCreate', 'canUpdate'];
let checkCapabilities = function(object) { let checkCapabilities = function (object) {
let array = []; let array = [];
// we only want to look at the canList, canCreate and canUpdate on the capabilities record // we only want to look at the canList, canCreate and canUpdate on the capabilities record
capabilitiesArray.forEach(item => { capabilitiesArray.forEach((item) => {
// object is sometimes null // object is sometimes null
if (object) { if (object) {
array.push(object[item]); array.push(object[item]);
@ -47,7 +47,7 @@ export default class SecretListHeaderTab extends Component {
}); });
return array; return array;
}; };
let checker = arr => arr.every(item => !item); // same things as listing every item as !item && !item, etc. let checker = (arr) => arr.every((item) => !item); // same things as listing every item as !item && !item, etc.
// For now only check capabilities for the Database Secrets Engine // For now only check capabilities for the Database Secrets Engine
if (this.args.displayName === 'Database') { if (this.args.displayName === 'Database') {
let peekRecordRoles = this.store.peekRecord('capabilities', 'database/roles/'); let peekRecordRoles = this.store.peekRecord('capabilities', 'database/roles/');

Some files were not shown because too many files have changed in this diff Show More