Remove hidden properties from aggregate responses (#4351)
* Remove hidden properties from aggregrate responses * transform results from mongo & postgres * Adjust ordering to comply with tests
This commit is contained in:
committed by
GitHub
parent
37ceae0812
commit
7944e2bd2d
@@ -409,4 +409,49 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
done();
|
||||
}).catch(done.fail);
|
||||
});
|
||||
|
||||
it('does not return sensitive hidden properties', (done) => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
match: {
|
||||
score: {
|
||||
$gt: 5
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const username = 'leaky_user';
|
||||
const score = 10;
|
||||
|
||||
const user = new Parse.User();
|
||||
user.setUsername(username);
|
||||
user.setPassword('password');
|
||||
user.set('score', score);
|
||||
user.signUp().then(function() {
|
||||
return rp.get(Parse.serverURL + '/aggregate/_User', options);
|
||||
}).then(function(resp) {
|
||||
expect(resp.results.length).toBe(1);
|
||||
const result = resp.results[0];
|
||||
|
||||
// verify server-side keys are not present...
|
||||
expect(result._hashed_password).toBe(undefined);
|
||||
expect(result._wperm).toBe(undefined);
|
||||
expect(result._rperm).toBe(undefined);
|
||||
expect(result._acl).toBe(undefined);
|
||||
expect(result._created_at).toBe(undefined);
|
||||
expect(result._updated_at).toBe(undefined);
|
||||
|
||||
// verify createdAt, updatedAt and others are present
|
||||
expect(result.createdAt).not.toBe(undefined);
|
||||
expect(result.updatedAt).not.toBe(undefined);
|
||||
expect(result.objectId).not.toBe(undefined);
|
||||
expect(result.username).toBe(username);
|
||||
expect(result.score).toBe(score);
|
||||
|
||||
done();
|
||||
}).catch(function(err) {
|
||||
fail(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -409,10 +409,11 @@ export class MongoStorageAdapter {
|
||||
distinct(className, schema, query, fieldName) {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => collection.distinct(fieldName, transformWhere(className, query, schema)));
|
||||
.then(collection => collection.distinct(fieldName, transformWhere(className, query, schema)))
|
||||
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)));
|
||||
}
|
||||
|
||||
aggregate(className, pipeline, readPreference) {
|
||||
aggregate(className, schema, pipeline, readPreference) {
|
||||
readPreference = this._parseReadPreference(readPreference);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => collection.aggregate(pipeline, { readPreference, maxTimeMS: this._maxTimeMS }))
|
||||
@@ -424,7 +425,8 @@ export class MongoStorageAdapter {
|
||||
}
|
||||
});
|
||||
return results;
|
||||
});
|
||||
})
|
||||
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)));
|
||||
}
|
||||
|
||||
_parseReadPreference(readPreference) {
|
||||
|
||||
@@ -1261,7 +1261,12 @@ export class PostgresStorageAdapter {
|
||||
}
|
||||
return Promise.reject(err);
|
||||
})
|
||||
.then(results => results.map(object => {
|
||||
.then(results => results.map(object => this.postgresObjectToParseObject(className, object, schema)));
|
||||
}
|
||||
|
||||
// Converts from a postgres-format object to a REST-format object.
|
||||
// Does not strip out anything based on a lack of authentication.
|
||||
postgresObjectToParseObject(className, object, schema) {
|
||||
Object.keys(schema.fields).forEach(fieldName => {
|
||||
if (schema.fields[fieldName].type === 'Pointer' && object[fieldName]) {
|
||||
object[fieldName] = { objectId: object[fieldName], __type: 'Pointer', className: schema.fields[fieldName].targetClass };
|
||||
@@ -1333,7 +1338,6 @@ export class PostgresStorageAdapter {
|
||||
}
|
||||
|
||||
return object;
|
||||
}));
|
||||
}
|
||||
|
||||
// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't
|
||||
@@ -1406,10 +1410,10 @@ export class PostgresStorageAdapter {
|
||||
}
|
||||
const child = fieldName.split('.')[1];
|
||||
return results.map(object => object[column][child]);
|
||||
});
|
||||
}).then(results => results.map(object => this.postgresObjectToParseObject(className, object, schema)));
|
||||
}
|
||||
|
||||
aggregate(className, pipeline) {
|
||||
aggregate(className, schema, pipeline) {
|
||||
debug('aggregate', className, pipeline);
|
||||
const values = [className];
|
||||
let columns = [];
|
||||
@@ -1498,7 +1502,9 @@ export class PostgresStorageAdapter {
|
||||
|
||||
const qs = `SELECT ${columns} FROM $1:name ${wherePattern} ${sortPattern} ${limitPattern} ${skipPattern} ${groupPattern}`;
|
||||
debug(qs, values);
|
||||
return this._client.any(qs, values).then(results => {
|
||||
return this._client.any(qs, values)
|
||||
.then(results => results.map(object => this.postgresObjectToParseObject(className, object, schema)))
|
||||
.then(results => {
|
||||
if (countField) {
|
||||
results[0][countField] = parseInt(results[0][countField], 10);
|
||||
}
|
||||
|
||||
@@ -875,7 +875,7 @@ DatabaseController.prototype.find = function(className, query, {
|
||||
if (!classExists) {
|
||||
return [];
|
||||
} else {
|
||||
return this.adapter.aggregate(className, pipeline, readPreference);
|
||||
return this.adapter.aggregate(className, schema, pipeline, readPreference);
|
||||
}
|
||||
} else {
|
||||
if (!classExists) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import ClassesRouter from './ClassesRouter';
|
||||
import rest from '../rest';
|
||||
import * as middleware from '../middlewares';
|
||||
import Parse from 'parse/node';
|
||||
import UsersRouter from './UsersRouter';
|
||||
|
||||
const ALLOWED_KEYS = [
|
||||
'where',
|
||||
@@ -65,8 +66,14 @@ export class AggregateRouter extends ClassesRouter {
|
||||
if (typeof body.where === 'string') {
|
||||
body.where = JSON.parse(body.where);
|
||||
}
|
||||
return rest.find(req.config, req.auth, this.className(req), body.where, options, req.info.clientSDK)
|
||||
.then((response) => { return { response }; });
|
||||
return rest.find(req.config, req.auth, this.className(req), body.where, options, req.info.clientSDK).then((response) => {
|
||||
for(const result of response.results) {
|
||||
if(typeof result === 'object') {
|
||||
UsersRouter.removeHiddenProperties(result);
|
||||
}
|
||||
}
|
||||
return { response };
|
||||
});
|
||||
}
|
||||
|
||||
mountRoutes() {
|
||||
|
||||
Reference in New Issue
Block a user