Merge branch 'android-installation-duplicate-token-test' into installation-handling-fix
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -176,7 +197,7 @@ RestWrite.prototype.validateAuthData = function() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.data.authData) {
|
||||
if (!this.data.authData || !Object.keys(this.data.authData).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -426,6 +426,12 @@ Schema.prototype.validateField = function(className, key, type, freeze) {
|
||||
// Just to check that the key is valid
|
||||
transform.transformKey(this, className, key);
|
||||
|
||||
if( key.indexOf(".") > 0 ) {
|
||||
// subdocument key (x.y) => ok if x is of type 'object'
|
||||
key = key.split(".")[ 0 ];
|
||||
type = 'object';
|
||||
}
|
||||
|
||||
var expected = this.data[className][key];
|
||||
if (expected) {
|
||||
expected = (expected === 'map' ? 'object' : expected);
|
||||
|
||||
@@ -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",
|
||||
|
||||
12
src/index.js
12
src/index.js
@@ -79,20 +79,21 @@ function ParseServer({
|
||||
databaseURI,
|
||||
cloud,
|
||||
collectionPrefix = '',
|
||||
clientKey = '',
|
||||
javascriptKey = randomString(20),
|
||||
dotNetKey = '',
|
||||
restAPIKey = '',
|
||||
clientKey,
|
||||
javascriptKey,
|
||||
dotNetKey,
|
||||
restAPIKey,
|
||||
fileKey = 'invalid-file-key',
|
||||
facebookAppIds = [],
|
||||
enableAnonymousUsers = true,
|
||||
allowClientClassCreation = true,
|
||||
oauth = {},
|
||||
serverURL = requiredParameter('You must provide a serverURL!'),
|
||||
maxUploadSize = '20mb'
|
||||
}) {
|
||||
|
||||
// Initialize the node client SDK automatically
|
||||
Parse.initialize(appId, javascriptKey, masterKey);
|
||||
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
|
||||
Parse.serverURL = serverURL;
|
||||
|
||||
if (databaseAdapter) {
|
||||
@@ -139,6 +140,7 @@ function ParseServer({
|
||||
loggerController: loggerController,
|
||||
hooksController: hooksController,
|
||||
enableAnonymousUsers: enableAnonymousUsers,
|
||||
allowClientClassCreation: allowClientClassCreation,
|
||||
oauth: oauth,
|
||||
};
|
||||
|
||||
|
||||
@@ -99,20 +99,20 @@ function handleParseHeaders(req, res, next) {
|
||||
|
||||
// Client keys are not required in parse-server, but if any have been configured in the server, validate them
|
||||
// to preserve original behavior.
|
||||
var keyRequired = (req.config.clientKey
|
||||
|| req.config.javascriptKey
|
||||
|| req.config.dotNetKey
|
||||
|| req.config.restAPIKey);
|
||||
var keyHandled = false;
|
||||
if (keyRequired
|
||||
&& ((info.clientKey && req.config.clientKey && info.clientKey === req.config.clientKey)
|
||||
|| (info.javascriptKey && req.config.javascriptKey && info.javascriptKey === req.config.javascriptKey)
|
||||
|| (info.dotNetKey && req.config.dotNetKey && info.dotNetKey === req.config.dotNetKey)
|
||||
|| (info.restAPIKey && req.config.restAPIKey && info.restAPIKey === req.config.restAPIKey)
|
||||
)) {
|
||||
keyHandled = true;
|
||||
}
|
||||
if (keyRequired && !keyHandled) {
|
||||
let keys = ["clientKey", "javascriptKey", "dotNetKey", "restAPIKey"];
|
||||
|
||||
// We do it with mismatching keys to support no-keys config
|
||||
var keyMismatch = keys.reduce(function(mismatch, key){
|
||||
|
||||
// check if set in the config and compare
|
||||
if (req.config[key] && info[key] !== req.config[key]) {
|
||||
mismatch++;
|
||||
}
|
||||
return mismatch;
|
||||
}, 0);
|
||||
|
||||
// All keys mismatch
|
||||
if (keyMismatch == keys.length) {
|
||||
return invalidRequest(req, res);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user