feat: Add dynamic master key by setting Parse Server option masterKey to a function (#9582)

This commit is contained in:
Daniel
2025-02-13 08:23:18 +11:00
committed by GitHub
parent 415373708a
commit 6f1d161a2f
8 changed files with 102 additions and 25 deletions

View File

@@ -46,32 +46,32 @@ describe('middlewares', () => {
});
});
it('should give invalid response when keys are configured but no key supplied', () => {
it('should give invalid response when keys are configured but no key supplied', async () => {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey',
});
middlewares.handleParseHeaders(fakeReq, fakeRes);
await middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should give invalid response when keys are configured but supplied key is incorrect', () => {
it('should give invalid response when keys are configured but supplied key is incorrect', async () => {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey',
});
fakeReq.headers['x-parse-rest-api-key'] = 'wrongKey';
middlewares.handleParseHeaders(fakeReq, fakeRes);
await middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should give invalid response when keys are configured but different key is supplied', () => {
it('should give invalid response when keys are configured but different key is supplied', async () => {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey',
});
fakeReq.headers['x-parse-client-key'] = 'clientKey';
middlewares.handleParseHeaders(fakeReq, fakeRes);
await middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
@@ -157,13 +157,7 @@ describe('middlewares', () => {
fakeReq.ip = '127.0.0.1';
fakeReq.headers['x-parse-master-key'] = 'masterKey';
let error;
try {
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
} catch (err) {
error = err;
}
const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e);
expect(error).toBeDefined();
expect(error.message).toEqual(`unauthorized`);
@@ -182,13 +176,7 @@ describe('middlewares', () => {
fakeReq.ip = '10.0.0.2';
fakeReq.headers['x-parse-maintenance-key'] = 'masterKey';
let error;
try {
await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
} catch (err) {
error = err;
}
const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e);
expect(error).toBeDefined();
expect(error.message).toEqual(`unauthorized`);

View File

@@ -601,6 +601,63 @@ describe('server', () => {
await new Promise(resolve => server.close(resolve));
});
it('should load masterKey', async () => {
await reconfigureServer({
masterKey: () => 'testMasterKey',
masterKeyTtl: 1000, // TTL is set
});
await new Parse.Object('TestObject').save();
const config = Config.get(Parse.applicationId);
expect(config.masterKeyCache.masterKey).toEqual('testMasterKey');
expect(config.masterKeyCache.expiresAt.getTime()).toBeGreaterThan(Date.now());
});
it('should not reload if ttl is not set', async () => {
const masterKeySpy = jasmine.createSpy().and.returnValue(Promise.resolve('initialMasterKey'));
await reconfigureServer({
masterKey: masterKeySpy,
masterKeyTtl: null, // No TTL set
});
await new Parse.Object('TestObject').save();
const config = Config.get(Parse.applicationId);
const firstMasterKey = config.masterKeyCache.masterKey;
// Simulate calling the method again
await config.loadMasterKey();
const secondMasterKey = config.masterKeyCache.masterKey;
expect(firstMasterKey).toEqual('initialMasterKey');
expect(secondMasterKey).toEqual('initialMasterKey');
expect(masterKeySpy).toHaveBeenCalledTimes(1); // Should only be called once
expect(config.masterKeyCache.expiresAt).toBeNull(); // TTL is not set, so expiresAt should remain null
});
it('should reload masterKey if ttl is set and expired', async () => {
const masterKeySpy = jasmine.createSpy()
.and.returnValues(Promise.resolve('firstMasterKey'), Promise.resolve('secondMasterKey'));
await reconfigureServer({
masterKey: masterKeySpy,
masterKeyTtl: 1 / 1000, // TTL is set to 1ms
});
await new Parse.Object('TestObject').save();
await new Promise(resolve => setTimeout(resolve, 10));
await new Parse.Object('TestObject').save();
const config = Config.get(Parse.applicationId);
expect(masterKeySpy).toHaveBeenCalledTimes(2);
expect(config.masterKeyCache.masterKey).toEqual('secondMasterKey');
});
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');