Now handles top level files and recursive files in folders. Set max line length to be 100
1692 lines
51 KiB
JavaScript
1692 lines
51 KiB
JavaScript
const Config = require('../lib/Config');
|
|
const Parse = require('parse/node');
|
|
const request = require('../lib/request');
|
|
const { className, createRole, createUser, logIn, updateCLP } = require('./dev');
|
|
|
|
describe('ProtectedFields', function () {
|
|
it('should handle and empty protectedFields', async function () {
|
|
const protectedFields = {};
|
|
await reconfigureServer({ protectedFields });
|
|
|
|
const user = new Parse.User();
|
|
user.setUsername('Alice');
|
|
user.setPassword('sekrit');
|
|
user.set('email', 'alice@aol.com');
|
|
user.set('favoriteColor', 'yellow');
|
|
await user.save();
|
|
|
|
const fetched = await new Parse.Query(Parse.User).get(user.id);
|
|
expect(fetched.has('email')).toBeFalsy();
|
|
expect(fetched.has('favoriteColor')).toBeTruthy();
|
|
});
|
|
|
|
describe('interaction with legacy userSensitiveFields', function () {
|
|
it('should fall back on sensitive fields if protected fields are not configured', async function () {
|
|
const userSensitiveFields = ['phoneNumber', 'timeZone'];
|
|
|
|
const protectedFields = { _User: { '*': ['email'] } };
|
|
|
|
await reconfigureServer({ userSensitiveFields, protectedFields });
|
|
const user = new Parse.User();
|
|
user.setUsername('Alice');
|
|
user.setPassword('sekrit');
|
|
user.set('email', 'alice@aol.com');
|
|
user.set('phoneNumber', 8675309);
|
|
user.set('timeZone', 'America/Los_Angeles');
|
|
user.set('favoriteColor', 'yellow');
|
|
user.set('favoriteFood', 'pizza');
|
|
await user.save();
|
|
|
|
const fetched = await new Parse.Query(Parse.User).get(user.id);
|
|
expect(fetched.has('email')).toBeFalsy();
|
|
expect(fetched.has('phoneNumber')).toBeFalsy();
|
|
expect(fetched.has('favoriteColor')).toBeTruthy();
|
|
});
|
|
|
|
it('should merge protected and sensitive for extra safety', async function () {
|
|
const userSensitiveFields = ['phoneNumber', 'timeZone'];
|
|
|
|
const protectedFields = { _User: { '*': ['email', 'favoriteFood'] } };
|
|
|
|
await reconfigureServer({ userSensitiveFields, protectedFields });
|
|
const user = new Parse.User();
|
|
user.setUsername('Alice');
|
|
user.setPassword('sekrit');
|
|
user.set('email', 'alice@aol.com');
|
|
user.set('phoneNumber', 8675309);
|
|
user.set('timeZone', 'America/Los_Angeles');
|
|
user.set('favoriteColor', 'yellow');
|
|
user.set('favoriteFood', 'pizza');
|
|
await user.save();
|
|
|
|
const fetched = await new Parse.Query(Parse.User).get(user.id);
|
|
expect(fetched.has('email')).toBeFalsy();
|
|
expect(fetched.has('phoneNumber')).toBeFalsy();
|
|
expect(fetched.has('favoriteFood')).toBeFalsy();
|
|
expect(fetched.has('favoriteColor')).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('non user class', function () {
|
|
it('should hide fields in a non user class', async function () {
|
|
const protectedFields = {
|
|
ClassA: { '*': ['foo'] },
|
|
ClassB: { '*': ['bar'] },
|
|
};
|
|
await reconfigureServer({ protectedFields });
|
|
|
|
const objA = await new Parse.Object('ClassA').set('foo', 'zzz').set('bar', 'yyy').save();
|
|
|
|
const objB = await new Parse.Object('ClassB').set('foo', 'zzz').set('bar', 'yyy').save();
|
|
|
|
const [fetchedA, fetchedB] = await Promise.all([
|
|
new Parse.Query('ClassA').get(objA.id),
|
|
new Parse.Query('ClassB').get(objB.id),
|
|
]);
|
|
|
|
expect(fetchedA.has('foo')).toBeFalsy();
|
|
expect(fetchedA.has('bar')).toBeTruthy();
|
|
|
|
expect(fetchedB.has('foo')).toBeTruthy();
|
|
expect(fetchedB.has('bar')).toBeFalsy();
|
|
});
|
|
|
|
it('should hide fields in non user class and non standard user field at same time', async function () {
|
|
const protectedFields = {
|
|
_User: { '*': ['phoneNumber'] },
|
|
ClassA: { '*': ['foo'] },
|
|
ClassB: { '*': ['bar'] },
|
|
};
|
|
|
|
await reconfigureServer({ protectedFields });
|
|
|
|
const user = new Parse.User();
|
|
user.setUsername('Alice');
|
|
user.setPassword('sekrit');
|
|
user.set('email', 'alice@aol.com');
|
|
user.set('phoneNumber', 8675309);
|
|
user.set('timeZone', 'America/Los_Angeles');
|
|
user.set('favoriteColor', 'yellow');
|
|
user.set('favoriteFood', 'pizza');
|
|
await user.save();
|
|
|
|
const objA = await new Parse.Object('ClassA').set('foo', 'zzz').set('bar', 'yyy').save();
|
|
|
|
const objB = await new Parse.Object('ClassB').set('foo', 'zzz').set('bar', 'yyy').save();
|
|
|
|
const [fetchedUser, fetchedA, fetchedB] = await Promise.all([
|
|
new Parse.Query(Parse.User).get(user.id),
|
|
new Parse.Query('ClassA').get(objA.id),
|
|
new Parse.Query('ClassB').get(objB.id),
|
|
]);
|
|
|
|
expect(fetchedA.has('foo')).toBeFalsy();
|
|
expect(fetchedA.has('bar')).toBeTruthy();
|
|
|
|
expect(fetchedB.has('foo')).toBeTruthy();
|
|
expect(fetchedB.has('bar')).toBeFalsy();
|
|
|
|
expect(fetchedUser.has('email')).toBeFalsy();
|
|
expect(fetchedUser.has('phoneNumber')).toBeFalsy();
|
|
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 intersect 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: {
|
|
'*': ['owners', 'owner', 'test'],
|
|
'userField:owners': ['owners', 'owner'],
|
|
'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').length).toBe(1);
|
|
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 intersect 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('test', 'test2');
|
|
await Parse.Object.saveAll([obj, obj2]);
|
|
|
|
const schema = await config.database.loadSchema();
|
|
await schema.updateClass(
|
|
'AnObject',
|
|
{},
|
|
{
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['owners', 'owner', 'test'],
|
|
'userField:owners': ['owners', 'owner'],
|
|
'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').length).toBe(1);
|
|
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();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('schema setup', () => {
|
|
let object;
|
|
|
|
async function initialize() {
|
|
await Config.get(Parse.applicationId).database.schemaCache.clear();
|
|
|
|
object = new Parse.Object(className);
|
|
|
|
object.set('revision', 0);
|
|
object.set('test', 'test');
|
|
|
|
await object.save(null, { useMasterKey: true });
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
await initialize();
|
|
});
|
|
|
|
it('should fail setting non-existing protected field', async done => {
|
|
const field = 'non-existing';
|
|
const entity = '*';
|
|
|
|
await expectAsync(
|
|
updateCLP({
|
|
protectedFields: {
|
|
[entity]: [field],
|
|
},
|
|
})
|
|
).toBeRejectedWith(
|
|
new Parse.Error(
|
|
Parse.Error.INVALID_JSON,
|
|
`Field '${field}' in protectedFields:${entity} does not exist`
|
|
)
|
|
);
|
|
done();
|
|
});
|
|
|
|
it('should allow setting authenticated', async () => {
|
|
await expectAsync(
|
|
updateCLP({
|
|
protectedFields: {
|
|
authenticated: ['test'],
|
|
},
|
|
})
|
|
).toBeResolved();
|
|
});
|
|
|
|
it('should not allow protecting default fields', async () => {
|
|
const defaultFields = ['objectId', 'createdAt', 'updatedAt', 'ACL'];
|
|
for (const field of defaultFields) {
|
|
await expectAsync(
|
|
updateCLP({
|
|
protectedFields: {
|
|
'*': [field],
|
|
},
|
|
})
|
|
).toBeRejectedWith(
|
|
new Parse.Error(Parse.Error.INVALID_JSON, `Default field '${field}' can not be protected`)
|
|
);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('targeting public access', () => {
|
|
let obj1;
|
|
|
|
async function initialize() {
|
|
await Config.get(Parse.applicationId).database.schemaCache.clear();
|
|
|
|
obj1 = new Parse.Object(className);
|
|
|
|
obj1.set('foo', 'foo');
|
|
obj1.set('bar', 'bar');
|
|
obj1.set('qux', 'qux');
|
|
|
|
await obj1.save(null, {
|
|
useMasterKey: true,
|
|
});
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
await initialize();
|
|
});
|
|
|
|
it('should hide field', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['foo'],
|
|
},
|
|
});
|
|
|
|
// unauthenticated
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('foo')).toBe(undefined);
|
|
expect(object.get('bar')).toBeDefined();
|
|
expect(object.get('qux')).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
|
|
it('should hide mutiple fields', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['foo', 'bar'],
|
|
},
|
|
});
|
|
|
|
// unauthenticated
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('foo')).toBe(undefined);
|
|
expect(object.get('bar')).toBe(undefined);
|
|
expect(object.get('qux')).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
|
|
it('should not hide any fields when set as empty array', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': [],
|
|
},
|
|
});
|
|
|
|
// unauthenticated
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('foo')).toBeDefined();
|
|
expect(object.get('bar')).toBeDefined();
|
|
expect(object.get('qux')).toBeDefined();
|
|
expect(object.id).toBeDefined();
|
|
expect(object.createdAt).toBeDefined();
|
|
expect(object.updatedAt).toBeDefined();
|
|
expect(object.getACL()).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
describe('targeting authenticated', () => {
|
|
/**
|
|
* is **owner** of: _obj1_
|
|
*
|
|
* is **tester** of: [ _obj1, obj2_ ]
|
|
*/
|
|
let user1;
|
|
|
|
/**
|
|
* is **owner** of: _obj2_
|
|
*
|
|
* is **tester** of: [ _obj1_ ]
|
|
*/
|
|
let user2;
|
|
|
|
/**
|
|
* **owner**: _user1_
|
|
*
|
|
* **testers**: [ _user1,user2_ ]
|
|
*/
|
|
let obj1;
|
|
|
|
/**
|
|
* **owner**: _user2_
|
|
*
|
|
* **testers**: [ _user1_ ]
|
|
*/
|
|
let obj2;
|
|
|
|
async function initialize() {
|
|
await Config.get(Parse.applicationId).database.schemaCache.clear();
|
|
|
|
await Parse.User.logOut();
|
|
|
|
[user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]);
|
|
|
|
obj1 = new Parse.Object(className);
|
|
obj2 = new Parse.Object(className);
|
|
|
|
obj1.set('owner', user1);
|
|
obj1.set('testers', [user1, user2]);
|
|
obj1.set('test', 'test');
|
|
|
|
obj2.set('owner', user2);
|
|
obj2.set('testers', [user1]);
|
|
obj2.set('test', 'test');
|
|
|
|
await Parse.Object.saveAll([obj1, obj2], {
|
|
useMasterKey: true,
|
|
});
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
await initialize();
|
|
});
|
|
|
|
it('should not hide any fields when set as empty array', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
authenticated: [],
|
|
},
|
|
});
|
|
|
|
// authenticated
|
|
await logIn(user1);
|
|
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('owner')).toBeDefined();
|
|
expect(object.get('testers')).toBeDefined();
|
|
expect(object.get('test')).toBeDefined();
|
|
expect(object.id).toBeDefined();
|
|
expect(object.createdAt).toBeDefined();
|
|
expect(object.updatedAt).toBeDefined();
|
|
expect(object.getACL()).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
|
|
it('should hide fields for authenticated users only (* not set)', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
authenticated: ['test'],
|
|
},
|
|
});
|
|
|
|
// not authenticated
|
|
const objectNonAuth = await obj1.fetch();
|
|
|
|
expect(objectNonAuth.get('test')).toBeDefined();
|
|
|
|
// authenticated
|
|
await logIn(user1);
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('test')).toBe(undefined);
|
|
|
|
done();
|
|
});
|
|
|
|
it('should intersect public and auth for authenticated user', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['owner', 'testers'],
|
|
authenticated: ['testers'],
|
|
},
|
|
});
|
|
|
|
// authenticated
|
|
await logIn(user1);
|
|
const objectAuth = await obj1.fetch();
|
|
|
|
// ( {A,B} intersect {B} ) == {B}
|
|
|
|
expect(objectAuth.get('testers')).not.toBeDefined(
|
|
'Should not be visible - protected for * and authenticated'
|
|
);
|
|
expect(objectAuth.get('test')).toBeDefined(
|
|
'Should be visible - not protected for everyone (* and authenticated)'
|
|
);
|
|
expect(objectAuth.get('owner')).toBeDefined(
|
|
'Should be visible - not protected for authenticated'
|
|
);
|
|
|
|
done();
|
|
});
|
|
|
|
it('should have higher prio than public for logged in users (intersect)', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['test'],
|
|
authenticated: [],
|
|
},
|
|
});
|
|
// authenticated, permitted
|
|
await logIn(user1);
|
|
|
|
const object = await obj1.fetch();
|
|
expect(object.get('test')).toBe('test');
|
|
|
|
done();
|
|
});
|
|
|
|
it('should have no effect on unauthenticated users (public not set)', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
authenticated: ['test'],
|
|
},
|
|
});
|
|
|
|
// unauthenticated, protected
|
|
const objectNonAuth = await obj1.fetch();
|
|
expect(objectNonAuth.get('test')).toBe('test');
|
|
|
|
done();
|
|
});
|
|
|
|
it('should protect multiple fields for authenticated users', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
authenticated: ['test', 'owner'],
|
|
},
|
|
});
|
|
|
|
// authenticated
|
|
await logIn(user1);
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('test')).toBe(undefined);
|
|
expect(object.get('owner')).toBe(undefined);
|
|
|
|
done();
|
|
});
|
|
|
|
it('should not be affected by rules not applicable to user (smoke)', async done => {
|
|
const role = await createRole({ users: user1 });
|
|
const roleName = role.get('name');
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
authenticated: ['owner', 'testers'],
|
|
[`role:${roleName}`]: ['test'],
|
|
'userField:owner': [],
|
|
[user1.id]: [],
|
|
},
|
|
});
|
|
|
|
// authenticated, non-owner, no role
|
|
await logIn(user2);
|
|
const objectNotOwned = await obj1.fetch();
|
|
|
|
expect(objectNotOwned.get('owner')).toBe(undefined);
|
|
expect(objectNotOwned.get('testers')).toBe(undefined);
|
|
expect(objectNotOwned.get('test')).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
describe('targeting roles', () => {
|
|
let user1, user2;
|
|
|
|
/**
|
|
* owner: user1
|
|
*
|
|
* testers: [user1,user2]
|
|
*/
|
|
let obj1;
|
|
|
|
/**
|
|
* owner: user2
|
|
*
|
|
* testers: [user1]
|
|
*/
|
|
let obj2;
|
|
|
|
async function initialize() {
|
|
await Config.get(Parse.applicationId).database.schemaCache.clear();
|
|
|
|
[user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]);
|
|
|
|
obj1 = new Parse.Object(className);
|
|
obj2 = new Parse.Object(className);
|
|
|
|
obj1.set('owner', user1);
|
|
obj1.set('testers', [user1, user2]);
|
|
obj1.set('test', 'test');
|
|
|
|
obj2.set('owner', user2);
|
|
obj2.set('testers', [user1]);
|
|
obj2.set('test', 'test');
|
|
|
|
await Parse.Object.saveAll([obj1, obj2], {
|
|
useMasterKey: true,
|
|
});
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
await initialize();
|
|
});
|
|
|
|
it('should hide field when user belongs to a role', async done => {
|
|
const role = await createRole({ users: user1 });
|
|
const roleName = role.get('name');
|
|
|
|
await updateCLP({
|
|
protectedFields: {
|
|
[`role:${roleName}`]: ['test'],
|
|
},
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
});
|
|
|
|
// user has role
|
|
await logIn(user1);
|
|
|
|
const object = await obj1.fetch();
|
|
expect(object.get('test')).toBe(undefined); // field protected
|
|
expect(object.get('owner')).toBeDefined();
|
|
expect(object.get('testers')).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
|
|
it('should not hide any fields when set as empty array', async done => {
|
|
const role = await createRole({ users: user1 });
|
|
const roleName = role.get('name');
|
|
|
|
await updateCLP({
|
|
protectedFields: {
|
|
[`role:${roleName}`]: [],
|
|
},
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
});
|
|
|
|
// user has role
|
|
await logIn(user1);
|
|
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('owner')).toBeDefined();
|
|
expect(object.get('testers')).toBeDefined();
|
|
expect(object.get('test')).toBeDefined();
|
|
expect(object.id).toBeDefined();
|
|
expect(object.createdAt).toBeDefined();
|
|
expect(object.updatedAt).toBeDefined();
|
|
expect(object.getACL()).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
|
|
it('should hide multiple fields when user belongs to a role', async done => {
|
|
const role = await createRole({ users: user1 });
|
|
const roleName = role.get('name');
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
[`role:${roleName}`]: ['test', 'owner'],
|
|
},
|
|
});
|
|
|
|
// user has role
|
|
await logIn(user1);
|
|
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('test')).toBe(undefined, 'Field should not be visible - protected by role');
|
|
expect(object.get('owner')).toBe(
|
|
undefined,
|
|
'Field should not be visible - protected by role'
|
|
);
|
|
expect(object.get('testers')).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
|
|
it('should not protect when user does not belong to a role', async done => {
|
|
const role = await createRole({ users: user1 });
|
|
const roleName = role.get('name');
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
[`role:${roleName}`]: ['test', 'owner'],
|
|
},
|
|
});
|
|
|
|
// user doesn't have role
|
|
await logIn(user2);
|
|
const object = await obj1.fetch();
|
|
|
|
expect(object.get('test')).toBeDefined();
|
|
expect(object.get('owner')).toBeDefined();
|
|
expect(object.get('testers')).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
|
|
it('should intersect protected fields when user belongs to multiple roles', async done => {
|
|
const role1 = await createRole({ users: user1 });
|
|
const role2 = await createRole({ users: user1 });
|
|
|
|
const role1name = role1.get('name');
|
|
const role2name = role2.get('name');
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
[`role:${role1name}`]: ['owner'],
|
|
[`role:${role2name}`]: ['test', 'owner'],
|
|
},
|
|
});
|
|
|
|
// user has both roles
|
|
await logIn(user1);
|
|
const object = await obj1.fetch();
|
|
|
|
// "owner" is a result of intersection
|
|
expect(object.get('owner')).toBe(
|
|
undefined,
|
|
'Must not be visible - protected for all roles the user belongs to'
|
|
);
|
|
expect(object.get('test')).toBeDefined(
|
|
'Has to be visible - is not protected for users with role1'
|
|
);
|
|
done();
|
|
});
|
|
|
|
it('should intersect protected fields when user belongs to multiple roles hierarchy', async done => {
|
|
const admin = await createRole({
|
|
users: user1,
|
|
roleName: 'admin',
|
|
});
|
|
|
|
const moder = await createRole({
|
|
users: [user1, user2],
|
|
roleName: 'moder',
|
|
});
|
|
|
|
const tester = await createRole({
|
|
roleName: 'tester',
|
|
});
|
|
|
|
// admin supersets moder role
|
|
moder.relation('roles').add(admin);
|
|
await moder.save(null, { useMasterKey: true });
|
|
|
|
tester.relation('roles').add(moder);
|
|
await tester.save(null, { useMasterKey: true });
|
|
|
|
const roleAdmin = `role:${admin.get('name')}`;
|
|
const roleModer = `role:${moder.get('name')}`;
|
|
const roleTester = `role:${tester.get('name')}`;
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
[roleAdmin]: [],
|
|
[roleModer]: ['owner'],
|
|
[roleTester]: ['test', 'owner'],
|
|
},
|
|
});
|
|
|
|
// user1 has admin & moder & tester roles, (moder includes tester).
|
|
await logIn(user1);
|
|
const object = await obj1.fetch();
|
|
|
|
// being admin makes all fields visible
|
|
expect(object.get('test')).toBeDefined(
|
|
'Should be visible - admin role explicitly removes protection for all fields ( [] )'
|
|
);
|
|
expect(object.get('owner')).toBeDefined(
|
|
'Should be visible - admin role explicitly removes protection for all fields ( [] )'
|
|
);
|
|
|
|
// user2 has moder & tester role, moder includes tester.
|
|
await logIn(user2);
|
|
const objectAgain = await obj1.fetch();
|
|
|
|
// being moder allows "test" field
|
|
expect(objectAgain.get('owner')).toBe(
|
|
undefined,
|
|
'"owner" should not be visible - protected for each role user belongs to'
|
|
);
|
|
expect(objectAgain.get('test')).toBeDefined(
|
|
'Should be visible - moder role does not protect "test" field'
|
|
);
|
|
|
|
done();
|
|
});
|
|
|
|
it('should be able to clear protected fields for role (protected for authenticated)', async done => {
|
|
const role = await createRole({ users: user1 });
|
|
const roleName = role.get('name');
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
authenticated: ['test'],
|
|
[`role:${roleName}`]: [],
|
|
},
|
|
});
|
|
|
|
// user has role, test field visible
|
|
await logIn(user1);
|
|
const object = await obj1.fetch();
|
|
expect(object.get('test')).toBe('test');
|
|
|
|
done();
|
|
});
|
|
|
|
it('should determine protectedFields as intersection of field sets for public and role', async done => {
|
|
const role = await createRole({ users: user1 });
|
|
const roleName = role.get('name');
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['test', 'owner'],
|
|
[`role:${roleName}`]: ['owner', 'testers'],
|
|
},
|
|
});
|
|
|
|
// user has role
|
|
await logIn(user1);
|
|
|
|
const object = await obj1.fetch();
|
|
expect(object.get('test')).toBeDefined(
|
|
'Should be visible - "test" is not protected for role user belongs to'
|
|
);
|
|
expect(object.get('testers')).toBeDefined(
|
|
'Should be visible - "testers" is allowed for everyone (*)'
|
|
);
|
|
expect(object.get('owner')).toBe(
|
|
undefined,
|
|
'Should not be visible - "test" is not allowed for both public(*) and role'
|
|
);
|
|
done();
|
|
});
|
|
|
|
it('should be determined as an intersection of protecedFields for authenticated and role', async done => {
|
|
const role = await createRole({ users: user1 });
|
|
const roleName = role.get('name');
|
|
|
|
// this is an example of misunderstood configuration.
|
|
// If you allow (== do not restrict) some field for broader audience
|
|
// (having a role implies user inheres to 'authenticated' group)
|
|
// it's not possible to narrow by protecting field for a role.
|
|
// You'd have to protect it for 'authenticated' as well.
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
authenticated: ['test'],
|
|
[`role:${roleName}`]: ['owner'],
|
|
},
|
|
});
|
|
|
|
// user has role
|
|
await logIn(user1);
|
|
const object = await obj1.fetch();
|
|
|
|
//
|
|
expect(object.get('test')).toBeDefined(
|
|
"Being both auhenticated and having a role leads to clearing protection on 'test' (by role rules)"
|
|
);
|
|
expect(object.get('owner')).toBeDefined('All authenticated users allowed to see "owner"');
|
|
expect(object.get('testers')).toBeDefined();
|
|
|
|
done();
|
|
});
|
|
|
|
it('should not hide fields when user does not belong to a role protectedFields set for', async done => {
|
|
const role = await createRole({ users: user2 });
|
|
const roleName = role.get('name');
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
[`role:${roleName}`]: ['test'],
|
|
},
|
|
});
|
|
|
|
// relate user1 to some role, no protectedFields for it
|
|
await createRole({ users: user1 });
|
|
|
|
await logIn(user1);
|
|
|
|
const object = await obj1.fetch();
|
|
expect(object.get('test')).toBeDefined(
|
|
'Field should be visible - user belongs to a role that has no protectedFields set'
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
describe('using pointer-fields and queries with keys projection', () => {
|
|
/*
|
|
* Pointer variant ("userField:column") relies on User ids
|
|
* returned after query executed (hides fields before sending it to client)
|
|
* If such column is excluded/not included (not returned from db because of 'project')
|
|
* there will be no user ids to check against
|
|
* and protectedFields won't be applied correctly.
|
|
*/
|
|
|
|
let user1;
|
|
/**
|
|
* owner: user1
|
|
*
|
|
* testers: [user1]
|
|
*/
|
|
let obj;
|
|
|
|
let headers;
|
|
|
|
/**
|
|
* Clear cache, create user and object, login user and setup rest headers with token
|
|
*/
|
|
async function initialize() {
|
|
await Config.get(Parse.applicationId).database.schemaCache.clear();
|
|
|
|
user1 = await createUser('user1');
|
|
user1 = await logIn(user1);
|
|
|
|
// await user1.fetch();
|
|
obj = new Parse.Object(className);
|
|
|
|
obj.set('owner', user1);
|
|
obj.set('field', 'field');
|
|
obj.set('test', 'test');
|
|
|
|
await Parse.Object.saveAll([obj], { useMasterKey: true });
|
|
|
|
headers = {
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
'X-Parse-Rest-API-Key': 'rest',
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Session-Token': user1.getSessionToken(),
|
|
};
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
await initialize();
|
|
});
|
|
|
|
it('should be enforced regardless of pointer-field being included in keys (select)', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['field', 'test'],
|
|
'userField:owner': [],
|
|
},
|
|
});
|
|
|
|
const query = new Parse.Query('AnObject');
|
|
query.select('field', 'test');
|
|
|
|
const object = await query.get(obj.id);
|
|
expect(object.get('field')).toBe('field');
|
|
expect(object.get('test')).toBe('test');
|
|
done();
|
|
});
|
|
|
|
it('should protect fields for query where pointer field is not included via keys (REST GET)', async done => {
|
|
const obj = new Parse.Object(className);
|
|
|
|
obj.set('owner', user1);
|
|
obj.set('field', 'field');
|
|
obj.set('test', 'test');
|
|
|
|
await Parse.Object.saveAll([obj], { useMasterKey: true });
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['field', 'test'],
|
|
'userField:owner': ['test'],
|
|
},
|
|
});
|
|
|
|
const { data: object } = await request({
|
|
url: `${Parse.serverURL}/classes/${className}/${obj.id}`,
|
|
qs: {
|
|
keys: 'field,test',
|
|
},
|
|
headers: headers,
|
|
});
|
|
|
|
expect(object.field).toBe(
|
|
'field',
|
|
'Should BE in response - not protected by "userField:owner"'
|
|
);
|
|
expect(object.test).toBe(
|
|
undefined,
|
|
'Should NOT be in response - protected by "userField:owner"'
|
|
);
|
|
expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"');
|
|
done();
|
|
});
|
|
|
|
it('should protect fields for query where pointer field is not included via keys (REST FIND)', async done => {
|
|
const obj = new Parse.Object(className);
|
|
|
|
obj.set('owner', user1);
|
|
obj.set('field', 'field');
|
|
obj.set('test', 'test');
|
|
|
|
await Parse.Object.saveAll([obj], { useMasterKey: true });
|
|
|
|
await obj.fetch();
|
|
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['field', 'test'],
|
|
'userField:owner': ['test'],
|
|
},
|
|
});
|
|
|
|
const { data } = await request({
|
|
url: `${Parse.serverURL}/classes/${className}`,
|
|
qs: {
|
|
keys: 'field,test',
|
|
where: JSON.stringify({ objectId: obj.id }),
|
|
},
|
|
headers,
|
|
});
|
|
|
|
const object = data.results[0];
|
|
|
|
expect(object.field).toBe(
|
|
'field',
|
|
'Should be in response - not protected by "userField:owner"'
|
|
);
|
|
expect(object.test).toBe(
|
|
undefined,
|
|
'Should not be in response - protected by "userField:owner"'
|
|
);
|
|
expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"');
|
|
done();
|
|
});
|
|
|
|
it('should protect fields for query where pointer field is in excludeKeys (REST GET)', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['field', 'test'],
|
|
'userField:owner': ['test'],
|
|
},
|
|
});
|
|
|
|
const { data: object } = await request({
|
|
qs: {
|
|
excludeKeys: 'owner',
|
|
},
|
|
headers,
|
|
url: `${Parse.serverURL}/classes/${className}/${obj.id}`,
|
|
});
|
|
|
|
expect(object.field).toBe(
|
|
'field',
|
|
'Should be in response - not protected by "userField:owner"'
|
|
);
|
|
expect(object['test']).toBe(
|
|
undefined,
|
|
'Should not be in response - protected by "userField:owner"'
|
|
);
|
|
expect(object['owner']).toBe(undefined, 'Should not be in response - not included in "keys"');
|
|
done();
|
|
});
|
|
|
|
it('should protect fields for query where pointer field is in excludedKeys (REST FIND)', async done => {
|
|
await updateCLP({
|
|
protectedFields: {
|
|
'*': ['field', 'test'],
|
|
'userField:owner': ['test'],
|
|
},
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
});
|
|
|
|
const { data } = await request({
|
|
qs: {
|
|
excludeKeys: 'owner',
|
|
where: JSON.stringify({ objectId: obj.id }),
|
|
},
|
|
headers,
|
|
url: `${Parse.serverURL}/classes/${className}`,
|
|
});
|
|
|
|
const object = data.results[0];
|
|
|
|
expect(object.field).toBe(
|
|
'field',
|
|
'Should be in response - not protected by "userField:owner"'
|
|
);
|
|
expect(object.test).toBe(
|
|
undefined,
|
|
'Should not be in response - protected by "userField:owner"'
|
|
);
|
|
expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"');
|
|
done();
|
|
});
|
|
|
|
xit('todo: should be enforced regardless of pointer-field being excluded', async done => {
|
|
await updateCLP({
|
|
get: { '*': true },
|
|
find: { '*': true },
|
|
protectedFields: {
|
|
'*': ['field', 'test'],
|
|
'userField:owner': [],
|
|
},
|
|
});
|
|
|
|
const query = new Parse.Query('AnObject');
|
|
|
|
/* TODO: this has some caching problems on JS-SDK (2.11.) side */
|
|
// query.exclude('owner')
|
|
|
|
const object = await query.get(obj.id);
|
|
expect(object.get('field')).toBe('field');
|
|
expect(object.get('test')).toBe('test');
|
|
expect(object.get('owner')).toBe(undefined);
|
|
done();
|
|
});
|
|
});
|
|
});
|