Protected fields pointer-permissions support (#5951)

* moved whitelisting of own user to remove conflict with custom classes and * permission

* added new pointer-perm regex to permissions

* added pointer-permissions support

* added tests

* fixed typo

* fixed typo 2

* added tests using find operation

* renamed protectedFields pointerPerm to userField

* decoupled readUserFields from CLP and removed readUser from protectedFields before querying

* updated tests
This commit is contained in:
Dobbias Nan
2019-08-22 21:01:50 +02:00
committed by Antonio Davi Macedo Coelho de Castro
parent 6ed0a2289a
commit 2b1c591cb7
3 changed files with 706 additions and 23 deletions

View File

@@ -1,3 +1,6 @@
const Config = require('../lib/Config');
const Parse = require('parse/node');
describe('ProtectedFields', function() {
it('should handle and empty protectedFields', async function() {
const protectedFields = {};
@@ -138,4 +141,621 @@ describe('ProtectedFields', function() {
expect(fetchedUser.has('favoriteColor')).toBeTruthy();
});
});
describe('using the pointer-permission variant', () => {
let user1, user2;
beforeEach(async () => {
Config.get(Parse.applicationId).database.schemaCache.clear();
user1 = await Parse.User.signUp('user1', 'password');
user2 = await Parse.User.signUp('user2', 'password');
await Parse.User.logOut();
});
describe('and get/fetch', () => {
it('should allow access using single user pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
obj.set('owner', user1);
obj.set('test', 'test');
await obj.save();
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owner'], 'userField:owner': [] },
}
);
await Parse.User.logIn('user1', 'password');
const objectAgain = await obj.fetch();
expect(objectAgain.get('owner').id).toBe(user1.id);
expect(objectAgain.get('test')).toBe('test');
done();
});
it('should deny access to other users using single user pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
obj.set('owner', user1);
obj.set('test', 'test');
await obj.save();
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owner'], 'userField:owner': [] },
}
);
await Parse.User.logIn('user2', 'password');
const objectAgain = await obj.fetch();
expect(objectAgain.get('owner')).toBe(undefined);
expect(objectAgain.get('test')).toBe('test');
done();
});
it('should deny access to public using single user pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
obj.set('owner', user1);
obj.set('test', 'test');
await obj.save();
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owner'], 'userField:owner': [] },
}
);
const objectAgain = await obj.fetch();
expect(objectAgain.get('owner')).toBe(undefined);
expect(objectAgain.get('test')).toBe('test');
done();
});
it('should allow access using user array pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
obj.set('owners', [user1, user2]);
obj.set('test', 'test');
await obj.save();
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owners'], 'userField:owners': [] },
}
);
await Parse.User.logIn('user1', 'password');
let objectAgain = await obj.fetch();
expect(objectAgain.get('owners')[0].id).toBe(user1.id);
expect(objectAgain.get('test')).toBe('test');
await Parse.User.logIn('user2', 'password');
objectAgain = await obj.fetch();
expect(objectAgain.get('owners')[1].id).toBe(user2.id);
expect(objectAgain.get('test')).toBe('test');
done();
});
it('should deny access to other users using user array pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
obj.set('owners', [user1]);
obj.set('test', 'test');
await obj.save();
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owners'], 'userField:owners': [] },
}
);
await Parse.User.logIn('user2', 'password');
const objectAgain = await obj.fetch();
expect(objectAgain.get('owners')).toBe(undefined);
expect(objectAgain.get('test')).toBe('test');
done();
});
it('should deny access to public using user array pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
obj.set('owners', [user1, user2]);
obj.set('test', 'test');
await obj.save();
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owners'], 'userField:owners': [] },
}
);
const objectAgain = await obj.fetch();
expect(objectAgain.get('owners')).toBe(undefined);
expect(objectAgain.get('test')).toBe('test');
done();
});
it('should create merge protected fields when using multiple pointer-permission fields', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
obj.set('owners', [user1]);
obj.set('owner', user1);
obj.set('test', 'test');
await obj.save();
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: {
'*': [],
'userField:owners': ['owners'],
'userField:owner': ['owner'],
},
}
);
// Check if protectFields from pointer-permissions got combined
await Parse.User.logIn('user1', 'password');
const objectAgain = await obj.fetch();
expect(objectAgain.get('owners')).toBe(undefined);
expect(objectAgain.get('owner')).toBe(undefined);
expect(objectAgain.get('test')).toBe('test');
done();
});
it('should ignore pointer-permission fields not present in object', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
obj.set('owners', [user1]);
obj.set('owner', user1);
obj.set('test', 'test');
await obj.save();
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: {
'*': [],
'userField:idontexist': ['owner'],
'userField:idontexist2': ['owners'],
},
}
);
await Parse.User.logIn('user1', 'password');
const objectAgain = await obj.fetch();
expect(objectAgain.get('owners')).not.toBe(undefined);
expect(objectAgain.get('owner')).not.toBe(undefined);
expect(objectAgain.get('test')).toBe('test');
done();
});
});
describe('and find', () => {
it('should allow access using single user pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
obj.set('owner', user1);
obj.set('test', 'test');
obj2.set('owner', user1);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owner'], 'userField:owner': [] },
}
);
await Parse.User.logIn('user1', 'password');
const q = new Parse.Query('AnObject');
const results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owner').id).toBe(user1.id);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owner').id).toBe(user1.id);
expect(results[1].get('test')).toBe('test2');
done();
});
it('should deny access to other users using single user pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
obj.set('owner', user1);
obj.set('test', 'test');
obj2.set('owner', user1);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owner'], 'userField:owner': [] },
}
);
await Parse.User.logIn('user2', 'password');
const q = new Parse.Query('AnObject');
const results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owner')).toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owner')).toBe(undefined);
expect(results[1].get('test')).toBe('test2');
done();
});
it('should deny access to public using single user pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
obj.set('owner', user1);
obj.set('test', 'test');
obj2.set('owner', user1);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owner'], 'userField:owner': [] },
}
);
const q = new Parse.Query('AnObject');
const results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owner')).toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owner')).toBe(undefined);
expect(results[1].get('test')).toBe('test2');
done();
});
it('should allow access using user array pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
obj.set('owners', [user1, user2]);
obj.set('test', 'test');
obj2.set('owners', [user1, user2]);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owners'], 'userField:owners': [] },
}
);
const q = new Parse.Query('AnObject');
let results;
await Parse.User.logIn('user1', 'password');
results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owners')[0].id).toBe(user1.id);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owners')[0].id).toBe(user1.id);
expect(results[1].get('test')).toBe('test2');
await Parse.User.logIn('user2', 'password');
results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owners')[1].id).toBe(user2.id);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owners')[1].id).toBe(user2.id);
expect(results[1].get('test')).toBe('test2');
done();
});
it('should deny access to other users using user array pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
obj.set('owners', [user1]);
obj.set('test', 'test');
obj2.set('owners', [user1]);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owners'], 'userField:owners': [] },
}
);
await Parse.User.logIn('user2', 'password');
const q = new Parse.Query('AnObject');
const results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owners')).toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owners')).toBe(undefined);
expect(results[1].get('test')).toBe('test2');
done();
});
it('should deny access to public using user array pointer-permissions', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
obj.set('owners', [user1, user2]);
obj.set('test', 'test');
obj2.set('owners', [user1, user2]);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: { '*': ['owners'], 'userField:owners': [] },
}
);
const q = new Parse.Query('AnObject');
const results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owners')).toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owners')).toBe(undefined);
expect(results[1].get('test')).toBe('test2');
done();
});
it('should create merge protected fields when using multiple pointer-permission fields', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
obj.set('owners', [user1]);
obj.set('owner', user1);
obj.set('test', 'test');
obj2.set('owners', [user1]);
obj2.set('owner', user1);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: {
'*': [],
'userField:owners': ['owners'],
'userField:owner': ['owner'],
},
}
);
// Check if protectFields from pointer-permissions got combined
await Parse.User.logIn('user1', 'password');
const q = new Parse.Query('AnObject');
const results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owners')).toBe(undefined);
expect(results[0].get('owner')).toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owners')).toBe(undefined);
expect(results[1].get('owner')).toBe(undefined);
expect(results[1].get('test')).toBe('test2');
done();
});
it('should ignore pointer-permission fields not present in object', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
obj.set('owners', [user1]);
obj.set('owner', user1);
obj.set('test', 'test');
obj2.set('owners', [user1]);
obj2.set('owner', user1);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: {
'*': [],
'userField:idontexist': ['owner'],
'userField:idontexist2': ['owners'],
},
}
);
await Parse.User.logIn('user1', 'password');
const q = new Parse.Query('AnObject');
const results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
expect(results[0].get('owners')).not.toBe(undefined);
expect(results[0].get('owner')).not.toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owners')).not.toBe(undefined);
expect(results[1].get('owner')).not.toBe(undefined);
expect(results[1].get('test')).toBe('test2');
done();
});
it('should filter only fields from objects not owned by the user', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
const obj3 = new Parse.Object('AnObject');
obj.set('owner', user1);
obj.set('test', 'test');
obj2.set('owner', user2);
obj2.set('test', 'test2');
obj3.set('owner', user2);
obj3.set('test', 'test3');
await Parse.Object.saveAll([obj, obj2, obj3]);
const schema = await config.database.loadSchema();
await schema.updateClass(
'AnObject',
{},
{
get: { '*': true },
find: { '*': true },
protectedFields: {
'*': ['owner'],
'userField:owner': [],
},
}
);
const q = new Parse.Query('AnObject');
let results;
await Parse.User.logIn('user1', 'password');
results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(3);
expect(results[0].get('owner')).not.toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owner')).toBe(undefined);
expect(results[1].get('test')).toBe('test2');
expect(results[2].get('owner')).toBe(undefined);
expect(results[2].get('test')).toBe('test3');
await Parse.User.logIn('user2', 'password');
results = await q.find();
// sort for checking in correct order
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(3);
expect(results[0].get('owner')).toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owner')).not.toBe(undefined);
expect(results[1].get('test')).toBe('test2');
expect(results[2].get('owner')).not.toBe(undefined);
expect(results[2].get('test')).toBe('test3');
done();
});
});
});
});