feat: Add support for MongoDB 7 (#8761)

BREAKING CHANGE: `Parse.Query` no longer supports the BSON type `code`; although this feature was never officially documented, its removal is announced as a breaking change to protect deployments where it might be in use.
This commit is contained in:
Lucas Coratger
2023-12-10 02:42:40 +01:00
committed by GitHub
parent d3087ed69f
commit 3de8494a22
17 changed files with 667 additions and 2367 deletions

View File

@@ -339,11 +339,12 @@ describe('AudiencesRouter', () => {
)
.then(result => {
expect(result).toBeTruthy();
database
.collection('test__Audience')
.find({ _id: audience.objectId })
.toArray((error, rows) => {
expect(error).toEqual(undefined);
.toArray()
.then(rows => {
expect(rows[0]['times_used']).toEqual(1);
expect(rows[0]['_last_used']).toEqual(now);
Parse._request(
@@ -362,6 +363,9 @@ describe('AudiencesRouter', () => {
.catch(error => {
done.fail(error);
});
})
.catch(error => {
done.fail(error);
});
});
});

View File

@@ -1454,7 +1454,6 @@ describe('oauth2 auth adapter', () => {
describe('apple signin auth adapter', () => {
const apple = require('../lib/Adapters/Auth/apple');
const jwt = require('jsonwebtoken');
const util = require('util');
const authUtils = require('../lib/Adapters/Auth/utils');
it('(using client id as string) should throw error with missing id_token', async () => {
@@ -1512,12 +1511,10 @@ describe('apple signin auth adapter', () => {
sub: 'the_user_id',
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
const result = await apple.validateAuthData(
{ id: 'the_user_id', token: 'the_token' },
@@ -1529,11 +1526,9 @@ describe('apple signin auth adapter', () => {
it('should not verify invalid id_token', async () => {
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
try {
await apple.validateAuthData(
@@ -1566,11 +1561,9 @@ describe('apple signin auth adapter', () => {
sub: 'the_user_id',
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
const result = await apple.validateAuthData(
@@ -1588,11 +1581,9 @@ describe('apple signin auth adapter', () => {
sub: 'the_user_id',
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
const result = await apple.validateAuthData(
@@ -1610,11 +1601,9 @@ describe('apple signin auth adapter', () => {
sub: 'the_user_id',
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
const result = await apple.validateAuthData(
@@ -1630,11 +1619,9 @@ describe('apple signin auth adapter', () => {
sub: 'the_user_id',
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
try {
@@ -1658,11 +1645,9 @@ describe('apple signin auth adapter', () => {
sub: 'the_user_id',
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
try {
@@ -1687,11 +1672,9 @@ describe('apple signin auth adapter', () => {
sub: 'the_user_id',
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
try {
@@ -1759,11 +1742,9 @@ describe('apple signin auth adapter', () => {
sub: 'a_different_user_id',
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
try {
@@ -2025,7 +2006,6 @@ describe('microsoft graph auth adapter', () => {
describe('facebook limited auth adapter', () => {
const facebook = require('../lib/Adapters/Auth/facebook');
const jwt = require('jsonwebtoken');
const util = require('util');
const authUtils = require('../lib/Adapters/Auth/utils');
// TODO: figure out a way to run this test alongside facebook classic tests
@@ -2086,18 +2066,11 @@ describe('facebook limited auth adapter', () => {
exp: Date.now(),
sub: 'the_user_id',
};
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
const result = await facebook.validateAuthData(
{ id: 'the_user_id', token: 'the_token' },
@@ -2108,17 +2081,10 @@ describe('facebook limited auth adapter', () => {
});
it('should not verify invalid id_token', async () => {
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
try {
await facebook.validateAuthData(
@@ -2150,17 +2116,10 @@ describe('facebook limited auth adapter', () => {
exp: Date.now(),
sub: 'the_user_id',
};
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
const result = await facebook.validateAuthData(
@@ -2177,17 +2136,10 @@ describe('facebook limited auth adapter', () => {
exp: Date.now(),
sub: 'the_user_id',
};
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
const result = await facebook.validateAuthData(
@@ -2204,17 +2156,10 @@ describe('facebook limited auth adapter', () => {
exp: Date.now(),
sub: 'the_user_id',
};
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
const result = await facebook.validateAuthData(
@@ -2229,17 +2174,10 @@ describe('facebook limited auth adapter', () => {
iss: 'https://not.facebook.com',
sub: 'the_user_id',
};
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
try {
@@ -2262,17 +2200,10 @@ describe('facebook limited auth adapter', () => {
iss: 'https://not.facebook.com',
sub: 'the_user_id',
};
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
try {
@@ -2296,17 +2227,10 @@ describe('facebook limited auth adapter', () => {
iss: 'https://not.facebook.com',
sub: 'the_user_id',
};
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
try {
@@ -2382,17 +2306,10 @@ describe('facebook limited auth adapter', () => {
aud: 'invalid_client_id',
sub: 'a_different_user_id',
};
const fakeDecodedToken = {
header: { kid: '123', alg: 'RS256' },
};
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
const fakeGetSigningKeyAsyncFunction = () => {
return {
kid: '123',
rsaPublicKey: 'the_rsa_public_key',
};
};
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
try {

View File

@@ -284,25 +284,11 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
amount: 1,
},
};
await Parse.Server.database.update(
'MyClass',
query,
update,
{ upsert: true },
);
await Parse.Server.database.update('MyClass', query, update, { upsert: true });
update.objectId.amount = uuid2;
await Parse.Server.database.update(
'MyClass',
query,
update,
{ upsert: true },
);
await Parse.Server.database.update('MyClass', query, update, { upsert: true });
const res = await Parse.Server.database.find(
schema.className,
{},
{},
);
const res = await Parse.Server.database.find(schema.className, {}, {});
expect(res.length).toBe(1);
expect(res[0].objectId).toBe(uuid1);
expect(res[0].count).toBe(2);

View File

@@ -86,6 +86,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
let result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
explain: true,
});
let { queryPlanner } = result[0].stages[0].$cursor;
expect(queryPlanner.winningPlan.stage).toBe('COLLSCAN');
@@ -93,6 +94,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
hint: '_id_',
explain: true,
});
queryPlanner = result[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.stage).toBe('FETCH');
expect(queryPlanner.winningPlan.inputStage.indexName).toBe('_id_');

View File

@@ -251,8 +251,8 @@ afterEach(function (done) {
})
.then(() => Parse.User.logOut())
.then(
() => { },
() => { }
() => {},
() => {}
) // swallow errors
.then(() => {
// Connection close events are not immediate on node 10+... wait a bit

View File

@@ -143,10 +143,10 @@ describe('rest create', () => {
const res = await request({
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest'
'X-Parse-REST-API-Key': 'rest',
},
method: 'GET',
url: `http://localhost:8378/1/classes/TestObject/${id}`
url: `http://localhost:8378/1/classes/TestObject/${id}`,
});
return res.data;
@@ -158,10 +158,10 @@ describe('rest create', () => {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
'X-Parse-Maintenance-Key': 'testing'
'X-Parse-Maintenance-Key': 'testing',
},
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject'
url: 'http://localhost:8378/1/classes/TestObject',
};
});
@@ -206,8 +206,7 @@ describe('rest create', () => {
try {
await request(req);
fail();
}
catch (err) {
} catch (err) {
expect(err.data.code).toEqual(Parse.Error.VALIDATION_ERROR);
}
});
@@ -230,8 +229,7 @@ describe('rest create', () => {
try {
await request(req);
fail();
}
catch (err) {
} catch (err) {
expect(err.data.code).toEqual(Parse.Error.INCORRECT_TYPE);
}
});

View File

@@ -235,32 +235,6 @@ describe('Vulnerabilities', () => {
await new Promise(resolve => server.close(resolve));
});
it('allows BSON type code data in write request with custom denylist', async () => {
await reconfigureServer({
requestKeywordDenylist: [],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
_bsontype: 'Code',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(201);
const text = JSON.parse(response.text);
expect(text.objectId).toBeDefined();
});
it('denies write request with custom denylist of key/value', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }],