Skip to content

Commit 6de1e4a

Browse files
authored
Merge 34ce439 into b7815ed
2 parents b7815ed + 34ce439 commit 6de1e4a

File tree

7 files changed

+19
-125
lines changed

7 files changed

+19
-125
lines changed

spec/Middlewares.spec.js

+3-108
Original file line numberDiff line numberDiff line change
@@ -173,72 +173,6 @@ describe('middlewares', () => {
173173
expect(fakeReq.auth.isMaster).toBe(true);
174174
});
175175

176-
it('should not succeed if the connection.remoteAddress does not belong to masterKeyIps list', async () => {
177-
AppCache.put(fakeReq.body._ApplicationId, {
178-
masterKey: 'masterKey',
179-
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
180-
});
181-
fakeReq.connection = { remoteAddress: '127.0.0.1' };
182-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
183-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
184-
expect(fakeReq.auth.isMaster).toBe(false);
185-
});
186-
187-
it('should succeed if the connection.remoteAddress does belong to masterKeyIps list', async () => {
188-
AppCache.put(fakeReq.body._ApplicationId, {
189-
masterKey: 'masterKey',
190-
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
191-
});
192-
fakeReq.connection = { remoteAddress: '10.0.0.1' };
193-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
194-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
195-
expect(fakeReq.auth.isMaster).toBe(true);
196-
});
197-
198-
it('should not succeed if the socket.remoteAddress does not belong to masterKeyIps list', async () => {
199-
AppCache.put(fakeReq.body._ApplicationId, {
200-
masterKey: 'masterKey',
201-
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
202-
});
203-
fakeReq.socket = { remoteAddress: '127.0.0.1' };
204-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
205-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
206-
expect(fakeReq.auth.isMaster).toBe(false);
207-
});
208-
209-
it('should succeed if the socket.remoteAddress does belong to masterKeyIps list', async () => {
210-
AppCache.put(fakeReq.body._ApplicationId, {
211-
masterKey: 'masterKey',
212-
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
213-
});
214-
fakeReq.socket = { remoteAddress: '10.0.0.1' };
215-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
216-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
217-
expect(fakeReq.auth.isMaster).toBe(true);
218-
});
219-
220-
it('should not succeed if the connection.socket.remoteAddress does not belong to masterKeyIps list', async () => {
221-
AppCache.put(fakeReq.body._ApplicationId, {
222-
masterKey: 'masterKey',
223-
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
224-
});
225-
fakeReq.connection = { socket: { remoteAddress: 'ip3' } };
226-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
227-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
228-
expect(fakeReq.auth.isMaster).toBe(false);
229-
});
230-
231-
it('should succeed if the connection.socket.remoteAddress does belong to masterKeyIps list', async () => {
232-
AppCache.put(fakeReq.body._ApplicationId, {
233-
masterKey: 'masterKey',
234-
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
235-
});
236-
fakeReq.connection = { socket: { remoteAddress: '10.0.0.1' } };
237-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
238-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
239-
expect(fakeReq.auth.isMaster).toBe(true);
240-
});
241-
242176
it('should allow any ip to use masterKey if masterKeyIps is empty', async () => {
243177
AppCache.put(fakeReq.body._ApplicationId, {
244178
masterKey: 'masterKey',
@@ -250,48 +184,9 @@ describe('middlewares', () => {
250184
expect(fakeReq.auth.isMaster).toBe(true);
251185
});
252186

253-
it('should succeed if xff header does belong to masterKeyIps', async () => {
254-
AppCache.put(fakeReq.body._ApplicationId, {
255-
masterKey: 'masterKey',
256-
masterKeyIps: ['10.0.0.1'],
257-
});
258-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
259-
fakeReq.headers['x-forwarded-for'] = '10.0.0.1, 10.0.0.2, ip3';
260-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
261-
expect(fakeReq.auth.isMaster).toBe(true);
262-
});
263-
264-
it('should succeed if xff header with one ip does belong to masterKeyIps', async () => {
265-
AppCache.put(fakeReq.body._ApplicationId, {
266-
masterKey: 'masterKey',
267-
masterKeyIps: ['10.0.0.1'],
268-
});
269-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
270-
fakeReq.headers['x-forwarded-for'] = '10.0.0.1';
271-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
272-
expect(fakeReq.auth.isMaster).toBe(true);
273-
});
274-
275-
it('should not succeed if xff header does not belong to masterKeyIps', async () => {
276-
AppCache.put(fakeReq.body._ApplicationId, {
277-
masterKey: 'masterKey',
278-
masterKeyIps: ['ip4'],
279-
});
280-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
281-
fakeReq.headers['x-forwarded-for'] = '10.0.0.1, 10.0.0.2, ip3';
282-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
283-
expect(fakeReq.auth.isMaster).toBe(false);
284-
});
285-
286-
it('should not succeed if xff header is empty and masterKeyIps is set', async () => {
287-
AppCache.put(fakeReq.body._ApplicationId, {
288-
masterKey: 'masterKey',
289-
masterKeyIps: ['10.0.0.1'],
290-
});
291-
fakeReq.headers['x-parse-master-key'] = 'masterKey';
292-
fakeReq.headers['x-forwarded-for'] = '';
293-
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
294-
expect(fakeReq.auth.isMaster).toBe(false);
187+
it('can set trust proxy', async () => {
188+
const server = await reconfigureServer({ trustProxy: 1 });
189+
expect(server.app.parent.settings['trust proxy']).toBe(1);
295190
});
296191

297192
it('should properly expose the headers', () => {

src/Options/Definitions.js

+7
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,13 @@ module.exports.ParseServerOptions = {
486486
help: 'Starts the liveQuery server',
487487
action: parsers.booleanParser,
488488
},
489+
trustProxy: {
490+
env: 'PARSE_SERVER_TRUST_PROXY',
491+
help:
492+
'The trust proxy settings. It is important to understand the exact setup of the reverse proxy, since this setting will trust values provided in the Parse Server API request. See the <a href="https://expressjs.com/en/guide/behind-proxies.html">express trust proxy settings</a> documentation. Defaults to `false`.',
493+
action: parsers.objectParser,
494+
default: [],
495+
},
489496
userSensitiveFields: {
490497
env: 'PARSE_SERVER_USER_SENSITIVE_FIELDS',
491498
help:

src/Options/docs.js

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
* @property {Number} sessionLength Session duration, in seconds, defaults to 1 year
9090
* @property {Boolean} silent Disables console output
9191
* @property {Boolean} startLiveQueryServer Starts the liveQuery server
92+
* @property {Any} trustProxy The trust proxy settings. It is important to understand the exact setup of the reverse proxy, since this setting will trust values provided in the Parse Server API request. See the <a href="https://expressjs.com/en/guide/behind-proxies.html">express trust proxy settings</a> documentation. Defaults to `false`.
9293
* @property {String[]} userSensitiveFields Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields
9394
* @property {Boolean} verbose Set the logging to verbose
9495
* @property {Boolean} verifyUserEmails Set to `true` to require users to verify their email address to complete the sign-up process.<br><br>Default is `false`.

src/Options/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ export interface ParseServerOptions {
241241
cluster: ?NumberOrBoolean;
242242
/* middleware for express server, can be string or function */
243243
middleware: ?((() => void) | string);
244+
/* The trust proxy settings. It is important to understand the exact setup of the reverse proxy, since this setting will trust values provided in the Parse Server API request. See the <a href="https://expressjs.com/en/guide/behind-proxies.html">express trust proxy settings</a> documentation. Defaults to `false`.
245+
:DEFAULT: false */
246+
trustProxy: ?any;
244247
/* Starts the liveQuery server */
245248
startLiveQueryServer: ?boolean;
246249
/* Live query server configuration options (will start the liveQuery server) */

src/ParseServer.js

+3
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,9 @@ class ParseServer {
343343
options
344344
);
345345
}
346+
if (options.trustProxy) {
347+
app.set('trust proxy', options.trustProxy);
348+
}
346349
/* istanbul ignore next */
347350
if (!process.env.TESTING) {
348351
configureListeners(this);

src/cloud-code/Parse.Cloud.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ module.exports = ParseCloud;
722722
* @property {Boolean} isChallenge If true, means the current request is originally triggered by an auth challenge.
723723
* @property {Parse.User} user If set, the user that made the request.
724724
* @property {Parse.Object} object The object triggering the hook.
725-
* @property {String} ip The IP address of the client making the request.
725+
* @property {String} ip The IP address of the client making the request. To ensure retrieving the correct IP address, set the Parse Server option `trustProxy: true` if Parse Server runs behind a proxy server, for example behind a load balancer.
726726
* @property {Object} headers The original HTTP headers for the request.
727727
* @property {String} triggerName The name of the trigger (`beforeSave`, `afterSave`, ...)
728728
* @property {Object} log The current logger inside Parse Server.

src/middlewares.js

+1-16
Original file line numberDiff line numberDiff line change
@@ -288,22 +288,7 @@ export function handleParseHeaders(req, res, next) {
288288
}
289289

290290
function getClientIp(req) {
291-
if (req.headers['x-forwarded-for']) {
292-
// try to get from x-forwared-for if it set (behind reverse proxy)
293-
return req.headers['x-forwarded-for'].split(',')[0];
294-
} else if (req.connection && req.connection.remoteAddress) {
295-
// no proxy, try getting from connection.remoteAddress
296-
return req.connection.remoteAddress;
297-
} else if (req.socket) {
298-
// try to get it from req.socket
299-
return req.socket.remoteAddress;
300-
} else if (req.connection && req.connection.socket) {
301-
// try to get it form the connection.socket
302-
return req.connection.socket.remoteAddress;
303-
} else {
304-
// if non above, fallback.
305-
return req.ip;
306-
}
291+
return req.ip;
307292
}
308293

309294
function httpAuth(req) {

0 commit comments

Comments
 (0)