Ensure User ACL's are more flexible and secure #3588 (#4860)

* Fixes an issue that would let the beforeDelete be called when user has no access to the object

* Ensure we properly lock user

- Improves find method so we can attempt to read for a write poking the right ACL instead of using masterKey
- This ensure we do not run beforeDelete/beforeFind/beforeSave in the wrong scenarios

* nits

* Caps insufficient
This commit is contained in:
Florent Vilmart
2018-06-28 16:31:22 -04:00
parent 82fec72ec4
commit 6b36ce1bb5
9 changed files with 158 additions and 39 deletions

View File

@@ -24,12 +24,13 @@ function RestQuery(config, auth, className, restWhere = {}, restOptions = {}, cl
this.clientSDK = clientSDK;
this.response = null;
this.findOptions = {};
this.isWrite = false;
if (!this.auth.isMaster) {
this.findOptions.acl = this.auth.user ? [this.auth.user.id] : null;
if (this.className == '_Session') {
if (!this.findOptions.acl) {
if (!this.auth.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN,
'This session token is invalid.');
'Invalid session token');
}
this.restWhere = {
'$and': [this.restWhere, {
@@ -188,17 +189,28 @@ RestQuery.prototype.buildRestWhere = function() {
});
}
// Marks the query for a write attempt, so we read the proper ACL (write instead of read)
RestQuery.prototype.forWrite = function() {
this.isWrite = true;
return this;
}
// Uses the Auth object to get the list of roles, adds the user id
RestQuery.prototype.getUserAndRoleACL = function() {
if (this.auth.isMaster || !this.auth.user) {
if (this.auth.isMaster) {
return Promise.resolve();
}
return this.auth.getUserRoles().then((roles) => {
// Concat with the roles to prevent duplications on multiple calls
const aclSet = new Set([].concat(this.findOptions.acl, roles));
this.findOptions.acl = Array.from(aclSet);
this.findOptions.acl = ['*'];
if (this.auth.user) {
return this.auth.getUserRoles().then((roles) => {
this.findOptions.acl = this.findOptions.acl.concat(roles, [this.auth.user.id]);
return;
});
} else {
return Promise.resolve();
});
}
};
// Changes the className if redirectClassNameForKey is set.
@@ -523,6 +535,9 @@ RestQuery.prototype.runFind = function(options = {}) {
if (options.op) {
findOptions.op = options.op;
}
if (this.isWrite) {
findOptions.isWrite = true;
}
return this.config.database.find(this.className, this.restWhere, findOptions)
.then((results) => {
if (this.className === '_User') {