From e75d233b7eaeebd5ab29a114791ec30389c81747 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Mon, 7 Mar 2016 23:07:24 -0500 Subject: [PATCH] Adds validation of addFields --- spec/schemas.spec.js | 58 +++++++++++++++++++++++++++ src/Controllers/DatabaseController.js | 24 ++++++++++- src/RestWrite.js | 2 +- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index bb40dbc21c..f37cf1ca0e 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -938,4 +938,62 @@ describe('schemas', () => { }); }); }); + + it('should not be able to add a field', done => { + request.post({ + url: 'http://localhost:8378/1/schemas/AClass', + headers: masterKeyHeaders, + json: true, + body: { + classLevelPermissions: { + find: { + '*': true + }, + addField: { + 'role:admin': true + } + } + } + }, (error, response, body) => { + expect(error).toEqual(null); + let object = new Parse.Object('AClass'); + object.set('hello', 'world'); + return object.save().then(() => { + fail('should not be able to add a field'); + done(); + }, (err) => { + expect(err.message).toEqual('Permission denied for this action.'); + done(); + }) + }) + }); + + it('should not be able to add a field', done => { + request.post({ + url: 'http://localhost:8378/1/schemas/AClass', + headers: masterKeyHeaders, + json: true, + body: { + classLevelPermissions: { + find: { + '*': true + }, + addField: { + '*': true + } + } + } + }, (error, response, body) => { + expect(error).toEqual(null); + let object = new Parse.Object('AClass'); + object.set('hello', 'world'); + return object.save().then(() => { + done(); + }, (err) => { + fail('should be able to add a field'); + done(); + }) + }) + }); + }); diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index d575208873..3e85eca0e6 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -101,8 +101,12 @@ DatabaseController.prototype.redirectClassNameForKey = function(className, key) // Returns a promise that resolves to the new schema. // This does not update this.schema, because in a situation like a // batch request, that could confuse other users of the schema. -DatabaseController.prototype.validateObject = function(className, object, query) { - return this.loadSchema().then((schema) => { +DatabaseController.prototype.validateObject = function(className, object, query, options) { + let schema; + return this.loadSchema().then(s => { + schema = s; + return this.canAddField(schema, className, object, options.acl || []); + }).then(() => { return schema.validateObject(className, object, query); }); }; @@ -332,6 +336,22 @@ DatabaseController.prototype.create = function(className, object, options) { }); }; +DatabaseController.prototype.canAddField = function(schema, className, object, aclGroup) { + let classSchema = schema.data[className]; + if (!classSchema) { + return Promise.resolve(); + } + let fields = Object.keys(object); + let schemaFields = Object.keys(classSchema); + let newKeys = fields.filter((field) => { + return schemaFields.indexOf(field) < 0; + }) + if (newKeys.length > 0) { + return schema.validatePermission(className, aclGroup, 'addField'); + } + return Promise.resolve(); +} + // Runs a mongo query on the database. // This should only be used for testing - use 'find' for normal code // to avoid Mongo-format dependencies. diff --git a/src/RestWrite.js b/src/RestWrite.js index 9e07c93a10..b68074d421 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -128,7 +128,7 @@ RestWrite.prototype.validateClientClassCreation = function() { // Validates this operation against the schema. RestWrite.prototype.validateSchema = function() { - return this.config.database.validateObject(this.className, this.data, this.query); + return this.config.database.validateObject(this.className, this.data, this.query, this.runOptions); }; // Runs any beforeSave triggers against this operation.