Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Database/models And LOGIN SIGNUP routes #159

Merged
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,10 @@ Special thanks to our amazing mentors who are guiding this project! 🙌
</a>
</td>
<td align="center">
<a href="https://github.com/itznayan">
<img src="https://avatars.githubusercontent.com/u/136584376?v=4" width="100;" alt="itznayan"/>
<a href="https://github.com/vishnuprasad2004">
<img src="https://avatars.githubusercontent.com/u/116942066?v=4" width="100;" alt="vishnuprasad2004"/>
<br />
<sub><b>Mahera Nayan</b></sub>
<sub><b>Vishnu Prasad Korada</b></sub>
</a>
</td>
<td align="center">
Expand Down Expand Up @@ -192,6 +192,8 @@ Special thanks to our amazing mentors who are guiding this project! 🙌
<sub><b>Ayush Yadav</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/mishradev1">
<img src="https://avatars.githubusercontent.com/u/118660840?v=4" width="100;" alt="mishradev1"/>
Expand Down
65 changes: 65 additions & 0 deletions backend/controller/admin.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const bcrypt = require("bcrypt");
const { z } = require("zod");
const Admin = require("../models/admin.model");

// Define the schema
const adminSchema = z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email address"),
password: z.string().min(6, "Password must be at least 6 characters long"),
});
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved

async function createAdmin(req, res) {
// Validate the request body
const validation = adminSchema.safeParse(req.body);

if (!validation.success) {
return res.status(400).json({ error: validation.error.errors });
}

try {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
const admin = new Admin({
name: req.body.name,
email: req.body.email,
password: hashedPassword,
});
await admin.save();
res.status(201).json({ message: "Admin created successfully" });
} catch (error) {
res.status(500).json({ error: "Internal server error" });
}
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved
}

async function loginAdmin(req, res) {

const adminSchema = z.object({
email: z.string().email("Invalid email address"),
password: z.string().min(6, "Password must be at least 6 characters long"),
});
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved
// Validate the request body
const validation = adminSchema.safeParse(req.body);
if(!validation.success) {
return res.status(400).json({ error: validation.error.errors });
}

try {
const admin = await Admin.findOne({ email: req.body.email });
if (!admin) {
return res.status(404).json({ error: "Admin not found" });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid Detailed Error Messages to Prevent User Enumeration

Returning specific error messages like "Admin not found" and "Invalid password" can allow attackers to determine whether a particular email is registered in the system, leading to user enumeration vulnerabilities.

[security]

Consider returning a generic error message instead.

Apply this change to use a generic authentication error message:

 if (!admin) {
-  return res.status(404).json({ error: "Admin not found" });
+  return res.status(401).json({ error: "Invalid email or password" });
 }

 if (!validPassword) {
-  return res.status(401).json({ error: "Invalid password" });
+  return res.status(401).json({ error: "Invalid email or password" });
 }

Also applies to: 53-53

}
const validPassword = await bcrypt.compare(req.body.password, admin.password);
if (!validPassword) {
return res.status(401).json({ error: "Invalid password" });
}
res.json({ message: "Login successful" });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Return Authentication Token Upon Successful Login

Currently, upon successful login, the API returns a success message, but no token is provided for authentication in subsequent requests. Consider implementing JWT or session tokens to handle authenticated requests securely.

Would you like assistance in implementing JWT authentication and generating tokens upon successful login?

}
catch (error) {
res.status(500).json({ error: "Internal server error" });
}
}
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved


module.exports = { createAdmin
, loginAdmin
};
68 changes: 68 additions & 0 deletions backend/controller/user.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const bcrypt = require("bcrypt");
const { z } = require("zod");
const User = require("../models/user.model");

// Define the schema
const userSchema = z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email address"),
password: z.string().min(6, "Password must be at least 6 characters long"),
});

async function createUser(req, res) {
// Validate the request body
const validation = userSchema.safeParse(req.body);

if (!validation.success) {
return res.status(400).json({ error: validation.error.errors });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid exposing detailed validation errors to the client

Returning detailed validation errors can expose internal implementation details and potentially sensitive information. Consider returning a generic error message or sanitizing the errors before sending them to the client.

Apply this diff to return a general error message:

     if (!validation.success) {
-      return res.status(400).json({ error: validation.error.errors });
+      return res.status(400).json({ error: "Invalid input data" });
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return res.status(400).json({ error: validation.error.errors });
return res.status(400).json({ error: "Invalid input data" });

}

try {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashedPassword,
});
await user.save();
res.status(201).json({ message: "User created successfully" });
} catch (error) {
res.status(500).json({ error: "Internal server error" });
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle duplicate email errors when creating a new user

Currently, if a user tries to register with an email that already exists, the server responds with a generic "Internal server error". It's better to catch this specific error and return a more informative message to the client.

Apply this diff to handle duplicate email errors:

     await user.save();
     res.status(201).json({ message: "User created successfully" });
   } catch (error) {
+    if (error.code === 11000) {
+      return res.status(400).json({ error: "Email already exists" });
+    }
     res.status(500).json({ error: "Internal server error" });
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await user.save();
res.status(201).json({ message: "User created successfully" });
} catch (error) {
res.status(500).json({ error: "Internal server error" });
}
await user.save();
res.status(201).json({ message: "User created successfully" });
} catch (error) {
if (error.code === 11000) {
return res.status(400).json({ error: "Email already exists" });
}
res.status(500).json({ error: "Internal server error" });
}

}

