Skip to content

Commit 121d151

Browse files
Kenishiflovilmart
authored andcommitted
Add master key override to live query ACL checks (#4133)
* Add master key override to live query ACL checks * Fix mockClient so masterKey tests work correctly
1 parent 52c4dd3 commit 121d151

File tree

3 files changed

+103
-5
lines changed

3 files changed

+103
-5
lines changed

spec/ParseLiveQueryServer.spec.js

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('ParseLiveQueryServer', function() {
1212
var mockParseWebSocketServer = jasmine.createSpy('ParseWebSocketServer');
1313
jasmine.mockLibrary('../src/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer', mockParseWebSocketServer);
1414
// Mock Client
15-
var mockClient = function() {
15+
var mockClient = function(id, socket, hasMasterKey) {
1616
this.pushConnect = jasmine.createSpy('pushConnect');
1717
this.pushSubscribe = jasmine.createSpy('pushSubscribe');
1818
this.pushUnsubscribe = jasmine.createSpy('pushUnsubscribe');
@@ -24,6 +24,7 @@ describe('ParseLiveQueryServer', function() {
2424
this.addSubscriptionInfo = jasmine.createSpy('addSubscriptionInfo');
2525
this.getSubscriptionInfo = jasmine.createSpy('getSubscriptionInfo');
2626
this.deleteSubscriptionInfo = jasmine.createSpy('deleteSubscriptionInfo');
27+
this.hasMasterKey = hasMasterKey;
2728
}
2829
mockClient.pushError = jasmine.createSpy('pushError');
2930
jasmine.mockLibrary('../src/LiveQuery/Client', 'Client', mockClient);
@@ -1018,6 +1019,89 @@ describe('ParseLiveQueryServer', function() {
10181019
expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy();
10191020
});
10201021

1022+
it('can validate client has master key when valid', function() {
1023+
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
1024+
keyPairs: {
1025+
masterKey: 'test'
1026+
}
1027+
});
1028+
var request = {
1029+
masterKey: 'test'
1030+
};
1031+
1032+
expect(parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)).toBeTruthy();
1033+
});
1034+
1035+
it('can validate client doesn\'t have master key when invalid', function() {
1036+
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
1037+
keyPairs: {
1038+
masterKey: 'test'
1039+
}
1040+
});
1041+
var request = {
1042+
masterKey: 'notValid'
1043+
};
1044+
1045+
expect(parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)).not.toBeTruthy();
1046+
});
1047+
1048+
it('can validate client doesn\'t have master key when not provided', function() {
1049+
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
1050+
keyPairs: {
1051+
masterKey: 'test'
1052+
}
1053+
});
1054+
1055+
expect(parseLiveQueryServer._hasMasterKey({}, parseLiveQueryServer.keyPairs)).not.toBeTruthy();
1056+
});
1057+
1058+
it('can validate client doesn\'t have master key when validKeyPairs is empty', function() {
1059+
var parseLiveQueryServer = new ParseLiveQueryServer({}, {});
1060+
var request = {
1061+
masterKey: 'test'
1062+
};
1063+
1064+
expect(parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)).not.toBeTruthy();
1065+
});
1066+
1067+
it('will match non-public ACL when client has master key', function(done){
1068+
1069+
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
1070+
var acl = new Parse.ACL();
1071+
acl.setPublicReadAccess(false);
1072+
var client = {
1073+
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
1074+
}),
1075+
hasMasterKey: true
1076+
};
1077+
var requestId = 0;
1078+
1079+
parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) {
1080+
expect(isMatched).toBe(true);
1081+
done();
1082+
});
1083+
1084+
});
1085+
1086+
it('won\'t match non-public ACL when client has no master key', function(done){
1087+
1088+
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
1089+
var acl = new Parse.ACL();
1090+
acl.setPublicReadAccess(false);
1091+
var client = {
1092+
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
1093+
}),
1094+
hasMasterKey: false
1095+
};
1096+
var requestId = 0;
1097+
1098+
parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) {
1099+
expect(isMatched).toBe(false);
1100+
done();
1101+
});
1102+
1103+
});
1104+
10211105
afterEach(function(){
10221106
jasmine.restoreLibrary('../src/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer');
10231107
jasmine.restoreLibrary('../src/LiveQuery/Client', 'Client');

src/LiveQuery/Client.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const dafaultFields = ['className', 'objectId', 'updatedAt', 'createdAt', 'ACL']
88
class Client {
99
id: number;
1010
parseWebSocket: any;
11+
hasMasterKey: boolean;
1112
userId: string;
1213
roles: Array<string>;
1314
subscriptionInfos: Object;
@@ -20,9 +21,10 @@ class Client {
2021
pushDelete: Function;
2122
pushLeave: Function;
2223

23-
constructor(id: number, parseWebSocket: any) {
24+
constructor(id: number, parseWebSocket: any, hasMasterKey: boolean) {
2425
this.id = id;
2526
this.parseWebSocket = parseWebSocket;
27+
this.hasMasterKey = hasMasterKey;
2628
this.roles = [];
2729
this.subscriptionInfos = new Map();
2830
this.pushConnect = this._pushEvent('connected');

src/LiveQuery/ParseLiveQueryServer.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,8 @@ class ParseLiveQueryServer {
310310
}
311311

312312
_matchesACL(acl: any, client: any, requestId: number): any {
313-
// If ACL is undefined or null, or ACL has public read access, return true directly
314-
if (!acl || acl.getPublicReadAccess()) {
313+
// Return true directly if ACL isn't present, ACL is public read, or client has master key
314+
if (!acl || acl.getPublicReadAccess() || client.hasMasterKey) {
315315
return Parse.Promise.as(true);
316316
}
317317
// Check subscription sessionToken matches ACL first
@@ -403,14 +403,26 @@ class ParseLiveQueryServer {
403403
logger.error('Key in request is not valid');
404404
return;
405405
}
406-
const client = new Client(this.clientId, parseWebsocket);
406+
const hasMasterKey = this._hasMasterKey(request, this.keyPairs);
407+
const client = new Client(this.clientId, parseWebsocket, hasMasterKey);
407408
parseWebsocket.clientId = this.clientId;
408409
this.clientId += 1;
409410
this.clients.set(parseWebsocket.clientId, client);
410411
logger.info('Create new client: %d', parseWebsocket.clientId);
411412
client.pushConnect();
412413
}
413414

415+
_hasMasterKey(request: any, validKeyPairs: any): boolean {
416+
if(!validKeyPairs || validKeyPairs.size == 0 ||
417+
!validKeyPairs.has("masterKey")) {
418+
return false;
419+
}
420+
if(!request || !request.hasOwnProperty("masterKey")) {
421+
return false;
422+
}
423+
return request.masterKey === validKeyPairs.get("masterKey");
424+
}
425+
414426
_validateKeys(request: any, validKeyPairs: any): boolean {
415427
if (!validKeyPairs || validKeyPairs.size == 0) {
416428
return true;

0 commit comments

Comments
 (0)