Skip to content

ACL Options to Cloud Validator #6975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 209 additions & 10 deletions spec/CloudCode.Validator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,211 @@ describe('cloud validator', () => {
done();
});

it('basic beforeSave request.user ACL', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: 'request.user',
});
const user = await Parse.User.signUp('testuser', 'p@ssword');
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save(null, { sessionToken: user.getSessionToken() });
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*']).toBeUndefined();
expect(acl[user.id].write).toBeTrue();
expect(acl[user.id].read).toBeTrue();
done();
});

it('basic beforeSave ACL publicRead', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: 'publicRead',
});
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save();
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*'].read).toBe(true);
expect(acl['*'].write).toBeUndefined();
done();
});

it('basic beforeSave ACL publicWrite', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: 'publicWrite',
});
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save();
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*'].write).toBe(true);
expect(acl['*'].read).toBeUndefined();
done();
});

it('basic beforeSave ACL roleRead', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: 'roleRead:Administrator',
});
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save();
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*']).toBeUndefined();
expect(acl['role:Administrator'].read).toBe(true);
expect(acl['role:Administrator'].write).toBeUndefined();
done();
});

it('basic beforeSave ACL roleWrite', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: 'roleWrite:Administrator',
});
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save();
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*']).toBeUndefined();
expect(acl['role:Administrator'].read).toBeUndefined();
expect(acl['role:Administrator'].write).toBe(true);
done();
});

it('basic beforeSave ACL roleReadWrite', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: 'roleReadWrite:Administrator',
});
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save();
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*']).toBeUndefined();
expect(acl['role:Administrator'].read).toBe(true);
expect(acl['role:Administrator'].write).toBe(true);
done();
});

it('basic beforeSave ACL object readWrite', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: {
public: 'readWrite',
'request.user': 'readWrite',
'role:Administrator': 'readWrite',
customId: 'readWrite',
},
});
const user = await Parse.User.signUp('testuser', 'p@ssword');
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
const beforeAcl = new Parse.ACL();
beforeAcl.setWriteAccess('test', true);
beforeAcl.setReadAccess('test', true);
obj.setACL(beforeAcl);
await obj.save(null, { sessionToken: user.getSessionToken() });
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*'].read).toBe(true);
expect(acl['*'].write).toBe(true);
expect(acl[user.id].read).toBe(true);
expect(acl[user.id].write).toBe(true);
expect(acl['role:Administrator'].read).toBe(true);
expect(acl['role:Administrator'].write).toBe(true);
expect(acl['customId'].read).toBe(true);
expect(acl['customId'].write).toBe(true);
expect(acl['test'].read).toBe(true);
expect(acl['test'].write).toBe(true);
done();
});

it('basic beforeSave ACL object readWrite override', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: {
override: true,
public: 'readWrite',
'request.user': 'readWrite',
'role:Administrator': 'readWrite',
customId: 'readWrite',
},
});
const user = await Parse.User.signUp('testuser', 'p@ssword');
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
const beforeAcl = new Parse.ACL();
beforeAcl.setWriteAccess('test', true);
beforeAcl.setReadAccess('test', true);
obj.setACL(beforeAcl);
await obj.save(null, { sessionToken: user.getSessionToken() });
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*'].read).toBe(true);
expect(acl['*'].write).toBe(true);
expect(acl[user.id].read).toBe(true);
expect(acl[user.id].write).toBe(true);
expect(acl['role:Administrator'].read).toBe(true);
expect(acl['role:Administrator'].write).toBe(true);
expect(acl['customId'].read).toBe(true);
expect(acl['customId'].write).toBe(true);
expect(acl['test']).toBeUndefined();
done();
});

it('basic beforeSave ACL object read', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: {
public: 'read',
'request.user': 'read',
'role:Administrator': 'read',
customId: 'read',
},
});
const user = await Parse.User.signUp('testuser', 'p@ssword');
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save(null, { sessionToken: user.getSessionToken() });
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*'].read).toBe(true);
expect(acl['*'].write).toBeUndefined();
expect(acl[user.id].read).toBe(true);
expect(acl[user.id].write).toBeUndefined();
expect(acl['role:Administrator'].read).toBe(true);
expect(acl['role:Administrator'].write).toBeUndefined();
expect(acl['customId'].read).toBe(true);
expect(acl['customId'].write).toBeUndefined();
done();
});

it('basic beforeSave ACL object write', async function (done) {
Parse.Cloud.beforeSave('BeforeSave', () => {}, {
setACL: {
public: 'write',
'request.user': 'write',
'role:Administrator': 'write',
customId: 'write',
},
});
const user = await Parse.User.signUp('testuser', 'p@ssword');
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save(null, { sessionToken: user.getSessionToken() });
expect(obj.getACL()).toBeDefined();
const acl = obj.getACL().toJSON();
expect(acl['*'].write).toBe(true);
expect(acl['*'].read).toBeUndefined();
expect(acl[user.id].write).toBe(true);
expect(acl[user.id].read).toBeUndefined();
expect(acl['role:Administrator'].write).toBe(true);
expect(acl['role:Administrator'].read).toBeUndefined();
expect(acl['customId'].write).toBe(true);
expect(acl['customId'].read).toBeUndefined();
done();
});

