Skip to content

Adds class level permission requiring authenticated user #893

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

Merged
merged 8 commits into from
Dec 3, 2016
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