Skip to content

Commit 18906f1

Browse files
blachaflovilmart
authored andcommitted
Auth._loadRoles should not query the same role twice.
1 parent 442c723 commit 18906f1

File tree

2 files changed

+96
-24
lines changed

2 files changed

+96
-24
lines changed

spec/ParseRole.spec.js

+85-18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
// Roles are not accessible without the master key, so they are not intended
44
// for use by clients. We can manually test them using the master key.
5+
var RestQuery = require("../src/RestQuery");
56
var Auth = require("../src/Auth").Auth;
67
var Config = require("../src/Config");
78

@@ -60,21 +61,87 @@ describe('Parse Role testing', () => {
6061

6162
});
6263

63-
it("should recursively load roles", (done) => {
64+
var createRole = function(name, sibling, user) {
65+
var role = new Parse.Role(name, new Parse.ACL());
66+
if (user) {
67+
var users = role.relation('users');
68+
users.add(user);
69+
}
70+
if (sibling) {
71+
role.relation('roles').add(sibling);
72+
}
73+
return role.save({}, { useMasterKey: true });
74+
};
6475

65-
var rolesNames = ["FooRole", "BarRole", "BazRole"];
76+
it("should not recursively load the same role multiple times", (done) => {
77+
var rootRole = "RootRole";
78+
var roleNames = ["FooRole", "BarRole", "BazRole"];
79+
var allRoles = [rootRole].concat(roleNames);
6680

67-
var createRole = function(name, sibling, user) {
68-
var role = new Parse.Role(name, new Parse.ACL());
69-
if (user) {
70-
var users = role.relation('users');
71-
users.add(user);
72-
}
73-
if (sibling) {
74-
role.relation('roles').add(sibling);
75-
}
76-
return role.save({}, { useMasterKey: true });
77-
}
81+
var roleObjs = {};
82+
var createAllRoles = function(user) {
83+
var promises = allRoles.map(function(roleName) {
84+
return createRole(roleName, null, user)
85+
.then(function(roleObj) {
86+
roleObjs[roleName] = roleObj;
87+
return roleObj;
88+
});
89+
});
90+
return Promise.all(promises);
91+
};
92+
93+
var restExecute = spyOn(RestQuery.prototype, "execute").and.callThrough();
94+
95+
var user,
96+
auth,
97+
getAllRolesSpy;
98+
createTestUser().then( (newUser) => {
99+
user = newUser;
100+
return createAllRoles(user);
101+
}).then ( (roles) => {
102+
var rootRoleObj = roleObjs[rootRole];
103+
roles.forEach(function(role, i) {
104+
// Add all roles to the RootRole
105+
if (role.id !== rootRoleObj.id) {
106+
role.relation("roles").add(rootRoleObj);
107+
}
108+
// Add all "roleNames" roles to the previous role
109+
if (i > 0) {
110+
role.relation("roles").add(roles[i - 1]);
111+
}
112+
});
113+
114+
return Parse.Object.saveAll(roles, { useMasterKey: true });
115+
}).then( () => {
116+
auth = new Auth({config: new Config("test"), isMaster: true, user: user});
117+
getAllRolesSpy = spyOn(auth, "_getAllRoleNamesForId").and.callThrough();
118+
119+
return auth._loadRoles();
120+
}).then ( (roles) => {
121+
expect(roles.length).toEqual(4);
122+
123+
allRoles.forEach(function(name) {
124+
expect(roles.indexOf("role:"+name)).not.toBe(-1);
125+
});
126+
127+
// 1 Query for the initial setup
128+
// 4 Queries for all the specific roles
129+
// 1 Query for the final $in
130+
expect(restExecute.calls.count()).toEqual(6);
131+
132+
// 4 One query for each of the roles
133+
// 3 Queries for the "RootRole"
134+
expect(getAllRolesSpy.calls.count()).toEqual(7);
135+
done()
136+
}).catch( () => {
137+
fail("should succeed");
138+
done();
139+
});
140+
141+
});
142+
143+
it("should recursively load roles", (done) => {
144+
var rolesNames = ["FooRole", "BarRole", "BazRole"];
78145
var roleIds = {};
79146
createTestUser().then( (user) => {
80147
// Put the user on the 1st role
@@ -97,7 +164,7 @@ describe('Parse Role testing', () => {
97164
expect(roles.length).toEqual(3);
98165
rolesNames.forEach( (name) => {
99166
expect(roles.indexOf('role:'+name)).not.toBe(-1);
100-
})
167+
});
101168
done();
102169
}, function(err){
103170
fail("should succeed")
@@ -122,7 +189,7 @@ describe('Parse Role testing', () => {
122189
});
123190
});
124191
});
125-
192+
126193
it("Should properly resolve roles", (done) => {
127194
let admin = new Parse.Role("Admin", new Parse.ACL());
128195
let moderator = new Parse.Role("Moderator", new Parse.ACL());
@@ -134,7 +201,7 @@ describe('Parse Role testing', () => {
134201
moderator.getRoles().add([admin, superModerator]);
135202
superContentManager.getRoles().add(superModerator);
136203
return Parse.Object.saveAll([admin, moderator, contentManager, superModerator, superContentManager], {useMasterKey: true});
137-
}).then(() => {
204+
}).then(() => {
138205
var auth = new Auth({ config: new Config("test"), isMaster: true });
139206
// For each role, fetch their sibling, what they inherit
140207
// return with result and roleId for later comparison
@@ -147,7 +214,7 @@ describe('Parse Role testing', () => {
147214
});
148215
})
149216
});
150-
217+
151218
return Parse.Promise.when(promises);
152219
}).then((results) => {
153220
results.forEach((result) => {
@@ -174,7 +241,7 @@ describe('Parse Role testing', () => {
174241
console.error(err);
175242
done();
176243
})
177-
244+
178245
});
179246

180247
it('can create role and query empty users', (done)=> {

src/Auth.js

+11-6
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ var getAuthForSessionToken = function({ config, sessionToken, installationId } =
6363
return nobody(config);
6464
}
6565

66-
var now = new Date(),
66+
var now = new Date(),
6767
expiresAt = new Date(results[0].expiresAt.iso);
6868
if(expiresAt < now) {
6969
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN,
@@ -117,8 +117,9 @@ Auth.prototype._loadRoles = function() {
117117

118118
var roleIDs = results.map(r => r.objectId);
119119
var promises = [Promise.resolve(roleIDs)];
120+
var queriedRoles = {};
120121
for (var role of roleIDs) {
121-
promises.push(this._getAllRoleNamesForId(role));
122+
promises.push(this._getAllRoleNamesForId(role, queriedRoles));
122123
}
123124
return Promise.all(promises).then((results) => {
124125
var allIDs = [];
@@ -146,8 +147,12 @@ Auth.prototype._loadRoles = function() {
146147
};
147148

148149
// Given a role object id, get any other roles it is part of
149-
Auth.prototype._getAllRoleNamesForId = function(roleID) {
150-
150+
Auth.prototype._getAllRoleNamesForId = function(roleID, queriedRoles = {}) {
151+
// Don't need to requery this role as it is already being queried for.
152+
if (queriedRoles[roleID] != null) {
153+
return Promise.resolve([]);
154+
}
155+
queriedRoles[roleID] = true;
151156
// As per documentation, a Role inherits AnotherRole
152157
// if this Role is in the roles pointer of this AnotherRole
153158
// Let's find all the roles where this role is in a roles relation
@@ -167,13 +172,13 @@ Auth.prototype._getAllRoleNamesForId = function(roleID) {
167172
return Promise.resolve([]);
168173
}
169174
var roleIDs = results.map(r => r.objectId);
170-
175+
171176
// we found a list of roles where the roleID
172177
// is referenced in the roles relation,
173178
// Get the roles where those found roles are also
174179
// referenced the same way
175180
var parentRolesPromises = roleIDs.map( (roleId) => {
176-
return this._getAllRoleNamesForId(roleId);
181+
return this._getAllRoleNamesForId(roleId, queriedRoles);
177182
});
178183
parentRolesPromises.push(Promise.resolve(roleIDs));
179184
return Promise.all(parentRolesPromises);

0 commit comments

Comments
 (0)