From 8e6cd0de7170a935ede40061df847c661a6cafe3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 22 Jul 2024 11:49:22 -0700 Subject: [PATCH 1/6] Added verifyAdmin middleware. Implemented getAllUsers route --- Server/controllers/authController.js | 13 +++++++ Server/db/MongoDB.js | 12 ++++++ Server/middleware/verifyAdmin.js | 57 ++++++++++++++++++++++++++++ Server/routes/authRoute.js | 3 ++ Server/utils/messages.js | 1 + 5 files changed, 86 insertions(+) create mode 100644 Server/middleware/verifyAdmin.js diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 53fc9c384..28126c0a2 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -428,6 +428,18 @@ const deleteUserController = async (req, res, next) => { } }; +const getAllUsersController = async (req, res) => { + try { + const allUsers = await req.db.getAllUsers(req, res); + res + .status(200) + .json({ success: true, msg: "Got all users", data: allUsers }); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; + module.exports = { registerController, loginController, @@ -438,4 +450,5 @@ module.exports = { validateRecoveryTokenController, resetPasswordController, deleteUserController, + getAllUsersController, }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index a59822f98..52dc59084 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -159,6 +159,17 @@ const deleteUser = async (req, res) => { } }; +const getAllUsers = async (req, res) => { + try { + const users = await UserModel.find() + .select("-password") + .select("-profileImage"); + return users; + } catch (error) { + throw error; + } +}; + const requestInviteToken = async (req, res) => { try { await InviteToken.deleteMany({ email: req.body.email }); @@ -629,6 +640,7 @@ module.exports = { getUserByEmail, updateUser, deleteUser, + getAllUsers, requestInviteToken, requestRecoveryToken, validateRecoveryToken, diff --git a/Server/middleware/verifyAdmin.js b/Server/middleware/verifyAdmin.js new file mode 100644 index 000000000..7ef123451 --- /dev/null +++ b/Server/middleware/verifyAdmin.js @@ -0,0 +1,57 @@ +const jwt = require("jsonwebtoken"); +const logger = require("../utils/logger"); +const SERVICE_NAME = "verifyAdmin"; +const TOKEN_PREFIX = "Bearer "; +const { errorMessages } = require("../utils/messages"); +/** + * Verifies the JWT token + * @function + * @param {express.Request} req + * @param {express.Response} res + * @param {express.NextFunction} next + * @returns {express.Response} + */ +const verifyAdmin = (req, res, next) => { + const token = req.headers["authorization"]; + // Make sure a token is provided + if (!token) { + const error = new Error(errorMessages.NO_AUTH_TOKEN); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; + } + // Make sure it is properly formatted + if (!token.startsWith(TOKEN_PREFIX)) { + const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token + error.status = 400; + error.service = SERVICE_NAME; + next(error); + return; + } + + const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); + // verify admin role is present + jwt.verify(parsedToken, process.env.JWT_SECRET, (err, decoded) => { + if (err) { + logger.error(errorMessages.INVALID_AUTH_TOKEN, { + service: SERVICE_NAME, + }); + return res + .status(401) + .json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN }); + } + + if (decoded.role.includes("admin") === false) { + logger.error(errorMessages.INVALID_AUTH_TOKEN, { + service: SERVICE_NAME, + }); + return res + .status(401) + .json({ success: false, msg: errorMessages.UNAUTHORIZED }); + } + next(); + }); +}; + +module.exports = { verifyAdmin }; diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index 219032e26..02a1a67dd 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -1,5 +1,6 @@ const router = require("express").Router(); const { verifyJWT } = require("../middleware/verifyJWT"); +const { verifyAdmin } = require("../middleware/verifyAdmin"); const { verifyOwnership } = require("../middleware/verifyOwnership"); const multer = require("multer"); const upload = multer(); @@ -13,6 +14,7 @@ const { validateRecoveryTokenController, resetPasswordController, checkAdminController, + getAllUsersController, deleteUserController, inviteController, } = require("../controllers/authController"); @@ -27,6 +29,7 @@ router.post( userEditController ); router.get("/users/admin", checkAdminController); +router.get("/users", verifyJWT, verifyAdmin, getAllUsersController); router.delete( "/user/:userId", verifyJWT, diff --git a/Server/utils/messages.js b/Server/utils/messages.js index 787adc8f6..9ef5c6938 100644 --- a/Server/utils/messages.js +++ b/Server/utils/messages.js @@ -2,6 +2,7 @@ const errorMessages = { // General Errors: FRIENDLY_ERROR: "Something went wrong...", UNKNOWN_ERROR: "An unknown error occurred", + // Auth Controller UNAUTHORIZED: "Unauthorized access", AUTH_ADMIN_EXISTS: "Admin already exists", From d8947d090e81b9cc203922570a521f31e7c29387 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 22 Jul 2024 11:56:16 -0700 Subject: [PATCH 2/6] Added docs --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/README.md b/README.md index 16d5232c9..eae2d9e8c 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ You can see the designs [here](https://www.figma.com/design/RPSfaw66HjzSwzntKcgD - POST [/api/v1/auth/register](#post-register) - POST [/api/v1/auth/login](#post-login) - POST [/api/v1/auth/user/{userId}](#post-auth-user-edit-id) + - GET [/api/v1/auth/users](#get-all-users-id) - POST [/api/v1/auth/recovery/request](#post-auth-recovery-request-id) - POST [/api/v1/auth/recovery/validate](#post-auth-recovery-validate-id) - POST [/api/v1/auth/recovery/reset](#post-auth-recovery-reset-id) @@ -453,6 +454,55 @@ curl --request POST \ +
+GET/api/v1/auth/users + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | GET | + +###### Response Payload + +> | Type | Notes | +> | ------------- | ------------------------------------- | +> | `Array` | Returns an array containing all users | + +##### Sample CURL request + +``` +curl --request GET \ + --url http://localhost:5000/api/v1/auth/users \ + --header 'Authorization: \ + --header 'User-Agent: insomnia/2023.5.8' +``` + +##### Sample Resonse + +```json +{ + "success": true, + "msg": "Got all users", + "data": [ + { + "_id": "669e90072d5663d25808bc7b", + "firstName": "Alex", + "lastName": "Holliday", + "email": "test@test.com", + "isActive": true, + "isVerified": false, + "role": ["admin"], + "createdAt": "2024-07-22T16:59:51.695Z", + "updatedAt": "2024-07-22T16:59:51.695Z", + "__v": 0 + } + ] +} +``` + +
+
POST/api/v1/auth/recovery/request From 6aacfe0ca836967badedbb51f96706a4cd49cdc7 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Mon, 22 Jul 2024 16:35:57 -0400 Subject: [PATCH 3/6] Implemented fallback component --- Client/src/Components/Check/Check.jsx | 11 +++-- Client/src/Components/Fallback/index.css | 23 ++++++++++ Client/src/Components/Fallback/index.jsx | 44 +++++++++++++++++++ Client/src/Pages/Status/index.jsx | 23 +++++++++- .../src/assets/Images/create-placeholder.svg | 29 ++++++++++++ Client/src/assets/icons/check-outlined.svg | 3 ++ 6 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 Client/src/Components/Fallback/index.css create mode 100644 Client/src/Components/Fallback/index.jsx create mode 100644 Client/src/assets/Images/create-placeholder.svg create mode 100644 Client/src/assets/icons/check-outlined.svg diff --git a/Client/src/Components/Check/Check.jsx b/Client/src/Components/Check/Check.jsx index 501d0a344..b43754fa9 100644 --- a/Client/src/Components/Check/Check.jsx +++ b/Client/src/Components/Check/Check.jsx @@ -1,6 +1,7 @@ import "./check.css"; import PropTypes from "prop-types"; import CheckGrey from "../../assets/icons/check.svg?react"; +import CheckOutlined from "../../assets/icons/check-outlined.svg?react"; import { Stack, Typography } from "@mui/material"; import { useTheme } from "@emotion/react"; @@ -18,12 +19,12 @@ import { useTheme } from "@emotion/react"; * * @returns {React.Element} The `Check` component with a check icon and a label, defined by the `text` prop. */ -const Check = ({ text, variant = "info" }) => { +const Check = ({ text, variant = "info", outlined = false }) => { const theme = useTheme(); return ( { }`} alignItems="center" > - + {outlined ? ( + + ) : ( + + )} {text} ); diff --git a/Client/src/Components/Fallback/index.css b/Client/src/Components/Fallback/index.css new file mode 100644 index 000000000..31c706241 --- /dev/null +++ b/Client/src/Components/Fallback/index.css @@ -0,0 +1,23 @@ +[class*="fallback__"] { + width: 255px; + margin: auto; + margin-top: 100px; +} +[class*="fallback__"] h1.MuiTypography-root { + font-size: var(--env-var-font-size-large); + font-weight: 600; +} +[class*="fallback__"] .check span.MuiTypography-root, +[class*="fallback__"] h1.MuiTypography-root { + color: var(--env-var-color-5); +} +[class*="fallback__"] button.MuiButtonBase-root, +[class*="fallback__"] .check { + width: max-content; +} +[class*="fallback__"] button.MuiButtonBase-root { + height: 34px; +} +[class*="fallback__"] .check span.MuiTypography-root { + font-size: var(--env-var-font-size-medium); +} diff --git a/Client/src/Components/Fallback/index.jsx b/Client/src/Components/Fallback/index.jsx new file mode 100644 index 000000000..8df84feba --- /dev/null +++ b/Client/src/Components/Fallback/index.jsx @@ -0,0 +1,44 @@ +import { useTheme } from "@emotion/react"; +import { Stack, Typography } from "@mui/material"; +import Skeleton from "../../assets/Images/create-placeholder.svg?react"; +import Button from "../Button"; +import Check from "../Check/Check"; +import { useNavigate } from "react-router-dom"; +import "./index.css"; + +const Fallback = ({ title, checks, link = "/" }) => { + const theme = useTheme(); + const navigate = useNavigate(); + + return ( + + + + + A {title} is used to: + + {checks.map((check, index) => ( + + ))} + + +
+
); }; From d871907d976edc829845739826121443794da41f Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Mon, 22 Jul 2024 17:03:09 -0400 Subject: [PATCH 6/6] Pagination fix --- Client/src/Components/BasicTable/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Components/BasicTable/index.jsx b/Client/src/Components/BasicTable/index.jsx index fbd8759e5..93eba2024 100644 --- a/Client/src/Components/BasicTable/index.jsx +++ b/Client/src/Components/BasicTable/index.jsx @@ -91,7 +91,7 @@ const BasicTable = ({ data, paginated, reversed }) => { } let paginationComponent = <>; - if (paginated === true && displayData.length > rowsPerPage) { + if (paginated === true && data.rows.length > rowsPerPage) { paginationComponent = (