'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'); 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(); } ); }); 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 }); }; 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(); }); }); 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(); } ); } 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.', done => { 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(101); } 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(); }); }); }); }); });