async function loginUser(req, res) {
const userSchema = z.object({
email: z.string().email("Invalid email address"),
password: z.string().min(6, "Password must be at least 6 characters long"),
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Extract loginSchema definition to module scope to avoid redefinition

Defining the schema inside the loginUser function leads to redefinition every time the function is called. For better performance and maintainability, consider defining it at the module level.

Apply this diff to extract loginSchema to the module scope:

+const loginSchema = z.object({
+  email: z.string().email("Invalid email address"),
+  password: z.string().min(6, "Password must be at least 6 characters long"),
+});

 async function loginUser(req, res) {
-  const userSchema = z.object({
-    email: z.string().email("Invalid email address"),
-    password: z.string().min(6, "Password must be at least 6 characters long"),
-  });
   // Validate the request body
-  const validation = userSchema.safeParse(req.body);
+  const validation = loginSchema.safeParse(req.body);

Committable suggestion was skipped due to low confidence.

// Validate the request body
const validation = userSchema.safeParse(req.body);
if (!validation.success) {
return res.status(400).json({ error: validation.error.errors });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid exposing detailed validation errors to the client

As with createUser, returning detailed validation errors can expose internal implementation details. Return a generic error message instead.

Apply this diff:

     if (!validation.success) {
-      return res.status(400).json({ error: validation.error.errors });
+      return res.status(400).json({ error: "Invalid email or password" });
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return res.status(400).json({ error: validation.error.errors });
return res.status(400).json({ error: "Invalid email or password" });

}

try {
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res.status(404).json({ error: "User not found" });
}
const validPassword = await bcrypt.compare(
req.body.password,
user.password
);
if (!validPassword) {
return res.status(401).json({ error: "Invalid password" });
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use generic error messages to prevent user enumeration attacks

Returning different error messages for "User not found" and "Invalid password" allows an attacker to determine if an email exists in the database. For security reasons, use a generic error message.

Apply this diff to return a generic error message:

         if (!user) {
-          return res.status(404).json({ error: "User not found" });
+          return res.status(401).json({ error: "Invalid email or password" });
         }

         const validPassword = await bcrypt.compare(
           req.body.password,
           user.password
         );
         if (!validPassword) {
-          return res.status(401).json({ error: "Invalid password" });
+          return res.status(401).json({ error: "Invalid email or password" });
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return res.status(404).json({ error: "User not found" });
}
const validPassword = await bcrypt.compare(
req.body.password,
user.password
);
if (!validPassword) {
return res.status(401).json({ error: "Invalid password" });
}
if (!user) {
return res.status(401).json({ error: "Invalid email or password" });
}
const validPassword = await bcrypt.compare(
req.body.password,
user.password
);
if (!validPassword) {
return res.status(401).json({ error: "Invalid email or password" });
}

res.json({ message: "Login successful" });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider returning a token upon successful login

Currently, the loginUser function responds with a simple success message. It's common practice to generate and return a JWT or session token that the client can use for authenticated requests.

Would you like assistance in implementing token-based authentication?

} catch (error) {
res.status(500).json({ error: "Internal server error" });
}
}



module.exports = {
createUser,
loginUser
};
19 changes: 19 additions & 0 deletions backend/models/admin.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const adminSchema = new Schema({
name: String,
password: String,
email: String,
role: {
type: String,
default: "admin"
},
bio: String,
profilePicture: String,
});
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved

const Admin = mongoose.model("User", adminSchema);

module.exports = Admin;
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved
20 changes: 20 additions & 0 deletions backend/models/user.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;



const userSchema = new Schema({
name: String,
password: String,
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved
email: String,
role: {
type: String,
default: "user"
},
bio: String,
profilePicture: String,
});
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved

const User = mongoose.models.User || mongoose.model('User', userSchema);

module.exports = User;
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"license": "ISC",
"description": "",
"dependencies": {
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.21.0",
Expand Down
20 changes: 20 additions & 0 deletions backend/routes/adminRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const express = require("express");
const { createAdmin, loginAdmin } = require("../controller/admin.controller");
const router = express.Router();
require("dotenv").config();


router.get("/", (req, res) => {
res.json({
message: "Welcome to the Admin API!",
version: "1.0.0",
endpoints: {
login: "/login",
register: "/register",
},
documentation: "https://api-docs-url.com",});
});
router.post("/register", createAdmin);
router.post("/login", loginAdmin);

module.exports = router;
12 changes: 10 additions & 2 deletions backend/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const logger = require("../config/logger"); // Import your Winston logger
const router = express.Router();

let feedbackRouter;



try {
feedbackRouter = require("./feedbackRouter");
} catch (error) {
Expand All @@ -16,8 +19,6 @@ try {
};
}

router.use("/feedback", feedbackRouter);
router.use("/reservation", require("./reservationRouter"));

router.get("/", (req, res) => {
res.json({
Expand All @@ -31,4 +32,11 @@ router.get("/", (req, res) => {
});
});


router.use("/admin", require("./adminRouter"));
router.use("/feedback", feedbackRouter);
router.use("/user", require("./userRouter"));
router.use("/reservation", require("./reservationRouter"));


module.exports = router;
22 changes: 22 additions & 0 deletions backend/routes/userRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const express = require("express");
const { createUser, loginUser } = require("../controller/user.controller");
const router = express.Router();
require("dotenv").config();



router.get("/", (req, res) => {
res.json({
message: "Welcome to the User API!",
version: "1.0.0",
endpoints: {
login: "/login",
register: "/register",
},
documentation: "https://api-docs-url.com",});
});

router.post("/register", createUser);
router.post("/login", loginUser);
samar12-rad marked this conversation as resolved.
Show resolved Hide resolved

module.exports = router;
Loading