feat: Upgrade to express 5.0.1 (#9530)
BREAKING CHANGE: This upgrades the internally used Express framework from version 4 to 5, which may be a breaking change. If Parse Server is set up to be mounted on an Express application, we recommend to also use version 5 of the Express framework to avoid any compatibility issues. Note that even if there are no issues after upgrading, future releases of Parse Server may introduce issues if Parse Server internally relies on Express 5-specific features which are unsupported by the Express version on which it is mounted. See the Express [migration guide](https://expressjs.com/en/guide/migrating-5.html) and [release announcement](https://expressjs.com/2024/10/15/v5-release.html#breaking-changes) for more info.
This commit is contained in:
1261
package-lock.json
generated
1261
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -28,11 +28,10 @@
|
|||||||
"@parse/fs-files-adapter": "3.0.0",
|
"@parse/fs-files-adapter": "3.0.0",
|
||||||
"@parse/push-adapter": "6.10.0",
|
"@parse/push-adapter": "6.10.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.20.3",
|
|
||||||
"commander": "13.0.0",
|
"commander": "13.0.0",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"deepcopy": "2.1.0",
|
"deepcopy": "2.1.0",
|
||||||
"express": "4.21.2",
|
"express": "5.0.1",
|
||||||
"express-rate-limit": "7.5.0",
|
"express-rate-limit": "7.5.0",
|
||||||
"follow-redirects": "1.15.9",
|
"follow-redirects": "1.15.9",
|
||||||
"graphql": "16.9.0",
|
"graphql": "16.9.0",
|
||||||
@@ -58,6 +57,7 @@
|
|||||||
"punycode": "2.3.1",
|
"punycode": "2.3.1",
|
||||||
"rate-limit-redis": "4.2.0",
|
"rate-limit-redis": "4.2.0",
|
||||||
"redis": "4.7.0",
|
"redis": "4.7.0",
|
||||||
|
"router": "2.0.0",
|
||||||
"semver": "7.7.1",
|
"semver": "7.7.1",
|
||||||
"subscriptions-transport-ws": "0.11.0",
|
"subscriptions-transport-ws": "0.11.0",
|
||||||
"tv4": "1.3.0",
|
"tv4": "1.3.0",
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
const httpRequest = require('../lib/request'),
|
const httpRequest = require('../lib/request'),
|
||||||
HTTPResponse = require('../lib/request').HTTPResponse,
|
HTTPResponse = require('../lib/request').HTTPResponse,
|
||||||
bodyParser = require('body-parser'),
|
|
||||||
express = require('express');
|
express = require('express');
|
||||||
|
|
||||||
const port = 13371;
|
const port = 13371;
|
||||||
@@ -10,7 +9,7 @@ const httpRequestServer = `http://localhost:${port}`;
|
|||||||
|
|
||||||
function startServer(done) {
|
function startServer(done) {
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(bodyParser.json({ type: '*/*' }));
|
app.use(express.json({ type: '*/*' }));
|
||||||
app.get('/hello', function (req, res) {
|
app.get('/hello', function (req, res) {
|
||||||
res.json({ response: 'OK' });
|
res.json({ response: 'OK' });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ const request = require('../lib/request');
|
|||||||
const triggers = require('../lib/triggers');
|
const triggers = require('../lib/triggers');
|
||||||
const HooksController = require('../lib/Controllers/HooksController').default;
|
const HooksController = require('../lib/Controllers/HooksController').default;
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
|
||||||
const auth = require('../lib/Auth');
|
const auth = require('../lib/Auth');
|
||||||
const Config = require('../lib/Config');
|
const Config = require('../lib/Config');
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ describe('Hooks', () => {
|
|||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = express();
|
app = express();
|
||||||
app.use(bodyParser.json({ type: '*/*' }));
|
app.use(express.json({ type: '*/*' }));
|
||||||
server = app.listen(port, undefined, done);
|
server = app.listen(port, undefined, done);
|
||||||
} else {
|
} else {
|
||||||
done();
|
done();
|
||||||
|
|||||||
@@ -250,11 +250,10 @@ describe('Vulnerabilities', () => {
|
|||||||
|
|
||||||
it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)('denies creating a hook with polluted data', async () => {
|
it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)('denies creating a hook with polluted data', async () => {
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
|
||||||
const port = 34567;
|
const port = 34567;
|
||||||
const hookServerURL = 'http://localhost:' + port;
|
const hookServerURL = 'http://localhost:' + port;
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(bodyParser.json({ type: '*/*' }));
|
app.use(express.json({ type: '*/*' }));
|
||||||
const server = await new Promise(resolve => {
|
const server = await new Promise(resolve => {
|
||||||
const res = app.listen(port, undefined, () => resolve(res));
|
const res = app.listen(port, undefined, () => resolve(res));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export class AnalyticsController extends AdaptableController {
|
|||||||
appOpened(req) {
|
appOpened(req) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.adapter.appOpened(req.body, req);
|
return this.adapter.appOpened(req.body || {}, req);
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
return { response: response || {} };
|
return { response: response || {} };
|
||||||
@@ -18,7 +18,7 @@ export class AnalyticsController extends AdaptableController {
|
|||||||
trackEvent(req) {
|
trackEvent(req) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.adapter.trackEvent(req.params.eventName, req.body, req);
|
return this.adapter.trackEvent(req.params.eventName, req.body || {}, req);
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
return { response: response || {} };
|
return { response: response || {} };
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// ParseServer - open-source compatible API Server for Parse apps
|
// ParseServer - open-source compatible API Server for Parse apps
|
||||||
|
|
||||||
var batch = require('./batch'),
|
var batch = require('./batch'),
|
||||||
bodyParser = require('body-parser'),
|
|
||||||
express = require('express'),
|
express = require('express'),
|
||||||
middlewares = require('./middlewares'),
|
middlewares = require('./middlewares'),
|
||||||
Parse = require('parse/node').Parse,
|
Parse = require('parse/node').Parse,
|
||||||
@@ -253,6 +252,7 @@ class ParseServer {
|
|||||||
var api = express();
|
var api = express();
|
||||||
//api.use("/apps", express.static(__dirname + "/public"));
|
//api.use("/apps", express.static(__dirname + "/public"));
|
||||||
api.use(middlewares.allowCrossDomain(appId));
|
api.use(middlewares.allowCrossDomain(appId));
|
||||||
|
api.use(middlewares.allowDoubleForwardSlash);
|
||||||
// File handling needs to be before default middlewares are applied
|
// File handling needs to be before default middlewares are applied
|
||||||
api.use(
|
api.use(
|
||||||
'/',
|
'/',
|
||||||
@@ -273,15 +273,16 @@ class ParseServer {
|
|||||||
|
|
||||||
api.use(
|
api.use(
|
||||||
'/',
|
'/',
|
||||||
bodyParser.urlencoded({ extended: false }),
|
express.urlencoded({ extended: false }),
|
||||||
pages.enableRouter
|
pages.enableRouter
|
||||||
? new PagesRouter(pages).expressRouter()
|
? new PagesRouter(pages).expressRouter()
|
||||||
: new PublicAPIRouter().expressRouter()
|
: new PublicAPIRouter().expressRouter()
|
||||||
);
|
);
|
||||||
|
|
||||||
api.use(bodyParser.json({ type: '*/*', limit: maxUploadSize }));
|
api.use(express.json({ type: '*/*', limit: maxUploadSize }));
|
||||||
api.use(middlewares.allowMethodOverride);
|
api.use(middlewares.allowMethodOverride);
|
||||||
api.use(middlewares.handleParseHeaders);
|
api.use(middlewares.handleParseHeaders);
|
||||||
|
api.set('query parser', 'extended');
|
||||||
const routes = Array.isArray(rateLimit) ? rateLimit : [rateLimit];
|
const routes = Array.isArray(rateLimit) ? rateLimit : [rateLimit];
|
||||||
for (const route of routes) {
|
for (const route of routes) {
|
||||||
middlewares.addRateLimit(route, options);
|
middlewares.addRateLimit(route, options);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import Parse from 'parse/node';
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import log from './logger';
|
import log from './logger';
|
||||||
import { inspect } from 'util';
|
import { inspect } from 'util';
|
||||||
const Layer = require('express/lib/router/layer');
|
const Layer = require('router/lib/layer');
|
||||||
|
|
||||||
function validateParameter(key, value) {
|
function validateParameter(key, value) {
|
||||||
if (key == 'className') {
|
if (key == 'className') {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import UsersRouter from './UsersRouter';
|
|||||||
|
|
||||||
export class AggregateRouter extends ClassesRouter {
|
export class AggregateRouter extends ClassesRouter {
|
||||||
handleFind(req) {
|
handleFind(req) {
|
||||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||||
const options = {};
|
const options = {};
|
||||||
if (body.distinct) {
|
if (body.distinct) {
|
||||||
options.distinct = String(body.distinct);
|
options.distinct = String(body.distinct);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export class AudiencesRouter extends ClassesRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleFind(req) {
|
handleFind(req) {
|
||||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||||
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
||||||
|
|
||||||
return rest
|
return rest
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class ClassesRouter extends PromiseRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleFind(req) {
|
handleFind(req) {
|
||||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||||
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
||||||
if (req.config.maxLimit && body.limit > req.config.maxLimit) {
|
if (req.config.maxLimit && body.limit > req.config.maxLimit) {
|
||||||
// Silently replace the limit on the query with the max configured
|
// Silently replace the limit on the query with the max configured
|
||||||
@@ -48,7 +48,7 @@ export class ClassesRouter extends PromiseRouter {
|
|||||||
|
|
||||||
// Returns a promise for a {response} object.
|
// Returns a promise for a {response} object.
|
||||||
handleGet(req) {
|
handleGet(req) {
|
||||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||||
const options = {};
|
const options = {};
|
||||||
|
|
||||||
for (const key of Object.keys(body)) {
|
for (const key of Object.keys(body)) {
|
||||||
@@ -117,7 +117,7 @@ export class ClassesRouter extends PromiseRouter {
|
|||||||
req.config,
|
req.config,
|
||||||
req.auth,
|
req.auth,
|
||||||
this.className(req),
|
this.className(req),
|
||||||
req.body,
|
req.body || {},
|
||||||
req.info.clientSDK,
|
req.info.clientSDK,
|
||||||
req.info.context
|
req.info.context
|
||||||
);
|
);
|
||||||
@@ -130,7 +130,7 @@ export class ClassesRouter extends PromiseRouter {
|
|||||||
req.auth,
|
req.auth,
|
||||||
this.className(req),
|
this.className(req),
|
||||||
where,
|
where,
|
||||||
req.body,
|
req.body || {},
|
||||||
req.info.clientSDK,
|
req.info.clientSDK,
|
||||||
req.info.context
|
req.info.context
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export class CloudCodeRouter extends PromiseRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static createJob(req) {
|
static createJob(req) {
|
||||||
const { job_schedule } = req.body;
|
const { job_schedule } = req.body || {};
|
||||||
validateJobSchedule(req.config, job_schedule);
|
validateJobSchedule(req.config, job_schedule);
|
||||||
return rest.create(
|
return rest.create(
|
||||||
req.config,
|
req.config,
|
||||||
@@ -91,7 +91,7 @@ export class CloudCodeRouter extends PromiseRouter {
|
|||||||
|
|
||||||
static editJob(req) {
|
static editJob(req) {
|
||||||
const { objectId } = req.params;
|
const { objectId } = req.params;
|
||||||
const { job_schedule } = req.body;
|
const { job_schedule } = req.body || {};
|
||||||
validateJobSchedule(req.config, job_schedule);
|
validateJobSchedule(req.config, job_schedule);
|
||||||
return rest
|
return rest
|
||||||
.update(
|
.update(
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import BodyParser from 'body-parser';
|
|
||||||
import * as Middlewares from '../middlewares';
|
import * as Middlewares from '../middlewares';
|
||||||
import Parse from 'parse/node';
|
import Parse from 'parse/node';
|
||||||
import Config from '../Config';
|
import Config from '../Config';
|
||||||
@@ -45,7 +44,7 @@ export class FilesRouter {
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/files/:filename',
|
'/files/:filename',
|
||||||
BodyParser.raw({
|
express.raw({
|
||||||
type: () => {
|
type: () => {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class FunctionsRouter extends PromiseRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static handleCloudJob(req) {
|
static handleCloudJob(req) {
|
||||||
const jobName = req.params.jobName || req.body.jobName;
|
const jobName = req.params.jobName || req.body?.jobName;
|
||||||
const applicationId = req.config.applicationId;
|
const applicationId = req.config.applicationId;
|
||||||
const jobHandler = jobStatusHandler(req.config);
|
const jobHandler = jobStatusHandler(req.config);
|
||||||
const jobFunction = triggers.getJob(jobName, applicationId);
|
const jobFunction = triggers.getJob(jobName, applicationId);
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ export class GlobalConfigRouter extends PromiseRouter {
|
|||||||
"read-only masterKey isn't allowed to update the config."
|
"read-only masterKey isn't allowed to update the config."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const params = req.body.params;
|
const params = req.body.params || {};
|
||||||
const masterKeyOnly = req.body.masterKeyOnly || {};
|
const masterKeyOnly = req.body?.masterKeyOnly || {};
|
||||||
// Transform in dot notation to make sure it works
|
// Transform in dot notation to make sure it works
|
||||||
const update = Object.keys(params).reduce((acc, key) => {
|
const update = Object.keys(params).reduce((acc, key) => {
|
||||||
acc[`params.${key}`] = params[key];
|
acc[`params.${key}`] = params[key];
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class GraphQLRouter extends PromiseRouter {
|
|||||||
"read-only masterKey isn't allowed to update the GraphQL config."
|
"read-only masterKey isn't allowed to update the GraphQL config."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const data = await req.config.parseGraphQLController.updateGraphQLConfig(req.body.params);
|
const data = await req.config.parseGraphQLController.updateGraphQLConfig(req.body?.params || {});
|
||||||
return {
|
return {
|
||||||
response: data,
|
response: data,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class HooksRouter extends PromiseRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlePost(req) {
|
handlePost(req) {
|
||||||
return this.createHook(req.body, req.config);
|
return this.createHook(req.body || {}, req.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleGetFunctions(req) {
|
handleGetFunctions(req) {
|
||||||
@@ -66,11 +66,11 @@ export class HooksRouter extends PromiseRouter {
|
|||||||
|
|
||||||
handleUpdate(req) {
|
handleUpdate(req) {
|
||||||
var hook;
|
var hook;
|
||||||
if (req.params.functionName && req.body.url) {
|
if (req.params.functionName && req.body?.url) {
|
||||||
hook = {};
|
hook = {};
|
||||||
hook.functionName = req.params.functionName;
|
hook.functionName = req.params.functionName;
|
||||||
hook.url = req.body.url;
|
hook.url = req.body.url;
|
||||||
} else if (req.params.className && req.params.triggerName && req.body.url) {
|
} else if (req.params.className && req.params.triggerName && req.body?.url) {
|
||||||
hook = {};
|
hook = {};
|
||||||
hook.className = req.params.className;
|
hook.className = req.params.className;
|
||||||
hook.triggerName = req.params.triggerName;
|
hook.triggerName = req.params.triggerName;
|
||||||
@@ -82,7 +82,7 @@ export class HooksRouter extends PromiseRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlePut(req) {
|
handlePut(req) {
|
||||||
var body = req.body;
|
var body = req.body || {};
|
||||||
if (body.__op == 'Delete') {
|
if (body.__op == 'Delete') {
|
||||||
return this.handleDelete(req);
|
return this.handleDelete(req);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ function getFileForProductIdentifier(productIdentifier, req) {
|
|||||||
|
|
||||||
export class IAPValidationRouter extends PromiseRouter {
|
export class IAPValidationRouter extends PromiseRouter {
|
||||||
handleRequest(req) {
|
handleRequest(req) {
|
||||||
let receipt = req.body.receipt;
|
let receipt = req.body?.receipt;
|
||||||
const productIdentifier = req.body.productIdentifier;
|
const productIdentifier = req.body?.productIdentifier;
|
||||||
|
|
||||||
if (!receipt || !productIdentifier) {
|
if (!receipt || !productIdentifier) {
|
||||||
// TODO: Error, malformed request
|
// TODO: Error, malformed request
|
||||||
@@ -84,7 +84,7 @@ export class IAPValidationRouter extends PromiseRouter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.TESTING == '1' && req.body.bypassAppStoreValidation) {
|
if (process.env.TESTING == '1' && req.body?.bypassAppStoreValidation) {
|
||||||
return getFileForProductIdentifier(productIdentifier, req);
|
return getFileForProductIdentifier(productIdentifier, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class InstallationsRouter extends ClassesRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleFind(req) {
|
handleFind(req) {
|
||||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||||
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
||||||
return rest
|
return rest
|
||||||
.find(
|
.find(
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ export class PagesRouter extends PromiseRouter {
|
|||||||
|
|
||||||
resendVerificationEmail(req) {
|
resendVerificationEmail(req) {
|
||||||
const config = req.config;
|
const config = req.config;
|
||||||
const username = req.body.username;
|
const username = req.body?.username;
|
||||||
const token = req.body.token;
|
const token = req.body?.token;
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
this.invalidRequest();
|
this.invalidRequest();
|
||||||
@@ -178,7 +178,7 @@ export class PagesRouter extends PromiseRouter {
|
|||||||
this.invalidRequest();
|
this.invalidRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { new_password, token: rawToken } = req.body;
|
const { new_password, token: rawToken } = req.body || {};
|
||||||
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
||||||
|
|
||||||
if ((!token || !new_password) && req.xhr === false) {
|
if ((!token || !new_password) && req.xhr === false) {
|
||||||
@@ -320,7 +320,7 @@ export class PagesRouter extends PromiseRouter {
|
|||||||
*/
|
*/
|
||||||
staticRoute(req) {
|
staticRoute(req) {
|
||||||
// Get requested path
|
// Get requested path
|
||||||
const relativePath = req.params[0];
|
const relativePath = req.params['resource'][0];
|
||||||
|
|
||||||
// Resolve requested path to absolute path
|
// Resolve requested path to absolute path
|
||||||
const absolutePath = path.resolve(this.pagesPath, relativePath);
|
const absolutePath = path.resolve(this.pagesPath, relativePath);
|
||||||
@@ -716,7 +716,7 @@ export class PagesRouter extends PromiseRouter {
|
|||||||
mountStaticRoute() {
|
mountStaticRoute() {
|
||||||
this.route(
|
this.route(
|
||||||
'GET',
|
'GET',
|
||||||
`/${this.pagesEndpoint}/(*)?`,
|
`/${this.pagesEndpoint}/*resource`,
|
||||||
req => {
|
req => {
|
||||||
this.setConfig(req, true);
|
this.setConfig(req, true);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resendVerificationEmail(req) {
|
resendVerificationEmail(req) {
|
||||||
const username = req.body.username;
|
const username = req.body?.username;
|
||||||
const appId = req.params.appId;
|
const appId = req.params.appId;
|
||||||
const config = Config.get(appId);
|
const config = Config.get(appId);
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
|||||||
return this.missingPublicServerURL();
|
return this.missingPublicServerURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { new_password, token: rawToken } = req.body;
|
const { new_password, token: rawToken } = req.body || {};
|
||||||
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
||||||
|
|
||||||
if ((!token || !new_password) && req.xhr === false) {
|
if ((!token || !new_password) && req.xhr === false) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export class PushRouter extends PromiseRouter {
|
|||||||
});
|
});
|
||||||
let pushStatusId;
|
let pushStatusId;
|
||||||
pushController
|
pushController
|
||||||
.sendPush(req.body, where, req.config, req.auth, objectId => {
|
.sendPush(req.body || {}, where, req.config, req.auth, objectId => {
|
||||||
pushStatusId = objectId;
|
pushStatusId = objectId;
|
||||||
resolve({
|
resolve({
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -77,18 +77,18 @@ async function createSchema(req) {
|
|||||||
"read-only masterKey isn't allowed to create a schema."
|
"read-only masterKey isn't allowed to create a schema."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (req.params.className && req.body.className) {
|
if (req.params.className && req.body?.className) {
|
||||||
if (req.params.className != req.body.className) {
|
if (req.params.className != req.body.className) {
|
||||||
return classNameMismatchResponse(req.body.className, req.params.className);
|
return classNameMismatchResponse(req.body.className, req.params.className);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const className = req.params.className || req.body.className;
|
const className = req.params.className || req.body?.className;
|
||||||
if (!className) {
|
if (!className) {
|
||||||
throw new Parse.Error(135, `POST ${req.path} needs a class name.`);
|
throw new Parse.Error(135, `POST ${req.path} needs a class name.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await internalCreateSchema(className, req.body, req.config);
|
return await internalCreateSchema(className, req.body || {}, req.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
function modifySchema(req) {
|
function modifySchema(req) {
|
||||||
@@ -99,12 +99,12 @@ function modifySchema(req) {
|
|||||||
"read-only masterKey isn't allowed to update a schema."
|
"read-only masterKey isn't allowed to update a schema."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (req.body.className && req.body.className != req.params.className) {
|
if (req.body?.className && req.body.className != req.params.className) {
|
||||||
return classNameMismatchResponse(req.body.className, req.params.className);
|
return classNameMismatchResponse(req.body.className, req.params.className);
|
||||||
}
|
}
|
||||||
const className = req.params.className;
|
const className = req.params.className;
|
||||||
|
|
||||||
return internalUpdateSchema(className, req.body, req.config);
|
return internalUpdateSchema(className, req.body || {}, req.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteSchema = req => {
|
const deleteSchema = req => {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
_authenticateUserFromRequest(req) {
|
_authenticateUserFromRequest(req) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Use query parameters instead if provided in url
|
// Use query parameters instead if provided in url
|
||||||
let payload = req.body;
|
let payload = req.body || {};
|
||||||
if (
|
if (
|
||||||
(!payload.username && req.query && req.query.username) ||
|
(!payload.username && req.query && req.query.username) ||
|
||||||
(!payload.email && req.query && req.query.email)
|
(!payload.email && req.query && req.query.email)
|
||||||
@@ -219,7 +219,7 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
req.auth,
|
req.auth,
|
||||||
'_User',
|
'_User',
|
||||||
{ objectId: user.objectId },
|
{ objectId: user.objectId },
|
||||||
req.body,
|
req.body || {},
|
||||||
user,
|
user,
|
||||||
req.info.clientSDK,
|
req.info.clientSDK,
|
||||||
req.info.context
|
req.info.context
|
||||||
@@ -336,7 +336,7 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'master key is required');
|
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'master key is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
const userId = req.body.userId || req.query.userId;
|
const userId = req.body?.userId || req.query.userId;
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_VALUE,
|
Parse.Error.INVALID_VALUE,
|
||||||
@@ -438,8 +438,9 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
async handleResetRequest(req) {
|
async handleResetRequest(req) {
|
||||||
this._throwOnBadEmailConfig(req);
|
this._throwOnBadEmailConfig(req);
|
||||||
|
|
||||||
let email = req.body.email;
|
let email = req.body?.email;
|
||||||
const token = req.body.token;
|
const token = req.body?.token;
|
||||||
|
|
||||||
if (!email && !token) {
|
if (!email && !token) {
|
||||||
throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email');
|
throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email');
|
||||||
}
|
}
|
||||||
@@ -480,7 +481,7 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
async handleVerificationEmailRequest(req) {
|
async handleVerificationEmailRequest(req) {
|
||||||
this._throwOnBadEmailConfig(req);
|
this._throwOnBadEmailConfig(req);
|
||||||
|
|
||||||
const { email } = req.body;
|
const { email } = req.body || {};
|
||||||
if (!email) {
|
if (!email) {
|
||||||
throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email');
|
throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email');
|
||||||
}
|
}
|
||||||
@@ -513,7 +514,7 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleChallenge(req) {
|
async handleChallenge(req) {
|
||||||
const { username, email, password, authData, challengeData } = req.body;
|
const { username, email, password, authData, challengeData } = req.body || {};
|
||||||
|
|
||||||
// if username or email provided with password try to authenticate the user by username
|
// if username or email provided with password try to authenticate the user by username
|
||||||
let user;
|
let user;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) {
|
|||||||
// Returns a promise for a {response} object.
|
// Returns a promise for a {response} object.
|
||||||
// TODO: pass along auth correctly
|
// TODO: pass along auth correctly
|
||||||
function handleBatch(router, req) {
|
function handleBatch(router, req) {
|
||||||
if (!Array.isArray(req.body.requests)) {
|
if (!Array.isArray(req.body?.requests)) {
|
||||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'requests must be an array');
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'requests must be an array');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,12 +85,12 @@ function handleBatch(router, req) {
|
|||||||
|
|
||||||
const batch = transactionRetries => {
|
const batch = transactionRetries => {
|
||||||
let initialPromise = Promise.resolve();
|
let initialPromise = Promise.resolve();
|
||||||
if (req.body.transaction === true) {
|
if (req.body?.transaction === true) {
|
||||||
initialPromise = req.config.database.createTransactionalSession();
|
initialPromise = req.config.database.createTransactionalSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
return initialPromise.then(() => {
|
return initialPromise.then(() => {
|
||||||
const promises = req.body.requests.map(restRequest => {
|
const promises = req.body?.requests.map(restRequest => {
|
||||||
const routablePath = makeRoutablePath(restRequest.path);
|
const routablePath = makeRoutablePath(restRequest.path);
|
||||||
|
|
||||||
// Construct a request that we can send to a handler
|
// Construct a request that we can send to a handler
|
||||||
@@ -113,7 +113,7 @@ function handleBatch(router, req) {
|
|||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
if (req.body.transaction === true) {
|
if (req.body?.transaction === true) {
|
||||||
if (results.find(result => typeof result.error === 'object')) {
|
if (results.find(result => typeof result.error === 'object')) {
|
||||||
return req.config.database.abortTransactionalSession().then(() => {
|
return req.config.database.abortTransactionalSession().then(() => {
|
||||||
return Promise.reject({ response: results });
|
return Promise.reject({ response: results });
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ export async function handleParseHeaders(req, res, next) {
|
|||||||
info.clientSDK = ClientSDK.fromString(info.clientVersion);
|
info.clientSDK = ClientSDK.fromString(info.clientVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileViaJSON) {
|
if (fileViaJSON && req.body) {
|
||||||
req.fileData = req.body.fileData;
|
req.fileData = req.body.fileData;
|
||||||
// We need to repopulate req.body with a buffer
|
// We need to repopulate req.body with a buffer
|
||||||
var base64 = req.body.base64;
|
var base64 = req.body.base64;
|
||||||
@@ -450,7 +450,7 @@ export function allowCrossDomain(appId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function allowMethodOverride(req, res, next) {
|
export function allowMethodOverride(req, res, next) {
|
||||||
if (req.method === 'POST' && req.body._method) {
|
if (req.method === 'POST' && req.body?._method) {
|
||||||
req.originalMethod = req.method;
|
req.originalMethod = req.method;
|
||||||
req.method = req.body._method;
|
req.method = req.body._method;
|
||||||
delete req.body._method;
|
delete req.body._method;
|
||||||
@@ -685,3 +685,16 @@ function malformedContext(req, res) {
|
|||||||
res.status(400);
|
res.status(400);
|
||||||
res.json({ code: Parse.Error.INVALID_JSON, error: 'Invalid object for context.' });
|
res.json({ code: Parse.Error.INVALID_JSON, error: 'Invalid object for context.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Express 4 allowed a double forward slash between a route and router. Although
|
||||||
|
* this should be considered an anti-pattern, we need to support it for backwards
|
||||||
|
* compatibility.
|
||||||
|
*
|
||||||
|
* Technically valid URL with double foroward slash:
|
||||||
|
* http://localhost:1337/parse//functions/testFunction
|
||||||
|
*/
|
||||||
|
export function allowDoubleForwardSlash(req, res, next) {
|
||||||
|
req.url = req.url.startsWith('//') ? req.url.substring(1) : req.url;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user