From e4998c256a516d4a69553f43db5f95ec96dd257a Mon Sep 17 00:00:00 2001 From: Drew Date: Fri, 13 May 2016 15:28:14 -0700 Subject: [PATCH] Move field name validation logic out of mongo (#1752) * Remove transformKey(...) * Move validation logic into Parse Server and out of Mongo Adapter * Fix nits --- spec/MongoTransform.spec.js | 11 ----------- src/Adapters/Storage/Mongo/MongoTransform.js | 13 +------------ src/Controllers/DatabaseController.js | 19 ++++++++++++++++--- src/Controllers/SchemaController.js | 12 +++++++----- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/spec/MongoTransform.spec.js b/spec/MongoTransform.spec.js index bae5805fad..755187dd9e 100644 --- a/spec/MongoTransform.spec.js +++ b/spec/MongoTransform.spec.js @@ -191,17 +191,6 @@ describe('untransformObject', () => { }); }); -describe('transformKey', () => { - it('throws out _password', (done) => { - try { - transform.transformKey(dummySchema, '_User', '_password'); - fail('should have thrown'); - } catch (e) { - done(); - } - }); -}); - describe('transform schema key changes', () => { it('changes new pointer key', (done) => { diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 0013a39d0e..d445e7cec2 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -15,14 +15,11 @@ var Parse = require('parse/node').Parse; // in the value are converted to a mongo update form. Otherwise they are // converted to static data. // -// validate: true indicates that key names are to be validated. -// // Returns an object with {key: key, value: value}. function transformKeyValue(schema, className, restKey, restValue, { inArray, inObject, update, - validate, } = {}) { // Check if the schema is known since it's a built-in field. var key = restKey; @@ -71,9 +68,6 @@ function transformKeyValue(schema, className, restKey, restValue, { if (authDataMatch) { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'can only query on ' + key); } - if (validate && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'invalid key name: ' + key); - } } // Handle special schema key changes @@ -454,11 +448,6 @@ function untransformACL(mongoObject) { return output; } -// Transforms a key used in the REST API format to its mongo format. -function transformKey(schema, className, key) { - return transformKeyValue(schema, className, key, null, {validate: true}).key; -} - // A sentinel value that helper transformations return when they // cannot perform a transformation function CannotTransform() {} @@ -1038,7 +1027,7 @@ var FileCoder = { }; module.exports = { - transformKey, + transformKeyValue, parseObjectToMongoObjectForCreate, transformUpdate, transformWhere, diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index de78895ed5..ab90fc3bfd 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -618,9 +618,22 @@ DatabaseController.prototype.find = function(className, query, { .then(schemaController => { if (sort) { mongoOptions.sort = {}; - for (let key in sort) { - let mongoKey = this.transform.transformKey(schemaController, className, key); - mongoOptions.sort[mongoKey] = sort[key]; + for (let fieldName in sort) { + // Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt, + // so duplicate that behaviour here. + if (fieldName === '_created_at') { + fieldName = 'createdAt'; + sort['createdAt'] = sort['_created_at']; + } else if (fieldName === '_updated_at') { + fieldName = 'updatedAt'; + sort['updatedAt'] = sort['_updated_at']; + } + + if (!SchemaController.fieldNameIsValid(fieldName)) { + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`); + } + const mongoKey = this.transform.transformKeyValue(schemaController, className, fieldName, null).key; + mongoOptions.sort[mongoKey] = sort[fieldName]; } } return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, op)) diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index af56610b99..8c78d1cf57 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -253,7 +253,7 @@ class SchemaController { this.data[schema.className] = schema.fields; this.perms[schema.className] = schema.classLevelPermissions; }); - + // Inject the in-memory classes volatileClasses.forEach(className => { this.data[className] = injectDefaultSchema({ @@ -466,15 +466,16 @@ class SchemaController { // If 'freeze' is true, refuse to update the schema for this field. validateField(className, fieldName, type, freeze) { return this.reloadData().then(() => { - // Just to check that the fieldName is valid - this._collection.transform.transformKey(this, className, fieldName); - - if( fieldName.indexOf(".") > 0 ) { + if (fieldName.indexOf(".") > 0) { // subdocument key (x.y) => ok if x is of type 'object' fieldName = fieldName.split(".")[ 0 ]; type = 'Object'; } + if (!fieldNameIsValid(fieldName)) { + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`); + } + let expected = this.data[className][fieldName]; if (expected) { expected = (expected === 'map' ? 'Object' : expected); @@ -847,6 +848,7 @@ function getObjectType(obj) { export { load, classNameIsValid, + fieldNameIsValid, invalidClassNameMessage, buildMergedSchemaObject, systemClasses,