Skip to content

Commit

Permalink
Fixed user service (#88)
Browse files Browse the repository at this point in the history
* Fixed formatting

* Fixed redirects

* Fixed issues with auth

* Fixed redirects

* change docs

* Merged in main

* Also fixed profile

* Fixed format
  • Loading branch information
AydanPirani committed Oct 15, 2023
1 parent a19d6cc commit 6eac882
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 43 deletions.
2 changes: 1 addition & 1 deletion src/database/attendee-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class AttendeeProfile {
public avatarUrl: string;

@prop({ required: true })
public discordName: string;
public discordTag: string;

@prop({ required: true })
public points: number;
Expand Down
1 change: 1 addition & 0 deletions src/services/auth/auth-models.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface ProfileData {
id?: string;
login?: string;
email: string;
displayName?: string;
}
Expand Down
8 changes: 5 additions & 3 deletions src/services/auth/auth-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,16 @@ authRouter.get(
const redirect: string = Constants.REDIRECT_MAPPINGS.get(device) ?? Constants.DEFAULT_REDIRECT;

data.id = data.id ?? user.id;
data.displayName = user.displayName;
data.displayName = data.displayName ?? data.login;

try {
// Load in the payload with the actual values stored in the database
const payload: JwtPayload = await getJwtPayloadFromProfile(user.provider, data);
console.log(data, payload);
const userId: string = payload.id;
await UserInfoModel.findOneAndUpdate(
{ userId: data.id },
{ email: data.email, name: data.displayName, userId: payload.id },
{ userId: userId },
{ email: data.email, name: data.displayName, userId: userId },
{ upsert: true },
);

Expand Down
17 changes: 13 additions & 4 deletions src/services/profile/profile-formats.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { AttendeeProfile } from "../../database/attendee-db.js";
export interface ProfileFormat {
userId: string;
avatarUrl: string;
discordTag: string;
displayName: string;
points: number;
}

export function isValidProfileModel(profile: AttendeeProfile): boolean {
export function isValidProfileFormat(profile: ProfileFormat): boolean {
if (!profile) {
return false;
}
if (!profile.avatarUrl || !profile.discordName || !profile.displayName) {

if (!profile.userId || !profile.avatarUrl || !profile.discordTag || !profile.displayName) {
return false;
}

if (
typeof profile.discordName !== "string" ||
typeof profile.userId !== "string" ||
typeof profile.discordTag !== "string" ||
typeof profile.displayName !== "string" ||
typeof profile.avatarUrl !== "string"
) {
Expand Down
84 changes: 50 additions & 34 deletions src/services/profile/profile-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { Query } from "mongoose";
import { LeaderboardEntry } from "./profile-models.js";

import { JwtPayload } from "../auth/auth-models.js";
import { strongJwtVerification, weakJwtVerification } from "../../middleware/verify-jwt.js";
import { isValidProfileModel } from "./profile-formats.js";
import { strongJwtVerification } from "../../middleware/verify-jwt.js";
import { ProfileFormat, isValidProfileFormat } from "./profile-formats.js";
import { hasElevatedPerms } from "../auth/auth-lib.js";

const profileRouter: Router = Router();

Expand All @@ -34,7 +35,7 @@ profileRouter.use(cors({ origin: "*" }));
"points": 2021,
},
{
"displayName": "test2"
"displayName": "patrick"
"points": 2020,
},
]
Expand Down Expand Up @@ -79,17 +80,20 @@ profileRouter.get("/leaderboard/", async (req: Request, res: Response) => {
* @apiGroup Profile
* @apiDescription Retrieve the user profile based on their authentication.
*
* @apiSuccess (200: Success) {Json} user User's profile information.
* @apiSuccess (200: Success) {string} userID ID of the user
* @apiSuccess (200: Success) {string} displayName Publicly-visible display name for the user
* @apiSuccess (200: Success) {string} discordTag Discord tag for the user
* @apiSuccess (200: Success) {string} avatarUrl URL that contains the user avatar
* @apiSuccess (200: Success) {number} points Points that the user has
* @apiSuccessExample Example Success Response:
* HTTP/1.1 200 OK
* {
* "_id": "12345",
* "displayName": "Illinois",
* "discordName": "hackillinois",
* "userId": "google12345"
* "displayName": "hackillinois",
* "discordTag": "discordtag",
* "avatarUrl": "na",
* "points": 0,
* "userId": "abcde",
* "foodWave": 0
* }
*
* @apiError (404: Not Found) {String} UserNotFound The user's profile was not found.
Expand All @@ -104,13 +108,14 @@ profileRouter.get("/leaderboard/", async (req: Request, res: Response) => {
*/

profileRouter.get("/", strongJwtVerification, async (_: Request, res: Response) => {
const decodedData: JwtPayload = res.locals.payload as JwtPayload;
const payload: JwtPayload = res.locals.payload as JwtPayload;

const userId: string = decodedData.id;
const userId: string = payload.id;
console.log(userId);
const user: AttendeeProfile | null = await AttendeeProfileModel.findOne({ userId: userId });

if (!user) {
return res.status(Constants.NOT_FOUND).send({ error: "UserNotFound" });
return res.status(Constants.BAD_REQUEST).send({ error: "UserNotFound" });
}

return res.status(Constants.SUCCESS).send(user);
Expand All @@ -123,16 +128,21 @@ profileRouter.get("/", strongJwtVerification, async (_: Request, res: Response)
*
* @apiParam {String} USERID User's unique ID.
*
* @apiSuccess (200: Success) {Json} user User's profile information.
* @apiSuccess (200: Success) {string} userID ID of the user
* @apiSuccess (200: Success) {string} displayName Publicly-visible display name for the user
* @apiSuccess (200: Success) {string} discordTag Discord tag for the user
* @apiSuccess (200: Success) {string} avatarUrl URL that contains the user avatar
* @apiSuccess (200: Success) {number} points Points that the user has
*
* @apiSuccessExample Example Success Response:
* HTTP/1.1 200 OK
* {
* "_id": "12345",
* "displayName": "Hackk",
* "discordName": "hackillinois",
* "userId": "google12345",
* "displayName": "Hack",
* "discordTag": "hackillinois",
* "avatarUrl": "na",
* "points": 0,
* "userId": "abcde",
* }
*
* @apiError (404: Not Found) {String} UserNotFound The user's profile was not found.
Expand All @@ -146,17 +156,23 @@ profileRouter.get("/", strongJwtVerification, async (_: Request, res: Response)
* {"error": "InternalError"}
*/

profileRouter.get("/id/:USERID", weakJwtVerification, async (req: Request, res: Response) => {
profileRouter.get("/id/:USERID", strongJwtVerification, async (req: Request, res: Response) => {
const userId: string | undefined = req.params.USERID;
const payload: JwtPayload = res.locals.payload as JwtPayload;

if (!userId) {
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
return res.redirect("/user/");
}

// Trying to perform elevated operation (getting someone else's profile without elevated perms)
if (userId != payload.id && !hasElevatedPerms(payload)) {
return res.status(Constants.FORBIDDEN).send({ error: "Forbidden" });
}

const user: AttendeeProfile | null = await AttendeeProfileModel.findOne({ userId: userId });

if (!user) {
return res.status(Constants.NOT_FOUND).send({ error: "UserNotFound" });
return res.status(Constants.BAD_REQUEST).send({ error: "UserNotFound" });
}

return res.status(Constants.SUCCESS).send(user);
Expand All @@ -172,16 +188,21 @@ profileRouter.get("/id/:USERID", weakJwtVerification, async (req: Request, res:
* @apiBody {String} discord User's Discord username.
* @apiBody {String} avatarUrl User's avatar URL.
*
* @apiSuccess (200: Success) {Json} user Created user's profile information.
* @apiSuccess (200: Success) {string} userID ID of the user
* @apiSuccess (200: Success) {string} displayName Publicly-visible display name for the user
* @apiSuccess (200: Success) {string} discordTag Discord tag for the user
* @apiSuccess (200: Success) {string} avatarUrl URL that contains the user avatar
* @apiSuccess (200: Success) {number} points Points that the user has
*
* @apiSuccessExample Example Success Response:
* HTTP/1.1 200 OK
* {
* "_id": "abc12345",
* "displayName": "Illinois",
* "discordName": "HackIllinois",
* "userId": "github12345",
* "displayName": "Hack",
* "discord": "HackIllinois",
* "avatarUrl": "na",
* "points": 0,
* "userId": "12345",
* }
*
* @apiError (400: Bad Request) {String} UserAlreadyExists The user profile already exists.
Expand All @@ -194,28 +215,23 @@ profileRouter.get("/id/:USERID", weakJwtVerification, async (req: Request, res:
* HTTP/1.1 500 Internal Server Error
* {"error": "InternalError"}
*/

profileRouter.post("/", strongJwtVerification, async (req: Request, res: Response) => {
const profile: AttendeeProfile = req.body as AttendeeProfile;
const profile: ProfileFormat = req.body as ProfileFormat;
profile.points = Constants.DEFAULT_POINT_VALUE;

if (!isValidProfileModel(profile)) {
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidPostData" });
if (!isValidProfileFormat(profile)) {
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
}

const decodedData: JwtPayload = res.locals.payload as JwtPayload;

profile.userId = decodedData.id;
profile.points = Constants.DEFAULT_POINT_VALUE;

// Ensure that user doesn't already exist before creating
const user: AttendeeProfile | null = await AttendeeProfileModel.findOne({ userId: profile.userId });

if (user) {
return res.status(Constants.FAILURE).send({ error: "UserAlreadyExists" });
}

const profileMetadata: AttendeeMetadata = new AttendeeMetadata(profile.userId, Constants.DEFAULT_FOOD_WAVE);

// Create a metadata object, and return it
try {
const profileMetadata: AttendeeMetadata = new AttendeeMetadata(profile.userId, Constants.DEFAULT_FOOD_WAVE);
const newProfile = await AttendeeProfileModel.create(profile);
await AttendeeMetadataModel.create(profileMetadata);
return res.status(Constants.SUCCESS).send(newProfile);
Expand Down
2 changes: 1 addition & 1 deletion src/services/user/user-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ userRouter.get("/qr/:USERID", strongJwtVerification, async (req: Request, res: R
userRouter.get("/:USERID", strongJwtVerification, async (req: Request, res: Response) => {
// If no target user, exact same as next route
if (!req.params.USERID) {
res.redirect("/");
return res.redirect("/");
}

const targetUser: string = req.params.USERID ?? "";
Expand Down

0 comments on commit 6eac882

Please sign in to comment.