Skip to content

Commit 3b627bc

Browse files
authored
Merge pull request #1935 from Real-Dev-Squad/develop
Dev to Main
2 parents a70f00f + 7d19126 commit 3b627bc

File tree

13 files changed

+268
-61
lines changed

13 files changed

+268
-61
lines changed

config/default.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ module.exports = {
6464

6565
userToken: {
6666
cookieName: `rds-session-${NODE_ENV}`,
67+
cookieV2Name: `rds-session-v2-${NODE_ENV}`,
6768
ttl: 30 * 24 * 60 * 60, // in seconds
6869
refreshTtl: 180 * 24 * 60 * 60, // in seconds
6970
publicKey: "<publicKey>",

config/production.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
discordMissedUpdatesRoleId: "1183553844811153458",
1111
userToken: {
1212
cookieName: "rds-session",
13+
cookieV2Name: "rds-session-v2",
1314
},
1415

1516
services: {

constants/authorities.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const AUTHORITIES = {
2+
SUPERUSER: "super_user",
3+
MEMBER: "member",
4+
USER: "user",
5+
};
6+
7+
module.exports = { AUTHORITIES };

controllers/auth.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,18 @@ const githubAuthCallback = (req, res, next) => {
4545
const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl"));
4646
let authRedirectionUrl = rdsUiUrl;
4747
let devMode = false;
48+
let isV2FlagPresent = false;
49+
4850
if ("state" in req.query) {
4951
try {
5052
const redirectUrl = new URL(req.query.state);
5153
if (redirectUrl.searchParams.get("isMobileApp") === "true") {
5254
isMobileApp = true;
5355
redirectUrl.searchParams.delete("isMobileApp");
5456
}
57+
58+
if (redirectUrl.searchParams.get("v2") === "true") isV2FlagPresent = true;
59+
5560
if (`.${redirectUrl.hostname}`.endsWith(`.${rdsUiUrl.hostname}`)) {
5661
// Matching *.realdevsquad.com
5762
authRedirectionUrl = redirectUrl;
@@ -78,18 +83,25 @@ const githubAuthCallback = (req, res, next) => {
7883
updated_at: Date.now(),
7984
};
8085

81-
const { userId, incompleteUserDetails } = await users.addOrUpdate(userData);
86+
const { userId, incompleteUserDetails, role } = await users.addOrUpdate(userData);
8287

8388
const token = authService.generateAuthToken({ userId });
8489

85-
// respond with a cookie
86-
res.cookie(config.get("userToken.cookieName"), token, {
90+
const cookieOptions = {
8791
domain: rdsUiUrl.hostname,
8892
expires: new Date(Date.now() + config.get("userToken.ttl") * 1000),
8993
httpOnly: true,
9094
secure: true,
9195
sameSite: "lax",
92-
});
96+
};
97+
// respond with a cookie
98+
res.cookie(config.get("userToken.cookieName"), token, cookieOptions);
99+
100+
/* redirectUrl woud be like https://realdevsquad.com?v2=true */
101+
if (isV2FlagPresent) {
102+
const tokenV2 = authService.generateAuthToken({ userId, role });
103+
res.cookie(config.get("userToken.cookieV2Name"), tokenV2, cookieOptions);
104+
}
93105

94106
if (!devMode) {
95107
// TODO: Revisit incompleteUserDetails redirect condition
@@ -112,12 +124,15 @@ const githubAuthCallback = (req, res, next) => {
112124
const signout = (req, res) => {
113125
const cookieName = config.get("userToken.cookieName");
114126
const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl"));
115-
res.clearCookie(cookieName, {
127+
const cookieOptions = {
116128
domain: rdsUiUrl.hostname,
117129
httpOnly: true,
118130
secure: true,
119131
sameSite: "lax",
120-
});
132+
};
133+
res.clearCookie(cookieName, cookieOptions);
134+
const cookieV2Name = config.get("userToken.cookieV2Name");
135+
res.clearCookie(cookieV2Name, cookieOptions);
121136
return res.json({
122137
message: "Signout successful",
123138
});

controllers/requests.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from "../constants/requests";
1313
import { createRequest, getRequests, updateRequest } from "../models/requests";
1414
import { addLog } from "../models/logs";
15+
import { getPaginatedLink } from "../utils/helper";
1516

1617
export const createRequestController = async (req: any, res: any) => {
1718
const requestBody = req.body;
@@ -113,9 +114,47 @@ export const getRequestsController = async (req: any, res: any) => {
113114
if (!requests) {
114115
return res.status(204).send();
115116
}
116-
return res.status(200).send({
117+
118+
const { allRequests, next, prev, page } = requests;
119+
if (allRequests.length === 0) {
120+
return res.status(204).send();
121+
}
122+
123+
if(page) {
124+
const pageLink = `/requests?page=${page}&dev=${query.dev}`;
125+
return res.status(200).json({
126+
message: REQUEST_FETCHED_SUCCESSFULLY,
127+
data: allRequests,
128+
page: pageLink,
129+
});
130+
}
131+
132+
let nextUrl = null;
133+
let prevUrl = null;
134+
if (next) {
135+
const nextLink = getPaginatedLink({
136+
endpoint: "/requests",
137+
query,
138+
cursorKey: "next",
139+
docId: next,
140+
});
141+
nextUrl = nextLink;
142+
}
143+
if (prev) {
144+
const prevLink = getPaginatedLink({
145+
endpoint: "/requests",
146+
query,
147+
cursorKey: "prev",
148+
docId: prev,
149+
});
150+
prevUrl = prevLink;
151+
}
152+
153+
return res.status(200).json({
117154
message: REQUEST_FETCHED_SUCCESSFULLY,
118-
data: requests,
155+
data: allRequests,
156+
next: nextUrl,
157+
prev: prevUrl,
119158
});
120159
} catch (err) {
121160
logger.error(ERROR_WHILE_FETCHING_REQUEST, err);

controllers/userStatus.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,18 @@ const getAllUserStatus = async (req, res) => {
7474
try {
7575
const { allUserStatus } = await userStatusModel.getAllUserStatus(req.query);
7676
const activeUsers = [];
77-
for (const status of allUserStatus) {
78-
// fetching users from users collection by userID in userStatus collection
79-
const result = await dataAccess.retrieveUsers({ id: status.userId });
80-
if (!result.user?.roles?.archived) {
81-
status.full_name = `${result.user.first_name} ${result.user.last_name}`;
82-
status.picture = result.user.picture;
83-
status.username = result.user.username;
84-
activeUsers.push(status);
85-
}
77+
if (allUserStatus) {
78+
const allUsersStatusFetchPromises = allUserStatus.map(async (status) => {
79+
// fetching users from users collection with the help of userID in userStatus collection
80+
const result = await dataAccess.retrieveUsers({ id: status.userId });
81+
if (!result.user?.roles?.archived) {
82+
status.full_name = `${result.user.first_name} ${result.user.last_name}`;
83+
status.picture = result.user.picture;
84+
status.username = result.user.username;
85+
activeUsers.push(status);
86+
}
87+
});
88+
await Promise.all(allUsersStatusFetchPromises);
8689
}
8790
return res.json({
8891
message: "All User Status found successfully.",

middlewares/validators/requests.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,17 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O
7474
.string()
7575
.valid(REQUEST_STATE.APPROVED, REQUEST_STATE.PENDING, REQUEST_STATE.REJECTED)
7676
.optional(),
77-
page: joi.number().integer().min(0),
77+
page: joi.number().integer().min(0).when("next", {
78+
is: joi.exist(),
79+
then: joi.forbidden().messages({
80+
"any.only": "next is not allowed when using page",
81+
}),
82+
}).when("prev", {
83+
is: joi.exist(),
84+
then: joi.forbidden().messages({
85+
"any.only": "page is not allowed when using prev",
86+
}),
87+
}).optional(),
7888
next: joi
7989
.string()
8090
.optional(),

models/requests.ts

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
REQUEST_ALREADY_PENDING,
1111
} from "../constants/requests";
1212
import * as admin from "firebase-admin";
13+
import { getUserId } from "../utils/users";
14+
const SIZE = 5;
1315

1416
export const createRequest = async (body: any) => {
1517
try {
@@ -76,41 +78,90 @@ export const updateRequest = async (id: string, body: any, lastModifiedBy: strin
7678
}
7779
};
7880

79-
export const getRequests = async (query: RequestQuery) => {
80-
const { id, type, requestedBy, state } = query;
81+
export const getRequests = async (query: any) => {
82+
let { id, type, requestedBy, state, prev, next, page, size = SIZE } = query;
83+
size = parseInt(size);
84+
page = parseInt(page);
8185
try {
82-
let requestQuery: admin.firestore.Query = requestModel;
86+
let requestQuery: any = requestModel;
87+
8388
if (id) {
84-
const requestsDoc = await requestModel.doc(id).get();
85-
const request = requestsDoc.data();
86-
if (!request) {
89+
const requestDoc = await requestModel.doc(id).get();
90+
if (!requestDoc.exists) {
8791
return null;
8892
}
8993
return {
90-
id,
91-
...request,
94+
allRequests: [
95+
{
96+
id: requestDoc.id,
97+
...requestDoc.data(),
98+
},
99+
],
92100
};
93101
}
102+
103+
if (requestedBy) {
104+
const requestedByUserId = await getUserId(requestedBy);
105+
requestQuery = requestQuery.where("requestedBy", "==", requestedByUserId);
106+
}
94107
if (type) {
95108
requestQuery = requestQuery.where("type", "==", type);
96109
}
97-
if (requestedBy) {
98-
requestQuery = requestQuery.where("requestedBy", "==", requestedBy);
99-
}
100110
if (state) {
101111
requestQuery = requestQuery.where("state", "==", state);
102112
}
103113

104-
const requestsDoc = await requestQuery.get();
105-
if (requestsDoc.empty) {
114+
requestQuery = requestQuery.orderBy("createdAt", "desc");
115+
116+
let requestQueryDoc = requestQuery;
117+
118+
if (prev) {
119+
requestQueryDoc = requestQueryDoc.limitToLast(size);
120+
} else {
121+
requestQueryDoc = requestQueryDoc.limit(size);
122+
}
123+
124+
if (page) {
125+
const startAfter = (page - 1) * size;
126+
requestQueryDoc = requestQueryDoc.offset(startAfter);
127+
} else if (next) {
128+
const doc = await requestModel.doc(next).get();
129+
requestQueryDoc = requestQueryDoc.startAt(doc);
130+
} else if (prev) {
131+
const doc = await requestModel.doc(prev).get();
132+
requestQueryDoc = requestQueryDoc.endAt(doc);
133+
}
134+
135+
const snapshot = await requestQueryDoc.get();
136+
let nextDoc: any, prevDoc: any;
137+
138+
if (!snapshot.empty) {
139+
const first = snapshot.docs[0];
140+
prevDoc = await requestQuery.endBefore(first).limitToLast(1).get();
141+
142+
const last = snapshot.docs[snapshot.docs.length - 1];
143+
nextDoc = await requestQuery.startAfter(last).limit(1).get();
144+
}
145+
146+
let allRequests = [];
147+
if (!snapshot.empty) {
148+
snapshot.forEach((doc: any) => {
149+
allRequests.push({
150+
id: doc.id,
151+
...doc.data(),
152+
});
153+
});
154+
}
155+
if (allRequests.length === 0) {
106156
return null;
107157
}
108-
return requestsDoc.docs.map((doc: any) => {
109-
return {
110-
id: doc.id,
111-
...doc.data(),
112-
};
113-
});
158+
159+
return {
160+
allRequests,
161+
prev: prevDoc.empty ? null : prevDoc.docs[0].id,
162+
next: nextDoc.empty ? null : nextDoc.docs[0].id,
163+
page: page ? page + 1 : null,
164+
};
114165
} catch (error) {
115166
logger.error(ERROR_WHILE_FETCHING_REQUEST, error);
116167
throw error;

models/users.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const photoVerificationModel = firestore.collection("photo-verification");
2222
const { ITEM_TAG, USER_STATE } = ALLOWED_FILTER_PARAMS;
2323
const admin = require("firebase-admin");
2424
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
25+
const { AUTHORITIES } = require("../constants/authorities");
2526

2627
/**
2728
* Adds or updates the user data
@@ -59,13 +60,15 @@ const addOrUpdate = async (userData, userId = null) => {
5960
if (!user || (user && user.empty)) {
6061
user = await userModel.where("github_id", "==", userData.github_id).limit(1).get();
6162
}
62-
if (user && !user.empty) {
63+
if (user && !user.empty && user.docs !== null) {
6364
await userModel.doc(user.docs[0].id).set(userData, { merge: true });
65+
const data = user.docs[0].data();
6466
return {
6567
isNewUser: false,
6668
userId: user.docs[0].id,
6769
incompleteUserDetails: user.docs[0].data().incompleteUserDetails,
6870
updated_at: Date.now(),
71+
role: Object.values(AUTHORITIES).find((role) => data.roles[role]) || AUTHORITIES.USER,
6972
};
7073
}
7174

@@ -78,7 +81,13 @@ const addOrUpdate = async (userData, userId = null) => {
7881
userData.roles = { archived: false, in_discord: false };
7982
userData.incompleteUserDetails = true;
8083
const userInfo = await userModel.add(userData);
81-
return { isNewUser: true, userId: userInfo.id, incompleteUserDetails: true, updated_at: Date.now() };
84+
return {
85+
isNewUser: true,
86+
role: AUTHORITIES.USER,
87+
userId: userInfo.id,
88+
incompleteUserDetails: true,
89+
updated_at: Date.now(),
90+
};
8291
} catch (err) {
8392
logger.error("Error in adding or updating user", err);
8493
throw err;

test/integration/auth.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,30 @@ describe("auth", function () {
227227
return done();
228228
});
229229
});
230+
231+
it("should send rds-session-v2 in res cookie", async function () {
232+
const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl"));
233+
234+
sinon.stub(passport, "authenticate").callsFake((strategy, options, callback) => {
235+
callback(null, "accessToken", githubUserInfo[0]);
236+
return (req, res, next) => {};
237+
});
238+
239+
const res = await chai
240+
.request(app)
241+
.get("/auth/github/callback")
242+
.query({ code: "codeReturnedByGithub", state: rdsUiUrl.href + "?v2=true" })
243+
.redirects(0);
244+
245+
expect(res).to.have.status(302);
246+
// rds-session-v2=token; Domain=realdevsquad.com; Path=/; Expires=Tue, 06 Oct 2020 11:23:07 GMT; HttpOnly; Secure
247+
expect(res.headers["set-cookie"]).to.have.length(2); /* res has 2 cookies rds-session & rds-session-v2 */
248+
expect(res.headers["set-cookie"][1])
249+
.to.be.a("string")
250+
.and.satisfy((msg) => msg.startsWith(config.get("userToken.cookieV2Name")));
251+
expect(res.headers["set-cookie"][1]).to.include("HttpOnly");
252+
expect(res.headers["set-cookie"][1]).to.include("Secure");
253+
expect(res.headers["set-cookie"][1]).to.include(`Domain=${rdsUiUrl.hostname}`);
254+
expect(res.headers["set-cookie"][1]).to.include("SameSite=Lax");
255+
});
230256
});

0 commit comments

Comments
 (0)