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
This commit is contained in:
committed by
Florent Vilmart
parent
52c4dd3704
commit
121d151af9
@@ -12,7 +12,7 @@ describe('ParseLiveQueryServer', function() {
|
|||||||
var mockParseWebSocketServer = jasmine.createSpy('ParseWebSocketServer');
|
var mockParseWebSocketServer = jasmine.createSpy('ParseWebSocketServer');
|
||||||
jasmine.mockLibrary('../src/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer', mockParseWebSocketServer);
|
jasmine.mockLibrary('../src/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer', mockParseWebSocketServer);
|
||||||
// Mock Client
|
// Mock Client
|
||||||
var mockClient = function() {
|
var mockClient = function(id, socket, hasMasterKey) {
|
||||||
this.pushConnect = jasmine.createSpy('pushConnect');
|
this.pushConnect = jasmine.createSpy('pushConnect');
|
||||||
this.pushSubscribe = jasmine.createSpy('pushSubscribe');
|
this.pushSubscribe = jasmine.createSpy('pushSubscribe');
|
||||||
this.pushUnsubscribe = jasmine.createSpy('pushUnsubscribe');
|
this.pushUnsubscribe = jasmine.createSpy('pushUnsubscribe');
|
||||||
@@ -24,6 +24,7 @@ describe('ParseLiveQueryServer', function() {
|
|||||||
this.addSubscriptionInfo = jasmine.createSpy('addSubscriptionInfo');
|
this.addSubscriptionInfo = jasmine.createSpy('addSubscriptionInfo');
|
||||||
this.getSubscriptionInfo = jasmine.createSpy('getSubscriptionInfo');
|
this.getSubscriptionInfo = jasmine.createSpy('getSubscriptionInfo');
|
||||||
this.deleteSubscriptionInfo = jasmine.createSpy('deleteSubscriptionInfo');
|
this.deleteSubscriptionInfo = jasmine.createSpy('deleteSubscriptionInfo');
|
||||||
|
this.hasMasterKey = hasMasterKey;
|
||||||
}
|
}
|
||||||
mockClient.pushError = jasmine.createSpy('pushError');
|
mockClient.pushError = jasmine.createSpy('pushError');
|
||||||
jasmine.mockLibrary('../src/LiveQuery/Client', 'Client', mockClient);
|
jasmine.mockLibrary('../src/LiveQuery/Client', 'Client', mockClient);
|
||||||
@@ -1018,6 +1019,89 @@ describe('ParseLiveQueryServer', function() {
|
|||||||
expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy();
|
expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can validate client has master key when valid', function() {
|
||||||
|
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
|
||||||
|
keyPairs: {
|
||||||
|
masterKey: 'test'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var request = {
|
||||||
|
masterKey: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can validate client doesn\'t have master key when invalid', function() {
|
||||||
|
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
|
||||||
|
keyPairs: {
|
||||||
|
masterKey: 'test'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var request = {
|
||||||
|
masterKey: 'notValid'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)).not.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can validate client doesn\'t have master key when not provided', function() {
|
||||||
|
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
|
||||||
|
keyPairs: {
|
||||||
|
masterKey: 'test'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(parseLiveQueryServer._hasMasterKey({}, parseLiveQueryServer.keyPairs)).not.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can validate client doesn\'t have master key when validKeyPairs is empty', function() {
|
||||||
|
var parseLiveQueryServer = new ParseLiveQueryServer({}, {});
|
||||||
|
var request = {
|
||||||
|
masterKey: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)).not.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('will match non-public ACL when client has master key', function(done){
|
||||||
|
|
||||||
|
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||||
|
var acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(false);
|
||||||
|
var client = {
|
||||||
|
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
|
||||||
|
}),
|
||||||
|
hasMasterKey: true
|
||||||
|
};
|
||||||
|
var requestId = 0;
|
||||||
|
|
||||||
|
parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) {
|
||||||
|
expect(isMatched).toBe(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('won\'t match non-public ACL when client has no master key', function(done){
|
||||||
|
|
||||||
|
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||||
|
var acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(false);
|
||||||
|
var client = {
|
||||||
|
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
|
||||||
|
}),
|
||||||
|
hasMasterKey: false
|
||||||
|
};
|
||||||
|
var requestId = 0;
|
||||||
|
|
||||||
|
parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) {
|
||||||
|
expect(isMatched).toBe(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(function(){
|
afterEach(function(){
|
||||||
jasmine.restoreLibrary('../src/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer');
|
jasmine.restoreLibrary('../src/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer');
|
||||||
jasmine.restoreLibrary('../src/LiveQuery/Client', 'Client');
|
jasmine.restoreLibrary('../src/LiveQuery/Client', 'Client');
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const dafaultFields = ['className', 'objectId', 'updatedAt', 'createdAt', 'ACL']
|
|||||||
class Client {
|
class Client {
|
||||||
id: number;
|
id: number;
|
||||||
parseWebSocket: any;
|
parseWebSocket: any;
|
||||||
|
hasMasterKey: boolean;
|
||||||
userId: string;
|
userId: string;
|
||||||
roles: Array<string>;
|
roles: Array<string>;
|
||||||
subscriptionInfos: Object;
|
subscriptionInfos: Object;
|
||||||
@@ -20,9 +21,10 @@ class Client {
|
|||||||
pushDelete: Function;
|
pushDelete: Function;
|
||||||
pushLeave: Function;
|
pushLeave: Function;
|
||||||
|
|
||||||
constructor(id: number, parseWebSocket: any) {
|
constructor(id: number, parseWebSocket: any, hasMasterKey: boolean) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.parseWebSocket = parseWebSocket;
|
this.parseWebSocket = parseWebSocket;
|
||||||
|
this.hasMasterKey = hasMasterKey;
|
||||||
this.roles = [];
|
this.roles = [];
|
||||||
this.subscriptionInfos = new Map();
|
this.subscriptionInfos = new Map();
|
||||||
this.pushConnect = this._pushEvent('connected');
|
this.pushConnect = this._pushEvent('connected');
|
||||||
|
|||||||
@@ -310,8 +310,8 @@ class ParseLiveQueryServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_matchesACL(acl: any, client: any, requestId: number): any {
|
_matchesACL(acl: any, client: any, requestId: number): any {
|
||||||
// If ACL is undefined or null, or ACL has public read access, return true directly
|
// Return true directly if ACL isn't present, ACL is public read, or client has master key
|
||||||
if (!acl || acl.getPublicReadAccess()) {
|
if (!acl || acl.getPublicReadAccess() || client.hasMasterKey) {
|
||||||
return Parse.Promise.as(true);
|
return Parse.Promise.as(true);
|
||||||
}
|
}
|
||||||
// Check subscription sessionToken matches ACL first
|
// Check subscription sessionToken matches ACL first
|
||||||
@@ -403,7 +403,8 @@ class ParseLiveQueryServer {
|
|||||||
logger.error('Key in request is not valid');
|
logger.error('Key in request is not valid');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const client = new Client(this.clientId, parseWebsocket);
|
const hasMasterKey = this._hasMasterKey(request, this.keyPairs);
|
||||||
|
const client = new Client(this.clientId, parseWebsocket, hasMasterKey);
|
||||||
parseWebsocket.clientId = this.clientId;
|
parseWebsocket.clientId = this.clientId;
|
||||||
this.clientId += 1;
|
this.clientId += 1;
|
||||||
this.clients.set(parseWebsocket.clientId, client);
|
this.clients.set(parseWebsocket.clientId, client);
|
||||||
@@ -411,6 +412,17 @@ class ParseLiveQueryServer {
|
|||||||
client.pushConnect();
|
client.pushConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_hasMasterKey(request: any, validKeyPairs: any): boolean {
|
||||||
|
if(!validKeyPairs || validKeyPairs.size == 0 ||
|
||||||
|
!validKeyPairs.has("masterKey")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!request || !request.hasOwnProperty("masterKey")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return request.masterKey === validKeyPairs.get("masterKey");
|
||||||
|
}
|
||||||
|
|
||||||
_validateKeys(request: any, validKeyPairs: any): boolean {
|
_validateKeys(request: any, validKeyPairs: any): boolean {
|
||||||
if (!validKeyPairs || validKeyPairs.size == 0) {
|
if (!validKeyPairs || validKeyPairs.size == 0) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user