diff --git a/backend/package-lock.json b/backend/package-lock.json index f1e8a8f..9673afe 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -15,7 +15,9 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.21.0", + "express-mongo-sanitize": "^2.2.0", "express-rate-limit": "^7.5.0", + "helmet": "^8.1.0", "http": "^0.0.1-security", "jsonwebtoken": "^9.0.2", "moment": "^2.30.1", @@ -745,6 +747,15 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-mongo-sanitize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/express-mongo-sanitize/-/express-mongo-sanitize-2.2.0.tgz", + "integrity": "sha512-PZBs5nwhD6ek9ZuP+W2xmpvcrHwXZxD5GdieX2dsjPbAbH4azOkrHbycBud2QRU+YQF1CT+pki/lZGedHgo/dQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/express-rate-limit": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", @@ -1000,6 +1011,15 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/http": { "version": "0.0.1-security", "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", diff --git a/backend/package.json b/backend/package.json index 84dd041..3f6982c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -19,7 +19,9 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.21.0", + "express-mongo-sanitize": "^2.2.0", "express-rate-limit": "^7.5.0", + "helmet": "^8.1.0", "http": "^0.0.1-security", "jsonwebtoken": "^9.0.2", "moment": "^2.30.1", diff --git a/backend/src/app.js b/backend/src/app.js index 7323b8e..cf3609f 100644 --- a/backend/src/app.js +++ b/backend/src/app.js @@ -1,12 +1,15 @@ import express from "express"; import cookieParser from "cookie-parser"; import cors from "cors"; +import helmet from "helmet"; +import mongoSanitize from "express-mongo-sanitize"; const app = express(); import { User } from "./models/user.model.js"; import { Company } from "./models/company.model.js"; import { Professor } from "./models/professor.model.js"; - +// Security headers (disable CSP initially to avoid breaking frontend inline scripts/styles) +app.use(helmet({ contentSecurityPolicy: false })); app.use( cors({ @@ -22,6 +25,9 @@ app.use(express.urlencoded({ extended: true, limit: "16kb" })); app.use(express.static("public")); app.use(cookieParser()); +// Sanitize MongoDB queries to prevent NoSQL injection +app.use(mongoSanitize()); + import academicsRouter from "./routes/academic.routes.js"; app.use("/api/v1/academics", academicsRouter); @@ -96,7 +102,6 @@ import { app.use(notFoundHandler); // Global error handler - must be the last middleware -// Temporary debug test for companyInterview - +app.use(errorHandler); export { app }; diff --git a/backend/src/controllers/admin.controller.js b/backend/src/controllers/admin.controller.js index fbe4f28..e57b23f 100644 --- a/backend/src/controllers/admin.controller.js +++ b/backend/src/controllers/admin.controller.js @@ -8,6 +8,7 @@ import { Professor } from "../models/professor.model.js"; import { ApiError } from "../utils/ApiError.js"; import { ApiResponse } from "../utils/ApiResponse.js"; import { asyncHandler } from "../utils/asyncHandler.js"; +import { Otp } from "../models/otp.model.js"; const getUnverifiedUsers = asyncHandler(async (req, res) => { const admin = req.admin; @@ -193,7 +194,7 @@ const loginAdmin = asyncHandler(async (req, res) => { admin._id ); const loggedInAdmin = await Admin.findById(admin._id).select( - "-password -refeshToken" + "-password -refreshToken" ); // if (!admin.isAdmin) { // throw new ApiError(403, "You are not verified as an admin yet!"); @@ -201,7 +202,8 @@ const loginAdmin = asyncHandler(async (req, res) => { const options = { httpOnly: true, - secure: false, + secure: process.env.NODE_ENV === "production", + sameSite: "Lax", }; return res .status(200) @@ -230,8 +232,13 @@ const logoutAdmin = asyncHandler(async (req, res) => { new: true, } ); - res.clearCookie("accessToken"); - res.clearCookie("refreshToken"); + const cookieOptions = { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "Lax", + }; + res.clearCookie("accessToken", cookieOptions); + res.clearCookie("refreshToken", cookieOptions); return res .status(200) .json(new ApiResponse(200, {}, "Admin logged out successfully!")); @@ -239,13 +246,11 @@ const logoutAdmin = asyncHandler(async (req, res) => { const getCurrendAdmin = asyncHandler(async (req, res) => { const _id = req?.admin?._id; - const admin = await Admin.findById({ _id }); + const admin = await Admin.findById({ _id }).select("-password -refreshToken"); if (!admin) throw new ApiError(404, "admin not found"); res.status(200).json(new ApiResponse(200, admin, "admin fetched")); }); -import { Otp } from "../models/otp.model.js"; - export const rejectUser = asyncHandler(async (req, res) => { const { userId, reason } = req.body; if (!userId) { diff --git a/backend/src/controllers/awards.controller.js b/backend/src/controllers/awards.controller.js index 61f50d4..8b94a7d 100644 --- a/backend/src/controllers/awards.controller.js +++ b/backend/src/controllers/awards.controller.js @@ -46,6 +46,13 @@ const deleteAward = asyncHandler(async (req, res) => { const { id } = req.params; try { + const award = await Award.findById(id); + if (!award) { + throw new ApiError(404, "Award not found"); + } + if (award.student.toString() !== req.user._id.toString()) { + throw new ApiError(403, "You can only delete your own awards"); + } const deletedAward = await Award.findByIdAndDelete(id); if (!deletedAward) { @@ -78,6 +85,9 @@ const getAwardById = asyncHandler(async (req, res) => { if (!award) { throw new ApiError(404, "Award not found"); } + if (award.student.toString() !== req.user._id.toString()) { + throw new ApiError(403, "You can only view your own awards"); + } res.status(200).json({ success: true, @@ -96,7 +106,7 @@ const getAllAwards = asyncHandler(async (req, res) => { const updateAward = asyncHandler(async (req, res) => { const { id } = req.params; - const { title, description, date, student } = req.body; + const { title, description, date } = req.body; try { const award = await Award.findById(id); @@ -104,11 +114,13 @@ const updateAward = asyncHandler(async (req, res) => { if (!award) { throw new ApiError(404, "Award not found"); } + if (award.student.toString() !== req.user._id.toString()) { + throw new ApiError(403, "You can only update your own awards"); + } award.title = title; award.description = description; award.date = date; - award.student = student; const docURL = award.doc; if (docURL) { diff --git a/backend/src/controllers/exam.controller.js b/backend/src/controllers/exam.controller.js index 0028499..4e080d6 100644 --- a/backend/src/controllers/exam.controller.js +++ b/backend/src/controllers/exam.controller.js @@ -116,6 +116,9 @@ const getExamById = asyncHandler(async (req, res) => { if (!exam) { throw new ApiError(404, "Exam not found"); } + if (exam.name.toString() !== req.user._id.toString()) { + throw new ApiError(403, "You can only view your own exam records"); + } res.status(200).json({ success: true, diff --git a/backend/src/controllers/higher-education.controller.js b/backend/src/controllers/higher-education.controller.js index f821170..5107b63 100644 --- a/backend/src/controllers/higher-education.controller.js +++ b/backend/src/controllers/higher-education.controller.js @@ -104,6 +104,9 @@ const getHigherEducationById = asyncHandler(async (req, res) => { if (!higherEducation) { throw new ApiError(404, "Higher Education not found"); } + if (higherEducation.name.toString() !== req.user._id.toString()) { + throw new ApiError(403, "You can only view your own higher education records"); + } res.status(200).json({ success: true, diff --git a/backend/src/controllers/internship.controller.js b/backend/src/controllers/internship.controller.js index 5f9e858..402c1ed 100644 --- a/backend/src/controllers/internship.controller.js +++ b/backend/src/controllers/internship.controller.js @@ -34,7 +34,7 @@ const addInternship = asyncHandler(async (req, res) => { if (!uploadedDoc) { throw new ApiError(400, "Document upload failed!"); } - docUrl = uploadedDoc.url; + docUrl = uploadedDoc.secure_url; } else if (location === "outside_bit" && !req.file) { throw new ApiError( 400, @@ -97,11 +97,11 @@ const addInternDocs = asyncHandler(async (req, res) => { if (!InternDocsLocalPath) throw new ApiError(400, "Document is neccesary"); const InternDocs = await uploadOnCloudinary(InternDocsLocalPath); if (!InternDocs) { - throw new ApiError(400, "Not uploaded on Closuinary. something went wrong"); + throw new ApiError(400, "Not uploaded on Cloudinary. Something went wrong"); } const newInternRecord = await Internship.updateOne( { _id }, - { $set: { doc: InternDocs.url } } + { $set: { doc: InternDocs.secure_url } } ); res .status(200) @@ -226,12 +226,12 @@ const getAllVerifiedInternshipData = asyncHandler(async (req, res) => { }); const getInternshipDataforStudent = asyncHandler(async (req, res) => { - const { student_id } = req.body; + const student_id = req.user._id; const response = await Internship.find( { student: student_id }, { verfied: true } ) - .populate("student") + .populate("student", "-password -refreshToken") .populate("company"); res .status(200) diff --git a/backend/src/controllers/interview.controller.js b/backend/src/controllers/interview.controller.js index 73aa5b2..531b001 100644 --- a/backend/src/controllers/interview.controller.js +++ b/backend/src/controllers/interview.controller.js @@ -62,8 +62,11 @@ const getAllInterviewExps = asyncHandler(async (req, res) => { sort = "-createdAt", } = req.query; + // Whitelist allowed sort fields to prevent sort injection + const allowedSortFields = ["createdAt", "-createdAt", "company", "-company"]; + const sanitizedSort = allowedSortFields.includes(sort) ? sort : "-createdAt"; + const filter = {}; - console.log(companyId, studentId); if (companyId) { filter.company = companyId; } @@ -77,7 +80,7 @@ const getAllInterviewExps = asyncHandler(async (req, res) => { const interviewExps = await InterviewExp.find(filter) .populate("company", "companyName") .populate("student", "fullName email image branch cgpa") - .sort(sort) + .sort(sanitizedSort) .skip(skip) .limit(parseInt(limit, 10)); diff --git a/backend/src/controllers/profProject.controller.js b/backend/src/controllers/profProject.controller.js index a04bf8f..7af30ee 100644 --- a/backend/src/controllers/profProject.controller.js +++ b/backend/src/controllers/profProject.controller.js @@ -9,8 +9,10 @@ import { import { Professor } from "../models/professor.model.js"; const addNewProject = asyncHandler(async (req, res) => { - const { profId, title, desc, categories, startDate, endDate, relevantLinks } = + const { title, desc, categories, startDate, endDate, relevantLinks } = req.body; + // Derive professor ID from authenticated token, not client input + const profId = req.professor._id; const professor = await Professor.findById(profId); if (!professor) { throw new ApiError(404, "Professor not found!"); @@ -88,6 +90,11 @@ const editProject = asyncHandler(async (req, res) => { throw new ApiError(404, "Project not found"); } + // Verify ownership + if (project.professor.toString() !== req.professor._id.toString()) { + throw new ApiError(403, "Not authorized to edit this project"); + } + project.title = title || project.title; project.desc = desc || project.desc; project.categories = categories || project.categories; @@ -191,11 +198,13 @@ const applyToProject = asyncHandler(async (req, res) => { const getAllApplications = asyncHandler(async (req, res) => { const { status } = req.params; - const applications = await RequestProj.find({ status }) + // Only return applications for projects owned by this professor + const profProjects = await AdhocProject.find({ professor: req.professor._id }).select("_id"); + const projectIds = profProjects.map((p) => p._id); + const applications = await RequestProj.find({ status, projectId: { $in: projectIds } }) .populate("studentId", "fullName email rollNumber mobileNumber") - .populate("projectId", "title profName") // Added populate for projectId - .select("status applicationDate doc projectId"); // Included projectId in selected fields - console.log("applications", applications); + .populate("projectId", "title profName") + .select("status applicationDate doc projectId"); res.status(200).json({ success: true, data: applications }); }); @@ -210,16 +219,22 @@ const updateApplicationStatus = asyncHandler(async (req, res) => { ); } - const updatedApplication = await RequestProj.findByIdAndUpdate( - applicationId, - { status }, - { new: true } - ); - - if (!updatedApplication) { + const application = await RequestProj.findById(applicationId).populate("projectId"); + if (!application) { throw new ApiError(404, "Application not found"); } - res.status(200).json({ success: true, data: updatedApplication }); + if (!application.projectId) { + throw new ApiError(404, "Referenced project no longer exists"); + } + + // Verify the professor owns the project + if (application.projectId.professor.toString() !== req.professor._id.toString()) { + throw new ApiError(403, "Not authorized to update this application"); + } + + application.status = status; + await application.save(); + res.status(200).json({ success: true, data: application }); }); const getStudentApplications = asyncHandler(async (req, res) => { @@ -250,6 +265,11 @@ const closeProject = asyncHandler(async (req, res) => { throw new ApiError(404, "Project not found"); } + // Verify ownership + if (project.professor.toString() !== req.professor._id.toString()) { + throw new ApiError(403, "Not authorized to close this project"); + } + project.closed = true; await project.save(); diff --git a/backend/src/controllers/professor.controller.js b/backend/src/controllers/professor.controller.js index 409682a..6e69dee 100644 --- a/backend/src/controllers/professor.controller.js +++ b/backend/src/controllers/professor.controller.js @@ -15,7 +15,8 @@ import { Otp } from "../models/otp.model.js"; import { Review } from "../models/review.model.js"; import { Minor } from "../models/minor.model.js"; import { Major } from "../models/major.model.js"; -const url = "http://139.167.188.221:3000/faculty-login"; +const url = process.env.FACULTY_LOGIN_URL || "http://localhost:3000/faculty-login"; +const autoLoginBaseUrl = process.env.FACULTY_AUTO_LOGIN_URL || "http://localhost:3000/faculty-auto-login"; @@ -244,7 +245,7 @@ const addProf = asyncHandler(async (req, res) => { // }); const getProf = asyncHandler(async (req, res) => { - const professors = await Professor.find().select("-password"); + const professors = await Professor.find().select("-password -refreshToken"); res .status(200) .json( @@ -285,7 +286,7 @@ const loginProf = asyncHandler(async (req, res) => { professor._id ); const loggedInProfessor = await Professor.findById(professor._id).select( - "-password -refeshToken" + "-password -refreshToken" ); const reviewLog = await Review.findOne({ user: professor._id }); let review = false; @@ -294,7 +295,8 @@ const loginProf = asyncHandler(async (req, res) => { } const options = { httpOnly: true, - secure: false, + secure: process.env.NODE_ENV === "production", + sameSite: "Lax", }; return res .status(200) @@ -324,8 +326,13 @@ const logoutProf = asyncHandler(async (req, res) => { new: true, } ); - res.clearCookie("accessToken"); - res.clearCookie("refreshToken"); + const cookieOptions = { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "Lax", + }; + res.clearCookie("accessToken", cookieOptions); + res.clearCookie("refreshToken", cookieOptions); return res .status(200) .json(new ApiResponse(200, {}, "Prof logged out successfully!")); @@ -347,11 +354,11 @@ const generateAutoLoginUrl = asyncHandler(async (req, res) => { const autoLoginToken = jwt.sign( { _id: professor._id }, process.env.ACCESS_TOKEN_SECRET, - { expiresIn: "12h" } + { expiresIn: "30m" } ); // Create the auto-login URL - const autoLoginUrl = `http://139.167.188.221:3000/faculty-auto-login?token=${autoLoginToken}`; + const autoLoginUrl = `${autoLoginBaseUrl}?token=${autoLoginToken}`; return res .status(200) @@ -396,7 +403,8 @@ const autoLoginProf = asyncHandler(async (req, res) => { const options = { httpOnly: true, - secure: false, + secure: process.env.NODE_ENV === "production", + sameSite: "Lax", }; return res @@ -661,6 +669,10 @@ const denyGroup = asyncHandler(async (req, res) => { const profId = req?.professor?._id; const group = await Group.findById({ _id }); if (!group) throw new ApiError(409, "Group not exists"); + // Verify this professor is in the applied list + if (!group.summerAppliedProfs.some((p) => p.toString() === profId.toString())) { + throw new ApiError(403, "You are not in the applied professors list for this group"); + } group.summerAppliedProfs.pull(profId); const prof = await Professor.findById(profId); prof.appliedGroups.summer_training.pull(_id); @@ -713,6 +725,10 @@ const acceptGroup = asyncHandler(async (req, res) => { if (!group) { throw new ApiError(404, "Group not found"); } + // Verify this professor is in the applied list + if (!group.summerAppliedProfs.some((p) => p.toString() === profId.toString())) { + throw new ApiError(403, "You are not in the applied professors list for this group"); + } const numOfMem = group.members.length; if ( prof.currentCount.summer_training + numOfMem > @@ -920,7 +936,7 @@ const mergeGroups = asyncHandler(async (req, res) => { await newGroup.save(); // For each student in the merged groups, update their group field to the new group object id - await Student.updateMany( + await User.updateMany( { _id: { $in: uniqueMembers.map((member) => member._id) } }, { $set: { group: newGroup._id } } ); @@ -940,7 +956,7 @@ const otpForgotPassword = asyncHandler(async (req, res) => { if (!prof) { throw new ApiError(404, "Professor does not exists"); } - const otp = `${Math.floor(Math.random() * 9000 + 1000)}`; + const otp = `${crypto.randomInt(100000, 1000000)}`; await Otp.create({ email, otp }); const tOtp = await Otp.findOne({ email }); @@ -1050,7 +1066,7 @@ const changePassword = asyncHandler(async (req, res) => { const validOTP = otp === hashedOTP; // console.log(validOTP) if (!validOTP) { - throw new ApiError("Invalid code. Check your Inbox"); + throw new ApiError(400, "Invalid code. Check your Inbox"); } else { const savepass = await bcrypt.hash(newpassword, 12); const response = await Professor.updateOne( @@ -1202,6 +1218,10 @@ const denyMinorGroup = asyncHandler(async (req, res) => { const profId = req?.professor?._id; const group = await Minor.findById({ _id }); if (!group) throw new ApiError(409, "Group not exists"); + // Verify this professor is in the applied list + if (!group.minorAppliedProfs.some((p) => p.toString() === profId.toString())) { + throw new ApiError(403, "You are not in the applied professors list for this group"); + } group.minorAppliedProfs.pull(profId); const prof = await Professor.findById(profId); prof.appliedGroups.minor_project.pull(_id); @@ -1236,6 +1256,10 @@ const acceptMinorGroup = asyncHandler(async (req, res) => { if (!group) { throw new ApiError(404, "Group not found"); } + // Verify this professor is in the applied list + if (!group.minorAppliedProfs.some((p) => p.toString() === profId.toString())) { + throw new ApiError(403, "You are not in the applied professors list for this group"); + } const numOfMem = group.members.length; if ( prof.currentCount.minor_project + numOfMem > @@ -1546,6 +1570,10 @@ const denyMajorGroup = asyncHandler(async (req, res) => { const profId = req?.professor?._id; const group = await Major.findById({ _id }); if (!group) throw new ApiError(409, "Group not exists"); + // Verify this professor is in the applied list + if (!group.majorAppliedProfs.some((p) => p.toString() === profId.toString())) { + throw new ApiError(403, "You are not in the applied professors list for this group"); + } group.majorAppliedProfs.pull(profId); const prof = await Professor.findById(profId); prof.appliedGroups.major_project.pull(_id); @@ -1580,6 +1608,10 @@ const acceptMajorGroup = asyncHandler(async (req, res) => { if (!group) { throw new ApiError(404, "Group not found"); } + // Verify this professor is in the applied list + if (!group.majorAppliedProfs.some((p) => p.toString() === profId.toString())) { + throw new ApiError(403, "You are not in the applied professors list for this group"); + } const numOfMem = group.members.length; if ( prof.currentCount.major_project + numOfMem > diff --git a/backend/src/controllers/user.controller.js b/backend/src/controllers/user.controller.js index 86258c6..a259aa6 100644 --- a/backend/src/controllers/user.controller.js +++ b/backend/src/controllers/user.controller.js @@ -1,4 +1,5 @@ import bcrypt from "bcrypt"; +import crypto from "crypto"; import cron from "node-cron"; import { Otp } from "../models/otp.model.js"; import { Placement } from "../models/placement.model.js"; @@ -29,7 +30,7 @@ const generateAcessAndRefreshToken = async (userId) => { const verifyMail = asyncHandler(async (req, res) => { try { const { email } = req.body; - const otp = Math.floor(100000 + Math.random() * 900000); + const otp = crypto.randomInt(100000, 1000000); const existedUser = await User.findOne({ email }); if (existedUser) { @@ -126,15 +127,26 @@ const registerUser = asyncHandler(async (req, res) => { // throw new ApiError(500, "id card file is cannot be uploaded"); } - const user = await User.create({ - username: username.toLowerCase(), - password, - fullName, - rollNumber, - email, - idCard: idCard.url, - batch, - }); + let user; + try { + user = await User.create({ + username: username.toLowerCase(), + password, + fullName, + rollNumber, + email, + idCard: idCard.url, + batch, + }); + } catch (error) { + if (error.code === 11000) { + return res.status(409).json({ + success: false, + message: "User with this email, username, or roll number already exists", + }); + } + throw error; + } const createdUser = await User.findById(user._id).select( "-password -refreshToken" @@ -146,7 +158,6 @@ const registerUser = asyncHandler(async (req, res) => { success: false, message: "Something went wrong while registering the user", }); - // throw new ApiError(500, "Something went wrong while registering the user"); } return res @@ -183,24 +194,23 @@ const loginUser = asyncHandler(async (req, res) => { }); // throw new ApiError(401, "Invalid Credentials!"); } - const { accessToken, refreshToken } = await generateAcessAndRefreshToken( - user._id - ); - const loggedInUser = await User.findById(user._id).select( - "-password -refreshToken" - ); if (!user.isVerified) { console.log("You are not verified yet!"); return res.status(403).json({ success: false, message: "You are not verified yet!", }); - - //throw new ApiError(403, "You are not verified yet!"); } + const { accessToken, refreshToken } = await generateAcessAndRefreshToken( + user._id + ); + const loggedInUser = await User.findById(user._id).select( + "-password -refreshToken" + ); const options = { httpOnly: true, - secure: false, + secure: process.env.NODE_ENV === "production", + sameSite: "Lax", }; return res .status(200) @@ -229,7 +239,7 @@ export const otpForgotPass = asyncHandler(async (req, res) => { if (!user) { throw new ApiError(404, "User does not exists"); } - const otp = `${Math.floor(Math.random() * 9000 + 1000)}`; + const otp = `${crypto.randomInt(100000, 1000000)}`; await Otp.create({ email, otp }); // Send OTP email using utility function @@ -305,8 +315,13 @@ const logoutUser = asyncHandler(async (req, res) => { new: true, } ); - res.clearCookie("accessToken"); - res.clearCookie("refreshToken"); + const cookieOptions = { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "Lax", + }; + res.clearCookie("accessToken", cookieOptions); + res.clearCookie("refreshToken", cookieOptions); return res .status(200) .json(new ApiResponse(200, {}, "User logged out successfully!")); @@ -432,7 +447,7 @@ const updateUser1 = asyncHandler(async (req, res) => { const getCurrentUser = asyncHandler(async (req, res) => { const _id = req?.user?._id; - const user = await User.findById({ _id }).select("-marks"); + const user = await User.findById({ _id }).select("-password -refreshToken -marks"); if (!user) throw new ApiError(404, "user not found"); // console.log(user) res.status(200).json(new ApiResponse(200, user, "user fetched")); @@ -692,11 +707,22 @@ const fetchBranch = asyncHandler(async (req, res) => { }); const getUserbyRoll = asyncHandler(async (req, res) => { - const { rollNumber, isAdmin } = req.body; + const { rollNumber } = req.body; let query = User.findOne({ rollNumber: rollNumber }); - if (!isAdmin) { + // Admins get full data; regular users get limited fields + if (req.admin) { + query = query.select("-password -refreshToken"); + query = query + .populate("internShips") + .populate("placementOne") + .populate("placementTwo") + .populate("placementThree") + .populate("awards") + .populate("exams") + .populate("higherEd"); + } else { query = query.select( "-password -username -refreshToken -fatherName -fatherMobileNumber -motherName -residentialAddress -alternateEmail -alumni -awards -backlogs -codingProfiles -companyInterview -createdAt -exams -graduationYear -group -groupReq -higherEd -idCard -isSummerAllocated -isVerified -linkedin -marks -mobileNumber -peCourses -proj -resume -summerAppliedProfs -updatedAt -workExp -__v -abcId" ); @@ -705,18 +731,6 @@ const getUserbyRoll = asyncHandler(async (req, res) => { .populate("placementOne", "company role ctc date") .populate("placementTwo", "company role ctc date") .populate("placementThree", "company role ctc date"); - } else { - query = query - .populate("placementOne") - .populate("placementTwo") - .populate("placementThree") - .populate("proj") - .populate("awards") - .populate("higherEd") - .populate("internShips") - .populate("exams") - .populate("academics") - .populate("backlogs"); } const user = await query; diff --git a/backend/src/cron-jobs/notifyMajorProf.js b/backend/src/cron-jobs/notifyMajorProf.js index 12a9513..4b536bb 100644 --- a/backend/src/cron-jobs/notifyMajorProf.js +++ b/backend/src/cron-jobs/notifyMajorProf.js @@ -20,7 +20,7 @@ async function sendMajorNotificationEmail(professor) { { expiresIn: "30m" } ); - const autoLoginUrl = `http://139.167.188.221:3000/faculty-auto-login?token=${autoLoginToken}`; + const autoLoginUrl = `${process.env.FACULTY_AUTO_LOGIN_URL || "http://localhost:3000/faculty-auto-login"}?token=${autoLoginToken}`; const mailOptions = { from: process.env.AUTH_EMAIL, @@ -46,7 +46,7 @@ async function sendMajorNotificationEmail(professor) { Visit Dashboard -
This link is valid for 30 minutes. If you prefer to login manually, click here.
+This link is valid for 30 minutes. If you prefer to login manually, click here.
Best regards,
BITACADEMIA
This link is valid for 30 minutes. If you prefer to login manually, click here.
+This link is valid for 30 minutes. If you prefer to login manually, click here.
Best regards,
BITACADEMIA
This link is valid for 30 minutes. If you prefer to login manually, click here.
+This link is valid for 30 minutes. If you prefer to login manually, click here.
Best regards,
BITACADEMIA