Add role based ACL checks to LiveQuery (#2893)

* Add acl role check to _matchesACL, start adding tests.

* Add tests for ACL role checks in LiveQueryServer.

* Switch to arrow functions, add immutabalized code from @acinader, swap for loop style.
This commit is contained in:
Aaron Blondeau
2016-10-21 21:53:31 -06:00
committed by Florent Vilmart
parent 0faaec3224
commit af55cd1efb
2 changed files with 168 additions and 0 deletions

View File

@@ -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() { it('can validate key when valid key is provided', function() {
var parseLiveQueryServer = new ParseLiveQueryServer({}, { var parseLiveQueryServer = new ParseLiveQueryServer({}, {
keyPairs: { keyPairs: {

View File

@@ -327,6 +327,64 @@ class ParseLiveQueryServer {
if (isSubscriptionSessionTokenMatched) { if (isSubscriptionSessionTokenMatched) {
return Parse.Promise.as(true); 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 // Check client sessionToken matches ACL
let clientSessionToken = client.sessionToken; let clientSessionToken = client.sessionToken;
return this.sessionTokenCache.getUserId(clientSessionToken).then((userId) => { return this.sessionTokenCache.getUserId(clientSessionToken).then((userId) => {