Skip to content

Commit 689072b

Browse files
authored
Merge branch 'alpha' into moumouls/fixGraphIQL
2 parents 4750ac3 + b1bb1fc commit 689072b

19 files changed

+197
-278
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name: ci
22
on:
33
push:
4-
branches: [ release, alpha, beta ]
4+
branches: [ release*, alpha, beta ]
55
pull_request:
6-
branches: [ release, alpha, beta ]
6+
branches: [ release*, alpha, beta ]
77
env:
88
NODE_VERSION: 18.12.1
99
PARSE_SERVER_TEST_TIMEOUT: 20000

DEPRECATIONS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The following is a list of deprecations, according to the [Deprecation Policy](h
44

55
| ID | Change | Issue | Deprecation [ℹ️][i_deprecation] | Planned Removal [ℹ️][i_removal] | Status [ℹ️][i_status] | Notes |
66
|--------|-------------------------------------------------|----------------------------------------------------------------------|---------------------------------|---------------------------------|-----------------------|-------|
7-
| DEPPS1 | Native MongoDB syntax in aggregation pipeline | [#7338](https://github.com/parse-community/parse-server/issues/7338) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - |
7+
| DEPPS1 | Native MongoDB syntax in aggregation pipeline | [#7338](https://github.com/parse-community/parse-server/issues/7338) | 5.0.0 (2022) | 6.0.0 (2023) | removed | - |
88
| DEPPS2 | Config option `directAccess` defaults to `true` | [#6636](https://github.com/parse-community/parse-server/pull/6636) | 5.0.0 (2022) | 6.0.0 (2023) | removed | - |
99
| DEPPS3 | Config option `enforcePrivateUsers` defaults to `true` | [#7319](https://github.com/parse-community/parse-server/pull/7319) | 5.0.0 (2022) | 6.0.0 (2023) | removed | - |
1010
| DEPPS4 | Remove convenience method for http request `Parse.Cloud.httpRequest` | [#7589](https://github.com/parse-community/parse-server/pull/7589) | 5.0.0 (2022) | 6.0.0 (2023) | removed | - |

changelogs/CHANGELOG_alpha.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
# [6.0.0-alpha.19](https://github.com/parse-community/parse-server/compare/6.0.0-alpha.18...6.0.0-alpha.19) (2023-01-05)
2+
3+
4+
### Features
5+
6+
* Remove deprecation `DEPPS1`: Native MongoDB syntax in aggregation pipeline ([#8362](https://github.com/parse-community/parse-server/issues/8362)) ([d0d30c4](https://github.com/parse-community/parse-server/commit/d0d30c4f1394f563724644a8fc81734be538a2c0))
7+
8+
9+
### BREAKING CHANGES
10+
11+
* The MongoDB aggregation pipeline requires native MongoDB syntax instead of the custom Parse Server syntax; for example pipeline stage names require a leading dollar sign like `$match` and the MongoDB document ID is referenced using `_id` instead of `objectId` (#8362) ([d0d30c4](d0d30c4))
12+
13+
# [6.0.0-alpha.18](https://github.com/parse-community/parse-server/compare/6.0.0-alpha.17...6.0.0-alpha.18) (2023-01-05)
14+
15+
16+
### Bug Fixes
17+
18+
* The client IP address may be determined incorrectly in some cases; this fixes a security vulnerability in which the Parse Server option `masterKeyIps` may be circumvented, see [GHSA-vm5r-c87r-pf6x](https://github.com/parse-community/parse-server/security/advisories/GHSA-vm5r-c87r-pf6x) ([#8372](https://github.com/parse-community/parse-server/issues/8372)) ([892040d](https://github.com/parse-community/parse-server/commit/892040dc2f82a3e2abe2824e4b553521b6f894de))
19+
20+
21+
### BREAKING CHANGES
22+
23+
* The mechanism to determine the client IP address has been rewritten; to correctly determine the IP address it is now required to set the Parse Server option `trustProxy` accordingly if Parse Server runs behind a proxy server, see the express framework's [trust proxy](https://expressjs.com/en/guide/behind-proxies.html) setting (#8372) ([892040d](892040d))
24+
125
# [6.0.0-alpha.17](https://github.com/parse-community/parse-server/compare/6.0.0-alpha.16...6.0.0-alpha.17) (2022-12-22)
226

327

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "parse-server",
3-
"version": "6.0.0-alpha.17",
3+
"version": "6.0.0-alpha.19",
44
"description": "An express module providing a Parse-compatible API server",
55
"main": "lib/index.js",
66
"repository": {

spec/AggregateRouter.spec.js

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,31 @@
11
const AggregateRouter = require('../lib/Routers/AggregateRouter').AggregateRouter;
22

33
describe('AggregateRouter', () => {
4-
// TODO: update pipeline syntax. See [#7339](https://bit.ly/3incnWx)
54
it('get pipeline from Array', () => {
65
const body = [
76
{
8-
group: { objectId: {} },
7+
$group: { _id: {} },
98
},
109
];
1110
const expected = [{ $group: { _id: {} } }];
1211
const result = AggregateRouter.getPipeline(body);
1312
expect(result).toEqual(expected);
1413
});
1514

16-
// TODO: update pipeline syntax. See [#7339](https://bit.ly/3incnWx)
1715
it('get pipeline from Object', () => {
1816
const body = {
19-
group: { objectId: {} },
17+
$group: { _id: {} },
2018
};
2119
const expected = [{ $group: { _id: {} } }];
2220
const result = AggregateRouter.getPipeline(body);
2321
expect(result).toEqual(expected);
2422
});
2523

26-
// TODO: update pipeline syntax. See [#7339](https://bit.ly/3incnWx)
2724
it('get pipeline from Pipeline Operator (Array)', () => {
2825
const body = {
2926
pipeline: [
3027
{
31-
group: { objectId: {} },
28+
$group: { _id: {} },
3229
},
3330
],
3431
};
@@ -37,55 +34,53 @@ describe('AggregateRouter', () => {
3734
expect(result).toEqual(expected);
3835
});
3936

40-
// TODO: update pipeline syntax. See [#7339](https://bit.ly/3incnWx)
4137
it('get pipeline from Pipeline Operator (Object)', () => {
4238
const body = {
4339
pipeline: {
44-
group: { objectId: {} },
40+
$group: { _id: {} },
4541
},
4642
};
4743
const expected = [{ $group: { _id: {} } }];
4844
const result = AggregateRouter.getPipeline(body);
4945
expect(result).toEqual(expected);
5046
});
5147

52-
// TODO: update pipeline syntax. See [#7339](https://bit.ly/3incnWx)
5348
it('get pipeline fails multiple keys in Array stage ', () => {
5449
const body = [
5550
{
56-
group: { objectId: {} },
57-
match: { name: 'Test' },
51+
$group: { _id: {} },
52+
$match: { name: 'Test' },
5853
},
5954
];
60-
try {
61-
AggregateRouter.getPipeline(body);
62-
} catch (e) {
63-
expect(e.message).toBe('Pipeline stages should only have one key found group, match');
64-
}
55+
expect(() => AggregateRouter.getPipeline(body)).toThrow(
56+
new Parse.Error(
57+
Parse.Error.INVALID_QUERY,
58+
'Pipeline stages should only have one key but found $group, $match.'
59+
)
60+
);
6561
});
6662

67-
// TODO: update pipeline syntax. See [#7339](https://bit.ly/3incnWx)
6863
it('get pipeline fails multiple keys in Pipeline Operator Array stage ', () => {
6964
const body = {
7065
pipeline: [
7166
{
72-
group: { objectId: {} },
73-
match: { name: 'Test' },
67+
$group: { _id: {} },
68+
$match: { name: 'Test' },
7469
},
7570
],
7671
};
77-
try {
78-
AggregateRouter.getPipeline(body);
79-
} catch (e) {
80-
expect(e.message).toBe('Pipeline stages should only have one key found group, match');
81-
}
72+
expect(() => AggregateRouter.getPipeline(body)).toThrow(
73+
new Parse.Error(
74+
Parse.Error.INVALID_QUERY,
75+
'Pipeline stages should only have one key but found $group, $match.'
76+
)
77+
);
8278
});
8379

84-
// TODO: update pipeline syntax. See [#7339](https://bit.ly/3incnWx)
8580
it('get search pipeline from Pipeline Operator (Array)', () => {
8681
const body = {
8782
pipeline: {
88-
search: {},
83+
$search: {},
8984
},
9085
};
9186
const expected = [{ $search: {} }];
@@ -105,7 +100,7 @@ describe('AggregateRouter', () => {
105100
it('support nested stage names starting with `$`', () => {
106101
const body = [
107102
{
108-
lookup: {
103+
$lookup: {
109104
from: 'ACollection',
110105
let: { id: '_id' },
111106
as: 'results',
@@ -145,11 +140,11 @@ describe('AggregateRouter', () => {
145140

146141
it('support the use of `_id` in stages', () => {
147142
const body = [
148-
{ match: { _id: 'randomId' } },
149-
{ sort: { _id: -1 } },
150-
{ addFields: { _id: 1 } },
151-
{ group: { _id: {} } },
152-
{ project: { _id: 0 } },
143+
{ $match: { _id: 'randomId' } },
144+
{ $sort: { _id: -1 } },
145+
{ $addFields: { _id: 1 } },
146+
{ $group: { _id: {} } },
147+
{ $project: { _id: 0 } },
153148
];
154149
const expected = [
155150
{ $match: { _id: 'randomId' } },
@@ -161,4 +156,19 @@ describe('AggregateRouter', () => {
161156
const result = AggregateRouter.getPipeline(body);
162157
expect(result).toEqual(expected);
163158
});
159+
160+
it('should throw with invalid stage', () => {
161+
expect(() => AggregateRouter.getPipeline([{ foo: 'bar' }])).toThrow(
162+
new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid aggregate stage 'foo'.`)
163+
);
164+
});
165+
166+
it('should throw with invalid group', () => {
167+
expect(() => AggregateRouter.getPipeline([{ $group: { objectId: 'bar' } }])).toThrow(
168+
new Parse.Error(
169+
Parse.Error.INVALID_QUERY,
170+
`Cannot use 'objectId' in aggregation stage $group.`
171+
)
172+
);
173+
});
164174
});

spec/CloudCode.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2774,7 +2774,7 @@ describe('afterFind hooks', () => {
27742774
const obj = new Parse.Object('MyObject');
27752775
const pipeline = [
27762776
{
2777-
group: { objectId: {} },
2777+
$group: { _id: {} },
27782778
},
27792779
];
27802780
obj

spec/Middlewares.spec.js

Lines changed: 3 additions & 108 deletions
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', () => {

0 commit comments

Comments
 (0)