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:
committed by
Florent Vilmart
parent
0faaec3224
commit
af55cd1efb
@@ -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: {
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user