Adds class level permission requiring authenticated user (#893)

* Adds class level permission requiring authenticated user

* Updates to latest schema permissions syntax

* fix flaky test

* Exclude PG

* Rebased and nitted

* lints
This commit is contained in:
Florent Vilmart
2016-12-02 19:47:33 -05:00
committed by GitHub
parent 01b05b060f
commit e0704b440c
2 changed files with 250 additions and 1 deletions

View File

@@ -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();
});
});
})

View File

@@ -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) => {
@@ -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';