Skip to content

Commit 6318b9a

Browse files
Restricts what roles can be added to user discord profile (#1743)
* fix: restricts random roleid and userid. * fix: test cases
1 parent 4823421 commit 6318b9a

File tree

5 files changed

+176
-59
lines changed

5 files changed

+176
-59
lines changed

controllers/discordactions.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const config = require("config");
44
const jwt = require("jsonwebtoken");
55
const discordRolesModel = require("../models/discordactions");
66
const discordServices = require("../services/discordService");
7-
const { fetchAllUsers } = require("../models/users");
7+
const { fetchAllUsers, fetchUser } = require("../models/users");
88
const discordDeveloperRoleId = config.get("discordDeveloperRoleId");
99
const discordMavenRoleId = config.get("discordMavenRoleId");
1010

@@ -23,9 +23,9 @@ const createGroupRole = async (req, res) => {
2323
try {
2424
const rolename = `group-${req.body.rolename}`;
2525

26-
const { wasSuccess } = await discordRolesModel.isGroupRoleExists(rolename);
26+
const { roleExists } = await discordRolesModel.isGroupRoleExists({ rolename });
2727

28-
if (!wasSuccess) {
28+
if (roleExists) {
2929
return res.status(400).json({
3030
message: "Role already exists!",
3131
});
@@ -109,6 +109,15 @@ const addGroupRoleToMember = async (req, res) => {
109109
...req.body,
110110
date: admin.firestore.Timestamp.fromDate(new Date()),
111111
};
112+
const roleExistsPromise = discordRolesModel.isGroupRoleExists({
113+
roleid: memberGroupRole.roleid,
114+
});
115+
const userDataPromise = fetchUser({ discordId: memberGroupRole.userid });
116+
const [{ roleExists }, userData] = await Promise.all([roleExistsPromise, userDataPromise]);
117+
118+
if (!roleExists || req.userData.id !== userData.user.id) {
119+
res.boom.forbidden("Permission denied. Cannot add the role.");
120+
}
112121

113122
const { roleData, wasSuccess } = await discordRolesModel.addGroupRoleToMember(memberGroupRole);
114123

@@ -146,6 +155,17 @@ const addGroupRoleToMember = async (req, res) => {
146155
const deleteRole = async (req, res) => {
147156
try {
148157
const { roleid, userid } = req.body;
158+
159+
const roleExistsPromise = discordRolesModel.isGroupRoleExists({
160+
roleid,
161+
});
162+
const userDataPromise = fetchUser({ discordId: userid });
163+
const [{ roleExists }, userData] = await Promise.all([roleExistsPromise, userDataPromise]);
164+
165+
if (!roleExists || req.userData.id !== userData.user.id) {
166+
res.boom.forbidden("Permission denied. Cannot delete the role.");
167+
}
168+
149169
const { wasSuccess } = await discordRolesModel.removeMemberGroup(roleid, userid);
150170
if (wasSuccess) {
151171
return res.status(200).json({ message: "Role deleted successfully" });

models/discordactions.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,32 @@ const updateGroupRole = async (roleData, docId) => {
124124
};
125125
/**
126126
*
127-
* @param roleData { Object }: Data of the new role
127+
* @param options { Object }: Data of the new role
128+
* @param options.rolename String : name of the role
129+
* @param options.roleId String : id of the role
128130
* @returns {Promise<discordRoleModel|Object>}
129131
*/
130132

131-
const isGroupRoleExists = async (rolename) => {
133+
const isGroupRoleExists = async (options = {}) => {
132134
try {
133-
const alreadyIsRole = await discordRoleModel.where("rolename", "==", rolename).limit(1).get();
134-
if (!alreadyIsRole.empty) {
135-
const oldRole = [];
136-
alreadyIsRole.forEach((role) => oldRole.push(role.data()));
137-
return { wasSuccess: false };
135+
const { rolename = null, roleid = null } = options;
136+
137+
let existingRoles;
138+
if (rolename && roleid) {
139+
existingRoles = await discordRoleModel
140+
.where("rolename", "==", rolename)
141+
.where("roleid", "==", roleid)
142+
.limit(1)
143+
.get();
144+
} else if (rolename) {
145+
existingRoles = await discordRoleModel.where("rolename", "==", rolename).limit(1).get();
146+
} else if (roleid) {
147+
existingRoles = await discordRoleModel.where("roleid", "==", roleid).limit(1).get();
148+
} else {
149+
throw Error("Either rolename or roleId is required");
138150
}
139-
return { wasSuccess: true };
151+
152+
return { roleExists: !existingRoles.empty };
140153
} catch (err) {
141154
logger.error("Error in getting all group-roles", err);
142155
throw err;

test/fixtures/discordactions/discordactions.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const groupData = [
2-
{ rolename: "Group 1", roleid: 1 },
3-
{ rolename: "Group 2", roleid: 2 },
4-
{ rolename: "Group 3", roleid: 3 },
2+
{ rolename: "Group 1", roleid: "1" },
3+
{ rolename: "Group 2", roleid: "2" },
4+
{ rolename: "Group 3", roleid: "3" },
55
];
66

77
const groupIdle7d = { rolename: "group-idle-7d+", roleid: 4, createdBy: "1dad23q23j131j" };

test/integration/discordactions.test.js

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ describe("Discord actions", function () {
5050
let jwt;
5151
beforeEach(async function () {
5252
fetchStub = sinon.stub(global, "fetch");
53-
userId = await addUser();
53+
userId = await addUser(userData[0]);
5454
superUserId = await addUser(superUser);
5555
superUserAuthToken = authService.generateAuthToken({ userId: superUserId });
5656
jwt = authService.generateAuthToken({ userId });
@@ -190,12 +190,70 @@ describe("Discord actions", function () {
190190
});
191191
});
192192

193+
describe("POST /discord-actions/roles", function () {
194+
let roleid;
195+
beforeEach(async function () {
196+
const discordRoleModelPromise = [discordRoleModel.add(groupData[0]), discordRoleModel.add(groupData[1])];
197+
roleid = groupData[0].roleid;
198+
await Promise.all(discordRoleModelPromise);
199+
});
200+
201+
afterEach(async function () {
202+
sinon.restore();
203+
await cleanDb();
204+
});
205+
206+
it("should allow role to be added", async function () {
207+
fetchStub.returns(
208+
Promise.resolve({
209+
status: 200,
210+
json: () => Promise.resolve({}),
211+
})
212+
);
213+
const res = await chai
214+
.request(app)
215+
.post("/discord-actions/roles")
216+
.set("cookie", `${cookieName}=${jwt}`)
217+
.send({ roleid, userid: userData[0].discordId });
218+
219+
expect(res).to.have.status(201);
220+
expect(res.body).to.be.an("object");
221+
expect(res.body.message).to.equal("Role added successfully!");
222+
});
223+
it("should not allow unknown role to be added to user", async function () {
224+
const res = await chai
225+
.request(app)
226+
.post("/discord-actions/roles")
227+
.set("cookie", `${cookieName}=${jwt}`)
228+
.send({ roleid: "randomId", userid: "abc" });
229+
230+
expect(res).to.have.status(403);
231+
expect(res.body).to.be.an("object");
232+
expect(res.body.message).to.equal("Permission denied. Cannot add the role.");
233+
});
234+
it("should not allow role to be added when userid does not belong to authenticated user", async function () {
235+
const res = await chai
236+
.request(app)
237+
.post("/discord-actions/roles")
238+
.set("cookie", `${cookieName}=${jwt}`)
239+
.send({ roleid, userid: "asdf" });
240+
241+
expect(res).to.have.status(403);
242+
expect(res.body).to.be.an("object");
243+
expect(res.body.message).to.equal("Permission denied. Cannot add the role.");
244+
});
245+
});
193246
describe("DELETE /discord-actions/roles", function () {
247+
let roleid;
248+
194249
beforeEach(async function () {
195250
const addRolePromises = memberGroupData.map(async (data) => {
196251
await memberRoleModel.add(data);
197252
});
198-
253+
const discordRoleModelPromise = [discordRoleModel.add(groupData[0]), discordRoleModel.add(groupData[1])];
254+
await Promise.all(discordRoleModelPromise);
255+
roleid = groupData[0].roleid;
256+
await memberRoleModel.add({ roleid, userid: userData[0].discordId });
199257
await Promise.all(addRolePromises);
200258
});
201259

@@ -215,7 +273,7 @@ describe("Discord actions", function () {
215273
.request(app)
216274
.delete("/discord-actions/roles")
217275
.set("cookie", `${cookieName}=${jwt}`)
218-
.send(memberGroupData[0])
276+
.send({ roleid, userid: userData[0].discordId })
219277
.end((err, res) => {
220278
if (err) {
221279
return done(err);
@@ -229,16 +287,34 @@ describe("Discord actions", function () {
229287
});
230288
});
231289

290+
it("should not allow unknown role to be deleted from user", async function () {
291+
const res = await chai
292+
.request(app)
293+
.delete("/discord-actions/roles")
294+
.set("cookie", `${cookieName}=${jwt}`)
295+
.send({ roleid: "randomId", userid: "abc" });
296+
297+
expect(res).to.have.status(403);
298+
expect(res.body).to.be.an("object");
299+
expect(res.body.message).to.equal("Permission denied. Cannot delete the role.");
300+
});
301+
it("should not allow role to be deleted when userid does not belong to authenticated user", async function () {
302+
const res = await chai
303+
.request(app)
304+
.delete("/discord-actions/roles")
305+
.set("cookie", `${cookieName}=${jwt}`)
306+
.send({ roleid, userid: "asdf" });
307+
308+
expect(res).to.have.status(403);
309+
expect(res.body).to.be.an("object");
310+
expect(res.body.message).to.equal("Permission denied. Cannot delete the role.");
311+
});
232312
it("should handle internal server error", function (done) {
233-
const mockdata = {
234-
roleid: "mockroleid",
235-
userid: "mockUserId",
236-
};
237313
chai
238314
.request(app)
239315
.delete("/discord-actions/roles")
240316
.set("cookie", `${cookieName}=${jwt}`)
241-
.send(mockdata)
317+
.send({ roleid, userid: userData[0].discordId })
242318
.end((err, res) => {
243319
if (err) {
244320
return done(err);

test/unit/models/discordactions.test.js

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -87,51 +87,59 @@ describe("discordactions", function () {
8787
});
8888

8989
describe("isGroupRoleExists", function () {
90-
let getStub;
91-
92-
beforeEach(function () {
93-
getStub = sinon.stub(discordRoleModel, "where").returns({
94-
limit: sinon.stub().resolves({
95-
empty: true,
96-
forEach: sinon.stub(),
97-
}),
98-
});
90+
let roleid;
91+
let rolename;
92+
beforeEach(async function () {
93+
const discordRoleModelPromise = [discordRoleModel.add(groupData[0]), discordRoleModel.add(groupData[1])];
94+
roleid = groupData[0].roleid;
95+
rolename = groupData[0].rolename;
96+
await Promise.all(discordRoleModelPromise);
9997
});
10098

101-
afterEach(function () {
102-
getStub.restore();
99+
afterEach(async function () {
100+
sinon.restore();
101+
await cleanDb();
103102
});
104103

105-
it("should return true if role doesn't exist in the database", async function () {
106-
const result = await isGroupRoleExists("Test Role");
107-
expect(result.wasSuccess).to.equal(true);
108-
expect(getStub.calledOnceWith("rolename", "==", "Test Role")).to.equal(false);
104+
it("should return false if rolename doesn't exist in the database", async function () {
105+
const rolename = "Test Role";
106+
const result = await isGroupRoleExists({ rolename });
107+
expect(result.roleExists).to.equal(false);
109108
});
110-
111-
it("should return false if role already exists in the database", async function () {
112-
const existingRole = { rolename: "Test Role" };
113-
const callbackFunction = (role) => {
114-
const roleData = role.data();
115-
existingRole.push(roleData);
116-
};
117-
118-
getStub.returns({
119-
limit: sinon.stub().resolves({
120-
empty: false,
121-
forEach: callbackFunction,
122-
}),
109+
it("should return false if roleid doesn't exist in the database", async function () {
110+
const roleid = "Test Role";
111+
const result = await isGroupRoleExists({ roleid });
112+
expect(result.roleExists).to.equal(false);
113+
});
114+
it("should return true if roleid exist in the database", async function () {
115+
const result = await isGroupRoleExists({ roleid });
116+
expect(result.roleExists).to.equal(true);
117+
});
118+
it("should return true if rolename exist in the database", async function () {
119+
const result = await isGroupRoleExists({ rolename });
120+
expect(result.roleExists).to.equal(true);
121+
});
122+
it("should return true if rolename and roleid exists in the database", async function () {
123+
const result = await isGroupRoleExists({ rolename, roleid });
124+
expect(result.roleExists).to.equal(true);
125+
});
126+
it("should return false if either rolename and roleid does not exist in the database", async function () {
127+
const rolenameResult = await isGroupRoleExists({ rolename: "adf", roleid });
128+
expect(rolenameResult.roleExists).to.equal(false);
129+
const roleIdResult = await isGroupRoleExists({ rolename, roleid: "abc44" });
130+
expect(roleIdResult.roleExists).to.equal(false);
131+
});
132+
it("should throw an error if rolename and roleid are not passed", async function () {
133+
return isGroupRoleExists({}).catch((err) => {
134+
expect(err).to.be.an.instanceOf(Error);
135+
expect(err.message).to.equal("Either rolename or roleId is required");
123136
});
124-
125-
const errorCallback = sinon.stub();
126-
const result = await isGroupRoleExists("Test Role");
127-
expect(result.wasSuccess).to.equal(true);
128-
expect(getStub.calledOnceWith("rolename", "==", "Test Role")).to.equal(false);
129-
expect(errorCallback.calledOnce).to.equal(false);
130137
});
131-
132138
it("should throw an error if getting group-roles fails", async function () {
133-
getStub.rejects(new Error("Database error"));
134-
return isGroupRoleExists("Test Role").catch((err) => {
139+
sinon.stub(discordRoleModel, "where").rejects(new Error("Database error"));
140+
const rolename = "Test Role";
141+
142+
return isGroupRoleExists({ rolename }).catch((err) => {
135143
expect(err).to.be.an.instanceOf(Error);
136144
expect(err.message).to.equal("Database error");
137145
});

0 commit comments

Comments
 (0)