From 18bcaf0b63062fc0a804933d25d404e1132bb115 Mon Sep 17 00:00:00 2001 From: Aydan Pirani Date: Tue, 26 Sep 2023 21:06:38 -0500 Subject: [PATCH] Fixed events key being wrong size (#45) * Fixed events key being wrong size * Removed arrow functions --- src/constants.ts | 3 +- src/services/auth/auth-lib.ts | 45 ++++---- src/services/auth/auth-router.ts | 111 +++++++++---------- src/services/event/event-formats.ts | 1 - src/services/event/event-router.ts | 2 +- src/services/newsletter/newsletter-lib.ts | 36 ------ src/services/newsletter/newsletter-router.ts | 33 +++++- src/services/profile/profile-router.ts | 9 +- src/services/user/user-router.ts | 37 +++---- 9 files changed, 127 insertions(+), 150 deletions(-) delete mode 100644 src/services/newsletter/newsletter-lib.ts diff --git a/src/constants.ts b/src/constants.ts index 66c10f3c..8e6b6ee8 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -49,7 +49,8 @@ abstract class Constants { // Constants for general usage static readonly ZERO: number = 0; - static readonly EVENT_ID_LENGTH: number = 16; + static readonly EVENT_ID_BYTES: number = 16; + static readonly EVENT_ID_LENGTH: number = 32; static readonly MILLISECONDS_PER_SECOND:number = 1000; // Constants for database names diff --git a/src/services/auth/auth-lib.ts b/src/services/auth/auth-lib.ts index cbaf7fef..eb9c9e64 100644 --- a/src/services/auth/auth-lib.ts +++ b/src/services/auth/auth-lib.ts @@ -64,21 +64,16 @@ export async function getJwtPayloadFromProfile(provider: string, data: ProfileDa }; // Get roles, and assign those to payload.roles if they exist - await getRoles(userId).then((userRoles: Role[]) => { - if (userRoles.length) { - payload.roles = userRoles; + try { + let roles: Role[] = await getRoles(userId); + + // If roles don't exist already - initialize them for the user, and return the new set of roles + if (!roles.length) { + roles = await initializeRoles(userId, provider.toUpperCase() as Provider, email); } - }).catch((error: string) => { + payload.roles = roles; + } catch (error) { console.error(error); - }); - - // No roles found for user -> initialize them - if (!payload.roles.length) { - await initializeRoles(userId, provider.toUpperCase() as Provider, email).then((newRoles: Role[]) => { - payload.roles = newRoles; - }).catch((error: string) => { - console.error(error); - }); } return payload; @@ -91,23 +86,23 @@ export async function getJwtPayloadFromProfile(provider: string, data: ProfileDa * @returns Promise, containing either JWT payload or reason for failure */ export async function getJwtPayloadFromDB(targetUser: string): Promise { + let authInfo: RolesSchema | undefined; let userInfo: UserSchema | undefined; // Fill in auth info, used for provider and roles - await getAuthInfo(targetUser).then((info: RolesSchema) => { - authInfo = info; - }).catch((error: string) => { - console.error(error); - }); - // Fill in user info, used for email - await getUser(targetUser).then((info: UserSchema) => { - userInfo = info; - }).catch((error: string) => { - console.error(error); - }); + + try { + authInfo = await getAuthInfo(targetUser); + userInfo = await getUser(targetUser); + + + } catch (error) { + console.error(error); + } + // If either one does not exist, the info doesn't exist in the database. Throw error if (!authInfo || !userInfo) { return Promise.reject("UserNotFound"); @@ -120,11 +115,9 @@ export async function getJwtPayloadFromDB(targetUser: string): Promise { if (!req.isAuthenticated()) { - res.status(Constants.UNAUTHORIZED_REQUEST).send({ error: "FailedAuth" }); + return res.status(Constants.UNAUTHORIZED_REQUEST).send({ error: "FailedAuth" }); } const device: string = (res.locals.device ?? Constants.DEFAULT_DEVICE) as string; const user: GithubProfile | GoogleProfile = req.user as GithubProfile | GoogleProfile; const data: ProfileData = user._json as ProfileData; + const redirect: string = (Constants.REDIRECT_MAPPINGS.get(device) ?? Constants.DEFAULT_REDIRECT); data.id = data.id ?? user.id; - let payload: JwtPayload | undefined = undefined; - // Load in the payload with the actual values stored in the database - await getJwtPayloadFromProfile(user.provider, data).then((parsedPayload: JwtPayload) => { - payload = parsedPayload; - }).catch((error: Error) => { + try { + // Load in the payload with the actual values stored in the database + const payload: JwtPayload = await getJwtPayloadFromProfile(user.provider, data); + + // Generate the token, and return it + const token: string = generateJwtToken(payload); + const url: string = `${redirect}?token=${token}`; + return res.redirect(url); + } catch (error) { console.error(error); - res.status(Constants.BAD_REQUEST).send({ error: "InvalidData" }); - }); + return res.status(Constants.BAD_REQUEST).send({ error: "InvalidData" }); + } - // Generate the token, and return it - const token: string = generateJwtToken(payload); - const redirect: string = (Constants.REDIRECT_MAPPINGS.get(device) ?? Constants.DEFAULT_REDIRECT); - const url: string = `${redirect}?token=${token}`; - res.redirect(url); }); @@ -168,26 +166,24 @@ authRouter.get("/roles/:USERID", strongJwtVerification, async (req: Request, res // Check if we have a user to get roles for - if not, get roles for current user if (!targetUser) { - res.redirect("/auth/roles/"); - return; + return res.redirect("/auth/roles/"); } const payload: JwtPayload = res.locals.payload as JwtPayload; // Cases: Target user already logged in, auth user is admin if (payload.id == targetUser) { - res.status(Constants.SUCCESS).send({ id: payload.id, roles: payload.roles }); + return res.status(Constants.SUCCESS).send({ id: payload.id, roles: payload.roles }); } else if (hasElevatedPerms(payload)) { - let roles: Role[] = []; - await getRoles(targetUser).then((targetRoles: Role[]) => { - roles = targetRoles; - res.status(Constants.SUCCESS).send({ id: targetUser, roles: roles }); - }).catch((error: Error) => { + try { + const roles: Role[] = await getRoles(targetUser); + return res.status(Constants.SUCCESS).send({ id: targetUser, roles: roles }); + } catch (error) { console.error(error); - res.status(Constants.BAD_REQUEST).send({ error: "UserNotFound" }); - }); + return res.status(Constants.BAD_REQUEST).send({ error: "UserNotFound" }); + } } else { - res.status(Constants.FORBIDDEN).send("Forbidden"); + return res.status(Constants.FORBIDDEN).send("Forbidden"); } }); @@ -218,7 +214,7 @@ authRouter.put("/roles/:OPERATION/", strongJwtVerification, async (req: Request, // Not authenticated with modify roles perms if (!hasElevatedPerms(payload)) { - res.status(Constants.FORBIDDEN).send({ error: "Forbidden" }); + return res.status(Constants.FORBIDDEN).send({ error: "Forbidden" }); } // Parse to get operation type @@ -226,31 +222,31 @@ authRouter.put("/roles/:OPERATION/", strongJwtVerification, async (req: Request, // No operation - fail out if (!op) { - res.status(Constants.BAD_REQUEST).send({ error: "InvalidOperation" }); - return; + return res.status(Constants.BAD_REQUEST).send({ error: "InvalidOperation" }); } // Check if role to add/remove actually exists const data: ModifyRoleRequest = req.body as ModifyRoleRequest; const role: Role | undefined = Role[data.role.toUpperCase() as keyof typeof Role]; if (!role) { - res.status(Constants.BAD_REQUEST).send({ error: "InvalidRole" }); - return; + return res.status(Constants.BAD_REQUEST).send({ error: "InvalidRole" }); } // Try to update roles, if possible - await updateRoles(data.id, role, op).catch((error: string) => { + try { + await updateRoles(data.id, role, op); + } catch (error) { console.error(error); - res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" }); - }); + return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" }); + } - // Get new roles for the current user, and return them - await getRoles(data.id).then((roles: Role[]) => { - res.status(Constants.SUCCESS).send({ id: data.id, roles: roles }); - }).catch((error: string) => { + try { + const roles: Role[] = await getRoles(data.id); + return res.status(Constants.SUCCESS).send({ id: data.id, roles: roles }); + } catch (error) { console.error(error); - res.status(Constants.BAD_REQUEST).send({ error: "UserNotFound" }); - }); + return res.status(Constants.BAD_REQUEST).send({ error: "UserNotFound" }); + } }); @@ -276,8 +272,7 @@ authRouter.get("/list/roles/", strongJwtVerification, (_: Request, res: Response // Check if current user should be able to access all roles if (!hasElevatedPerms(payload)) { - res.status(Constants.FORBIDDEN).send({ error: "Forbidden" }); - return; + return res.status(Constants.FORBIDDEN).send({ error: "Forbidden" }); } // Filter enum to get all possible string keys @@ -285,7 +280,7 @@ authRouter.get("/list/roles/", strongJwtVerification, (_: Request, res: Response return isNaN(Number(item)); }); - res.status(Constants.SUCCESS).send({ roles: roles }); + return res.status(Constants.SUCCESS).send({ roles: roles }); }); @@ -310,10 +305,10 @@ authRouter.get("/roles/", strongJwtVerification, async (_: Request, res: Respons const targetUser: string = payload.id; await getRoles(targetUser).then((roles: Role[]) => { - res.status(Constants.SUCCESS).send({ id: targetUser, roles: roles }); + return res.status(Constants.SUCCESS).send({ id: targetUser, roles: roles }); }).catch((error: Error) => { console.error(error); - res.status(Constants.BAD_REQUEST).send({ error: "UserNotFound" }); + return res.status(Constants.BAD_REQUEST).send({ error: "UserNotFound" }); }); }); @@ -339,15 +334,17 @@ authRouter.get("/token/refresh", strongJwtVerification, async (_: Request, res: email: oldPayload.email, }; - // Generate a new payload for the token - let newPayload: JwtPayload | undefined; - await getJwtPayloadFromProfile(oldPayload.provider, data).then((payload: JwtPayload) => { - newPayload = payload; - }); + try { + // Generate a new payload for the token + const newPayload: JwtPayload = await getJwtPayloadFromProfile(oldPayload.provider, data); - // Create and return a new token with the payload - const newToken: string = generateJwtToken(newPayload); - res.status(Constants.SUCCESS).send({ token: newToken }); + // Create and return a new token with the payload + const newToken: string = generateJwtToken(newPayload); + return res.status(Constants.SUCCESS).send({ token: newToken }); + } catch (error) { + console.error(error); + return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" }); + } }); diff --git a/src/services/event/event-formats.ts b/src/services/event/event-formats.ts index d26b856f..b45021d0 100644 --- a/src/services/event/event-formats.ts +++ b/src/services/event/event-formats.ts @@ -58,7 +58,6 @@ export function isEventFormat(obj: EventFormat): boolean { } } - if ( typeof obj.sponsor !== "string" || typeof obj.eventType !== "string" || diff --git a/src/services/event/event-router.ts b/src/services/event/event-router.ts index 9c884a7e..001d77b3 100644 --- a/src/services/event/event-router.ts +++ b/src/services/event/event-router.ts @@ -330,7 +330,7 @@ eventsRouter.post("/", strongJwtVerification, async (req: Request, res: Response // Verify that the input format is valid to create a new event or update it const eventFormat: EventFormat = req.body as EventFormat; - eventFormat.id = crypto.randomBytes(Constants.EVENT_ID_LENGTH).toString("hex"); + eventFormat.id = crypto.randomBytes(Constants.EVENT_ID_BYTES).toString("hex"); if (!isEventFormat(eventFormat)) { return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" }); } diff --git a/src/services/newsletter/newsletter-lib.ts b/src/services/newsletter/newsletter-lib.ts deleted file mode 100644 index d1420a1c..00000000 --- a/src/services/newsletter/newsletter-lib.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Request, Response } from "express"; -import Constants from "../../constants.js"; - -import { Collection } from "mongodb"; -import databaseClient from "../../database.js"; -import { SubscribeRequest } from "./newsletter-formats.js"; -import { NewsletterDB } from "./newsletter-schemas.js"; - - -/** - * Subscribe a user to a newsletter, given a body - * @param request HTTP request received from the user - * @param response Response to send to the user - * @returns Promise, void if successful. Else, promise is rejected with reason for error - */ -export async function subscribeToNewsletter(request: Request, response: Response): Promise { - const requestBody: SubscribeRequest = request.body as SubscribeRequest; - const listName: string | undefined = requestBody.listName; - const emailAddress: string | undefined = requestBody.emailAddress; - - // Verify that both parameters do exist - if (!listName || !emailAddress) { - response.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" }); - } - - // Upsert to update the list - update document if possible, else add the document - try { - const newsletterCollection: Collection = databaseClient.db(Constants.NEWSLETTER_DB).collection(NewsletterDB.NEWSLETTERS); - await newsletterCollection.updateOne({ listName: listName }, { "$addToSet": { "subscribers": emailAddress } }, { upsert: true }); - } catch (error) { - response.status(Constants.BAD_REQUEST).send({ error: "ListNotFound" }); - } - - response.status(Constants.SUCCESS).send({ status: "Successful" }); - return Promise.resolve(); -} diff --git a/src/services/newsletter/newsletter-router.ts b/src/services/newsletter/newsletter-router.ts index 2056fa1f..3529b919 100644 --- a/src/services/newsletter/newsletter-router.ts +++ b/src/services/newsletter/newsletter-router.ts @@ -1,8 +1,15 @@ -import { Router } from "express"; -import { subscribeToNewsletter } from "./newsletter-lib.js"; +import { Request, Response, Router } from "express"; import { regexPasses } from "../../utils.js"; import cors, { CorsOptions } from "cors"; +import Constants from "../../constants.js"; + +import { Collection } from "mongodb"; +import databaseClient from "../../database.js"; +import { SubscribeRequest } from "./newsletter-formats.js"; +import { NewsletterDB } from "./newsletter-schemas.js"; + + const newsletterRouter: Router = Router(); @@ -51,7 +58,27 @@ newsletterRouter.use(cors(corsOptions)); * HTTP/1.1 400 Bad Request * {"error": "InvalidParams"} */ -newsletterRouter.post("/subscribe/", subscribeToNewsletter); +newsletterRouter.post("/subscribe/", async (request: Request, res: Response) => { + const requestBody: SubscribeRequest = request.body as SubscribeRequest; + const listName: string | undefined = requestBody.listName; + const emailAddress: string | undefined = requestBody.emailAddress; + + // Verify that both parameters do exist + if (!listName || !emailAddress) { + return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" }); + } + + // Upsert to update the list - update document if possible, else add the document + const newsletterCollection: Collection = databaseClient.db(Constants.NEWSLETTER_DB).collection(NewsletterDB.NEWSLETTERS); + try { + await newsletterCollection.updateOne({ listName: listName }, { "$addToSet": { "subscribers": emailAddress } }, { upsert: true }); + return res.sendStatus(Constants.SUCCESS); + } catch (error) { + res.status(Constants.BAD_REQUEST).send({ error: "ListNotFound" }); + } + + return res.status(Constants.SUCCESS).send({ status: "Successful" }); +}); export default newsletterRouter; diff --git a/src/services/profile/profile-router.ts b/src/services/profile/profile-router.ts index 7c6a71b1..de7e3c9b 100644 --- a/src/services/profile/profile-router.ts +++ b/src/services/profile/profile-router.ts @@ -57,19 +57,16 @@ profileRouter.get("/leaderboard/", async (req: Request, res: Response) => { // If invalid limit - return InvalidInput if (!isValidLimit(limit)) { - res.status(Constants.BAD_REQUEST).send({ error: "InvalidInput" }); - return; + return res.status(Constants.BAD_REQUEST).send({ error: "InvalidInput" }); } leaderboardCursor = leaderboardCursor.limit(limit); } // Return the profiles, after mapping them to simple leaderboard entries const leaderboardProfiles: LeaderboardSchema[] = await leaderboardCursor.toArray() as LeaderboardSchema[]; - res.status(Constants.SUCCESS).send({ profiles: leaderboardProfiles.map(castLeaderboardEntries) }); - return; + return res.status(Constants.SUCCESS).send({ profiles: leaderboardProfiles.map(castLeaderboardEntries) }); } catch { - res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" }); - return; + return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" }); } }); diff --git a/src/services/user/user-router.ts b/src/services/user/user-router.ts index 81177c42..51589f7c 100644 --- a/src/services/user/user-router.ts +++ b/src/services/user/user-router.ts @@ -68,8 +68,7 @@ userRouter.get("/qr/:USERID", strongJwtVerification, async (req: Request, res: R // If target user -> redirect to base function if (!targetUser) { - res.redirect("/user/qr/"); - return; + return res.redirect("/user/qr/"); } const payload: JwtPayload = res.locals.payload as JwtPayload; @@ -81,18 +80,18 @@ userRouter.get("/qr/:USERID", strongJwtVerification, async (req: Request, res: R const uri: string = `hackillinois://user?userToken=${token}`; res.status(Constants.SUCCESS).send({ id: payload.id, qrInfo: uri }); } else if (hasElevatedPerms(payload)) { - // Get a new payload, and return the updated token - await getJwtPayloadFromDB(targetUser).then((newPayload: JwtPayload) => { + // Try to generate the new token for the given user, and return it + try { + const newPayload: JwtPayload = await getJwtPayloadFromDB(targetUser); const token: string = generateJwtToken(newPayload, "20s"); const uri: string = `hackillinois://user?userToken=${token}`; - res.status(Constants.SUCCESS).send({ id: targetUser, qrInfo: uri }); - }).catch( (error: string) => { + return res.status(Constants.SUCCESS).send({ id: targetUser, qrInfo: uri }); + } catch (error) { console.error(error); - res.status(Constants.BAD_REQUEST).send("UserNotFound"); - }); - } else { - res.status(Constants.FORBIDDEN).send("Forbidden"); + return res.status(Constants.BAD_REQUEST).send("UserNotFound"); + } } + return res.status(Constants.FORBIDDEN).send("Forbidden"); }); @@ -216,28 +215,28 @@ userRouter.post("/", strongJwtVerification, async (req: Request, res: Response) const token: JwtPayload = res.locals.payload as JwtPayload; if (!hasElevatedPerms(token)) { - res.status(Constants.FORBIDDEN).send({ error: "InvalidToken" }); + return res.status(Constants.FORBIDDEN).send({ error: "InvalidToken" }); } // Get userData from the request, and print to output const userData: UserFormat = req.body as UserFormat; if (!userData.id|| !userData.email || !userData.firstname || !userData.lastname || !userData.username) { - res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" }); - return; + return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" }); } // Update the given user await updateUser(userData); // Return new value of the user - await getUser(userData.id).then((user: UserSchema) => { - res.status(Constants.SUCCESS).send(user); - }).catch((error: string) => { + + try { + const user: UserSchema = await getUser(userData.id); + return res.status(Constants.SUCCESS).send(user); + } catch (error) { console.error(error); - res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" }); - }); + return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" }); + } }); - export default userRouter;