diff --git a/spec/ParseLiveQueryServer.spec.js b/spec/ParseLiveQueryServer.spec.js index d99d7e34..562570b6 100644 --- a/spec/ParseLiveQueryServer.spec.js +++ b/spec/ParseLiveQueryServer.spec.js @@ -834,6 +834,116 @@ describe('ParseLiveQueryServer', function() { }); }); + it('won\'t match ACL that doesn\'t have public read or any roles', function(done){ + + var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {}); + var acl = new Parse.ACL(); + acl.setPublicReadAccess(false); + var client = { + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: 'sessionToken' + }) + }; + var requestId = 0; + + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) { + expect(isMatched).toBe(false); + done(); + }); + + }); + + it('won\'t match non-public ACL with role when there is no user', function(done){ + + var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {}); + var acl = new Parse.ACL(); + acl.setPublicReadAccess(false); + acl.setRoleReadAccess("livequery", true); + var client = { + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + }) + }; + var requestId = 0; + + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) { + expect(isMatched).toBe(false); + done(); + }); + + }); + + it('won\'t match ACL with role based read access set to false', function(done){ + + var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {}); + var acl = new Parse.ACL(); + acl.setPublicReadAccess(false); + acl.setRoleReadAccess("liveQueryRead", false); + var client = { + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: 'sessionToken' + }) + }; + var requestId = 0; + + spyOn(Parse, "Query").and.callFake(function(){ + return { + equalTo(relation, value) { + // Nothing to do here + }, + find() { + //Return a role with the name "liveQueryRead" as that is what was set on the ACL + var liveQueryRole = new Parse.Role(); + liveQueryRole.set('name', 'liveQueryRead'); + return [ + liveQueryRole + ]; + } + } + }); + + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) { + expect(isMatched).toBe(false); + done(); + }); + + }); + + it('will match ACL with role based read access set to true', function(done){ + + var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {}); + var acl = new Parse.ACL(); + acl.setPublicReadAccess(false); + acl.setRoleReadAccess("liveQueryRead", true); + var client = { + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: 'sessionToken' + }) + }; + var requestId = 0; + + spyOn(Parse, "Query").and.callFake(function(){ + return { + equalTo(relation, value) { + // Nothing to do here + }, + find() { + //Return a role with the name "liveQueryRead" as that is what was set on the ACL + var liveQueryRole = new Parse.Role(); + liveQueryRole.set('name', 'liveQueryRead'); + return [ + liveQueryRole + ]; + } + } + }); + + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) { + expect(isMatched).toBe(true); + done(); + }); + + }); + it('can validate key when valid key is provided', function() { var parseLiveQueryServer = new ParseLiveQueryServer({}, { keyPairs: { diff --git a/src/LiveQuery/ParseLiveQueryServer.js b/src/LiveQuery/ParseLiveQueryServer.js index 3dc76d1c..68b1c220 100644 --- a/src/LiveQuery/ParseLiveQueryServer.js +++ b/src/LiveQuery/ParseLiveQueryServer.js @@ -327,6 +327,64 @@ class ParseLiveQueryServer { if (isSubscriptionSessionTokenMatched) { return Parse.Promise.as(true); } + + // Check if the user has any roles that match the ACL + return new Parse.Promise((resolve, reject) => { + + // Resolve false right away if the acl doesn't have any roles + const acl_has_roles = Object.keys(acl.permissionsById).some(key => key.startsWith("role:")); + if (!acl_has_roles) { + return resolve(false); + } + + this.sessionTokenCache.getUserId(subscriptionSessionToken) + .then((userId) => { + + // Pass along a null if there is no user id + if (!userId) { + return Parse.Promise.as(null); + } + + // Prepare a user object to query for roles + // To eliminate a query for the user, create one locally with the id + var user = new Parse.User(); + user.id = userId; + return user; + + }) + .then((user) => { + + // Pass along an empty array (of roles) if no user + if (!user) { + return Parse.Promise.as([]); + } + + // Then get the user's roles + var rolesQuery = new Parse.Query(Parse.Role); + rolesQuery.equalTo("users", user); + return rolesQuery.find(); + }). + then((roles) => { + + // Finally, see if any of the user's roles allow them read access + for (let role of roles) { + if (acl.getRoleReadAccess(role)) { + return resolve(true); + } + } + resolve(false); + }) + .catch((error) => { + reject(error); + }); + + }); + }).then((isRoleMatched) => { + + if(isRoleMatched) { + return Parse.Promise.as(true); + } + // Check client sessionToken matches ACL let clientSessionToken = client.sessionToken; return this.sessionTokenCache.getUserId(clientSessionToken).then((userId) => {