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:
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: ci
|
||||
on:
|
||||
push:
|
||||
branches: [ release, alpha, beta, next-major, 'release-[0-9]+.x.x' ]
|
||||
branches: [release, alpha, beta, next-major, 'release-[0-9]+.x.x']
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
language: ['javascript']
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
@@ -147,19 +147,23 @@ jobs:
|
||||
include:
|
||||
- name: MongoDB 4.2, ReplicaSet
|
||||
MONGODB_VERSION: 4.2.19
|
||||
MONGODB_TOPOLOGY: replicaset
|
||||
MONGODB_TOPOLOGY: replset
|
||||
NODE_VERSION: 19.3.0
|
||||
- name: MongoDB 4.4, ReplicaSet
|
||||
MONGODB_VERSION: 4.4.13
|
||||
MONGODB_TOPOLOGY: replicaset
|
||||
MONGODB_TOPOLOGY: replset
|
||||
NODE_VERSION: 19.3.0
|
||||
- name: MongoDB 5, ReplicaSet
|
||||
MONGODB_VERSION: 5.3.2
|
||||
MONGODB_TOPOLOGY: replicaset
|
||||
MONGODB_TOPOLOGY: replset
|
||||
NODE_VERSION: 19.3.0
|
||||
- name: MongoDB 6, ReplicaSet
|
||||
MONGODB_VERSION: 6.0.2
|
||||
MONGODB_TOPOLOGY: replicaset
|
||||
MONGODB_TOPOLOGY: replset
|
||||
NODE_VERSION: 19.3.0
|
||||
- name: MongoDB 7, ReplicaSet
|
||||
MONGODB_VERSION: 7.0.1
|
||||
MONGODB_TOPOLOGY: replset
|
||||
NODE_VERSION: 19.3.0
|
||||
- name: Redis Cache
|
||||
PARSE_SERVER_TEST_CACHE: redis
|
||||
|
||||
@@ -139,12 +139,13 @@ Parse Server is continuously tested with the most recent releases of Node.js to
|
||||
Parse Server is continuously tested with the most recent releases of MongoDB to ensure compatibility. We follow the [MongoDB support schedule](https://www.mongodb.com/support-policy) and [MongoDB lifecycle schedule](https://www.mongodb.com/support-policy/lifecycles) and only test against versions that are officially supported and have not reached their end-of-life date. We consider the end-of-life date of a MongoDB "rapid release" to be the same as its major version release.
|
||||
|
||||
| Version | Latest Version | End-of-Life | Compatible |
|
||||
|-------------|----------------|---------------|------------|
|
||||
| ----------- | -------------- | ------------- | ---------- |
|
||||
| MongoDB 4.0 | 4.0.28 | April 2022 | ✅ Yes |
|
||||
| MongoDB 4.2 | 4.2.19 | April 2023 | ✅ Yes |
|
||||
| MongoDB 4.4 | 4.4.13 | February 2024 | ✅ Yes |
|
||||
| MongoDB 5 | 5.3.2 | October 2024 | ✅ Yes |
|
||||
| MongoDB 6 | 6.0.2 | July 2025 | ✅ Yes |
|
||||
| MongoDB 7 | 7.0.1 | TDB | ✅ Yes |
|
||||
|
||||
#### PostgreSQL
|
||||
|
||||
|
||||
2590
package-lock.json
generated
2590
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -45,7 +45,7 @@
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "9.1.1",
|
||||
"mime": "3.0.0",
|
||||
"mongodb": "4.10.0",
|
||||
"mongodb": "5.9.0",
|
||||
"mustache": "4.2.0",
|
||||
"otpauth": "9.1.2",
|
||||
"parse": "4.1.0",
|
||||
@@ -98,7 +98,7 @@
|
||||
"madge": "5.0.1",
|
||||
"mock-files-adapter": "file:spec/dependencies/mock-files-adapter",
|
||||
"mock-mail-adapter": "file:spec/dependencies/mock-mail-adapter",
|
||||
"mongodb-runner": "4.8.1",
|
||||
"mongodb-runner": "5.4.4",
|
||||
"mongodb-version-list": "1.0.0",
|
||||
"node-abort-controller": "3.0.1",
|
||||
"node-fetch": "3.2.10",
|
||||
@@ -117,18 +117,18 @@
|
||||
"lint-fix": "eslint --fix --cache ./",
|
||||
"build": "babel src/ -d lib/ --copy-files",
|
||||
"watch": "babel --watch src/ -d lib/ --copy-files",
|
||||
"test:mongodb:runnerstart": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start",
|
||||
"test:mongodb:runnerstart": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017",
|
||||
"test:mongodb:testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
|
||||
"test:mongodb": "npm run test:mongodb:runnerstart --dbversion=$npm_config_dbversion && npm run test:mongodb:testonly --dbversion=$npm_config_dbversion",
|
||||
"test:mongodb:4.2.19": "npm run test:mongodb --dbversion=4.2.19",
|
||||
"test:mongodb:4.4.13": "npm run test:mongodb --dbversion=4.4.13",
|
||||
"test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2",
|
||||
"test:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.2",
|
||||
"posttest:mongodb": "mongodb-runner stop",
|
||||
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start",
|
||||
"test:mongodb:7.0.1": "npm run test:mongodb --dbversion=7.0.1",
|
||||
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017",
|
||||
"testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
|
||||
"test": "npm run testonly",
|
||||
"posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner stop",
|
||||
"posttest": "cross-env mongodb-runner stop --all",
|
||||
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 nyc jasmine",
|
||||
"start": "node ./bin/parse-server",
|
||||
"prettier": "prettier --write {src,spec}/{**/*,*}.js",
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_');
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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]*' }],
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
const Parse = require('parse/node').Parse;
|
||||
const jwksClient = require('jwks-rsa');
|
||||
const util = require('util');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const authUtils = require('./utils');
|
||||
|
||||
@@ -17,11 +16,9 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
||||
cacheMaxAge,
|
||||
});
|
||||
|
||||
const asyncGetSigningKeyFunction = util.promisify(client.getSigningKey);
|
||||
|
||||
let key;
|
||||
try {
|
||||
key = await asyncGetSigningKeyFunction(keyId);
|
||||
key = await authUtils.getSigningKey(client, keyId);
|
||||
} catch (error) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
const Parse = require('parse/node').Parse;
|
||||
const crypto = require('crypto');
|
||||
const jwksClient = require('jwks-rsa');
|
||||
const util = require('util');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const httpsRequest = require('./httpsRequest');
|
||||
const authUtils = require('./utils');
|
||||
@@ -60,11 +59,9 @@ const getFacebookKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
||||
cacheMaxAge,
|
||||
});
|
||||
|
||||
const asyncGetSigningKeyFunction = util.promisify(client.getSigningKey);
|
||||
|
||||
let key;
|
||||
try {
|
||||
key = await asyncGetSigningKeyFunction(keyId);
|
||||
key = await authUtils.getSigningKey(client, keyId);
|
||||
} catch (error) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const util = require('util');
|
||||
const Parse = require('parse/node').Parse;
|
||||
const getHeaderFromToken = token => {
|
||||
const decodedToken = jwt.decode(token, { complete: true });
|
||||
@@ -8,6 +9,16 @@ const getHeaderFromToken = token => {
|
||||
|
||||
return decodedToken.header;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the signing key from a JWKS client.
|
||||
* @param {Object} client The JWKS client.
|
||||
* @param {String} key The kid.
|
||||
*/
|
||||
async function getSigningKey(client, key) {
|
||||
return util.promisify(client.getSigningKey)(key);
|
||||
}
|
||||
module.exports = {
|
||||
getHeaderFromToken,
|
||||
getSigningKey,
|
||||
};
|
||||
|
||||
@@ -160,18 +160,10 @@ export default class MongoCollection {
|
||||
}
|
||||
|
||||
_ensureSparseUniqueIndexInBackground(indexRequest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._mongoCollection.createIndex(
|
||||
indexRequest,
|
||||
{ unique: true, background: true, sparse: true },
|
||||
error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
return this._mongoCollection.createIndex(indexRequest, {
|
||||
unique: true,
|
||||
background: true,
|
||||
sparse: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,6 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// parsing and re-formatting causes the auth value (if there) to get URI
|
||||
// encoded
|
||||
const encodedUri = formatUrl(parseUrl(this._uri));
|
||||
|
||||
this.connectionPromise = MongoClient.connect(encodedUri, this._mongoOptions)
|
||||
.then(client => {
|
||||
// Starting mongoDB 3.0, the MongoClient.connect don't return a DB anymore but a client
|
||||
@@ -687,13 +686,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
};
|
||||
|
||||
return this._adaptiveCollection(className)
|
||||
.then(
|
||||
collection =>
|
||||
new Promise((resolve, reject) =>
|
||||
collection._mongoCollection.createIndex(indexCreationRequest, indexOptions, error =>
|
||||
error ? reject(error) : resolve()
|
||||
)
|
||||
)
|
||||
.then(collection =>
|
||||
collection._mongoCollection.createIndex(indexCreationRequest, indexOptions)
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
@@ -457,6 +457,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (restKey, restValue, schema) =>
|
||||
);
|
||||
}
|
||||
value = mapValues(restValue, transformInteriorValue);
|
||||
|
||||
return { key: restKey, value };
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user