it('basic beforeSave skipWithMasterKey', async function (done) {
Parse.Cloud.beforeSave(
'BeforeSave',
Expand Down Expand Up @@ -587,16 +792,10 @@ describe('cloud validator', () => {
expect(obj.get('foo')).toBe('bar');

const query = new Parse.Query('beforeFind');
try {
const first = await query.first({ useMasterKey: true });
expect(first).toBeDefined();
expect(first.id).toBe(obj.id);
done();
} catch (e) {
console.log(e);
console.log(e.code);
throw e;
}
const first = await query.first({ useMasterKey: true });
expect(first).toBeDefined();
expect(first.id).toBe(obj.id);
done();
});

it('basic beforeDelete skipWithMasterKey', async function (done) {
Expand Down
116 changes: 114 additions & 2 deletions src/triggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ export function maybeRunValidator(request, functionName) {
return Promise.resolve()
.then(() => {
return typeof theValidator === 'object'
? builtInTriggerValidator(theValidator, request)
? builtInTriggerValidator(theValidator, request, functionName)
: theValidator(request);
})
.then(() => {
Expand All @@ -609,7 +609,7 @@ export function maybeRunValidator(request, functionName) {
});
});
}
function builtInTriggerValidator(options, request) {
function builtInTriggerValidator(options, request, functionName) {
if (request.master && !options.validateMasterKey) {
return;
}
Expand Down Expand Up @@ -733,6 +733,118 @@ function builtInTriggerValidator(options, request) {
}
}
}
const aclOptions = options.setACL;
if (aclOptions && request.object && functionName === 'beforeSave.BeforeSave') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe some error message if someone try to setACL for other function that not beforeSave?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do!

const getRoleName = roleStr => {
return aclOptions.split(`${roleStr}:`)[1];
};
if (typeof aclOptions === 'string') {
const acl = new Parse.ACL();
if (aclOptions === 'request.user') {
acl.setPublicReadAccess(false);
acl.setPublicWriteAccess(false);
if (reqUser) {
acl.setReadAccess(reqUser, true);
acl.setWriteAccess(reqUser, true);
}
} else if (aclOptions === 'publicRead') {
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(false);
} else if (aclOptions === 'publicWrite') {
acl.setPublicReadAccess(false);
acl.setPublicWriteAccess(true);
} else if (aclOptions === 'publicReadWrite') {
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
} else if (aclOptions.includes('roleRead:')) {
const roleName = getRoleName('roleRead');
if (roleName) {
acl.setPublicReadAccess(false);
acl.setPublicWriteAccess(false);
acl.setRoleReadAccess(roleName, true);
acl.setRoleWriteAccess(roleName, false);
}
} else if (aclOptions.includes('roleWrite:')) {
const roleName = getRoleName('roleWrite');
if (roleName) {
acl.setPublicReadAccess(false);
acl.setPublicWriteAccess(false);
acl.setRoleReadAccess(roleName, false);
acl.setRoleWriteAccess(roleName, true);
}
} else if (aclOptions.includes('roleReadWrite:')) {
const roleName = getRoleName('roleReadWrite');
if (roleName) {
acl.setPublicReadAccess(false);
acl.setPublicWriteAccess(false);
acl.setRoleReadAccess(roleName, true);
acl.setRoleWriteAccess(roleName, true);
}
}
request.object.setACL(acl);
} else {
const acl =
aclOptions.override || !request.object.getACL() ? new Parse.ACL() : request.object.getACL();
delete aclOptions.override;
if (aclOptions.public) {
if (aclOptions.public === 'readWrite') {
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
} else if (aclOptions.public === 'read') {
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(false);
} else if (aclOptions.public === 'write') {
acl.setPublicReadAccess(false);
acl.setPublicWriteAccess(true);
}
} else {
acl.setPublicReadAccess(false);
acl.setPublicWriteAccess(false);
}
if (aclOptions['request.user'] && reqUser) {
if (aclOptions['request.user'] === 'readWrite') {
acl.setReadAccess(reqUser, true);
acl.setWriteAccess(reqUser, true);
} else if (aclOptions['request.user'] === 'read') {
acl.setReadAccess(reqUser, true);
acl.setWriteAccess(reqUser, false);
} else if (aclOptions['request.user'] === 'write') {
acl.setReadAccess(reqUser, false);
acl.setWriteAccess(reqUser, true);
}
}
delete aclOptions['request.user'];
delete aclOptions['public'];
for (const user in aclOptions) {
const aclValue = aclOptions[user];
if (aclValue.includes('role:')) {
const roleName = getRoleName('role');
if (aclValue === 'readWrite') {
acl.setRoleReadAccess(roleName, true);
acl.setRoleWriteAccess(roleName, true);
} else if (aclValue === 'read') {
acl.setRoleReadAccess(roleName, true);
acl.setRoleWriteAccess(roleName, false);
} else if (aclValue === 'write') {
acl.setRoleReadAccess(roleName, false);
acl.setRoleWriteAccess(roleName, true);
}
} else {
if (aclValue === 'readWrite') {
acl.setReadAccess(user, true);
acl.setWriteAccess(user, true);
} else if (aclValue === 'read') {
acl.setReadAccess(user, true);
acl.setWriteAccess(user, false);
} else if (aclValue === 'write') {
acl.setReadAccess(user, false);
acl.setWriteAccess(user, true);
}
}
}
request.object.setACL(acl);
}
}
}

// To be used as part of the promise chain when saving/deleting an object
Expand Down