mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-04 04:28:08 +00: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:
		@@ -2,11 +2,12 @@ import { assign } from '@ember/polyfills';
 | 
			
		||||
import { get, set } from '@ember/object';
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import DS from 'ember-data';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  url(path) {
 | 
			
		||||
    const url = `${this.buildURL()}/auth`;
 | 
			
		||||
    return path ? url + '/' + path : url;
 | 
			
		||||
    return path ? url + '/' + encodePath(path) : url;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // used in updateRecord on the model#tune action
 | 
			
		||||
@@ -58,6 +59,6 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  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 { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  revokePrefix(prefix) {
 | 
			
		||||
    let url = this.buildURL() + '/leases/revoke-prefix/' + prefix;
 | 
			
		||||
    let url = this.buildURL() + '/leases/revoke-prefix/' + encodePath(prefix);
 | 
			
		||||
    url = url.replace(/\/$/, '');
 | 
			
		||||
    return this.ajax(url, 'PUT');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  forceRevokePrefix(prefix) {
 | 
			
		||||
    let url = this.buildURL() + '/leases/revoke-prefix/' + prefix;
 | 
			
		||||
    let url = this.buildURL() + '/leases/revoke-prefix/' + encodePath(prefix);
 | 
			
		||||
    url = url.replace(/\/$/, '');
 | 
			
		||||
    return this.ajax(url, 'PUT');
 | 
			
		||||
  },
 | 
			
		||||
@@ -43,7 +44,7 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
 | 
			
		||||
  query(store, type, query) {
 | 
			
		||||
    const prefix = query.prefix || '';
 | 
			
		||||
    return this.ajax(this.buildURL() + '/leases/lookup/' + prefix, 'GET', {
 | 
			
		||||
    return this.ajax(this.buildURL() + '/leases/lookup/' + encodePath(prefix), 'GET', {
 | 
			
		||||
      data: {
 | 
			
		||||
        list: true,
 | 
			
		||||
      },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { assign } from '@ember/polyfills';
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  namespace: 'v1',
 | 
			
		||||
@@ -31,9 +32,9 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  urlForRole(backend, id) {
 | 
			
		||||
    let url = `${this.buildURL()}/${backend}/roles`;
 | 
			
		||||
    let url = `${this.buildURL()}/${encodePath(backend)}/roles`;
 | 
			
		||||
    if (id) {
 | 
			
		||||
      url = url + '/' + id;
 | 
			
		||||
      url = url + '/' + encodePath(id);
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,14 @@
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import { inject as service } from '@ember/service';
 | 
			
		||||
import { get } from '@ember/object';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  router: service(),
 | 
			
		||||
 | 
			
		||||
  findRecord(store, type, id, snapshot) {
 | 
			
		||||
    let [path, role] = JSON.parse(id);
 | 
			
		||||
    path = encodePath(path);
 | 
			
		||||
 | 
			
		||||
    let namespace = get(snapshot, 'adapterOptions.namespace');
 | 
			
		||||
    let url = `/v1/auth/${path}/oidc/auth_url`;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { assign } from '@ember/polyfills';
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  namespace: 'v1',
 | 
			
		||||
@@ -31,9 +32,9 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  urlForRole(backend, id) {
 | 
			
		||||
    let url = `${this.buildURL()}/${backend}/roles`;
 | 
			
		||||
    let url = `${this.buildURL()}/${encodePath(backend)}/roles`;
 | 
			
		||||
    if (id) {
 | 
			
		||||
      url = url + '/' + id;
 | 
			
		||||
      url = url + '/' + encodePath(id);
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { assign } from '@ember/polyfills';
 | 
			
		||||
import { resolve, allSettled } from 'rsvp';
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  namespace: 'v1',
 | 
			
		||||
@@ -34,9 +35,9 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  urlForRole(backend, id) {
 | 
			
		||||
    let url = `${this.buildURL()}/${backend}/roles`;
 | 
			
		||||
    let url = `${this.buildURL()}/${encodePath(backend)}/roles`;
 | 
			
		||||
    if (id) {
 | 
			
		||||
      url = url + '/' + id;
 | 
			
		||||
      url = url + '/' + encodePath(id);
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  },
 | 
			
		||||
@@ -84,7 +85,7 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
 | 
			
		||||
  findAllZeroAddress(store, query) {
 | 
			
		||||
    const { backend } = query;
 | 
			
		||||
    const url = `/v1/${backend}/config/zeroaddress`;
 | 
			
		||||
    const url = `/v1/${encodePath(backend)}/config/zeroaddress`;
 | 
			
		||||
    return this.ajax(url, 'GET');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,17 @@
 | 
			
		||||
import { assign } from '@ember/polyfills';
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  url(path) {
 | 
			
		||||
    const url = `${this.buildURL()}/mounts`;
 | 
			
		||||
    return path ? url + '/' + path : url;
 | 
			
		||||
    return path ? url + '/' + encodePath(path) : url;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  internalURL(path) {
 | 
			
		||||
    let url = `/${this.urlPrefix()}/internal/ui/mounts`;
 | 
			
		||||
    if (path) {
 | 
			
		||||
      url = `${url}/${path}`;
 | 
			
		||||
      url = `${url}/${encodePath(path)}`;
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  },
 | 
			
		||||
@@ -38,14 +39,14 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
 | 
			
		||||
  findRecord(store, type, path, snapshot) {
 | 
			
		||||
    if (snapshot.attr('type') === 'ssh') {
 | 
			
		||||
      return this.ajax(`/v1/${path}/config/ca`, 'GET');
 | 
			
		||||
      return this.ajax(`/v1/${encodePath(path)}/config/ca`, 'GET');
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  queryRecord(store, type, query) {
 | 
			
		||||
    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 + '/';
 | 
			
		||||
        return resp;
 | 
			
		||||
      });
 | 
			
		||||
@@ -61,25 +62,25 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
    if (apiPath) {
 | 
			
		||||
      const serializer = store.serializerFor(type.modelName);
 | 
			
		||||
      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 });
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  saveAWSRoot(store, type, snapshot) {
 | 
			
		||||
    let { data } = snapshot.adapterOptions;
 | 
			
		||||
    const path = snapshot.id;
 | 
			
		||||
    const path = encodePath(snapshot.id);
 | 
			
		||||
    return this.ajax(`/v1/${path}/config/root`, 'POST', { data });
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  saveAWSLease(store, type, snapshot) {
 | 
			
		||||
    let { data } = snapshot.adapterOptions;
 | 
			
		||||
    const path = snapshot.id;
 | 
			
		||||
    const path = encodePath(snapshot.id);
 | 
			
		||||
    return this.ajax(`/v1/${path}/config/lease`, 'POST', { data });
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  saveZeroAddressConfig(store, type, snapshot) {
 | 
			
		||||
    const path = snapshot.id;
 | 
			
		||||
    const path = encodePath(snapshot.id);
 | 
			
		||||
    const roles = store
 | 
			
		||||
      .peekAll('role-ssh')
 | 
			
		||||
      .filterBy('zeroAddress')
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,14 @@ import { isEmpty } from '@ember/utils';
 | 
			
		||||
import { get } from '@ember/object';
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import DS from 'ember-data';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  namespace: 'v1',
 | 
			
		||||
  _url(backend, id, infix = 'data') {
 | 
			
		||||
    let url = `${this.buildURL()}/${backend}/${infix}/`;
 | 
			
		||||
    let url = `${this.buildURL()}/${encodePath(backend)}/${infix}/`;
 | 
			
		||||
    if (!isEmpty(id)) {
 | 
			
		||||
      url = url + id;
 | 
			
		||||
      url = url + encodePath(id);
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,14 @@
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import { isEmpty } from '@ember/utils';
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  namespace: 'v1',
 | 
			
		||||
  _url(backend, id) {
 | 
			
		||||
    let url = `${this.buildURL()}/${backend}/metadata/`;
 | 
			
		||||
    let url = `${this.buildURL()}/${encodePath(backend)}/metadata/`;
 | 
			
		||||
    if (!isEmpty(id)) {
 | 
			
		||||
      url = url + id;
 | 
			
		||||
      url = url + encodePath(id);
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { isEmpty } from '@ember/utils';
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  namespace: 'v1',
 | 
			
		||||
@@ -26,9 +27,9 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  urlForSecret(backend, id) {
 | 
			
		||||
    let url = `${this.buildURL()}/${backend}/`;
 | 
			
		||||
    let url = `${this.buildURL()}/${encodePath(backend)}/`;
 | 
			
		||||
    if (!isEmpty(id)) {
 | 
			
		||||
      url = url + id;
 | 
			
		||||
      url = url + encodePath(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return url;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import ApplicationAdapter from './application';
 | 
			
		||||
import { pluralize } from 'ember-inflector';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default ApplicationAdapter.extend({
 | 
			
		||||
  namespace: 'v1',
 | 
			
		||||
@@ -47,29 +48,29 @@ export default ApplicationAdapter.extend({
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  urlForSecret(backend, id) {
 | 
			
		||||
    let url = `${this.buildURL()}/${backend}/keys/`;
 | 
			
		||||
    let url = `${this.buildURL()}/${encodePath(backend)}/keys/`;
 | 
			
		||||
    if (id) {
 | 
			
		||||
      url += id;
 | 
			
		||||
      url += encodePath(id);
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  urlForAction(action, backend, id, param) {
 | 
			
		||||
    let urlBase = `${this.buildURL()}/${backend}/${action}`;
 | 
			
		||||
    let urlBase = `${this.buildURL()}/${encodePath(backend)}/${action}`;
 | 
			
		||||
    // these aren't key-specific
 | 
			
		||||
    if (action === 'hash' || action === 'random') {
 | 
			
		||||
      return urlBase;
 | 
			
		||||
    }
 | 
			
		||||
    if (action === 'datakey' && param) {
 | 
			
		||||
      // datakey action has `wrapped` or `plaintext` as part of the url
 | 
			
		||||
      return `${urlBase}/${param}/${id}`;
 | 
			
		||||
      return `${urlBase}/${param}/${encodePath(id)}`;
 | 
			
		||||
    }
 | 
			
		||||
    if (action === 'export' && param) {
 | 
			
		||||
      let [type, version] = param;
 | 
			
		||||
      const exportBase = `${urlBase}/${type}-key/${id}`;
 | 
			
		||||
      const exportBase = `${urlBase}/${type}-key/${encodePath(id)}`;
 | 
			
		||||
      return version ? `${exportBase}/${version}` : exportBase;
 | 
			
		||||
    }
 | 
			
		||||
    return `${urlBase}/${id}`;
 | 
			
		||||
    return `${urlBase}/${encodePath(id)}`;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  optionsForQuery(id) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { computed } from '@ember/object';
 | 
			
		||||
import Component from '@ember/component';
 | 
			
		||||
import utils from 'vault/lib/key-utils';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default Component.extend({
 | 
			
		||||
  tagName: 'nav',
 | 
			
		||||
@@ -31,7 +32,7 @@ export default Component.extend({
 | 
			
		||||
    let crumbs = [];
 | 
			
		||||
    const root = this.get('root');
 | 
			
		||||
    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) {
 | 
			
		||||
      crumbs.push(root);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { inject as service } from '@ember/service';
 | 
			
		||||
import Component from '@ember/component';
 | 
			
		||||
import hbs from 'htmlbars-inline-precompile';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
let LinkedBlockComponent = Component.extend({
 | 
			
		||||
  router: service(),
 | 
			
		||||
@@ -11,6 +12,8 @@ let LinkedBlockComponent = Component.extend({
 | 
			
		||||
 | 
			
		||||
  queryParams: null,
 | 
			
		||||
 | 
			
		||||
  encode: false,
 | 
			
		||||
 | 
			
		||||
  click(event) {
 | 
			
		||||
    const $target = this.$(event.target);
 | 
			
		||||
    const isAnchorOrButton =
 | 
			
		||||
@@ -19,7 +22,15 @@ let LinkedBlockComponent = Component.extend({
 | 
			
		||||
      $target.closest('button', event.currentTarget).length > 0 ||
 | 
			
		||||
      $target.closest('a', event.currentTarget).length > 0;
 | 
			
		||||
    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');
 | 
			
		||||
      if (queryParams) {
 | 
			
		||||
        params.push({ queryParams });
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import Component from '@ember/component';
 | 
			
		||||
import utils from 'vault/lib/key-utils';
 | 
			
		||||
import keys from 'vault/lib/keycodes';
 | 
			
		||||
import FocusOnInsertMixin from 'vault/mixins/focus-on-insert';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
const routeFor = function(type, mode) {
 | 
			
		||||
  const MODES = {
 | 
			
		||||
@@ -43,8 +44,15 @@ export default Component.extend(FocusOnInsertMixin, {
 | 
			
		||||
  filterMatchesKey: null,
 | 
			
		||||
  firstPartialMatch: null,
 | 
			
		||||
 | 
			
		||||
  transitionToRoute: function() {
 | 
			
		||||
    this.get('router').transitionTo(...arguments);
 | 
			
		||||
  transitionToRoute(...args) {
 | 
			
		||||
    let params = args.map((param, index) => {
 | 
			
		||||
      if (index === 0 || typeof param !== 'string') {
 | 
			
		||||
        return param;
 | 
			
		||||
      }
 | 
			
		||||
      return encodePath(param);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.get('router').transitionTo(...params);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  shouldFocus: false,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { computed } from '@ember/object';
 | 
			
		||||
import Component from '@ember/component';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export function linkParams({ mode, secret, queryParams }) {
 | 
			
		||||
  let params;
 | 
			
		||||
@@ -8,7 +9,7 @@ export function linkParams({ mode, secret, queryParams }) {
 | 
			
		||||
  if (!secret || secret === ' ') {
 | 
			
		||||
    params = [route + '-root'];
 | 
			
		||||
  } else {
 | 
			
		||||
    params = [route, secret];
 | 
			
		||||
    params = [route, encodePath(secret)];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (queryParams) {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,19 +2,12 @@ import { inject as service } from '@ember/service';
 | 
			
		||||
import { computed } from '@ember/object';
 | 
			
		||||
import Controller, { inject as controller } from '@ember/controller';
 | 
			
		||||
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(),
 | 
			
		||||
  store: service(),
 | 
			
		||||
  clusterController: controller('vault.cluster'),
 | 
			
		||||
  queryParams: {
 | 
			
		||||
    page: 'page',
 | 
			
		||||
    pageFilter: 'pageFilter',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  page: 1,
 | 
			
		||||
  pageFilter: null,
 | 
			
		||||
  filter: null,
 | 
			
		||||
 | 
			
		||||
  backendCrumb: computed(function() {
 | 
			
		||||
    return {
 | 
			
		||||
@@ -27,24 +20,6 @@ export default Controller.extend({
 | 
			
		||||
 | 
			
		||||
  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() {
 | 
			
		||||
    return !!utils.keyIsFolder(this.get('filter'));
 | 
			
		||||
  }),
 | 
			
		||||
@@ -65,14 +40,6 @@ export default Controller.extend({
 | 
			
		||||
  }),
 | 
			
		||||
 | 
			
		||||
  actions: {
 | 
			
		||||
    setFilter(val) {
 | 
			
		||||
      this.set('filter', val);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setFilterFocus(bool) {
 | 
			
		||||
      this.set('filterFocused', bool);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    revokePrefix(prefix, isForce) {
 | 
			
		||||
      const adapter = this.get('store').adapterFor('lease');
 | 
			
		||||
      const method = isForce ? 'forceRevokePrefix' : 'revokePrefix';
 | 
			
		||||
 
 | 
			
		||||
@@ -4,50 +4,19 @@ import Controller from '@ember/controller';
 | 
			
		||||
import utils from 'vault/lib/key-utils';
 | 
			
		||||
import BackendCrumbMixin from 'vault/mixins/backend-crumb';
 | 
			
		||||
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(),
 | 
			
		||||
  queryParams: ['page', 'pageFilter', '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() {
 | 
			
		||||
    return !!utils.keyIsFolder(this.get('filter'));
 | 
			
		||||
  }),
 | 
			
		||||
 | 
			
		||||
  actions: {
 | 
			
		||||
    setFilter(val) {
 | 
			
		||||
      this.set('filter', val);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setFilterFocus(bool) {
 | 
			
		||||
      this.set('filterFocused', bool);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    chooseAction(action) {
 | 
			
		||||
      this.set('selectedAction', action);
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -56,11 +56,14 @@ export function executeUICommand(command, logAndOutput, clearLog, toggleFullscre
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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') {
 | 
			
		||||
    args.shift();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  args = args.map(decodeURIComponent);
 | 
			
		||||
  let [method, ...rest] = args;
 | 
			
		||||
  let path;
 | 
			
		||||
  let flags = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { computed } from '@ember/object';
 | 
			
		||||
import Mixin from '@ember/object/mixin';
 | 
			
		||||
import escapeStringRegexp from 'escape-string-regexp';
 | 
			
		||||
 | 
			
		||||
export default Mixin.create({
 | 
			
		||||
  queryParams: {
 | 
			
		||||
@@ -10,6 +11,7 @@ export default Mixin.create({
 | 
			
		||||
  page: 1,
 | 
			
		||||
  pageFilter: null,
 | 
			
		||||
  filter: null,
 | 
			
		||||
  filterFocused: false,
 | 
			
		||||
 | 
			
		||||
  isLoading: false,
 | 
			
		||||
 | 
			
		||||
@@ -23,7 +25,7 @@ export default Mixin.create({
 | 
			
		||||
    var filter = this.get('filter');
 | 
			
		||||
    var content = this.get('model');
 | 
			
		||||
    var filterMatchesKey = this.get('filterMatchesKey');
 | 
			
		||||
    var re = new RegExp('^' + filter);
 | 
			
		||||
    var re = new RegExp('^' + escapeStringRegexp(filter));
 | 
			
		||||
    return filterMatchesKey
 | 
			
		||||
      ? null
 | 
			
		||||
      : content.find(function(key) {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,13 @@ import Route from '@ember/routing/route';
 | 
			
		||||
import { getOwner } from '@ember/application';
 | 
			
		||||
import { supportedSecretBackends } from 'vault/helpers/supported-secret-backends';
 | 
			
		||||
import { inject as service } from '@ember/service';
 | 
			
		||||
import { normalizePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
const SUPPORTED_BACKENDS = supportedSecretBackends();
 | 
			
		||||
 | 
			
		||||
export default Route.extend({
 | 
			
		||||
  templateName: 'vault/cluster/secrets/backend/list',
 | 
			
		||||
  pathHelp: service('path-help'),
 | 
			
		||||
  queryParams: {
 | 
			
		||||
    page: {
 | 
			
		||||
      refreshModel: true,
 | 
			
		||||
@@ -20,13 +23,21 @@ export default Route.extend({
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  templateName: 'vault/cluster/secrets/backend/list',
 | 
			
		||||
  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;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  beforeModel() {
 | 
			
		||||
    let owner = getOwner(this);
 | 
			
		||||
    let { secret } = this.paramsFor(this.routeName);
 | 
			
		||||
    let { backend, tab } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
    let secret = this.secretParam();
 | 
			
		||||
    let backend = this.enginePathParam();
 | 
			
		||||
    let { tab } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
    let secretEngine = this.store.peekRecord('secret-engine', backend);
 | 
			
		||||
    let type = secretEngine && secretEngine.get('engineType');
 | 
			
		||||
    if (!type || !SUPPORTED_BACKENDS.includes(type)) {
 | 
			
		||||
@@ -58,8 +69,8 @@ export default Route.extend({
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  model(params) {
 | 
			
		||||
    const secret = params.secret ? params.secret : '';
 | 
			
		||||
    const { backend } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
    const secret = this.secretParam() || '';
 | 
			
		||||
    const backend = this.enginePathParam();
 | 
			
		||||
    const backendModel = this.modelFor('vault.cluster.secrets.backend');
 | 
			
		||||
    return hash({
 | 
			
		||||
      secret,
 | 
			
		||||
@@ -89,7 +100,7 @@ export default Route.extend({
 | 
			
		||||
 | 
			
		||||
  afterModel(model) {
 | 
			
		||||
    const { tab } = this.paramsFor(this.routeName);
 | 
			
		||||
    const { backend } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
    const backend = this.enginePathParam();
 | 
			
		||||
    if (!tab || tab !== 'certs') {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -114,7 +125,7 @@ export default Route.extend({
 | 
			
		||||
    let secretParams = this.paramsFor(this.routeName);
 | 
			
		||||
    let secret = resolvedModel.secret;
 | 
			
		||||
    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 has404 = this.get('has404');
 | 
			
		||||
    // only clear store cache if this is a new model
 | 
			
		||||
@@ -155,8 +166,8 @@ export default Route.extend({
 | 
			
		||||
 | 
			
		||||
  actions: {
 | 
			
		||||
    error(error, transition) {
 | 
			
		||||
      let { secret } = this.paramsFor(this.routeName);
 | 
			
		||||
      let { backend } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
      let secret = this.secretParam();
 | 
			
		||||
      let backend = this.enginePathParam();
 | 
			
		||||
      let is404 = error.httpStatus === 404;
 | 
			
		||||
      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 { getOwner } from '@ember/application';
 | 
			
		||||
import UnloadModelRoute from 'vault/mixins/unload-model-route';
 | 
			
		||||
import { encodePath, normalizePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export default Route.extend(UnloadModelRoute, {
 | 
			
		||||
  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) {
 | 
			
		||||
    const { backend } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
    const backend = this.enginePathParam();
 | 
			
		||||
    let backendModel = this.modelFor('vault.cluster.secrets.backend');
 | 
			
		||||
    let backendType = backendModel.get('engineType');
 | 
			
		||||
    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'
 | 
			
		||||
    // perhaps in the future we could recurse _for_ users, but for now, just kick them
 | 
			
		||||
    // back to the list
 | 
			
		||||
    const { secret } = this.paramsFor(this.routeName);
 | 
			
		||||
    let secret = this.secretParam();
 | 
			
		||||
    return this.buildModel(secret).then(() => {
 | 
			
		||||
      const parentKey = utils.parentKeyForKey(secret);
 | 
			
		||||
      const mode = this.routeName.split('.').pop();
 | 
			
		||||
      if (mode === 'edit' && utils.keyIsFolder(secret)) {
 | 
			
		||||
        if (parentKey) {
 | 
			
		||||
          return this.transitionTo('vault.cluster.secrets.backend.list', parentKey);
 | 
			
		||||
          return this.transitionTo('vault.cluster.secrets.backend.list', encodePath(parentKey));
 | 
			
		||||
        } else {
 | 
			
		||||
          return this.transitionTo('vault.cluster.secrets.backend.list-root');
 | 
			
		||||
        }
 | 
			
		||||
@@ -52,7 +61,8 @@ export default Route.extend(UnloadModelRoute, {
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  buildModel(secret) {
 | 
			
		||||
    const { backend } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
    const backend = this.enginePathParam();
 | 
			
		||||
 | 
			
		||||
    let modelType = this.modelType(backend, secret);
 | 
			
		||||
    if (['secret', 'secret-v2'].includes(modelType)) {
 | 
			
		||||
      return resolve();
 | 
			
		||||
@@ -77,10 +87,10 @@ export default Route.extend(UnloadModelRoute, {
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  model(params) {
 | 
			
		||||
    let { secret } = params;
 | 
			
		||||
    const { backend } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
    let secret = this.secretParam();
 | 
			
		||||
    let backend = this.enginePathParam();
 | 
			
		||||
    let backendModel = this.modelFor('vault.cluster.secrets.backend', backend);
 | 
			
		||||
    const modelType = this.modelType(backend, secret);
 | 
			
		||||
    let modelType = this.modelType(backend, secret);
 | 
			
		||||
 | 
			
		||||
    if (!secret) {
 | 
			
		||||
      secret = '\u0020';
 | 
			
		||||
@@ -139,8 +149,8 @@ export default Route.extend(UnloadModelRoute, {
 | 
			
		||||
 | 
			
		||||
  setupController(controller, model) {
 | 
			
		||||
    this._super(...arguments);
 | 
			
		||||
    const { secret } = this.paramsFor(this.routeName);
 | 
			
		||||
    const { backend } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
    let secret = this.secretParam();
 | 
			
		||||
    let backend = this.enginePathParam();
 | 
			
		||||
    const preferAdvancedEdit =
 | 
			
		||||
      this.controllerFor('vault.cluster.secrets.backend').get('preferAdvancedEdit') || false;
 | 
			
		||||
    const backendType = this.backendType();
 | 
			
		||||
@@ -168,8 +178,8 @@ export default Route.extend(UnloadModelRoute, {
 | 
			
		||||
 | 
			
		||||
  actions: {
 | 
			
		||||
    error(error) {
 | 
			
		||||
      const { secret } = this.paramsFor(this.routeName);
 | 
			
		||||
      const { backend } = this.paramsFor('vault.cluster.secrets.backend');
 | 
			
		||||
      let secret = this.secretParam();
 | 
			
		||||
      let backend = this.enginePathParam();
 | 
			
		||||
      set(error, 'keyId', backend + '/' + secret);
 | 
			
		||||
      set(error, 'backend', backend);
 | 
			
		||||
      return true;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import Service from '@ember/service';
 | 
			
		||||
import { getOwner } from '@ember/application';
 | 
			
		||||
import { computed } from '@ember/object';
 | 
			
		||||
import { shiftCommandIndex } from 'vault/lib/console-helpers';
 | 
			
		||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
 | 
			
		||||
 | 
			
		||||
export function sanitizePath(path) {
 | 
			
		||||
  //remove whitespace + remove trailing and leading slashes
 | 
			
		||||
@@ -74,7 +75,7 @@ export default Service.extend({
 | 
			
		||||
  ajax(operation, path, options = {}) {
 | 
			
		||||
    let verb = VERBS[operation];
 | 
			
		||||
    let adapter = this.adapter();
 | 
			
		||||
    let url = adapter.buildURL(path);
 | 
			
		||||
    let url = adapter.buildURL(encodePath(path));
 | 
			
		||||
    let { data, wrapTTL } = options;
 | 
			
		||||
    return adapter.ajax(url, verb, {
 | 
			
		||||
      data,
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
  item.id
 | 
			
		||||
  class="list-item-row"
 | 
			
		||||
  data-test-secret-link=item.id
 | 
			
		||||
  encode=true
 | 
			
		||||
}}
 | 
			
		||||
  <div class="columns is-mobile">
 | 
			
		||||
    <div class="column is-10">
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
  item.id
 | 
			
		||||
  class="list-item-row"
 | 
			
		||||
  data-test-secret-link=item.id
 | 
			
		||||
  encode=true
 | 
			
		||||
}}
 | 
			
		||||
  <div class="columns is-mobile">
 | 
			
		||||
    <div class="column is-10">
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
  class="list-item-row"
 | 
			
		||||
  data-test-secret-link=item.id
 | 
			
		||||
  tagName="div"
 | 
			
		||||
  encode=true
 | 
			
		||||
}}
 | 
			
		||||
  <div class="columns is-mobile">
 | 
			
		||||
    <div class="column is-10">
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
  class="list-item-row"
 | 
			
		||||
  data-test-secret-link=item.id
 | 
			
		||||
  tagName="div"
 | 
			
		||||
  encode=true
 | 
			
		||||
}}
 | 
			
		||||
  <div class="columns is-mobile">
 | 
			
		||||
    <div class="column is-10">
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
  item.id
 | 
			
		||||
  class="list-item-row"
 | 
			
		||||
  data-test-secret-link=item.id
 | 
			
		||||
  encode=true
 | 
			
		||||
}}
 | 
			
		||||
  <div class="columns is-mobile">
 | 
			
		||||
    <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-test-selectors": "^1.0.0",
 | 
			
		||||
    "ember-truth-helpers": "^2.1.0",
 | 
			
		||||
    "escape-string-regexp": "^1.0.5",
 | 
			
		||||
    "eslint-config-prettier": "^3.1.0",
 | 
			
		||||
    "eslint-plugin-ember": "^5.2.0",
 | 
			
		||||
    "eslint-plugin-prettier": "^3.0.0",
 | 
			
		||||
@@ -106,6 +107,7 @@
 | 
			
		||||
    "prettier": "^1.14.3",
 | 
			
		||||
    "prettier-eslint-cli": "^4.7.1",
 | 
			
		||||
    "qunit-dom": "^0.7.1",
 | 
			
		||||
    "route-recognizer": "^0.3.4",
 | 
			
		||||
    "sass-svg-uri": "^1.0.0",
 | 
			
		||||
    "string.prototype.endswith": "^0.2.0",
 | 
			
		||||
    "string.prototype.startswith": "^0.2.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ module('Acceptance | cluster', function(hooks) {
 | 
			
		||||
    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 = `'
 | 
			
		||||
      path "sys/policies/rgp" {
 | 
			
		||||
        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 { module, test } from 'qunit';
 | 
			
		||||
import { setupApplicationTest } from 'ember-qunit';
 | 
			
		||||
@@ -177,7 +177,8 @@ module('Acceptance | secrets/secret/create', function(hooks) {
 | 
			
		||||
    await listPage.visitRoot({ backend: 'secret' });
 | 
			
		||||
    await listPage.create();
 | 
			
		||||
    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.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');
 | 
			
		||||
    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"
 | 
			
		||||
    sourcemap-codec "^1.4.1"
 | 
			
		||||
 | 
			
		||||
route-recognizer@^0.3.3:
 | 
			
		||||
route-recognizer@^0.3.3, route-recognizer@^0.3.4:
 | 
			
		||||
  version "0.3.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3"
 | 
			
		||||
  integrity sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user