Skip to content
Merged
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
227 changes: 227 additions & 0 deletions spec/Schema.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -881,3 +881,230 @@ describe('SchemaController', () => {
});
});
});

describe('Class Level Permissions for requiredAuth', () => {

beforeEach(() => {
config = new Config('test');
});

function createUser() {
let user = new Parse.User();
user.set("username", "hello");
user.set("password", "world");
return user.signUp(null);
}

it('required auth test find', (done) => {
config.database.loadSchema().then((schema) => {
// Just to create a valid class
return schema.validateObject('Stuff', {foo: 'bar'});
}).then((schema) => {
return schema.setPermissions('Stuff', {
'find': {
'requiresAuthentication': true
}
});
}).then(() => {
var query = new Parse.Query('Stuff');
return query.find();
}).then(() => {
fail('Class permissions should have rejected this query.');
done();
}, (e) => {
expect(e.message).toEqual('Permission denied, user needs to be authenticated.');
done();
});
});

it('required auth test find authenticated', (done) => {
config.database.loadSchema().then((schema) => {
// Just to create a valid class
return schema.validateObject('Stuff', {foo: 'bar'});
}).then((schema) => {
return schema.setPermissions('Stuff', {
'find': {
'requiresAuthentication': true
}
});
}).then(() => {
return createUser();
}).then(() => {
var query = new Parse.Query('Stuff');
return query.find();
}).then((results) => {
expect(results.length).toEqual(0);
done();
}, (e) => {
console.error(e);
fail("Should not have failed");
done();
});
});

it('required auth should allow create authenticated', (done) => {
config.database.loadSchema().then((schema) => {
// Just to create a valid class
return schema.validateObject('Stuff', {foo: 'bar'});
}).then((schema) => {
return schema.setPermissions('Stuff', {
'create': {
'requiresAuthentication': true
}
});
}).then(() => {
return createUser();
}).then(() => {
let stuff = new Parse.Object('Stuff');
stuff.set('foo', 'bar');
return stuff.save();
}).then(() => {
done();
}, (e) => {
console.error(e);
fail("Should not have failed");
done();
});
});

it('required auth should reject create when not authenticated', (done) => {
config.database.loadSchema().then((schema) => {
// Just to create a valid class
return schema.validateObject('Stuff', {foo: 'bar'});
}).then((schema) => {
return schema.setPermissions('Stuff', {
'create': {
'requiresAuthentication': true
}
});
}).then(() => {
let stuff = new Parse.Object('Stuff');
stuff.set('foo', 'bar');
return stuff.save();
}).then(() => {
fail('Class permissions should have rejected this query.');
done();
}, (e) => {
expect(e.message).toEqual('Permission denied, user needs to be authenticated.');
done();
});
});

it('required auth test create/get/update/delete authenticated', (done) => {
config.database.loadSchema().then((schema) => {
// Just to create a valid class
return schema.validateObject('Stuff', {foo: 'bar'});
}).then((schema) => {
return schema.setPermissions('Stuff', {
'create': {
'requiresAuthentication': true
},
'get': {
'requiresAuthentication': true
},
'delete': {
'requiresAuthentication': true
},
'update': {
'requiresAuthentication': true
}
});
}).then(() => {
return createUser();
}).then(() => {
let stuff = new Parse.Object('Stuff');
stuff.set('foo', 'bar');
return stuff.save().then(() => {
let query = new Parse.Query('Stuff');
return query.get(stuff.id);
});
}).then((gotStuff) => {
return gotStuff.save({'foo': 'baz'}).then(() => {
return gotStuff.destroy();
})
}).then(() => {
done();
}, (e) => {
console.error(e);
fail("Should not have failed");
done();
});
});

it('required auth test create/get/update/delete not authenitcated', (done) => {
config.database.loadSchema().then((schema) => {
// Just to create a valid class
return schema.validateObject('Stuff', {foo: 'bar'});
}).then((schema) => {
return schema.setPermissions('Stuff', {
'get': {
'requiresAuthentication': true
},
'delete': {
'requiresAuthentication': true
},
'update': {
'requiresAuthentication': true
},
'create': {
'*': true
}
});
}).then(() => {
let stuff = new Parse.Object('Stuff');
stuff.set('foo', 'bar');
return stuff.save().then(() => {
let query = new Parse.Query('Stuff');
return query.get(stuff.id);
});
}).then(() => {
fail("Should not succeed!");
done();
}, (e) => {
expect(e.message).toEqual('Permission denied, user needs to be authenticated.');
done();
});
});

it('required auth test create/get/update/delete not authenitcated', (done) => {
config.database.loadSchema().then((schema) => {
// Just to create a valid class
return schema.validateObject('Stuff', {foo: 'bar'});
}).then((schema) => {
return schema.setPermissions('Stuff', {
'find': {
'requiresAuthentication': true
},
'delete': {
'requiresAuthentication': true
},
'update': {
'requiresAuthentication': true
},
'create': {
'*': true
},
'get': {
'*': true
}
});
}).then(() => {
let stuff = new Parse.Object('Stuff');
stuff.set('foo', 'bar');
return stuff.save().then(() => {
let query = new Parse.Query('Stuff');
return query.get(stuff.id);
})
}).then((result) => {
expect(result.get('foo')).toEqual('bar');
let query = new Parse.Query('Stuff');
return query.find();
}).then(() => {
fail("Should not succeed!");
done();
}, (e) => {
expect(e.message).toEqual('Permission denied, user needs to be authenticated.');
done();
});
});
})
24 changes: 23 additions & 1 deletion src/Controllers/SchemaController.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ const roleRegex = /^role:.*/;
// * permission
const publicRegex = /^\*$/

const permissionKeyRegex = Object.freeze([userIdRegex, roleRegex, publicRegex]);
const requireAuthenticationRegex = /^requiresAuthentication$/

const permissionKeyRegex = Object.freeze([userIdRegex, roleRegex, publicRegex, requireAuthenticationRegex]);

function verifyPermissionKey(key) {
let result = permissionKeyRegex.reduce((isGood, regEx) => {
Expand Down Expand Up @@ -771,6 +773,26 @@ export default class SchemaController {
return true;
}
let classPerms = this.perms[className];
let perms = classPerms[operation];

// If only for authenticated users
// make sure we have an aclGroup
if (perms['requiresAuthentication']) {
// If aclGroup has * (public)
if (!aclGroup || aclGroup.length == 0) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Permission denied, user needs to be authenticated.');
} else if (aclGroup.indexOf('*') > -1 && aclGroup.length == 1) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Permission denied, user needs to be authenticated.');
}
// no other CLP than requiresAuthentication
// let's resolve that!
if (Object.keys(perms).length == 1) {
return Promise.resolve();
}
}

// No matching CLP, let's check the Pointer permissions
// And handle those later
let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
Expand Down