605 lines
19 KiB
JavaScript
605 lines
19 KiB
JavaScript
'use strict';
|
|
|
|
// Roles are not accessible without the master key, so they are not intended
|
|
// for use by clients. We can manually test them using the master key.
|
|
const RestQuery = require('../lib/RestQuery');
|
|
const Auth = require('../lib/Auth').Auth;
|
|
const Config = require('../lib/Config');
|
|
|
|
function testLoadRoles(config, done) {
|
|
const rolesNames = ['FooRole', 'BarRole', 'BazRole'];
|
|
const roleIds = {};
|
|
createTestUser()
|
|
.then(user => {
|
|
// Put the user on the 1st role
|
|
return createRole(rolesNames[0], null, user)
|
|
.then(aRole => {
|
|
roleIds[aRole.get('name')] = aRole.id;
|
|
// set the 1st role as a sibling of the second
|
|
// user will should have 2 role now
|
|
return createRole(rolesNames[1], aRole, null);
|
|
})
|
|
.then(anotherRole => {
|
|
roleIds[anotherRole.get('name')] = anotherRole.id;
|
|
// set this role as a sibling of the last
|
|
// the user should now have 3 roles
|
|
return createRole(rolesNames[2], anotherRole, null);
|
|
})
|
|
.then(lastRole => {
|
|
roleIds[lastRole.get('name')] = lastRole.id;
|
|
const auth = new Auth({ config, isMaster: true, user: user });
|
|
return auth._loadRoles();
|
|
});
|
|
})
|
|
.then(
|
|
roles => {
|
|
expect(roles.length).toEqual(3);
|
|
rolesNames.forEach(name => {
|
|
expect(roles.indexOf('role:' + name)).not.toBe(-1);
|
|
});
|
|
done();
|
|
},
|
|
function () {
|
|
fail('should succeed');
|
|
done();
|
|
}
|
|
);
|
|
}
|
|
|
|
const createRole = function (name, sibling, user) {
|
|
const role = new Parse.Role(name, new Parse.ACL());
|
|
if (user) {
|
|
const users = role.relation('users');
|
|
users.add(user);
|
|
}
|
|
if (sibling) {
|
|
role.relation('roles').add(sibling);
|
|
}
|
|
return role.save({}, { useMasterKey: true });
|
|
};
|
|
|
|
describe('Parse Role testing', () => {
|
|
it('Do a bunch of basic role testing', done => {
|
|
let user;
|
|
let role;
|
|
|
|
createTestUser()
|
|
.then(x => {
|
|
user = x;
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(true);
|
|
acl.setPublicWriteAccess(false);
|
|
role = new Parse.Object('_Role');
|
|
role.set('name', 'Foos');
|
|
role.setACL(acl);
|
|
const users = role.relation('users');
|
|
users.add(user);
|
|
return role.save({}, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
const query = new Parse.Query('_Role');
|
|
return query.find({ useMasterKey: true });
|
|
})
|
|
.then(x => {
|
|
expect(x.length).toEqual(1);
|
|
const relation = x[0].relation('users').query();
|
|
return relation.first({ useMasterKey: true });
|
|
})
|
|
.then(x => {
|
|
expect(x.id).toEqual(user.id);
|
|
// Here we've got a valid role and a user assigned.
|
|
// Lets create an object only the role can read/write and test
|
|
// the different scenarios.
|
|
const obj = new Parse.Object('TestObject');
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(false);
|
|
acl.setPublicWriteAccess(false);
|
|
acl.setRoleReadAccess('Foos', true);
|
|
acl.setRoleWriteAccess('Foos', true);
|
|
obj.setACL(acl);
|
|
return obj.save();
|
|
})
|
|
.then(() => {
|
|
const query = new Parse.Query('TestObject');
|
|
return query.find({ sessionToken: user.getSessionToken() });
|
|
})
|
|
.then(x => {
|
|
expect(x.length).toEqual(1);
|
|
const objAgain = x[0];
|
|
objAgain.set('foo', 'bar');
|
|
// This should succeed:
|
|
return objAgain.save({}, { sessionToken: user.getSessionToken() });
|
|
})
|
|
.then(x => {
|
|
x.set('foo', 'baz');
|
|
// This should fail:
|
|
return x.save({}, { sessionToken: '' });
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Should not have been able to save.');
|
|
},
|
|
e => {
|
|
expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('should not recursively load the same role multiple times', done => {
|
|
const rootRole = 'RootRole';
|
|
const roleNames = ['FooRole', 'BarRole', 'BazRole'];
|
|
const allRoles = [rootRole].concat(roleNames);
|
|
|
|
const roleObjs = {};
|
|
const createAllRoles = function (user) {
|
|
const promises = allRoles.map(function (roleName) {
|
|
return createRole(roleName, null, user).then(function (roleObj) {
|
|
roleObjs[roleName] = roleObj;
|
|
return roleObj;
|
|
});
|
|
});
|
|
return Promise.all(promises);
|
|
};
|
|
|
|
const restExecute = spyOn(RestQuery.prototype, 'execute').and.callThrough();
|
|
|
|
let user, auth, getAllRolesSpy;
|
|
createTestUser()
|
|
.then(newUser => {
|
|
user = newUser;
|
|
return createAllRoles(user);
|
|
})
|
|
.then(roles => {
|
|
const rootRoleObj = roleObjs[rootRole];
|
|
roles.forEach(function (role, i) {
|
|
// Add all roles to the RootRole
|
|
if (role.id !== rootRoleObj.id) {
|
|
role.relation('roles').add(rootRoleObj);
|
|
}
|
|
// Add all "roleNames" roles to the previous role
|
|
if (i > 0) {
|
|
role.relation('roles').add(roles[i - 1]);
|
|
}
|
|
});
|
|
|
|
return Parse.Object.saveAll(roles, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
auth = new Auth({
|
|
config: Config.get('test'),
|
|
isMaster: true,
|
|
user: user,
|
|
});
|
|
getAllRolesSpy = spyOn(auth, '_getAllRolesNamesForRoleIds').and.callThrough();
|
|
|
|
return auth._loadRoles();
|
|
})
|
|
.then(roles => {
|
|
expect(roles.length).toEqual(4);
|
|
|
|
allRoles.forEach(function (name) {
|
|
expect(roles.indexOf('role:' + name)).not.toBe(-1);
|
|
});
|
|
|
|
// 1 Query for the initial setup
|
|
// 1 query for the parent roles
|
|
expect(restExecute.calls.count()).toEqual(2);
|
|
|
|
// 1 call for the 1st layer of roles
|
|
// 1 call for the 2nd layer
|
|
expect(getAllRolesSpy.calls.count()).toEqual(2);
|
|
done();
|
|
})
|
|
.catch(() => {
|
|
fail('should succeed');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should recursively load roles', done => {
|
|
testLoadRoles(Config.get('test'), done);
|
|
});
|
|
|
|
it('should recursively load roles without config', done => {
|
|
testLoadRoles(undefined, done);
|
|
});
|
|
|
|
it('_Role object should not save without name.', done => {
|
|
const role = new Parse.Role();
|
|
role.save(null, { useMasterKey: true }).then(
|
|
() => {
|
|
fail('_Role object should not save without name.');
|
|
},
|
|
error => {
|
|
expect(error.code).toEqual(111);
|
|
role.set('name', 'testRole');
|
|
role.save(null, { useMasterKey: true }).then(
|
|
() => {
|
|
fail('_Role object should not save without ACL.');
|
|
},
|
|
error2 => {
|
|
expect(error2.code).toEqual(111);
|
|
done();
|
|
}
|
|
);
|
|
}
|
|
);
|
|
});
|
|
|
|
it('Different _Role objects cannot have the same name.', async done => {
|
|
await reconfigureServer();
|
|
const roleName = 'MyRole';
|
|
let aUser;
|
|
createTestUser()
|
|
.then(user => {
|
|
aUser = user;
|
|
return createRole(roleName, null, aUser);
|
|
})
|
|
.then(firstRole => {
|
|
expect(firstRole.getName()).toEqual(roleName);
|
|
return createRole(roleName, null, aUser);
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('_Role cannot have the same name as another role');
|
|
done();
|
|
},
|
|
error => {
|
|
expect(error.code).toEqual(137);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('Should properly resolve roles', done => {
|
|
const admin = new Parse.Role('Admin', new Parse.ACL());
|
|
const moderator = new Parse.Role('Moderator', new Parse.ACL());
|
|
const superModerator = new Parse.Role('SuperModerator', new Parse.ACL());
|
|
const contentManager = new Parse.Role('ContentManager', new Parse.ACL());
|
|
const superContentManager = new Parse.Role('SuperContentManager', new Parse.ACL());
|
|
Parse.Object.saveAll([admin, moderator, contentManager, superModerator, superContentManager], {
|
|
useMasterKey: true,
|
|
})
|
|
.then(() => {
|
|
contentManager.getRoles().add([moderator, superContentManager]);
|
|
moderator.getRoles().add([admin, superModerator]);
|
|
superContentManager.getRoles().add(superModerator);
|
|
return Parse.Object.saveAll(
|
|
[admin, moderator, contentManager, superModerator, superContentManager],
|
|
{ useMasterKey: true }
|
|
);
|
|
})
|
|
.then(() => {
|
|
const auth = new Auth({ config: Config.get('test'), isMaster: true });
|
|
// For each role, fetch their sibling, what they inherit
|
|
// return with result and roleId for later comparison
|
|
const promises = [admin, moderator, contentManager, superModerator].map(role => {
|
|
return auth._getAllRolesNamesForRoleIds([role.id]).then(result => {
|
|
return Promise.resolve({
|
|
id: role.id,
|
|
name: role.get('name'),
|
|
roleNames: result,
|
|
});
|
|
});
|
|
});
|
|
|
|
return Promise.all(promises);
|
|
})
|
|
.then(results => {
|
|
results.forEach(result => {
|
|
const id = result.id;
|
|
const roleNames = result.roleNames;
|
|
if (id == admin.id) {
|
|
expect(roleNames.length).toBe(2);
|
|
expect(roleNames.indexOf('Moderator')).not.toBe(-1);
|
|
expect(roleNames.indexOf('ContentManager')).not.toBe(-1);
|
|
} else if (id == moderator.id) {
|
|
expect(roleNames.length).toBe(1);
|
|
expect(roleNames.indexOf('ContentManager')).toBe(0);
|
|
} else if (id == contentManager.id) {
|
|
expect(roleNames.length).toBe(0);
|
|
} else if (id == superModerator.id) {
|
|
expect(roleNames.length).toBe(3);
|
|
expect(roleNames.indexOf('Moderator')).not.toBe(-1);
|
|
expect(roleNames.indexOf('ContentManager')).not.toBe(-1);
|
|
expect(roleNames.indexOf('SuperContentManager')).not.toBe(-1);
|
|
}
|
|
});
|
|
done();
|
|
})
|
|
.catch(() => {
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can create role and query empty users', done => {
|
|
const roleACL = new Parse.ACL();
|
|
roleACL.setPublicReadAccess(true);
|
|
const role = new Parse.Role('subscribers', roleACL);
|
|
role.save({}, { useMasterKey: true }).then(
|
|
() => {
|
|
const query = role.relation('users').query();
|
|
query.find({ useMasterKey: true }).then(
|
|
() => {
|
|
done();
|
|
},
|
|
() => {
|
|
fail('should not have errors');
|
|
done();
|
|
}
|
|
);
|
|
},
|
|
() => {
|
|
fail('should not have errored');
|
|
}
|
|
);
|
|
});
|
|
|
|
// Based on various scenarios described in issues #827 and #683,
|
|
it('should properly handle role permissions on objects', done => {
|
|
let user, user2, user3;
|
|
let role, role2, role3;
|
|
let obj, obj2;
|
|
|
|
const prACL = new Parse.ACL();
|
|
prACL.setPublicReadAccess(true);
|
|
let adminACL, superACL, customerACL;
|
|
|
|
createTestUser()
|
|
.then(x => {
|
|
user = x;
|
|
user2 = new Parse.User();
|
|
return user2.save({ username: 'user2', password: 'omgbbq' });
|
|
})
|
|
.then(() => {
|
|
user3 = new Parse.User();
|
|
return user3.save({ username: 'user3', password: 'omgbbq' });
|
|
})
|
|
.then(() => {
|
|
role = new Parse.Role('Admin', prACL);
|
|
role.getUsers().add(user);
|
|
return role.save({}, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
adminACL = new Parse.ACL();
|
|
adminACL.setRoleReadAccess('Admin', true);
|
|
adminACL.setRoleWriteAccess('Admin', true);
|
|
|
|
role2 = new Parse.Role('Super', prACL);
|
|
role2.getUsers().add(user2);
|
|
return role2.save({}, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
superACL = new Parse.ACL();
|
|
superACL.setRoleReadAccess('Super', true);
|
|
superACL.setRoleWriteAccess('Super', true);
|
|
|
|
role.getRoles().add(role2);
|
|
return role.save({}, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
role3 = new Parse.Role('Customer', prACL);
|
|
role3.getUsers().add(user3);
|
|
role3.getRoles().add(role);
|
|
return role3.save({}, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
customerACL = new Parse.ACL();
|
|
customerACL.setRoleReadAccess('Customer', true);
|
|
customerACL.setRoleWriteAccess('Customer', true);
|
|
|
|
const query = new Parse.Query('_Role');
|
|
return query.find({ useMasterKey: true });
|
|
})
|
|
.then(x => {
|
|
expect(x.length).toEqual(3);
|
|
|
|
obj = new Parse.Object('TestObjectRoles');
|
|
obj.set('ACL', customerACL);
|
|
return obj.save(null, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
// Above, the Admin role was added to the Customer role.
|
|
// An object secured by the Customer ACL should be able to be edited by the Admin user.
|
|
obj.set('changedByAdmin', true);
|
|
return obj.save(null, { sessionToken: user.getSessionToken() });
|
|
})
|
|
.then(
|
|
() => {
|
|
obj2 = new Parse.Object('TestObjectRoles');
|
|
obj2.set('ACL', adminACL);
|
|
return obj2.save(null, { useMasterKey: true });
|
|
},
|
|
() => {
|
|
fail('Admin user should have been able to save.');
|
|
done();
|
|
}
|
|
)
|
|
.then(() => {
|
|
// An object secured by the Admin ACL should not be able to be edited by a Customer role user.
|
|
obj2.set('changedByCustomer', true);
|
|
return obj2.save(null, { sessionToken: user3.getSessionToken() });
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Customer user should not have been able to save.');
|
|
done();
|
|
},
|
|
e => {
|
|
if (e) {
|
|
expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND);
|
|
} else {
|
|
fail('should return an error');
|
|
}
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('should add multiple users to a role and remove users', done => {
|
|
let user, user2, user3;
|
|
let role;
|
|
let obj;
|
|
|
|
const prACL = new Parse.ACL();
|
|
prACL.setPublicReadAccess(true);
|
|
prACL.setPublicWriteAccess(true);
|
|
|
|
createTestUser()
|
|
.then(x => {
|
|
user = x;
|
|
user2 = new Parse.User();
|
|
return user2.save({ username: 'user2', password: 'omgbbq' });
|
|
})
|
|
.then(() => {
|
|
user3 = new Parse.User();
|
|
return user3.save({ username: 'user3', password: 'omgbbq' });
|
|
})
|
|
.then(() => {
|
|
role = new Parse.Role('sharedRole', prACL);
|
|
const users = role.relation('users');
|
|
users.add(user);
|
|
users.add(user2);
|
|
users.add(user3);
|
|
return role.save({}, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
// query for saved role and get 3 users
|
|
const query = new Parse.Query('_Role');
|
|
query.equalTo('name', 'sharedRole');
|
|
return query.find({ useMasterKey: true });
|
|
})
|
|
.then(role => {
|
|
expect(role.length).toEqual(1);
|
|
const users = role[0].relation('users').query();
|
|
return users.find({ useMasterKey: true });
|
|
})
|
|
.then(users => {
|
|
expect(users.length).toEqual(3);
|
|
obj = new Parse.Object('TestObjectRoles');
|
|
obj.set('ACL', prACL);
|
|
return obj.save(null, { useMasterKey: true });
|
|
})
|
|
.then(() => {
|
|
// Above, the Admin role was added to the Customer role.
|
|
// An object secured by the Customer ACL should be able to be edited by the Admin user.
|
|
obj.set('changedByUsers', true);
|
|
return obj.save(null, { sessionToken: user.getSessionToken() });
|
|
})
|
|
.then(() => {
|
|
// query for saved role and get 3 users
|
|
const query = new Parse.Query('_Role');
|
|
query.equalTo('name', 'sharedRole');
|
|
return query.find({ useMasterKey: true });
|
|
})
|
|
.then(role => {
|
|
expect(role.length).toEqual(1);
|
|
const users = role[0].relation('users');
|
|
users.remove(user);
|
|
users.remove(user3);
|
|
return role[0].save({}, { useMasterKey: true });
|
|
})
|
|
.then(role => {
|
|
const users = role.relation('users').query();
|
|
return users.find({ useMasterKey: true });
|
|
})
|
|
.then(users => {
|
|
expect(users.length).toEqual(1);
|
|
expect(users[0].get('username')).toEqual('user2');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should be secure (#3835)', done => {
|
|
const acl = new Parse.ACL();
|
|
acl.getPublicReadAccess(true);
|
|
const role = new Parse.Role('admin', acl);
|
|
role
|
|
.save()
|
|
.then(() => {
|
|
const user = new Parse.User();
|
|
return user.signUp({ username: 'hello', password: 'world' });
|
|
})
|
|
.then(user => {
|
|
role.getUsers().add(user);
|
|
return role.save();
|
|
})
|
|
.then(done.fail, () => {
|
|
const query = role.getUsers().query();
|
|
return query.find({ useMasterKey: true });
|
|
})
|
|
.then(results => {
|
|
expect(results.length).toBe(0);
|
|
done();
|
|
})
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('should match when matching in users relation', done => {
|
|
const user = new Parse.User();
|
|
user.save({ username: 'admin', password: 'admin' }).then(user => {
|
|
const aCL = new Parse.ACL();
|
|
aCL.setPublicReadAccess(true);
|
|
aCL.setPublicWriteAccess(true);
|
|
const role = new Parse.Role('admin', aCL);
|
|
const users = role.relation('users');
|
|
users.add(user);
|
|
role.save({}, { useMasterKey: true }).then(() => {
|
|
const query = new Parse.Query(Parse.Role);
|
|
query.equalTo('name', 'admin');
|
|
query.equalTo('users', user);
|
|
query.find().then(function (roles) {
|
|
expect(roles.length).toEqual(1);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should not match any entry when not matching in users relation', done => {
|
|
const user = new Parse.User();
|
|
user.save({ username: 'admin', password: 'admin' }).then(user => {
|
|
const aCL = new Parse.ACL();
|
|
aCL.setPublicReadAccess(true);
|
|
aCL.setPublicWriteAccess(true);
|
|
const role = new Parse.Role('admin', aCL);
|
|
const users = role.relation('users');
|
|
users.add(user);
|
|
role.save({}, { useMasterKey: true }).then(() => {
|
|
const otherUser = new Parse.User();
|
|
otherUser.save({ username: 'otherUser', password: 'otherUser' }).then(otherUser => {
|
|
const query = new Parse.Query(Parse.Role);
|
|
query.equalTo('name', 'admin');
|
|
query.equalTo('users', otherUser);
|
|
query.find().then(function (roles) {
|
|
expect(roles.length).toEqual(0);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should not match any entry when searching for null in users relation', done => {
|
|
const user = new Parse.User();
|
|
user.save({ username: 'admin', password: 'admin' }).then(user => {
|
|
const aCL = new Parse.ACL();
|
|
aCL.setPublicReadAccess(true);
|
|
aCL.setPublicWriteAccess(true);
|
|
const role = new Parse.Role('admin', aCL);
|
|
const users = role.relation('users');
|
|
users.add(user);
|
|
role.save({}, { useMasterKey: true }).then(() => {
|
|
const query = new Parse.Query(Parse.Role);
|
|
query.equalTo('name', 'admin');
|
|
query.equalTo('users', null);
|
|
query.find().then(function (roles) {
|
|
expect(roles.length).toEqual(0);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|