Skip to content
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

Fixes for Class Level and Pointer Permissions #1989

Merged
merged 3 commits into from
Jun 6, 2016
Merged
Show file tree
Hide file tree
Changes from 2 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
18 changes: 9 additions & 9 deletions spec/PointerPermissions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ describe('Pointer Permissions', () => {
it('tests CLP / Pointer Perms / ACL write (PP Locked)', (done) => {
/*
tests:
CLP: update open ({"*": true})
CLP: update closed ({})
PointerPerm: "owner"
ACL: logged in user has access

Expand All @@ -300,7 +300,7 @@ describe('Pointer Permissions', () => {
password: 'password'
});
let obj = new Parse.Object('AnObject');
Parse.Object.saveAll([user, user2]).then(() => {
Parse.Object.saveAll([user, user2]).then(() => {
let ACL = new Parse.ACL();
ACL.setReadAccess(user, true);
ACL.setWriteAccess(user, true);
Expand All @@ -310,7 +310,7 @@ describe('Pointer Permissions', () => {
}).then(() => {
return config.database.loadSchema().then((schema) => {
// Lock the update, and let only owner write
return schema.updateClass('AnObject', {}, {update: {"*": true}, writeUserFields: ['owner']});
return schema.updateClass('AnObject', {}, {update: {}, writeUserFields: ['owner']});
});
}).then(() => {
return Parse.User.logIn('user1', 'password');
Expand All @@ -329,7 +329,7 @@ describe('Pointer Permissions', () => {
it('tests CLP / Pointer Perms / ACL write (ACL Locked)', (done) => {
/*
tests:
CLP: update open ({"*": true})
CLP: update closed ({})
PointerPerm: "owner"
ACL: logged in user has access
*/
Expand All @@ -355,7 +355,7 @@ describe('Pointer Permissions', () => {
}).then(() => {
return config.database.loadSchema().then((schema) => {
// Lock the update, and let only owner write
return schema.updateClass('AnObject', {}, {update: {"*": true}, writeUserFields: ['owner']});
return schema.updateClass('AnObject', {}, {update: {}, writeUserFields: ['owner']});
});
}).then(() => {
return Parse.User.logIn('user2', 'password');
Expand All @@ -374,7 +374,7 @@ describe('Pointer Permissions', () => {
it('tests CLP / Pointer Perms / ACL write (ACL/PP OK)', (done) => {
/*
tests:
CLP: update open ({"*": true})
CLP: update closed ({})
PointerPerm: "owner"
ACL: logged in user has access
*/
Expand All @@ -400,7 +400,7 @@ describe('Pointer Permissions', () => {
}).then(() => {
return config.database.loadSchema().then((schema) => {
// Lock the update, and let only owner write
return schema.updateClass('AnObject', {}, {update: {"*": true}, writeUserFields: ['owner']});
return schema.updateClass('AnObject', {}, {update: {}, writeUserFields: ['owner']});
});
}).then(() => {
return Parse.User.logIn('user2', 'password');
Expand All @@ -419,7 +419,7 @@ describe('Pointer Permissions', () => {
it('tests CLP / Pointer Perms / ACL read (PP locked)', (done) => {
/*
tests:
CLP: find/get open ({"*": true})
CLP: find/get open ({})
PointerPerm: "owner" : read
ACL: logged in user has access

Expand Down Expand Up @@ -447,7 +447,7 @@ describe('Pointer Permissions', () => {
}).then(() => {
return config.database.loadSchema().then((schema) => {
// Lock the update, and let only owner write
return schema.updateClass('AnObject', {}, {find: {"*": true}, get: {"*": true}, readUserFields: ['owner']});
return schema.updateClass('AnObject', {}, {find: {}, get: {}, readUserFields: ['owner']});
});
}).then(() => {
return Parse.User.logIn('user1', 'password');
Expand Down
2 changes: 2 additions & 0 deletions spec/Schema.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ describe('SchemaController', () => {
var get = {};
get[user.id] = true;
return schema.setPermissions('Stuff', {
'create': {'*': true},
'find': find,
'get': get
});
Expand All @@ -152,6 +153,7 @@ describe('SchemaController', () => {
done();
}, (e) => {
fail('Class permissions should have allowed this get query');
done();
});
});
});
Expand Down
68 changes: 50 additions & 18 deletions spec/schemas.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -963,18 +963,10 @@ describe('schemas', () => {
create: {
'role:admin': true
},
get: {
'*': true
},
update: {
'*': true
},
addField: {
'*': true
},
delete: {
'*': true
}
get: {},
update: {},
delete: {},
addField: {}
});
done();
});
Expand Down Expand Up @@ -1018,6 +1010,9 @@ describe('schemas', () => {
json: true,
body: {
classLevelPermissions: {
create: {
'*': true
},
find: {
'*': true
},
Expand All @@ -1040,14 +1035,14 @@ describe('schemas', () => {
})
});

it('should not be able to add a field', done => {
it('should be able to add a field', done => {
request.post({
url: 'http://localhost:8378/1/schemas/AClass',
headers: masterKeyHeaders,
json: true,
body: {
classLevelPermissions: {
find: {
create: {
'*': true
},
addField: {
Expand Down Expand Up @@ -1243,7 +1238,7 @@ describe('schemas', () => {
}).then(() => {
return Parse.User.logIn('user', 'user').then(() => {
let obj = new Parse.Object('AClass');
return obj.save();
return obj.save(null, {useMasterKey: true});
})
}).then(() => {
let query = new Parse.Query('AClass');
Expand Down Expand Up @@ -1292,7 +1287,7 @@ describe('schemas', () => {
}).then(() => {
return Parse.User.logIn('user', 'user').then(() => {
let obj = new Parse.Object('AClass');
return obj.save();
return obj.save(null, {useMasterKey: true});
})
}).then(() => {
let query = new Parse.Query('AClass');
Expand Down Expand Up @@ -1357,7 +1352,7 @@ describe('schemas', () => {
}).then(() => {
return Parse.User.logIn('user', 'user').then(() => {
let obj = new Parse.Object('AClass');
return obj.save();
return obj.save(null, {useMasterKey: true});
})
}).then(() => {
let query = new Parse.Query('AClass');
Expand Down Expand Up @@ -1415,7 +1410,7 @@ describe('schemas', () => {
}).then(() => {
return Parse.User.logIn('user', 'user').then(() => {
let obj = new Parse.Object('AClass');
return obj.save();
return obj.save(null, {useMasterKey: true});
})
}).then(() => {
let query = new Parse.Query('AClass');
Expand Down Expand Up @@ -1544,6 +1539,7 @@ describe('schemas', () => {

it('can login when addFields is false (issue #1355)', (done) => {
setPermissionsOnClass('_User', {
'create': {'*': true},
'addField': {}
}).then(() => {
return Parse.User.signUp('foo', 'bar');
Expand Down Expand Up @@ -1573,4 +1569,40 @@ describe('schemas', () => {
});
});
});

fit("regression test for #1991", done => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be it. Travis seems to have run every test anyway, though, so I will just merge and then fix this.

let user = new Parse.User();
user.setUsername('user');
user.setPassword('user');
let role = new Parse.Role('admin', new Parse.ACL());
let obj = new Parse.Object('AnObject');
Parse.Object.saveAll([user, role]).then(() => {
role.relation('users').add(user);
return role.save(null, {useMasterKey: true});
}).then(() => {
return setPermissionsOnClass('AnObject', {
'get': {"*": true},
'find': {"*": true},
'create': {'*': true},
'update': {'role:admin': true},
'delete': {'role:admin': true}
})
}).then(() => {
return obj.save();
}).then(() => {
return Parse.User.logIn('user', 'user')
}).then(() => {
return obj.destroy();
}).then((result) => {
let query = new Parse.Query('AnObject');
return query.find();
}).then((results) => {
expect(results.length).toBe(0);
done();
}).catch((err) => {
fail('should not fail');
console.error(err);
done();
});
});
});
15 changes: 12 additions & 3 deletions src/Adapters/Storage/Mongo/MongoSchemaCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ function mongoSchemaFieldsToParseSchemaFields(schema) {
return response;
}

const emptyCLPS = Object.freeze({
find: {},
get: {},
create: {},
update: {},
delete: {},
addField: {},
});

const defaultCLPS = Object.freeze({
find: {'*': true},
get: {'*': true},
Expand All @@ -53,14 +62,14 @@ const defaultCLPS = Object.freeze({
});

function mongoSchemaToParseSchema(mongoSchema) {
let clpsFromMongoObject = {};
let clps = defaultCLPS;
if (mongoSchema._metadata && mongoSchema._metadata.class_permissions) {
clpsFromMongoObject = mongoSchema._metadata.class_permissions;
clps = {...emptyCLPS, ...mongoSchema._metadata.class_permissions};
}
return {
className: mongoSchema._id,
fields: mongoSchemaFieldsToParseSchemaFields(mongoSchema),
classLevelPermissions: {...defaultCLPS, ...clpsFromMongoObject},
classLevelPermissions: clps,
};
}

Expand Down
5 changes: 5 additions & 0 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,11 @@ DatabaseController.prototype.deleteSchema = function(className) {
}

DatabaseController.prototype.addPointerPermissions = function(schema, className, operation, query, aclGroup = []) {
// Check if class has public permission for operation
// If the BaseCLP pass, let go through
if (schema.testBaseCLP(className, aclGroup, operation)) {
return query;
}
let perms = schema.perms[className];
let field = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
let userACL = aclGroup.filter((acl) => {
Expand Down
29 changes: 18 additions & 11 deletions src/Controllers/SchemaController.js
Original file line number Diff line number Diff line change
Expand Up @@ -632,30 +632,36 @@ class SchemaController {
}
return Promise.resolve(this);
}

// Validates an operation passes class-level-permissions set in the schema
validatePermission(className, aclGroup, operation) {
// Validates the base CLP for an operation
testBaseCLP(className, aclGroup, operation) {
if (!this.perms[className] || !this.perms[className][operation]) {
return Promise.resolve();
return true;
}
let classPerms = this.perms[className];
let perms = classPerms[operation];
// Handle the public scenario quickly
if (perms['*']) {
return Promise.resolve();
return true;
}
// Check permissions against the aclGroup provided (array of userId/roles)
let found = false;
for (let i = 0; i < aclGroup.length && !found; i++) {
if (perms[aclGroup[i]]) {
found = true;
}
if (aclGroup.some(acl => { return perms[acl] === true })) {
return true;
}
return false;
}

if (found) {
// Validates an operation passes class-level-permissions set in the schema
validatePermission(className, aclGroup, operation) {
if (this.testBaseCLP(className, aclGroup, operation)) {
return Promise.resolve();
}

if (!this.perms[className] || !this.perms[className][operation]) {
return true;
}
let classPerms = this.perms[className];
let perms = classPerms[operation];
// No matching CLP, let's check the Pointer permissions
// And handle those later
let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
Expand All @@ -666,6 +672,7 @@ class SchemaController {
'Permission denied for this action.');
}

// Process the readUserFields later
if (Array.isArray(classPerms[permissionField]) && classPerms[permissionField].length > 0) {
return Promise.resolve();
}
Expand Down