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:
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|||||||
@@ -123,7 +123,9 @@ const roleRegex = /^role:.*/;
|
|||||||
// * permission
|
// * permission
|
||||||
const publicRegex = /^\*$/
|
const publicRegex = /^\*$/
|
||||||
|
|
||||||
const permissionKeyRegex = Object.freeze([userIdRegex, roleRegex, publicRegex]);
|
const requireAuthenticationRegex = /^requiresAuthentication$/
|
||||||
|
|
||||||
|
const permissionKeyRegex = Object.freeze([userIdRegex, roleRegex, publicRegex, requireAuthenticationRegex]);
|
||||||
|
|
||||||
function verifyPermissionKey(key) {
|
function verifyPermissionKey(key) {
|
||||||
let result = permissionKeyRegex.reduce((isGood, regEx) => {
|
let result = permissionKeyRegex.reduce((isGood, regEx) => {
|
||||||
@@ -771,6 +773,26 @@ export default class SchemaController {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let classPerms = this.perms[className];
|
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
|
// No matching CLP, let's check the Pointer permissions
|
||||||
// And handle those later
|
// And handle those later
|
||||||
let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
|
let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
|
||||||
|
|||||||
Reference in New Issue
Block a user