mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
Compare commits
7
Commits
9.10.0-alpha.8
...
5.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b905137278 | ||
|
|
2c19c2e4d4 | ||
|
|
30576f1091 | ||
|
|
e016d813e0 | ||
|
|
c8bc200729 | ||
|
|
09d04b0aad | ||
|
|
38f64be596 |
@@ -1,9 +1,9 @@
|
||||
name: ci
|
||||
on:
|
||||
push:
|
||||
branches: [ release, alpha, beta ]
|
||||
branches: [ release*, alpha, beta ]
|
||||
pull_request:
|
||||
branches: [ release, alpha, beta ]
|
||||
branches: [ release*, alpha, beta ]
|
||||
env:
|
||||
NODE_VERSION: 18.1.0
|
||||
PARSE_SERVER_TEST_TIMEOUT: 20000
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: release-automated
|
||||
on:
|
||||
push:
|
||||
branches: [ release, alpha, beta, next-major ]
|
||||
branches: [ release, alpha, beta, next-major, release-4.x.x, release-5.x.x ]
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
+28
-1754
File diff suppressed because it is too large
Load Diff
Generated
+284
-273
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parse-server",
|
||||
"version": "5.4.0",
|
||||
"version": "5.4.2",
|
||||
"description": "An express module providing a Parse-compatible API server",
|
||||
"main": "lib/index.js",
|
||||
"repository": {
|
||||
@@ -37,7 +37,7 @@
|
||||
"graphql-tag": "2.12.6",
|
||||
"graphql-relay": "0.10.0",
|
||||
"intersect": "1.0.1",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jsonwebtoken": "9.0.0",
|
||||
"jwks-rsa": "2.1.4",
|
||||
"ldapjs": "2.3.3",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
+2
-15
@@ -28,7 +28,7 @@ async function config() {
|
||||
console.log(`Running on branch: ${branch}`);
|
||||
|
||||
// Set changelog file
|
||||
const changelogFile = `./changelogs/CHANGELOG_${branch}.md`;
|
||||
const changelogFile = `./changelogs/CHANGELOG_release.md`;
|
||||
console.log(`Changelog file output to: ${changelogFile}`);
|
||||
|
||||
// Load template file contents
|
||||
@@ -41,10 +41,7 @@ async function config() {
|
||||
{ name: 'beta', prerelease: true },
|
||||
'next-major',
|
||||
// Long-Term-Support branches
|
||||
// { name: 'release-1', range: '1.x.x', channel: '1.x' },
|
||||
// { name: 'release-2', range: '2.x.x', channel: '2.x' },
|
||||
// { name: 'release-3', range: '3.x.x', channel: '3.x' },
|
||||
// { name: 'release-4', range: '4.x.x', channel: '4.x' },
|
||||
{ name: 'release-5.x.x', range: '5.x.x', channel: '5.x.x' },
|
||||
],
|
||||
dryRun: false,
|
||||
debug: true,
|
||||
@@ -88,16 +85,6 @@ async function config() {
|
||||
labels: ['type:ci'],
|
||||
releasedLabels: ['state:released<%= nextRelease.channel ? `-\${nextRelease.channel}` : "" %>']
|
||||
}],
|
||||
// Back-merge module runs last because if it fails it should not impede the release process
|
||||
[
|
||||
"@saithodev/semantic-release-backmerge",
|
||||
{
|
||||
"branches": [
|
||||
{ from: "beta", to: "alpha" },
|
||||
{ from: "release", to: "beta" },
|
||||
]
|
||||
}
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -596,6 +596,7 @@ describe('instagram auth adapter', () => {
|
||||
describe('google auth adapter', () => {
|
||||
const google = require('../lib/Adapters/Auth/google');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const authUtils = require('../lib/Adapters/Auth/utils');
|
||||
|
||||
it('should throw error with missing id_token', async () => {
|
||||
try {
|
||||
@@ -618,7 +619,7 @@ describe('google auth adapter', () => {
|
||||
// it('should throw error if public key used to encode token is not available', async () => {
|
||||
// const fakeDecodedToken = { header: { kid: '789', alg: 'RS256' } };
|
||||
// try {
|
||||
// spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
// spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
|
||||
// await google.validateAuthData({ id: 'the_user_id', id_token: 'the_token' }, {});
|
||||
// fail();
|
||||
@@ -637,7 +638,7 @@ describe('google auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||
|
||||
const result = await google.validateAuthData(
|
||||
@@ -653,7 +654,7 @@ describe('google auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||
|
||||
try {
|
||||
@@ -677,7 +678,7 @@ describe('google auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||
|
||||
try {
|
||||
@@ -699,7 +700,7 @@ describe('google auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||
|
||||
try {
|
||||
@@ -1402,6 +1403,7 @@ 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 () => {
|
||||
try {
|
||||
@@ -1436,7 +1438,7 @@ describe('apple signin auth adapter', () => {
|
||||
it('should throw error if public key used to encode token is not available', async () => {
|
||||
const fakeDecodedToken = { header: { kid: '789', alg: 'RS256' } };
|
||||
try {
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
|
||||
|
||||
await apple.validateAuthData(
|
||||
{ id: 'the_user_id', token: 'the_token' },
|
||||
@@ -1458,7 +1460,7 @@ describe('apple signin auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
|
||||
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
@@ -1475,7 +1477,7 @@ describe('apple signin auth adapter', () => {
|
||||
|
||||
it('should not verify invalid id_token', async () => {
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
};
|
||||
@@ -1512,7 +1514,7 @@ describe('apple signin auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
};
|
||||
@@ -1534,7 +1536,7 @@ describe('apple signin auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
};
|
||||
@@ -1556,7 +1558,7 @@ describe('apple signin auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
};
|
||||
@@ -1576,7 +1578,7 @@ describe('apple signin auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
};
|
||||
@@ -1604,7 +1606,7 @@ describe('apple signin auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
};
|
||||
@@ -1633,7 +1635,7 @@ describe('apple signin auth adapter', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
};
|
||||
@@ -1705,7 +1707,7 @@ describe('apple signin auth adapter', () => {
|
||||
sub: 'a_different_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||
};
|
||||
@@ -1972,6 +1974,7 @@ 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
|
||||
xit('(using client id as string) should throw error with missing id_token', async () => {
|
||||
@@ -2010,7 +2013,7 @@ describe('facebook limited auth adapter', () => {
|
||||
header: { kid: '789', alg: 'RS256' },
|
||||
};
|
||||
try {
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
|
||||
|
||||
await facebook.validateAuthData(
|
||||
{ id: 'the_user_id', token: 'the_token' },
|
||||
@@ -2034,7 +2037,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
|
||||
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
@@ -2056,7 +2059,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
kid: '123',
|
||||
@@ -2098,7 +2101,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
kid: '123',
|
||||
@@ -2125,7 +2128,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
kid: '123',
|
||||
@@ -2152,7 +2155,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
kid: '123',
|
||||
@@ -2177,7 +2180,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
kid: '123',
|
||||
@@ -2210,7 +2213,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
kid: '123',
|
||||
@@ -2244,7 +2247,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
kid: '123',
|
||||
@@ -2330,7 +2333,7 @@ describe('facebook limited auth adapter', () => {
|
||||
const fakeDecodedToken = {
|
||||
header: { kid: '123', alg: 'RS256' },
|
||||
};
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
const fakeGetSigningKeyAsyncFunction = () => {
|
||||
return {
|
||||
kid: '123',
|
||||
|
||||
+3
-118
@@ -158,78 +158,6 @@ describe('middlewares', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not succeed if the connection.remoteAddress does not belong to masterKeyIps list', () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
});
|
||||
fakeReq.connection = { remoteAddress: 'ip3' };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
});
|
||||
|
||||
it('should succeed if the connection.remoteAddress does belong to masterKeyIps list', done => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
});
|
||||
fakeReq.connection = { remoteAddress: 'ip1' };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not succeed if the socket.remoteAddress does not belong to masterKeyIps list', () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
});
|
||||
fakeReq.socket = { remoteAddress: 'ip3' };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
});
|
||||
|
||||
it('should succeed if the socket.remoteAddress does belong to masterKeyIps list', done => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
});
|
||||
fakeReq.socket = { remoteAddress: 'ip1' };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not succeed if the connection.socket.remoteAddress does not belong to masterKeyIps list', () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
});
|
||||
fakeReq.connection = { socket: { remoteAddress: 'ip3' } };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
});
|
||||
|
||||
it('should succeed if the connection.socket.remoteAddress does belong to masterKeyIps list', done => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1', 'ip2'],
|
||||
});
|
||||
fakeReq.connection = { socket: { remoteAddress: 'ip1' } };
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow any ip to use masterKey if masterKeyIps is empty', done => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
@@ -243,52 +171,9 @@ describe('middlewares', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should succeed if xff header does belong to masterKeyIps', done => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1'],
|
||||
});
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should succeed if xff header with one ip does belong to masterKeyIps', done => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1'],
|
||||
});
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
fakeReq.headers['x-forwarded-for'] = 'ip1';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeRes.status).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not succeed if xff header does not belong to masterKeyIps', () => {
|
||||
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);
|
||||
});
|
||||
|
||||
it('should not succeed if xff header is empty and masterKeyIps is set', () => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['ip1'],
|
||||
});
|
||||
fakeReq.headers['x-parse-master-key'] = 'masterKey';
|
||||
fakeReq.headers['x-forwarded-for'] = '';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes);
|
||||
expect(fakeRes.status).toHaveBeenCalledWith(403);
|
||||
it('can set trust proxy', async () => {
|
||||
const server = await reconfigureServer({ trustProxy: 1 });
|
||||
expect(server.app.parent.settings['trust proxy']).toBe(1);
|
||||
});
|
||||
|
||||
it('should properly expose the headers', () => {
|
||||
|
||||
+2
-1
@@ -553,6 +553,7 @@ describe('server', () => {
|
||||
|
||||
it('should not fail when Google signin is introduced without the optional clientId', done => {
|
||||
const jwt = require('jsonwebtoken');
|
||||
const authUtils = require('../lib/Adapters/Auth/utils');
|
||||
|
||||
reconfigureServer({
|
||||
auth: { google: {} },
|
||||
@@ -565,7 +566,7 @@ describe('server', () => {
|
||||
sub: 'the_user_id',
|
||||
};
|
||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
|
||||
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||
const user = new Parse.User();
|
||||
user
|
||||
|
||||
@@ -5,6 +5,7 @@ const Parse = require('parse/node').Parse;
|
||||
const jwksClient = require('jwks-rsa');
|
||||
const util = require('util');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const authUtils = require('./utils');
|
||||
|
||||
const TOKEN_ISSUER = 'https://appleid.apple.com';
|
||||
|
||||
@@ -30,21 +31,12 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
||||
return key;
|
||||
};
|
||||
|
||||
const getHeaderFromToken = token => {
|
||||
const decodedToken = jwt.decode(token, { complete: true });
|
||||
if (!decodedToken) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
|
||||
}
|
||||
|
||||
return decodedToken.header;
|
||||
};
|
||||
|
||||
const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) => {
|
||||
if (!token) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
|
||||
}
|
||||
|
||||
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
||||
const { kid: keyId, alg: algorithm } = authUtils.getHeaderFromToken(token);
|
||||
const ONE_HOUR_IN_MS = 3600000;
|
||||
let jwtClaims;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ const jwksClient = require('jwks-rsa');
|
||||
const util = require('util');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const httpsRequest = require('./httpsRequest');
|
||||
const authUtils = require('./utils');
|
||||
|
||||
const TOKEN_ISSUER = 'https://facebook.com';
|
||||
|
||||
@@ -73,21 +74,12 @@ const getFacebookKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
||||
return key;
|
||||
};
|
||||
|
||||
const getHeaderFromToken = token => {
|
||||
const decodedToken = jwt.decode(token, { complete: true });
|
||||
if (!decodedToken) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'provided token does not decode as JWT');
|
||||
}
|
||||
|
||||
return decodedToken.header;
|
||||
};
|
||||
|
||||
const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) => {
|
||||
if (!token) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'id token is invalid for this user.');
|
||||
}
|
||||
|
||||
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
||||
const { kid: keyId, alg: algorithm } = authUtils.getHeaderFromToken(token);
|
||||
const ONE_HOUR_IN_MS = 3600000;
|
||||
let jwtClaims;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ var Parse = require('parse/node').Parse;
|
||||
|
||||
const https = require('https');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const authUtils = require('./utils');
|
||||
|
||||
const TOKEN_ISSUER = 'accounts.google.com';
|
||||
const HTTPS_TOKEN_ISSUER = 'https://accounts.google.com';
|
||||
@@ -51,22 +52,12 @@ function getGoogleKeyByKeyId(keyId) {
|
||||
});
|
||||
}
|
||||
|
||||
function getHeaderFromToken(token) {
|
||||
const decodedToken = jwt.decode(token, { complete: true });
|
||||
|
||||
if (!decodedToken) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
|
||||
}
|
||||
|
||||
return decodedToken.header;
|
||||
}
|
||||
|
||||
async function verifyIdToken({ id_token: token, id }, { clientId }) {
|
||||
if (!token) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
|
||||
}
|
||||
|
||||
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
||||
const { kid: keyId, alg: algorithm } = authUtils.getHeaderFromToken(token);
|
||||
let jwtClaims;
|
||||
const googleKey = await getGoogleKeyByKeyId(keyId);
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const Parse = require('parse/node').Parse;
|
||||
const getHeaderFromToken = token => {
|
||||
const decodedToken = jwt.decode(token, { complete: true });
|
||||
if (!decodedToken) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
|
||||
}
|
||||
|
||||
return decodedToken.header;
|
||||
};
|
||||
module.exports = {
|
||||
getHeaderFromToken,
|
||||
};
|
||||
@@ -477,6 +477,13 @@ module.exports.ParseServerOptions = {
|
||||
help: 'Starts the liveQuery server',
|
||||
action: parsers.booleanParser,
|
||||
},
|
||||
trustProxy: {
|
||||
env: 'PARSE_SERVER_TRUST_PROXY',
|
||||
help:
|
||||
'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`.',
|
||||
action: parsers.objectParser,
|
||||
default: [],
|
||||
},
|
||||
userSensitiveFields: {
|
||||
env: 'PARSE_SERVER_USER_SENSITIVE_FIELDS',
|
||||
help:
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
* @property {Number} sessionLength Session duration, in seconds, defaults to 1 year
|
||||
* @property {Boolean} silent Disables console output
|
||||
* @property {Boolean} startLiveQueryServer Starts the liveQuery server
|
||||
* @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`.
|
||||
* @property {String[]} userSensitiveFields Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields
|
||||
* @property {Boolean} verbose Set the logging to verbose
|
||||
* @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`.
|
||||
|
||||
@@ -238,6 +238,9 @@ export interface ParseServerOptions {
|
||||
cluster: ?NumberOrBoolean;
|
||||
/* middleware for express server, can be string or function */
|
||||
middleware: ?((() => void) | string);
|
||||
/* 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`.
|
||||
:DEFAULT: false */
|
||||
trustProxy: ?any;
|
||||
/* Starts the liveQuery server */
|
||||
startLiveQueryServer: ?boolean;
|
||||
/* Live query server configuration options (will start the liveQuery server) */
|
||||
|
||||
@@ -304,6 +304,9 @@ class ParseServer {
|
||||
options
|
||||
);
|
||||
}
|
||||
if (options.trustProxy) {
|
||||
app.set('trust proxy', options.trustProxy);
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
if (!process.env.TESTING) {
|
||||
configureListeners(this);
|
||||
|
||||
+1
-16
@@ -280,22 +280,7 @@ export function handleParseHeaders(req, res, next) {
|
||||
}
|
||||
|
||||
function getClientIp(req) {
|
||||
if (req.headers['x-forwarded-for']) {
|
||||
// try to get from x-forwared-for if it set (behind reverse proxy)
|
||||
return req.headers['x-forwarded-for'].split(',')[0];
|
||||
} else if (req.connection && req.connection.remoteAddress) {
|
||||
// no proxy, try getting from connection.remoteAddress
|
||||
return req.connection.remoteAddress;
|
||||
} else if (req.socket) {
|
||||
// try to get it from req.socket
|
||||
return req.socket.remoteAddress;
|
||||
} else if (req.connection && req.connection.socket) {
|
||||
// try to get it form the connection.socket
|
||||
return req.connection.socket.remoteAddress;
|
||||
} else {
|
||||
// if non above, fallback.
|
||||
return req.ip;
|
||||
}
|
||||
return req.ip;
|
||||
}
|
||||
|
||||
function httpAuth(req) {
|
||||
|
||||
Reference in New Issue
Block a user