ui: Fix URL params decoding (#11931)

* ui: Move wildcard param decoding to routlet service
This commit is contained in:
John Cowen 2022-01-04 16:08:06 +00:00 committed by GitHub
parent 2e571b6406
commit 1779246257
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 30 deletions

3
.changelog/11931.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
ui: Fixes a bug with URL decoding within KV area
```

View File

@ -2,17 +2,14 @@ import Route from '@ember/routing/route';
import { get, setProperties, action } from '@ember/object'; import { get, setProperties, action } from '@ember/object';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
// paramsFor
import { routes } from 'consul-ui/router'; import { routes } from 'consul-ui/router';
import wildcard from 'consul-ui/utils/routing/wildcard';
const isWildcard = wildcard(routes);
export default class BaseRoute extends Route { export default class BaseRoute extends Route {
@service('container') container; @service('container') container;
@service('env') env; @service('env') env;
@service('repository/permission') permissions; @service('repository/permission') permissions;
@service('router') router; @service('router') router;
@service('routlet') routlet;
_setRouteName() { _setRouteName() {
super._setRouteName(...arguments); super._setRouteName(...arguments);
@ -114,27 +111,14 @@ export default class BaseRoute extends Route {
} }
/** /**
* Adds urldecoding to any wildcard route `params` passed into ember `model` * Normalizes any params passed into ember `model` hooks, plus of course
* hooks, plus of course anywhere else where `paramsFor` is used. This means * anywhere else where `paramsFor` is used. This means the entire ember app
* the entire ember app is now changed so that all paramsFor calls returns * is now changed so that all paramsFor calls returns normalized params
* urldecoded params instead of raw ones. * instead of raw ones. For example we use this largely for URLs for the KV
* For example we use this largely for URLs for the KV store: * store: /kv/*key > /ui/kv/%25-kv-name/%25-here > key = '%-kv-name/%-here'
* /kv/*key > /ui/kv/%25-kv-name/%25-here > key = '%-kv-name/%-here'
*/ */
paramsFor(name) { paramsFor(name) {
const params = super.paramsFor(...arguments); return this.routlet.normalizeParamsFor(this.routeName, super.paramsFor(...arguments));
if (isWildcard(this.routeName)) {
return Object.keys(params).reduce(function(prev, item) {
if (typeof params[item] !== 'undefined') {
prev[item] = decodeURIComponent(params[item]);
} else {
prev[item] = params[item];
}
return prev;
}, {});
} else {
return params;
}
} }
@action @action

View File

@ -1,6 +1,11 @@
import Service, { inject as service } from '@ember/service'; import Service, { inject as service } from '@ember/service';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
import wildcard from 'consul-ui/utils/routing/wildcard';
import { routes } from 'consul-ui/router';
const isWildcard = wildcard(routes);
class Outlets { class Outlets {
constructor() { constructor() {
this.map = new Map(); this.map = new Map();
@ -87,6 +92,24 @@ export default class RoutletService extends Service {
return {}; return {};
} }
/**
* Adds urldecoding to any wildcard route `params`
*/
normalizeParamsFor(name, params = {}) {
if (isWildcard(name)) {
return Object.keys(params).reduce(function(prev, item) {
if (typeof params[item] !== 'undefined') {
prev[item] = decodeURIComponent(params[item]);
} else {
prev[item] = params[item];
}
return prev;
}, {});
} else {
return params;
}
}
paramsFor(name) { paramsFor(name) {
let outletParams = {}; let outletParams = {};
const outlet = outlets.get(name); const outlet = outlets.get(name);
@ -102,16 +125,14 @@ export default class RoutletService extends Service {
// of the specified params with the values specified // of the specified params with the values specified
let current = route; let current = route;
let parent; let parent;
let routeParams = { let routeParams = this.normalizeParamsFor(name, current.params);
...current.params,
};
// TODO: Not entirely sure whether we are ok exposing queryParams here // TODO: Not entirely sure whether we are ok exposing queryParams here
// seeing as accessing them from here means you can get them but not set // seeing as accessing them from here means you can get them but not set
// them as yet // them as yet
// let queryParams = {}; // let queryParams = {};
while ((parent = current.parent)) { while ((parent = current.parent)) {
routeParams = { routeParams = {
...parent.params, ...this.normalizeParamsFor(parent.name, parent.params),
...routeParams, ...routeParams,
}; };
// queryParams = { // queryParams = {

View File

@ -83,7 +83,7 @@ as |parts|}}
</BlockSlot> </BlockSlot>
<BlockSlot @name="header"> <BlockSlot @name="header">
<h1> <h1 data-test-kv-key={{item.Key}}>
{{#if (and item.Key (not-eq item.Key parentKey))}} {{#if (and item.Key (not-eq item.Key parentKey))}}
<route.Title @title="Edit Key / Value" @render={{false}} /> <route.Title @title="Edit Key / Value" @render={{false}} />
{{left-trim item.Key parentKey}} {{left-trim item.Key parentKey}}

View File

@ -1,6 +1,19 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / kvs / edit: KV Viewing Feature: dc / kvs / edit: KV Viewing
Scenario: Scenario: Viewing a KV with a URL unsafe character
Given 1 datacenter model with the value "datacenter"
And 1 kv model from yaml
---
Key: "@key"
---
When I visit the kv page for yaml
---
dc: datacenter
kv: "@key"
---
Then the url should be /datacenter/kv/%40key/edit
And I see Key on the kv like "@key"
Scenario: Viewing a Session attached to a KV
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
And 1 kv model from yaml And 1 kv model from yaml
--- ---
@ -14,7 +27,9 @@ Feature: dc / kvs / edit: KV Viewing
--- ---
Then the url should be /datacenter/kv/key/edit Then the url should be /datacenter/kv/key/edit
And I see ID on the session like "session-id" And I see ID on the session like "session-id"
Given 1 kv model from yaml Scenario: Viewing a Session attached to a KV
Given 1 datacenter model with the value "datacenter"
And 1 kv model from yaml
--- ---
Key: another-key Key: another-key
Session: ~ Session: ~

View File

@ -11,6 +11,9 @@ export default function(visitable, attribute, present, submitable, deletable, ca
...submitable({}, 'main'), ...submitable({}, 'main'),
...cancelable(), ...cancelable(),
...deletable(), ...deletable(),
kv: {
Key: attribute('data-test-kv-key', '[data-test-kv-key]')
},
session: { session: {
warning: present('[data-test-session-warning]'), warning: present('[data-test-session-warning]'),
ID: attribute('data-test-session', '[data-test-session]'), ID: attribute('data-test-session', '[data-test-session]'),