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:
Benjamin Wilson Friedman
2017-11-22 23:07:45 -08:00
committed by GitHub
parent 37ceae0812
commit 7944e2bd2d
5 changed files with 145 additions and 85 deletions

View File

@@ -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);
});
});
});

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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() {