From 97489106b4b760181874ee89d171a52f140d20dc Mon Sep 17 00:00:00 2001 From: Marco129 Date: Fri, 26 Feb 2016 22:55:39 +0800 Subject: [PATCH] Add allowClientClassCreation option --- spec/RestCreate.spec.js | 14 ++++++++++++++ spec/RestQuery.spec.js | 14 ++++++++++++++ src/Config.js | 1 + src/RestQuery.js | 21 +++++++++++++++++++++ src/RestWrite.js | 21 +++++++++++++++++++++ src/cli/cli-definitions.js | 10 ++++++++++ src/index.js | 2 ++ 7 files changed, 83 insertions(+) diff --git a/spec/RestCreate.spec.js b/spec/RestCreate.spec.js index 1e79d2bfbc..7ccd39d3a1 100644 --- a/spec/RestCreate.spec.js +++ b/spec/RestCreate.spec.js @@ -73,6 +73,20 @@ describe('rest create', () => { }); }); + it('handles create on non-existent class when disabled client class creation', (done) => { + var customConfig = Object.assign({}, config, {allowClientClassCreation: false}); + rest.create(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}) + .then(() => { + fail('Should throw an error'); + done(); + }, (err) => { + expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(err.message).toEqual('This user is not allowed to access ' + + 'non-existent class: ClientClassCreation'); + done(); + }); + }); + it('handles user signup', (done) => { var user = { username: 'asdf', diff --git a/spec/RestQuery.spec.js b/spec/RestQuery.spec.js index 279f45f606..59ed70f048 100644 --- a/spec/RestQuery.spec.js +++ b/spec/RestQuery.spec.js @@ -95,6 +95,20 @@ describe('rest query', () => { }).catch((error) => { console.log(error); }); }); + it('query non-existent class when disabled client class creation', (done) => { + var customConfig = Object.assign({}, config, {allowClientClassCreation: false}); + rest.find(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}) + .then(() => { + fail('Should throw an error'); + done(); + }, (err) => { + expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(err.message).toEqual('This user is not allowed to access ' + + 'non-existent class: ClientClassCreation'); + done(); + }); + }); + it('query with wrongly encoded parameter', (done) => { rest.create(config, nobody, 'TestParameterEncode', {foo: 'bar'} ).then(() => { diff --git a/src/Config.js b/src/Config.js index 510ebf001b..639c36b63e 100644 --- a/src/Config.js +++ b/src/Config.js @@ -26,6 +26,7 @@ export class Config { this.fileKey = cacheInfo.fileKey; this.facebookAppIds = cacheInfo.facebookAppIds; this.enableAnonymousUsers = cacheInfo.enableAnonymousUsers; + this.allowClientClassCreation = cacheInfo.allowClientClassCreation; this.database = DatabaseAdapter.getDatabaseConnection(applicationId); this.hooksController = cacheInfo.hooksController; this.filesController = cacheInfo.filesController; diff --git a/src/RestQuery.js b/src/RestQuery.js index b5bec1fbb6..86562e9df1 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -115,6 +115,8 @@ RestQuery.prototype.execute = function() { return this.getUserAndRoleACL(); }).then(() => { return this.redirectClassNameForKey(); + }).then(() => { + return this.validateClientClassCreation(); }).then(() => { return this.replaceSelect(); }).then(() => { @@ -161,6 +163,25 @@ RestQuery.prototype.redirectClassNameForKey = function() { }); }; +// Validates this operation against the allowClientClassCreation config. +RestQuery.prototype.validateClientClassCreation = function() { + if (this.config.allowClientClassCreation === false && !this.auth.isMaster) { + return this.config.database.loadSchema().then((schema) => { + return schema.hasClass(this.className) + }).then((hasClass) => { + if (hasClass === true) { + return Promise.resolve(); + } + + throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, + 'This user is not allowed to access ' + + 'non-existent class: ' + this.className); + }); + } else { + return Promise.resolve(); + } +}; + // Replaces a $inQuery clause by running the subquery, if there is an // $inQuery clause. // The $inQuery clause turns into an $in with values that are just diff --git a/src/RestWrite.js b/src/RestWrite.js index d23912a775..565bf86d95 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -59,6 +59,8 @@ function RestWrite(config, auth, className, query, data, originalData) { RestWrite.prototype.execute = function() { return Promise.resolve().then(() => { return this.getUserAndRoleACL(); + }).then(() => { + return this.validateClientClassCreation(); }).then(() => { return this.validateSchema(); }).then(() => { @@ -105,6 +107,25 @@ RestWrite.prototype.getUserAndRoleACL = function() { } }; +// Validates this operation against the allowClientClassCreation config. +RestWrite.prototype.validateClientClassCreation = function() { + if (this.config.allowClientClassCreation === false && !this.auth.isMaster) { + return this.config.database.loadSchema().then((schema) => { + return schema.hasClass(this.className) + }).then((hasClass) => { + if (hasClass === true) { + return Promise.resolve(); + } + + throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, + 'This user is not allowed to access ' + + 'non-existent class: ' + this.className); + }); + } else { + return Promise.resolve(); + } +}; + // Validates this operation against the schema. RestWrite.prototype.validateSchema = function() { return this.config.database.validateObject(this.className, this.data, this.query); diff --git a/src/cli/cli-definitions.js b/src/cli/cli-definitions.js index 8f6e18ec40..7da93c3845 100644 --- a/src/cli/cli-definitions.js +++ b/src/cli/cli-definitions.js @@ -85,6 +85,16 @@ export default { return false; } }, + "allowClientClassCreation": { + env: "PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION", + help: "Enable (or disable) client class creation, defaults to true", + action: function(opt) { + if (opt == "true" || opt == "1") { + return true; + } + return false; + } + }, "mountPath": { env: "PARSE_SERVER_MOUNT_PATH", help: "Mount path for the server, defaults to /parse", diff --git a/src/index.js b/src/index.js index 2a08d03e4a..06ceb19a87 100644 --- a/src/index.js +++ b/src/index.js @@ -86,6 +86,7 @@ function ParseServer({ fileKey = 'invalid-file-key', facebookAppIds = [], enableAnonymousUsers = true, + allowClientClassCreation = true, oauth = {}, serverURL = requiredParameter('You must provide a serverURL!'), maxUploadSize = '20mb' @@ -139,6 +140,7 @@ function ParseServer({ loggerController: loggerController, hooksController: hooksController, enableAnonymousUsers: enableAnonymousUsers, + allowClientClassCreation: allowClientClassCreation, oauth: oauth, };