Skip to content

Commit

Permalink
mgr/dashboard: Add decorator to skip parameter encoding
Browse files Browse the repository at this point in the history
By enconding all parameters of api services we were also encoding parameters
that were being sent in the body of the request.
Those parameters don't need to be enconded and the server never decodes them.

With this new decorator you can specify if you don't want a parameter to be
enconded.

This is a regression introduced in f21d0da.

Fixes: http://tracker.ceph.com/issues/26856

Signed-off-by: Tiago Melo <tmelo@suse.com>
(cherry picked from commit 45e645b)

Conflicts:
  src/pybind/mgr/dashboard/frontend/src/app/shared/api/rbd.service.spec.ts
  src/pybind/mgr/dashboard/frontend/src/app/shared/api/rbd.service.ts
  • Loading branch information
tspmelo committed Oct 9, 2018
1 parent 9baf120 commit eda4df3
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

import { cdEncode } from '../decorators/cd-encode';
import { cdEncode, cdEncodeNot } from '../decorators/cd-encode';

@cdEncode
@Injectable()
Expand Down Expand Up @@ -44,7 +44,7 @@ export class RbdService {
return this.http.get('api/block/image/default_features');
}

createSnapshot(poolName, rbdName, snapshotName) {
createSnapshot(poolName, rbdName, @cdEncodeNot snapshotName) {
const request = {
snapshot_name: snapshotName
};
Expand All @@ -53,7 +53,7 @@ export class RbdService {
});
}

renameSnapshot(poolName, rbdName, snapshotName, newSnapshotName) {
renameSnapshot(poolName, rbdName, snapshotName, @cdEncodeNot newSnapshotName) {
const request = {
new_snap_name: newSnapshotName
};
Expand All @@ -62,7 +62,7 @@ export class RbdService {
});
}

protectSnapshot(poolName, rbdName, snapshotName, isProtected) {
protectSnapshot(poolName, rbdName, snapshotName, @cdEncodeNot isProtected) {
const request = {
is_protected: isProtected
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { cdEncode, cdEncodeNot } from './cd-encode';

describe('cdEncode', () => {
@cdEncode
class ClassA {
x2: string;
y2: string;

methodA(x1: string, @cdEncodeNot y1: string) {
this.x2 = x1;
this.y2 = y1;
}
}

class ClassB {
x2: string;
y2: string;

@cdEncode
methodB(x1: string, @cdEncodeNot y1: string) {
this.x2 = x1;
this.y2 = y1;
}
}

const word = 'a+b/c-d';

it('should encode all params of ClassA, with exception of y1', () => {
const a = new ClassA();
a.methodA(word, word);
expect(a.x2).toBe('a%2Bb%2Fc-d');
expect(a.y2).toBe(word);
});

it('should encode all params of methodB, with exception of y1', () => {
const b = new ClassB();
b.methodB(word, word);
expect(b.x2).toBe('a%2Bb%2Fc-d');
expect(b.y2).toBe(word);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,58 +9,71 @@ import * as _ from 'lodash';
* @param {Function} [target=null]
* @returns {*}
*/
export function cdEncode(target: Function = null): any {
if (target) {
encodeClass(target);
export function cdEncode(...args: any[]): any {
switch (args.length) {
case 1:
return encodeClass.apply(this, args);
case 3:
return encodeMethod.apply(this, args);
default:
throw new Error();
}
}

/**
* This decorator can be used in parameters only.
* It will exclude the parameter from being encode.
* This should be used in parameters that are going
* to be sent in the request's body.
*
* @export
* @param {Object} target
* @param {string} propertyKey
* @param {number} index
*/
export function cdEncodeNot(target: Object, propertyKey: string, index: number) {
const metadataKey = `__ignore_${propertyKey}`;
if (Array.isArray(target[metadataKey])) {
target[metadataKey].push(index);
} else {
return encodeMethod();
target[metadataKey] = [index];
}
}

function encodeClass(target: Function) {
for (const propertyName of Object.keys(target.prototype)) {
const descriptor = Object.getOwnPropertyDescriptor(target.prototype, propertyName);

const isMethod = descriptor.value instanceof Function;
if (!isMethod) {
continue;
}

const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
args.forEach((arg, i, argsArray) => {
if (_.isString(arg)) {
argsArray[i] = encodeURIComponent(arg);
}
});

const result = originalMethod.apply(this, args);
return result;
};

encodeMethod(target.prototype, propertyName, descriptor);
Object.defineProperty(target.prototype, propertyName, descriptor);
}
}

function encodeMethod() {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
if (descriptor === undefined) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
const originalMethod = descriptor.value;
function encodeMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
if (descriptor === undefined) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
const originalMethod = descriptor.value;

descriptor.value = function() {
const args = [];
descriptor.value = function() {
const metadataKey = `__ignore_${propertyKey}`;
const indices: number[] = target[metadataKey] || [];
const args = [];

for (let i = 0; i < arguments.length; i++) {
if (_.isString(arguments[i])) {
args[i] = encodeURIComponent(arguments[i]);
} else {
args[i] = arguments[i];
}
for (let i = 0; i < arguments.length; i++) {
if (_.isString(arguments[i]) && indices.indexOf(i) === -1) {
args[i] = encodeURIComponent(arguments[i]);
} else {
args[i] = arguments[i];
}
}

const result = originalMethod.apply(this, args);
return result;
};
const result = originalMethod.apply(this, args);
return result;
};
}

0 comments on commit eda4df3

Please sign in to comment.