Merge pull request #684 from Marco129/client-class-creation

Add allowClientClassCreation option
This commit is contained in:
Drew
2016-02-26 11:46:50 -08:00
7 changed files with 83 additions and 0 deletions

View File

@@ -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) => { it('handles user signup', (done) => {
var user = { var user = {
username: 'asdf', username: 'asdf',

View File

@@ -95,6 +95,20 @@ describe('rest query', () => {
}).catch((error) => { console.log(error); }); }).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) => { it('query with wrongly encoded parameter', (done) => {
rest.create(config, nobody, 'TestParameterEncode', {foo: 'bar'} rest.create(config, nobody, 'TestParameterEncode', {foo: 'bar'}
).then(() => { ).then(() => {

View File

@@ -26,6 +26,7 @@ export class Config {
this.fileKey = cacheInfo.fileKey; this.fileKey = cacheInfo.fileKey;
this.facebookAppIds = cacheInfo.facebookAppIds; this.facebookAppIds = cacheInfo.facebookAppIds;
this.enableAnonymousUsers = cacheInfo.enableAnonymousUsers; this.enableAnonymousUsers = cacheInfo.enableAnonymousUsers;
this.allowClientClassCreation = cacheInfo.allowClientClassCreation;
this.database = DatabaseAdapter.getDatabaseConnection(applicationId); this.database = DatabaseAdapter.getDatabaseConnection(applicationId);
this.hooksController = cacheInfo.hooksController; this.hooksController = cacheInfo.hooksController;
this.filesController = cacheInfo.filesController; this.filesController = cacheInfo.filesController;

View File

@@ -115,6 +115,8 @@ RestQuery.prototype.execute = function() {
return this.getUserAndRoleACL(); return this.getUserAndRoleACL();
}).then(() => { }).then(() => {
return this.redirectClassNameForKey(); return this.redirectClassNameForKey();
}).then(() => {
return this.validateClientClassCreation();
}).then(() => { }).then(() => {
return this.replaceSelect(); return this.replaceSelect();
}).then(() => { }).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 // Replaces a $inQuery clause by running the subquery, if there is an
// $inQuery clause. // $inQuery clause.
// The $inQuery clause turns into an $in with values that are just // The $inQuery clause turns into an $in with values that are just

View File

@@ -59,6 +59,8 @@ function RestWrite(config, auth, className, query, data, originalData) {
RestWrite.prototype.execute = function() { RestWrite.prototype.execute = function() {
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
return this.getUserAndRoleACL(); return this.getUserAndRoleACL();
}).then(() => {
return this.validateClientClassCreation();
}).then(() => { }).then(() => {
return this.validateSchema(); return this.validateSchema();
}).then(() => { }).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. // Validates this operation against the schema.
RestWrite.prototype.validateSchema = function() { 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);

View File

@@ -85,6 +85,16 @@ export default {
return false; 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": { "mountPath": {
env: "PARSE_SERVER_MOUNT_PATH", env: "PARSE_SERVER_MOUNT_PATH",
help: "Mount path for the server, defaults to /parse", help: "Mount path for the server, defaults to /parse",

View File

@@ -86,6 +86,7 @@ function ParseServer({
fileKey = 'invalid-file-key', fileKey = 'invalid-file-key',
facebookAppIds = [], facebookAppIds = [],
enableAnonymousUsers = true, enableAnonymousUsers = true,
allowClientClassCreation = true,
oauth = {}, oauth = {},
serverURL = requiredParameter('You must provide a serverURL!'), serverURL = requiredParameter('You must provide a serverURL!'),
maxUploadSize = '20mb' maxUploadSize = '20mb'
@@ -139,6 +140,7 @@ function ParseServer({
loggerController: loggerController, loggerController: loggerController,
hooksController: hooksController, hooksController: hooksController,
enableAnonymousUsers: enableAnonymousUsers, enableAnonymousUsers: enableAnonymousUsers,
allowClientClassCreation: allowClientClassCreation,
oauth: oauth, oauth: oauth,
}; };