feat: Restrict use of masterKey to localhost by default (#8281)
BREAKING CHANGE: This release restricts the use of `masterKey` to localhost by default; if you are using Parse Dashboard on a different server to connect to Parse Server you need to add the IP address of the server that hosts Parse Dashboard to this option (#8281)
This commit is contained in:
@@ -3,7 +3,6 @@ const AppCache = require('../lib/cache').AppCache;
|
||||
|
||||
describe('middlewares', () => {
|
||||
let fakeReq, fakeRes;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeReq = {
|
||||
originalUrl: 'http://example.com/parse/',
|
||||
@@ -117,10 +116,12 @@ describe('middlewares', () => {
|
||||
const otherKeys = BodyKeys.filter(
|
||||
otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey'
|
||||
);
|
||||
|
||||
it(`it should pull ${bodyKey} into req.info`, done => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKeyIps: ['0.0.0.0/0'],
|
||||
});
|
||||
fakeReq.ip = '127.0.0.1';
|
||||
fakeReq.body[bodyKey] = keyValue;
|
||||
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeReq.body[bodyKey]).toEqual(undefined);
|
||||
expect(fakeReq.info[infoKey]).toEqual(keyValue);
|
||||
@@ -134,161 +135,147 @@ describe('middlewares', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not succeed if the ip does not belong to masterKeyIps list', () => {
|
||||
it('should not succeed if the ip does not belong to masterKeyIps list', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
masterKeyIps: ['10.0.0.1'],
|
||||
});
|
||||
fakeReq.ip = 'ip3';
|
||||
fakeReq.ip = '127.0.0.1';
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(false);
|
||||
});
|
||||
|
||||
it('should succeed if the ip does belong to masterKeyIps list', done => {
|
||||
it('should succeed if the ip does belong to masterKeyIps list', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
masterKeyIps: ['10.0.0.1'],
|
||||
});
|
||||
fakeReq.ip = 'ip1';
|
||||
fakeReq.ip = '10.0.0.1';
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(true);
|
||||
});
|
||||
|
||||
it('should not succeed if the connection.remoteAddress does not belong to masterKeyIps list', () => {
|
||||
it('should not succeed if the connection.remoteAddress does not belong to masterKeyIps list', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
|
||||
});
|
||||
fakeReq.connection = { remoteAddress: 'ip3' };
|
||||
fakeReq.connection = { remoteAddress: '127.0.0.1' };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(false);
|
||||
});
|
||||
|
||||
it('should succeed if the connection.remoteAddress does belong to masterKeyIps list', done => {
|
||||
it('should succeed if the connection.remoteAddress does belong to masterKeyIps list', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
|
||||
});
|
||||
fakeReq.connection = { remoteAddress: 'ip1' };
|
||||
fakeReq.connection = { remoteAddress: '10.0.0.1' };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(true);
|
||||
});
|
||||
|
||||
it('should not succeed if the socket.remoteAddress does not belong to masterKeyIps list', () => {
|
||||
it('should not succeed if the socket.remoteAddress does not belong to masterKeyIps list', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
|
||||
});
|
||||
fakeReq.socket = { remoteAddress: 'ip3' };
|
||||
fakeReq.socket = { remoteAddress: '127.0.0.1' };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(false);
|
||||
});
|
||||
|
||||
it('should succeed if the socket.remoteAddress does belong to masterKeyIps list', done => {
|
||||
it('should succeed if the socket.remoteAddress does belong to masterKeyIps list', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
|
||||
});
|
||||
fakeReq.socket = { remoteAddress: 'ip1' };
|
||||
fakeReq.socket = { remoteAddress: '10.0.0.1' };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(true);
|
||||
});
|
||||
|
||||
it('should not succeed if the connection.socket.remoteAddress does not belong to masterKeyIps list', () => {
|
||||
it('should not succeed if the connection.socket.remoteAddress does not belong to masterKeyIps list', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
|
||||
});
|
||||
fakeReq.connection = { socket: { remoteAddress: 'ip3' } };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(false);
|
||||
});
|
||||
|
||||
it('should succeed if the connection.socket.remoteAddress does belong to masterKeyIps list', done => {
|
||||
it('should succeed if the connection.socket.remoteAddress does belong to masterKeyIps list', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
masterKeyIps: ['10.0.0.1', '10.0.0.2'],
|
||||
});
|
||||
fakeReq.connection = { socket: { remoteAddress: 'ip1' } };
|
||||
fakeReq.connection = { socket: { remoteAddress: '10.0.0.1' } };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow any ip to use masterKey if masterKeyIps is empty', done => {
|
||||
it('should allow any ip to use masterKey if masterKeyIps is empty', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: [],
|
||||
masterKeyIps: ['0.0.0.0/0'],
|
||||
});
|
||||
fakeReq.ip = 'ip1';
|
||||
fakeReq.ip = '10.0.0.1';
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(true);
|
||||
});
|
||||
|
||||
it('should succeed if xff header does belong to masterKeyIps', done => {
|
||||
it('should succeed if xff header does belong to masterKeyIps', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1'],
|
||||
masterKeyIps: ['10.0.0.1'],
|
||||
});
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
fakeReq.headers['x-forwarded-for'] = 'ip1, ip2, ip3';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
fakeReq.headers['x-forwarded-for'] = '10.0.0.1, 10.0.0.2, ip3';
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(true);
|
||||
});
|
||||
|
||||
it('should succeed if xff header with one ip does belong to masterKeyIps', done => {
|
||||
it('should succeed if xff header with one ip does belong to masterKeyIps', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1'],
|
||||
masterKeyIps: ['10.0.0.1'],
|
||||
});
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
fakeReq.headers['x-forwarded-for'] = 'ip1';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
fakeReq.headers['x-forwarded-for'] = '10.0.0.1';
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(true);
|
||||
});
|
||||
|
||||
it('should not succeed if xff header does not belong to masterKeyIps', () => {
|
||||
it('should not succeed if xff header does not belong to masterKeyIps', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip4'],
|
||||
});
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
fakeReq.headers['x-forwarded-for'] = 'ip1, ip2, ip3';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
fakeReq.headers['x-forwarded-for'] = '10.0.0.1, 10.0.0.2, ip3';
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(false);
|
||||
});
|
||||
|
||||
it('should not succeed if xff header is empty and masterKeyIps is set', () => {
|
||||
it('should not succeed if xff header is empty and masterKeyIps is set', async () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1'],
|
||||
masterKeyIps: ['10.0.0.1'],
|
||||
});
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
fakeReq.headers['x-forwarded-for'] = '';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
|
||||
expect(fakeReq.auth.isMaster).toBe(false);
|
||||
});
|
||||
|
||||
it('should properly expose the headers', () => {
|
||||
|
||||
@@ -110,6 +110,7 @@ const defaultConfiguration = {
|
||||
enableForAnonymousUser: true,
|
||||
enableForAuthenticatedUser: true,
|
||||
},
|
||||
masterKeyIps: ['127.0.0.1'],
|
||||
push: {
|
||||
android: {
|
||||
senderId: 'yolo',
|
||||
|
||||
@@ -495,7 +495,9 @@ describe('server', () => {
|
||||
|
||||
it('fails if you provides invalid ip in masterKeyIps', done => {
|
||||
reconfigureServer({ masterKeyIps: ['invalidIp', '1.2.3.4'] }).catch(error => {
|
||||
expect(error).toEqual('Invalid ip in masterKeyIps: invalidIp');
|
||||
expect(error).toEqual(
|
||||
'The Parse Server option "masterKeyIps" contains an invalid IP address "invalidIp".'
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user