mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-18 01:01:12 +01:00
UI - fix encoding for user-entered paths (#6294)
* directly depend on route-recognizer * add path encode helper using route-recognizer normalizer methods * encode user-entered paths/ids for places we're not using the built-in ember data buildUrl method * encode secret link params * decode params from the url, and encode for linked-block and navigate-input components * add escape-string-regexp * use list-controller mixin and escape the string when contructing new Regex objects * encode paths in the console service * add acceptance tests for kv secrets * make encoding in linked-block an attribute, and use it on secret lists * egp endpoints are enterprise-only, so include 'enterprise' text in the test * fix routing test and exclude single quote from encoding tests * encode cli string before tokenizing * encode auth_path for use with urlFor * add test for single quote via UI input instead of web cli
This commit is contained in:
parent
9617832784
commit
b585c20d06
@ -2,11 +2,12 @@ import { assign } from '@ember/polyfills';
|
|||||||
import { get, set } from '@ember/object';
|
import { get, set } from '@ember/object';
|
||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
import DS from 'ember-data';
|
import DS from 'ember-data';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
url(path) {
|
url(path) {
|
||||||
const url = `${this.buildURL()}/auth`;
|
const url = `${this.buildURL()}/auth`;
|
||||||
return path ? url + '/' + path : url;
|
return path ? url + '/' + encodePath(path) : url;
|
||||||
},
|
},
|
||||||
|
|
||||||
// used in updateRecord on the model#tune action
|
// used in updateRecord on the model#tune action
|
||||||
@ -58,6 +59,6 @@ export default ApplicationAdapter.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
exchangeOIDC(path, state, code) {
|
exchangeOIDC(path, state, code) {
|
||||||
return this.ajax(`/v1/auth/${path}/oidc/callback`, 'GET', { data: { state, code } });
|
return this.ajax(`/v1/auth/${encodePath(path)}/oidc/callback`, 'GET', { data: { state, code } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
revokePrefix(prefix) {
|
revokePrefix(prefix) {
|
||||||
let url = this.buildURL() + '/leases/revoke-prefix/' + prefix;
|
let url = this.buildURL() + '/leases/revoke-prefix/' + encodePath(prefix);
|
||||||
url = url.replace(/\/$/, '');
|
url = url.replace(/\/$/, '');
|
||||||
return this.ajax(url, 'PUT');
|
return this.ajax(url, 'PUT');
|
||||||
},
|
},
|
||||||
|
|
||||||
forceRevokePrefix(prefix) {
|
forceRevokePrefix(prefix) {
|
||||||
let url = this.buildURL() + '/leases/revoke-prefix/' + prefix;
|
let url = this.buildURL() + '/leases/revoke-prefix/' + encodePath(prefix);
|
||||||
url = url.replace(/\/$/, '');
|
url = url.replace(/\/$/, '');
|
||||||
return this.ajax(url, 'PUT');
|
return this.ajax(url, 'PUT');
|
||||||
},
|
},
|
||||||
@ -43,7 +44,7 @@ export default ApplicationAdapter.extend({
|
|||||||
|
|
||||||
query(store, type, query) {
|
query(store, type, query) {
|
||||||
const prefix = query.prefix || '';
|
const prefix = query.prefix || '';
|
||||||
return this.ajax(this.buildURL() + '/leases/lookup/' + prefix, 'GET', {
|
return this.ajax(this.buildURL() + '/leases/lookup/' + encodePath(prefix), 'GET', {
|
||||||
data: {
|
data: {
|
||||||
list: true,
|
list: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { assign } from '@ember/polyfills';
|
import { assign } from '@ember/polyfills';
|
||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
namespace: 'v1',
|
namespace: 'v1',
|
||||||
@ -31,9 +32,9 @@ export default ApplicationAdapter.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
urlForRole(backend, id) {
|
urlForRole(backend, id) {
|
||||||
let url = `${this.buildURL()}/${backend}/roles`;
|
let url = `${this.buildURL()}/${encodePath(backend)}/roles`;
|
||||||
if (id) {
|
if (id) {
|
||||||
url = url + '/' + id;
|
url = url + '/' + encodePath(id);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
router: service(),
|
router: service(),
|
||||||
|
|
||||||
findRecord(store, type, id, snapshot) {
|
findRecord(store, type, id, snapshot) {
|
||||||
let [path, role] = JSON.parse(id);
|
let [path, role] = JSON.parse(id);
|
||||||
|
path = encodePath(path);
|
||||||
|
|
||||||
let namespace = get(snapshot, 'adapterOptions.namespace');
|
let namespace = get(snapshot, 'adapterOptions.namespace');
|
||||||
let url = `/v1/auth/${path}/oidc/auth_url`;
|
let url = `/v1/auth/${path}/oidc/auth_url`;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { assign } from '@ember/polyfills';
|
import { assign } from '@ember/polyfills';
|
||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
namespace: 'v1',
|
namespace: 'v1',
|
||||||
@ -31,9 +32,9 @@ export default ApplicationAdapter.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
urlForRole(backend, id) {
|
urlForRole(backend, id) {
|
||||||
let url = `${this.buildURL()}/${backend}/roles`;
|
let url = `${this.buildURL()}/${encodePath(backend)}/roles`;
|
||||||
if (id) {
|
if (id) {
|
||||||
url = url + '/' + id;
|
url = url + '/' + encodePath(id);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { assign } from '@ember/polyfills';
|
import { assign } from '@ember/polyfills';
|
||||||
import { resolve, allSettled } from 'rsvp';
|
import { resolve, allSettled } from 'rsvp';
|
||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
namespace: 'v1',
|
namespace: 'v1',
|
||||||
@ -34,9 +35,9 @@ export default ApplicationAdapter.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
urlForRole(backend, id) {
|
urlForRole(backend, id) {
|
||||||
let url = `${this.buildURL()}/${backend}/roles`;
|
let url = `${this.buildURL()}/${encodePath(backend)}/roles`;
|
||||||
if (id) {
|
if (id) {
|
||||||
url = url + '/' + id;
|
url = url + '/' + encodePath(id);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
@ -84,7 +85,7 @@ export default ApplicationAdapter.extend({
|
|||||||
|
|
||||||
findAllZeroAddress(store, query) {
|
findAllZeroAddress(store, query) {
|
||||||
const { backend } = query;
|
const { backend } = query;
|
||||||
const url = `/v1/${backend}/config/zeroaddress`;
|
const url = `/v1/${encodePath(backend)}/config/zeroaddress`;
|
||||||
return this.ajax(url, 'GET');
|
return this.ajax(url, 'GET');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
import { assign } from '@ember/polyfills';
|
import { assign } from '@ember/polyfills';
|
||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
url(path) {
|
url(path) {
|
||||||
const url = `${this.buildURL()}/mounts`;
|
const url = `${this.buildURL()}/mounts`;
|
||||||
return path ? url + '/' + path : url;
|
return path ? url + '/' + encodePath(path) : url;
|
||||||
},
|
},
|
||||||
|
|
||||||
internalURL(path) {
|
internalURL(path) {
|
||||||
let url = `/${this.urlPrefix()}/internal/ui/mounts`;
|
let url = `/${this.urlPrefix()}/internal/ui/mounts`;
|
||||||
if (path) {
|
if (path) {
|
||||||
url = `${url}/${path}`;
|
url = `${url}/${encodePath(path)}`;
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
@ -38,14 +39,14 @@ export default ApplicationAdapter.extend({
|
|||||||
|
|
||||||
findRecord(store, type, path, snapshot) {
|
findRecord(store, type, path, snapshot) {
|
||||||
if (snapshot.attr('type') === 'ssh') {
|
if (snapshot.attr('type') === 'ssh') {
|
||||||
return this.ajax(`/v1/${path}/config/ca`, 'GET');
|
return this.ajax(`/v1/${encodePath(path)}/config/ca`, 'GET');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
queryRecord(store, type, query) {
|
queryRecord(store, type, query) {
|
||||||
if (query.type === 'aws') {
|
if (query.type === 'aws') {
|
||||||
return this.ajax(`/v1/${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;
|
||||||
});
|
});
|
||||||
@ -61,25 +62,25 @@ export default ApplicationAdapter.extend({
|
|||||||
if (apiPath) {
|
if (apiPath) {
|
||||||
const serializer = store.serializerFor(type.modelName);
|
const serializer = store.serializerFor(type.modelName);
|
||||||
const data = serializer.serialize(snapshot);
|
const data = serializer.serialize(snapshot);
|
||||||
const path = snapshot.id;
|
const path = encodePath(snapshot.id);
|
||||||
return this.ajax(`/v1/${path}/${apiPath}`, options.isDelete ? 'DELETE' : 'POST', { data });
|
return this.ajax(`/v1/${path}/${apiPath}`, options.isDelete ? 'DELETE' : 'POST', { data });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
saveAWSRoot(store, type, snapshot) {
|
saveAWSRoot(store, type, snapshot) {
|
||||||
let { data } = snapshot.adapterOptions;
|
let { data } = snapshot.adapterOptions;
|
||||||
const path = snapshot.id;
|
const path = encodePath(snapshot.id);
|
||||||
return this.ajax(`/v1/${path}/config/root`, 'POST', { data });
|
return this.ajax(`/v1/${path}/config/root`, 'POST', { data });
|
||||||
},
|
},
|
||||||
|
|
||||||
saveAWSLease(store, type, snapshot) {
|
saveAWSLease(store, type, snapshot) {
|
||||||
let { data } = snapshot.adapterOptions;
|
let { data } = snapshot.adapterOptions;
|
||||||
const path = snapshot.id;
|
const path = encodePath(snapshot.id);
|
||||||
return this.ajax(`/v1/${path}/config/lease`, 'POST', { data });
|
return this.ajax(`/v1/${path}/config/lease`, 'POST', { data });
|
||||||
},
|
},
|
||||||
|
|
||||||
saveZeroAddressConfig(store, type, snapshot) {
|
saveZeroAddressConfig(store, type, snapshot) {
|
||||||
const path = snapshot.id;
|
const path = encodePath(snapshot.id);
|
||||||
const roles = store
|
const roles = store
|
||||||
.peekAll('role-ssh')
|
.peekAll('role-ssh')
|
||||||
.filterBy('zeroAddress')
|
.filterBy('zeroAddress')
|
||||||
|
|||||||
@ -3,13 +3,14 @@ import { isEmpty } from '@ember/utils';
|
|||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
import DS from 'ember-data';
|
import DS from 'ember-data';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
namespace: 'v1',
|
namespace: 'v1',
|
||||||
_url(backend, id, infix = 'data') {
|
_url(backend, id, infix = 'data') {
|
||||||
let url = `${this.buildURL()}/${backend}/${infix}/`;
|
let url = `${this.buildURL()}/${encodePath(backend)}/${infix}/`;
|
||||||
if (!isEmpty(id)) {
|
if (!isEmpty(id)) {
|
||||||
url = url + id;
|
url = url + encodePath(id);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { isEmpty } from '@ember/utils';
|
import { isEmpty } from '@ember/utils';
|
||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
namespace: 'v1',
|
namespace: 'v1',
|
||||||
_url(backend, id) {
|
_url(backend, id) {
|
||||||
let url = `${this.buildURL()}/${backend}/metadata/`;
|
let url = `${this.buildURL()}/${encodePath(backend)}/metadata/`;
|
||||||
if (!isEmpty(id)) {
|
if (!isEmpty(id)) {
|
||||||
url = url + id;
|
url = url + encodePath(id);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { isEmpty } from '@ember/utils';
|
import { isEmpty } from '@ember/utils';
|
||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
namespace: 'v1',
|
namespace: 'v1',
|
||||||
@ -26,9 +27,9 @@ export default ApplicationAdapter.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
urlForSecret(backend, id) {
|
urlForSecret(backend, id) {
|
||||||
let url = `${this.buildURL()}/${backend}/`;
|
let url = `${this.buildURL()}/${encodePath(backend)}/`;
|
||||||
if (!isEmpty(id)) {
|
if (!isEmpty(id)) {
|
||||||
url = url + id;
|
url = url + encodePath(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import ApplicationAdapter from './application';
|
import ApplicationAdapter from './application';
|
||||||
import { pluralize } from 'ember-inflector';
|
import { pluralize } from 'ember-inflector';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
export default ApplicationAdapter.extend({
|
||||||
namespace: 'v1',
|
namespace: 'v1',
|
||||||
@ -47,29 +48,29 @@ export default ApplicationAdapter.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
urlForSecret(backend, id) {
|
urlForSecret(backend, id) {
|
||||||
let url = `${this.buildURL()}/${backend}/keys/`;
|
let url = `${this.buildURL()}/${encodePath(backend)}/keys/`;
|
||||||
if (id) {
|
if (id) {
|
||||||
url += id;
|
url += encodePath(id);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|
||||||
urlForAction(action, backend, id, param) {
|
urlForAction(action, backend, id, param) {
|
||||||
let urlBase = `${this.buildURL()}/${backend}/${action}`;
|
let urlBase = `${this.buildURL()}/${encodePath(backend)}/${action}`;
|
||||||
// these aren't key-specific
|
// these aren't key-specific
|
||||||
if (action === 'hash' || action === 'random') {
|
if (action === 'hash' || action === 'random') {
|
||||||
return urlBase;
|
return urlBase;
|
||||||
}
|
}
|
||||||
if (action === 'datakey' && param) {
|
if (action === 'datakey' && param) {
|
||||||
// datakey action has `wrapped` or `plaintext` as part of the url
|
// datakey action has `wrapped` or `plaintext` as part of the url
|
||||||
return `${urlBase}/${param}/${id}`;
|
return `${urlBase}/${param}/${encodePath(id)}`;
|
||||||
}
|
}
|
||||||
if (action === 'export' && param) {
|
if (action === 'export' && param) {
|
||||||
let [type, version] = param;
|
let [type, version] = param;
|
||||||
const exportBase = `${urlBase}/${type}-key/${id}`;
|
const exportBase = `${urlBase}/${type}-key/${encodePath(id)}`;
|
||||||
return version ? `${exportBase}/${version}` : exportBase;
|
return version ? `${exportBase}/${version}` : exportBase;
|
||||||
}
|
}
|
||||||
return `${urlBase}/${id}`;
|
return `${urlBase}/${encodePath(id)}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
optionsForQuery(id) {
|
optionsForQuery(id) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
import utils from 'vault/lib/key-utils';
|
import utils from 'vault/lib/key-utils';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: 'nav',
|
tagName: 'nav',
|
||||||
@ -31,7 +32,7 @@ export default Component.extend({
|
|||||||
let crumbs = [];
|
let crumbs = [];
|
||||||
const root = this.get('root');
|
const root = this.get('root');
|
||||||
const baseKey = this.get('baseKey.display') || this.get('baseKey.id');
|
const baseKey = this.get('baseKey.display') || this.get('baseKey.id');
|
||||||
const baseKeyModel = this.get('baseKey.id');
|
const baseKeyModel = encodePath(this.get('baseKey.id'));
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
crumbs.push(root);
|
crumbs.push(root);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
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 hbs from 'htmlbars-inline-precompile';
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
let LinkedBlockComponent = Component.extend({
|
let LinkedBlockComponent = Component.extend({
|
||||||
router: service(),
|
router: service(),
|
||||||
@ -11,6 +12,8 @@ let LinkedBlockComponent = Component.extend({
|
|||||||
|
|
||||||
queryParams: null,
|
queryParams: null,
|
||||||
|
|
||||||
|
encode: false,
|
||||||
|
|
||||||
click(event) {
|
click(event) {
|
||||||
const $target = this.$(event.target);
|
const $target = this.$(event.target);
|
||||||
const isAnchorOrButton =
|
const isAnchorOrButton =
|
||||||
@ -19,7 +22,15 @@ let LinkedBlockComponent = Component.extend({
|
|||||||
$target.closest('button', event.currentTarget).length > 0 ||
|
$target.closest('button', event.currentTarget).length > 0 ||
|
||||||
$target.closest('a', event.currentTarget).length > 0;
|
$target.closest('a', event.currentTarget).length > 0;
|
||||||
if (!isAnchorOrButton) {
|
if (!isAnchorOrButton) {
|
||||||
const params = this.get('params');
|
let params = this.get('params');
|
||||||
|
if (this.encode) {
|
||||||
|
params = params.map((param, index) => {
|
||||||
|
if (index === 0 || typeof param !== 'string') {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
return encodePath(param);
|
||||||
|
});
|
||||||
|
}
|
||||||
const queryParams = this.get('queryParams');
|
const queryParams = this.get('queryParams');
|
||||||
if (queryParams) {
|
if (queryParams) {
|
||||||
params.push({ queryParams });
|
params.push({ queryParams });
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import Component from '@ember/component';
|
|||||||
import utils from 'vault/lib/key-utils';
|
import utils from 'vault/lib/key-utils';
|
||||||
import keys from 'vault/lib/keycodes';
|
import keys from 'vault/lib/keycodes';
|
||||||
import FocusOnInsertMixin from 'vault/mixins/focus-on-insert';
|
import FocusOnInsertMixin from 'vault/mixins/focus-on-insert';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
const routeFor = function(type, mode) {
|
const routeFor = function(type, mode) {
|
||||||
const MODES = {
|
const MODES = {
|
||||||
@ -43,8 +44,15 @@ export default Component.extend(FocusOnInsertMixin, {
|
|||||||
filterMatchesKey: null,
|
filterMatchesKey: null,
|
||||||
firstPartialMatch: null,
|
firstPartialMatch: null,
|
||||||
|
|
||||||
transitionToRoute: function() {
|
transitionToRoute(...args) {
|
||||||
this.get('router').transitionTo(...arguments);
|
let params = args.map((param, index) => {
|
||||||
|
if (index === 0 || typeof param !== 'string') {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
return encodePath(param);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.get('router').transitionTo(...params);
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldFocus: false,
|
shouldFocus: false,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export function linkParams({ mode, secret, queryParams }) {
|
export function linkParams({ mode, secret, queryParams }) {
|
||||||
let params;
|
let params;
|
||||||
@ -8,7 +9,7 @@ export function linkParams({ mode, secret, queryParams }) {
|
|||||||
if (!secret || secret === ' ') {
|
if (!secret || secret === ' ') {
|
||||||
params = [route + '-root'];
|
params = [route + '-root'];
|
||||||
} else {
|
} else {
|
||||||
params = [route, secret];
|
params = [route, encodePath(secret)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryParams) {
|
if (queryParams) {
|
||||||
|
|||||||
@ -2,19 +2,12 @@ import { inject as service } from '@ember/service';
|
|||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import Controller, { inject as controller } from '@ember/controller';
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
import utils from 'vault/lib/key-utils';
|
import utils from 'vault/lib/key-utils';
|
||||||
|
import ListController from 'vault/mixins/list-controller';
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend(ListController, {
|
||||||
flashMessages: service(),
|
flashMessages: service(),
|
||||||
store: service(),
|
store: service(),
|
||||||
clusterController: controller('vault.cluster'),
|
clusterController: controller('vault.cluster'),
|
||||||
queryParams: {
|
|
||||||
page: 'page',
|
|
||||||
pageFilter: 'pageFilter',
|
|
||||||
},
|
|
||||||
|
|
||||||
page: 1,
|
|
||||||
pageFilter: null,
|
|
||||||
filter: null,
|
|
||||||
|
|
||||||
backendCrumb: computed(function() {
|
backendCrumb: computed(function() {
|
||||||
return {
|
return {
|
||||||
@ -27,24 +20,6 @@ export default Controller.extend({
|
|||||||
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
|
||||||
filterMatchesKey: computed('filter', 'model', 'model.[]', function() {
|
|
||||||
var filter = this.get('filter');
|
|
||||||
var content = this.get('model');
|
|
||||||
return !!(content.length && content.findBy('id', filter));
|
|
||||||
}),
|
|
||||||
|
|
||||||
firstPartialMatch: computed('filter', 'model', 'model.[]', 'filterMatchesKey', function() {
|
|
||||||
var filter = this.get('filter');
|
|
||||||
var content = this.get('model');
|
|
||||||
var filterMatchesKey = this.get('filterMatchesKey');
|
|
||||||
var re = new RegExp('^' + filter);
|
|
||||||
return filterMatchesKey
|
|
||||||
? null
|
|
||||||
: content.find(function(key) {
|
|
||||||
return re.test(key.get('id'));
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
filterIsFolder: computed('filter', function() {
|
filterIsFolder: computed('filter', function() {
|
||||||
return !!utils.keyIsFolder(this.get('filter'));
|
return !!utils.keyIsFolder(this.get('filter'));
|
||||||
}),
|
}),
|
||||||
@ -65,14 +40,6 @@ export default Controller.extend({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
setFilter(val) {
|
|
||||||
this.set('filter', val);
|
|
||||||
},
|
|
||||||
|
|
||||||
setFilterFocus(bool) {
|
|
||||||
this.set('filterFocused', bool);
|
|
||||||
},
|
|
||||||
|
|
||||||
revokePrefix(prefix, isForce) {
|
revokePrefix(prefix, isForce) {
|
||||||
const adapter = this.get('store').adapterFor('lease');
|
const adapter = this.get('store').adapterFor('lease');
|
||||||
const method = isForce ? 'forceRevokePrefix' : 'revokePrefix';
|
const method = isForce ? 'forceRevokePrefix' : 'revokePrefix';
|
||||||
|
|||||||
@ -4,50 +4,19 @@ import Controller from '@ember/controller';
|
|||||||
import utils from 'vault/lib/key-utils';
|
import utils from 'vault/lib/key-utils';
|
||||||
import BackendCrumbMixin from 'vault/mixins/backend-crumb';
|
import BackendCrumbMixin from 'vault/mixins/backend-crumb';
|
||||||
import WithNavToNearestAncestor from 'vault/mixins/with-nav-to-nearest-ancestor';
|
import WithNavToNearestAncestor from 'vault/mixins/with-nav-to-nearest-ancestor';
|
||||||
|
import ListController from 'vault/mixins/list-controller';
|
||||||
|
|
||||||
export default Controller.extend(BackendCrumbMixin, WithNavToNearestAncestor, {
|
export default Controller.extend(ListController, BackendCrumbMixin, WithNavToNearestAncestor, {
|
||||||
flashMessages: service(),
|
flashMessages: service(),
|
||||||
queryParams: ['page', 'pageFilter', 'tab'],
|
queryParams: ['page', 'pageFilter', 'tab'],
|
||||||
|
|
||||||
tab: '',
|
tab: '',
|
||||||
page: 1,
|
|
||||||
pageFilter: null,
|
|
||||||
filterFocused: false,
|
|
||||||
|
|
||||||
// set via the route `loading` action
|
|
||||||
isLoading: false,
|
|
||||||
|
|
||||||
filterMatchesKey: computed('filter', 'model', 'model.[]', function() {
|
|
||||||
var filter = this.get('filter');
|
|
||||||
var content = this.get('model');
|
|
||||||
return !!(content.length && content.findBy('id', filter));
|
|
||||||
}),
|
|
||||||
|
|
||||||
firstPartialMatch: computed('filter', 'model', 'model.[]', 'filterMatchesKey', function() {
|
|
||||||
var filter = this.get('filter');
|
|
||||||
var content = this.get('model');
|
|
||||||
var filterMatchesKey = this.get('filterMatchesKey');
|
|
||||||
var re = new RegExp('^' + filter);
|
|
||||||
return filterMatchesKey
|
|
||||||
? null
|
|
||||||
: content.find(function(key) {
|
|
||||||
return re.test(key.get('id'));
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
filterIsFolder: computed('filter', function() {
|
filterIsFolder: computed('filter', function() {
|
||||||
return !!utils.keyIsFolder(this.get('filter'));
|
return !!utils.keyIsFolder(this.get('filter'));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
setFilter(val) {
|
|
||||||
this.set('filter', val);
|
|
||||||
},
|
|
||||||
|
|
||||||
setFilterFocus(bool) {
|
|
||||||
this.set('filterFocused', bool);
|
|
||||||
},
|
|
||||||
|
|
||||||
chooseAction(action) {
|
chooseAction(action) {
|
||||||
this.set('selectedAction', action);
|
this.set('selectedAction', action);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -56,11 +56,14 @@ export function executeUICommand(command, logAndOutput, clearLog, toggleFullscre
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parseCommand(command, shouldThrow) {
|
export function parseCommand(command, shouldThrow) {
|
||||||
let args = argTokenizer(command);
|
// encode everything but spaces
|
||||||
|
let cmd = encodeURIComponent(command).replace(/%20/g, decodeURIComponent);
|
||||||
|
let args = argTokenizer(cmd);
|
||||||
if (args[0] === 'vault') {
|
if (args[0] === 'vault') {
|
||||||
args.shift();
|
args.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args = args.map(decodeURIComponent);
|
||||||
let [method, ...rest] = args;
|
let [method, ...rest] = args;
|
||||||
let path;
|
let path;
|
||||||
let flags = [];
|
let flags = [];
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import Mixin from '@ember/object/mixin';
|
import Mixin from '@ember/object/mixin';
|
||||||
|
import escapeStringRegexp from 'escape-string-regexp';
|
||||||
|
|
||||||
export default Mixin.create({
|
export default Mixin.create({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
@ -10,6 +11,7 @@ export default Mixin.create({
|
|||||||
page: 1,
|
page: 1,
|
||||||
pageFilter: null,
|
pageFilter: null,
|
||||||
filter: null,
|
filter: null,
|
||||||
|
filterFocused: false,
|
||||||
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ export default Mixin.create({
|
|||||||
var filter = this.get('filter');
|
var filter = this.get('filter');
|
||||||
var content = this.get('model');
|
var content = this.get('model');
|
||||||
var filterMatchesKey = this.get('filterMatchesKey');
|
var filterMatchesKey = this.get('filterMatchesKey');
|
||||||
var re = new RegExp('^' + filter);
|
var re = new RegExp('^' + escapeStringRegexp(filter));
|
||||||
return filterMatchesKey
|
return filterMatchesKey
|
||||||
? null
|
? null
|
||||||
: content.find(function(key) {
|
: content.find(function(key) {
|
||||||
|
|||||||
@ -4,10 +4,13 @@ import Route from '@ember/routing/route';
|
|||||||
import { getOwner } from '@ember/application';
|
import { getOwner } from '@ember/application';
|
||||||
import { supportedSecretBackends } from 'vault/helpers/supported-secret-backends';
|
import { supportedSecretBackends } from 'vault/helpers/supported-secret-backends';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
import { normalizePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
const SUPPORTED_BACKENDS = supportedSecretBackends();
|
const SUPPORTED_BACKENDS = supportedSecretBackends();
|
||||||
|
|
||||||
export default Route.extend({
|
export default Route.extend({
|
||||||
|
templateName: 'vault/cluster/secrets/backend/list',
|
||||||
|
pathHelp: service('path-help'),
|
||||||
queryParams: {
|
queryParams: {
|
||||||
page: {
|
page: {
|
||||||
refreshModel: true,
|
refreshModel: true,
|
||||||
@ -20,13 +23,21 @@ export default Route.extend({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
templateName: 'vault/cluster/secrets/backend/list',
|
secretParam() {
|
||||||
pathHelp: service('path-help'),
|
let { secret } = this.paramsFor(this.routeName);
|
||||||
|
return secret ? normalizePath(secret) : '';
|
||||||
|
},
|
||||||
|
|
||||||
|
enginePathParam() {
|
||||||
|
let { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
||||||
|
return backend;
|
||||||
|
},
|
||||||
|
|
||||||
beforeModel() {
|
beforeModel() {
|
||||||
let owner = getOwner(this);
|
let owner = getOwner(this);
|
||||||
let { secret } = this.paramsFor(this.routeName);
|
let secret = this.secretParam();
|
||||||
let { backend, tab } = this.paramsFor('vault.cluster.secrets.backend');
|
let backend = this.enginePathParam();
|
||||||
|
let { tab } = this.paramsFor('vault.cluster.secrets.backend');
|
||||||
let secretEngine = this.store.peekRecord('secret-engine', backend);
|
let secretEngine = this.store.peekRecord('secret-engine', backend);
|
||||||
let type = secretEngine && secretEngine.get('engineType');
|
let type = secretEngine && secretEngine.get('engineType');
|
||||||
if (!type || !SUPPORTED_BACKENDS.includes(type)) {
|
if (!type || !SUPPORTED_BACKENDS.includes(type)) {
|
||||||
@ -58,8 +69,8 @@ export default Route.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
const secret = params.secret ? params.secret : '';
|
const secret = this.secretParam() || '';
|
||||||
const { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
const backend = this.enginePathParam();
|
||||||
const backendModel = this.modelFor('vault.cluster.secrets.backend');
|
const backendModel = this.modelFor('vault.cluster.secrets.backend');
|
||||||
return hash({
|
return hash({
|
||||||
secret,
|
secret,
|
||||||
@ -89,7 +100,7 @@ export default Route.extend({
|
|||||||
|
|
||||||
afterModel(model) {
|
afterModel(model) {
|
||||||
const { tab } = this.paramsFor(this.routeName);
|
const { tab } = this.paramsFor(this.routeName);
|
||||||
const { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
const backend = this.enginePathParam();
|
||||||
if (!tab || tab !== 'certs') {
|
if (!tab || tab !== 'certs') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -114,7 +125,7 @@ export default Route.extend({
|
|||||||
let secretParams = this.paramsFor(this.routeName);
|
let secretParams = this.paramsFor(this.routeName);
|
||||||
let secret = resolvedModel.secret;
|
let secret = resolvedModel.secret;
|
||||||
let model = resolvedModel.secrets;
|
let model = resolvedModel.secrets;
|
||||||
let { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
let backend = this.enginePathParam();
|
||||||
let backendModel = this.store.peekRecord('secret-engine', backend);
|
let backendModel = this.store.peekRecord('secret-engine', backend);
|
||||||
let has404 = this.get('has404');
|
let has404 = this.get('has404');
|
||||||
// only clear store cache if this is a new model
|
// only clear store cache if this is a new model
|
||||||
@ -155,8 +166,8 @@ export default Route.extend({
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
error(error, transition) {
|
error(error, transition) {
|
||||||
let { secret } = this.paramsFor(this.routeName);
|
let secret = this.secretParam();
|
||||||
let { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
let backend = this.enginePathParam();
|
||||||
let is404 = error.httpStatus === 404;
|
let is404 = error.httpStatus === 404;
|
||||||
let hasModel = this.controllerFor(this.routeName).get('hasModel');
|
let hasModel = this.controllerFor(this.routeName).get('hasModel');
|
||||||
|
|
||||||
|
|||||||
@ -6,11 +6,20 @@ import Route from '@ember/routing/route';
|
|||||||
import utils from 'vault/lib/key-utils';
|
import utils from 'vault/lib/key-utils';
|
||||||
import { getOwner } from '@ember/application';
|
import { getOwner } from '@ember/application';
|
||||||
import UnloadModelRoute from 'vault/mixins/unload-model-route';
|
import UnloadModelRoute from 'vault/mixins/unload-model-route';
|
||||||
|
import { encodePath, normalizePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export default Route.extend(UnloadModelRoute, {
|
export default Route.extend(UnloadModelRoute, {
|
||||||
pathHelp: service('path-help'),
|
pathHelp: service('path-help'),
|
||||||
|
secretParam() {
|
||||||
|
let { secret } = this.paramsFor(this.routeName);
|
||||||
|
return secret ? normalizePath(secret) : '';
|
||||||
|
},
|
||||||
|
enginePathParam() {
|
||||||
|
let { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
||||||
|
return backend;
|
||||||
|
},
|
||||||
capabilities(secret) {
|
capabilities(secret) {
|
||||||
const { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
const backend = this.enginePathParam();
|
||||||
let backendModel = this.modelFor('vault.cluster.secrets.backend');
|
let backendModel = this.modelFor('vault.cluster.secrets.backend');
|
||||||
let backendType = backendModel.get('engineType');
|
let backendType = backendModel.get('engineType');
|
||||||
if (backendType === 'kv' || backendType === 'cubbyhole' || backendType === 'generic') {
|
if (backendType === 'kv' || backendType === 'cubbyhole' || backendType === 'generic') {
|
||||||
@ -37,13 +46,13 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
// currently there is no recursive delete for folders in vault, so there's no need to 'edit folders'
|
// currently there is no recursive delete for folders in vault, so there's no need to 'edit folders'
|
||||||
// perhaps in the future we could recurse _for_ users, but for now, just kick them
|
// perhaps in the future we could recurse _for_ users, but for now, just kick them
|
||||||
// back to the list
|
// back to the list
|
||||||
const { secret } = this.paramsFor(this.routeName);
|
let secret = this.secretParam();
|
||||||
return this.buildModel(secret).then(() => {
|
return this.buildModel(secret).then(() => {
|
||||||
const parentKey = utils.parentKeyForKey(secret);
|
const parentKey = utils.parentKeyForKey(secret);
|
||||||
const mode = this.routeName.split('.').pop();
|
const mode = this.routeName.split('.').pop();
|
||||||
if (mode === 'edit' && utils.keyIsFolder(secret)) {
|
if (mode === 'edit' && utils.keyIsFolder(secret)) {
|
||||||
if (parentKey) {
|
if (parentKey) {
|
||||||
return this.transitionTo('vault.cluster.secrets.backend.list', parentKey);
|
return this.transitionTo('vault.cluster.secrets.backend.list', encodePath(parentKey));
|
||||||
} else {
|
} else {
|
||||||
return this.transitionTo('vault.cluster.secrets.backend.list-root');
|
return this.transitionTo('vault.cluster.secrets.backend.list-root');
|
||||||
}
|
}
|
||||||
@ -52,7 +61,8 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
buildModel(secret) {
|
buildModel(secret) {
|
||||||
const { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
const backend = this.enginePathParam();
|
||||||
|
|
||||||
let modelType = this.modelType(backend, secret);
|
let modelType = this.modelType(backend, secret);
|
||||||
if (['secret', 'secret-v2'].includes(modelType)) {
|
if (['secret', 'secret-v2'].includes(modelType)) {
|
||||||
return resolve();
|
return resolve();
|
||||||
@ -77,10 +87,10 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
let { secret } = params;
|
let secret = this.secretParam();
|
||||||
const { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
let backend = this.enginePathParam();
|
||||||
let backendModel = this.modelFor('vault.cluster.secrets.backend', backend);
|
let backendModel = this.modelFor('vault.cluster.secrets.backend', backend);
|
||||||
const modelType = this.modelType(backend, secret);
|
let modelType = this.modelType(backend, secret);
|
||||||
|
|
||||||
if (!secret) {
|
if (!secret) {
|
||||||
secret = '\u0020';
|
secret = '\u0020';
|
||||||
@ -139,8 +149,8 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
const { secret } = this.paramsFor(this.routeName);
|
let secret = this.secretParam();
|
||||||
const { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
let backend = this.enginePathParam();
|
||||||
const preferAdvancedEdit =
|
const preferAdvancedEdit =
|
||||||
this.controllerFor('vault.cluster.secrets.backend').get('preferAdvancedEdit') || false;
|
this.controllerFor('vault.cluster.secrets.backend').get('preferAdvancedEdit') || false;
|
||||||
const backendType = this.backendType();
|
const backendType = this.backendType();
|
||||||
@ -168,8 +178,8 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
error(error) {
|
error(error) {
|
||||||
const { secret } = this.paramsFor(this.routeName);
|
let secret = this.secretParam();
|
||||||
const { backend } = this.paramsFor('vault.cluster.secrets.backend');
|
let backend = this.enginePathParam();
|
||||||
set(error, 'keyId', backend + '/' + secret);
|
set(error, 'keyId', backend + '/' + secret);
|
||||||
set(error, 'backend', backend);
|
set(error, 'backend', backend);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import Service from '@ember/service';
|
|||||||
import { getOwner } from '@ember/application';
|
import { getOwner } from '@ember/application';
|
||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import { shiftCommandIndex } from 'vault/lib/console-helpers';
|
import { shiftCommandIndex } from 'vault/lib/console-helpers';
|
||||||
|
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||||
|
|
||||||
export function sanitizePath(path) {
|
export function sanitizePath(path) {
|
||||||
//remove whitespace + remove trailing and leading slashes
|
//remove whitespace + remove trailing and leading slashes
|
||||||
@ -74,7 +75,7 @@ export default Service.extend({
|
|||||||
ajax(operation, path, options = {}) {
|
ajax(operation, path, options = {}) {
|
||||||
let verb = VERBS[operation];
|
let verb = VERBS[operation];
|
||||||
let adapter = this.adapter();
|
let adapter = this.adapter();
|
||||||
let url = adapter.buildURL(path);
|
let url = adapter.buildURL(encodePath(path));
|
||||||
let { data, wrapTTL } = options;
|
let { data, wrapTTL } = options;
|
||||||
return adapter.ajax(url, verb, {
|
return adapter.ajax(url, verb, {
|
||||||
data,
|
data,
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
item.id
|
item.id
|
||||||
class="list-item-row"
|
class="list-item-row"
|
||||||
data-test-secret-link=item.id
|
data-test-secret-link=item.id
|
||||||
|
encode=true
|
||||||
}}
|
}}
|
||||||
<div class="columns is-mobile">
|
<div class="columns is-mobile">
|
||||||
<div class="column is-10">
|
<div class="column is-10">
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
item.id
|
item.id
|
||||||
class="list-item-row"
|
class="list-item-row"
|
||||||
data-test-secret-link=item.id
|
data-test-secret-link=item.id
|
||||||
|
encode=true
|
||||||
}}
|
}}
|
||||||
<div class="columns is-mobile">
|
<div class="columns is-mobile">
|
||||||
<div class="column is-10">
|
<div class="column is-10">
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
class="list-item-row"
|
class="list-item-row"
|
||||||
data-test-secret-link=item.id
|
data-test-secret-link=item.id
|
||||||
tagName="div"
|
tagName="div"
|
||||||
|
encode=true
|
||||||
}}
|
}}
|
||||||
<div class="columns is-mobile">
|
<div class="columns is-mobile">
|
||||||
<div class="column is-10">
|
<div class="column is-10">
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
class="list-item-row"
|
class="list-item-row"
|
||||||
data-test-secret-link=item.id
|
data-test-secret-link=item.id
|
||||||
tagName="div"
|
tagName="div"
|
||||||
|
encode=true
|
||||||
}}
|
}}
|
||||||
<div class="columns is-mobile">
|
<div class="columns is-mobile">
|
||||||
<div class="column is-10">
|
<div class="column is-10">
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
item.id
|
item.id
|
||||||
class="list-item-row"
|
class="list-item-row"
|
||||||
data-test-secret-link=item.id
|
data-test-secret-link=item.id
|
||||||
|
encode=true
|
||||||
}}
|
}}
|
||||||
<div class="columns is-mobile">
|
<div class="columns is-mobile">
|
||||||
<div class="column is-10">
|
<div class="column is-10">
|
||||||
|
|||||||
38
ui/app/utils/args-tokenizer.js
Normal file
38
ui/app/utils/args-tokenizer.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
export default function(argString) {
|
||||||
|
if (Array.isArray(argString)) return argString;
|
||||||
|
|
||||||
|
argString = argString.trim();
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
var prevC = null;
|
||||||
|
var c = null;
|
||||||
|
var opening = null;
|
||||||
|
var args = [];
|
||||||
|
|
||||||
|
for (var ii = 0; ii < argString.length; ii++) {
|
||||||
|
prevC = c;
|
||||||
|
c = argString.charAt(ii);
|
||||||
|
|
||||||
|
// split on spaces unless we're in quotes.
|
||||||
|
if (c === ' ' && !opening) {
|
||||||
|
if (!(prevC === ' ')) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't split the string if we're in matching
|
||||||
|
// opening or closing single and double quotes.
|
||||||
|
if (c === opening) {
|
||||||
|
if (!args[i]) args[i] = '';
|
||||||
|
opening = null;
|
||||||
|
} else if ((c === "'" || c === '"') && argString.indexOf(c, ii + 1) > 0 && !opening) {
|
||||||
|
opening = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args[i]) args[i] = '';
|
||||||
|
args[i] += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
16
ui/app/utils/path-encoding-helpers.js
Normal file
16
ui/app/utils/path-encoding-helpers.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import RouteRecognizer from 'route-recognizer';
|
||||||
|
|
||||||
|
const {
|
||||||
|
Normalizer: { normalizePath, encodePathSegment },
|
||||||
|
} = RouteRecognizer;
|
||||||
|
|
||||||
|
export function encodePath(path) {
|
||||||
|
return path
|
||||||
|
? path
|
||||||
|
.split('/')
|
||||||
|
.map(encodePathSegment)
|
||||||
|
.join('/')
|
||||||
|
: path;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { normalizePath, encodePathSegment };
|
||||||
@ -94,6 +94,7 @@
|
|||||||
"ember-source": "~3.4.0",
|
"ember-source": "~3.4.0",
|
||||||
"ember-test-selectors": "^1.0.0",
|
"ember-test-selectors": "^1.0.0",
|
||||||
"ember-truth-helpers": "^2.1.0",
|
"ember-truth-helpers": "^2.1.0",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
"eslint-config-prettier": "^3.1.0",
|
"eslint-config-prettier": "^3.1.0",
|
||||||
"eslint-plugin-ember": "^5.2.0",
|
"eslint-plugin-ember": "^5.2.0",
|
||||||
"eslint-plugin-prettier": "^3.0.0",
|
"eslint-plugin-prettier": "^3.0.0",
|
||||||
@ -106,6 +107,7 @@
|
|||||||
"prettier": "^1.14.3",
|
"prettier": "^1.14.3",
|
||||||
"prettier-eslint-cli": "^4.7.1",
|
"prettier-eslint-cli": "^4.7.1",
|
||||||
"qunit-dom": "^0.7.1",
|
"qunit-dom": "^0.7.1",
|
||||||
|
"route-recognizer": "^0.3.4",
|
||||||
"sass-svg-uri": "^1.0.0",
|
"sass-svg-uri": "^1.0.0",
|
||||||
"string.prototype.endswith": "^0.2.0",
|
"string.prototype.endswith": "^0.2.0",
|
||||||
"string.prototype.startswith": "^0.2.0",
|
"string.prototype.startswith": "^0.2.0",
|
||||||
|
|||||||
@ -54,7 +54,7 @@ module('Acceptance | cluster', function(hooks) {
|
|||||||
await logout.visit();
|
await logout.visit();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('nav item links to first route that user has access to', async function(assert) {
|
test('enterprise nav item links to first route that user has access to', async function(assert) {
|
||||||
const read_rgp_policy = `'
|
const read_rgp_policy = `'
|
||||||
path "sys/policies/rgp" {
|
path "sys/policies/rgp" {
|
||||||
capabilities = ["read"]
|
capabilities = ["read"]
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { settled, currentURL, currentRouteName } from '@ember/test-helpers';
|
import { visit, settled, currentURL, currentRouteName } from '@ember/test-helpers';
|
||||||
import { create } from 'ember-cli-page-object';
|
import { create } from 'ember-cli-page-object';
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
@ -177,7 +177,8 @@ module('Acceptance | secrets/secret/create', function(hooks) {
|
|||||||
await listPage.visitRoot({ backend: 'secret' });
|
await listPage.visitRoot({ backend: 'secret' });
|
||||||
await listPage.create();
|
await listPage.create();
|
||||||
await editPage.createSecret(path, 'foo', 'bar');
|
await editPage.createSecret(path, 'foo', 'bar');
|
||||||
await listPage.visit({ backend: 'secret', id: 'foo/bar' });
|
// use visit helper here because ids with / in them get encoded
|
||||||
|
await visit('/vault/secrets/secret/list/foo/bar');
|
||||||
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.list');
|
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.list');
|
||||||
assert.ok(currentURL().endsWith('/'), 'redirects to the path ending in a slash');
|
assert.ok(currentURL().endsWith('/'), 'redirects to the path ending in a slash');
|
||||||
});
|
});
|
||||||
@ -259,4 +260,61 @@ module('Acceptance | secrets/secret/create', function(hooks) {
|
|||||||
assert.ok(showPage.editIsPresent, 'shows the edit button');
|
assert.ok(showPage.editIsPresent, 'shows the edit button');
|
||||||
await logout.visit();
|
await logout.visit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('paths are properly encoded', async function(assert) {
|
||||||
|
let backend = 'kv';
|
||||||
|
let paths = [
|
||||||
|
'(',
|
||||||
|
')',
|
||||||
|
'"',
|
||||||
|
//"'",
|
||||||
|
'!',
|
||||||
|
'#',
|
||||||
|
'$',
|
||||||
|
'&',
|
||||||
|
'*',
|
||||||
|
'+',
|
||||||
|
'@',
|
||||||
|
'{',
|
||||||
|
'|',
|
||||||
|
'}',
|
||||||
|
'~',
|
||||||
|
'[',
|
||||||
|
'\\',
|
||||||
|
']',
|
||||||
|
'^',
|
||||||
|
'_',
|
||||||
|
].map(char => `${char}some`);
|
||||||
|
assert.expect(paths.length * 2);
|
||||||
|
let secretName = '2';
|
||||||
|
let commands = paths.map(path => `write ${backend}/${path}/${secretName} 3=4`);
|
||||||
|
await consoleComponent.runCommands(['write sys/mounts/kv type=kv', ...commands]);
|
||||||
|
for (let path of paths) {
|
||||||
|
await listPage.visit({ backend, id: path });
|
||||||
|
assert.ok(listPage.secrets.filterBy('text', '2')[0], `${path}: secret is displayed properly`);
|
||||||
|
await listPage.secrets.filterBy('text', '2')[0].click();
|
||||||
|
assert.equal(
|
||||||
|
currentRouteName(),
|
||||||
|
'vault.cluster.secrets.backend.show',
|
||||||
|
`${path}: show page renders correctly`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// the web cli does not handle a single quote in a path, so we test it here via the UI
|
||||||
|
test('creating a secret with a single quote works properly', async function(assert) {
|
||||||
|
await consoleComponent.runCommands('write sys/mounts/kv type=kv');
|
||||||
|
let path = "'some";
|
||||||
|
await listPage.visitRoot({ backend: 'kv' });
|
||||||
|
await listPage.create();
|
||||||
|
await editPage.createSecret(`${path}/2`, 'foo', 'bar');
|
||||||
|
await listPage.visit({ backend: 'kv', id: path });
|
||||||
|
assert.ok(listPage.secrets.filterBy('text', '2')[0], `${path}: secret is displayed properly`);
|
||||||
|
await listPage.secrets.filterBy('text', '2')[0].click();
|
||||||
|
assert.equal(
|
||||||
|
currentRouteName(),
|
||||||
|
'vault.cluster.secrets.backend.show',
|
||||||
|
`${path}: show page renders correctly`
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11452,7 +11452,7 @@ rollup@^0.57.1:
|
|||||||
signal-exit "^3.0.2"
|
signal-exit "^3.0.2"
|
||||||
sourcemap-codec "^1.4.1"
|
sourcemap-codec "^1.4.1"
|
||||||
|
|
||||||
route-recognizer@^0.3.3:
|
route-recognizer@^0.3.3, route-recognizer@^0.3.4:
|
||||||
version "0.3.4"
|
version "0.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3"
|
resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3"
|
||||||
integrity sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==
|
integrity sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user