diff --git a/.env.example b/.env.example index db12b357..37c697fa 100644 --- a/.env.example +++ b/.env.example @@ -37,4 +37,8 @@ GOOGLE_CALLBACK_URL = "" # CLOUDINARY CONFIGURATION CLOUDINARY_NAME="" CLOUDINARY_KEY="" -CLOUDINARY_SECRET="" \ No newline at end of file +CLOUDINARY_SECRET="" + +#USER ADMIN CREDENTIALS +ADMIN_PASSWORD="" +ADMIN_PHONE="" \ No newline at end of file diff --git a/src/controllers/authController.ts b/src/controllers/authController.ts index 8bdfcd26..1183a78c 100644 --- a/src/controllers/authController.ts +++ b/src/controllers/authController.ts @@ -3,11 +3,10 @@ import { Request, Response, NextFunction } from 'express'; import passport from 'passport'; import jwt from 'jsonwebtoken'; -import bcrypt from 'bcrypt'; import User, { UserAttributes } from '../database/models/user'; import { sendInternalErrorResponse, validateFields } from '../validations'; import logger from '../logs/config'; -import { UUID } from 'crypto'; +import { passwordCompare, passwordEncrypt } from '../helpers/encrypt'; const authenticateViaGoogle = (req: Request, res: Response, next: NextFunction) => { passport.authenticate('google', (err: unknown, user: UserAttributes | null) => { @@ -74,7 +73,7 @@ const login = async (req: Request, res: Response): Promise => { } // Verify password - const passwordValid = await bcrypt.compare(password, user.password); + const passwordValid = await passwordCompare(password, user.password); if (!passwordValid) { logger.error('Invalid credentials'); res.status(404).json({ ok: false, message: 'Invalid credentials' }); @@ -102,12 +101,12 @@ const updatePassword = async (req: Request, res: Response): Promise => { // Access decoded user information from the request object const user = req.user as { - id: UUID; + id: string; password: string; }; // Check if old password matches with the given one - const match = await bcrypt.compare(oldPassword, user.password); + const match = await passwordCompare(oldPassword, user.password); if (!match) { res.status(400).json({ ok: false, @@ -117,8 +116,7 @@ const updatePassword = async (req: Request, res: Response): Promise => { } // Generate salt and hash new password - const saltRound = await bcrypt.genSalt(10); - const hashedNewPassword = await bcrypt.hash(newPassword, saltRound); + const hashedNewPassword = await passwordEncrypt(newPassword); // Update user's password await User.update( diff --git a/src/controllers/userController.ts b/src/controllers/userController.ts index c6db3c33..aec6f8ee 100644 --- a/src/controllers/userController.ts +++ b/src/controllers/userController.ts @@ -1,5 +1,4 @@ import { Request, Response } from 'express'; -import bcrypt from 'bcrypt'; import User from '../database/models/user'; import logger from '../logs/config'; import { userToken } from '../helpers/token.generator'; @@ -8,6 +7,7 @@ import * as jwt from 'jsonwebtoken'; import Role from '../database/models/role'; import { sendEmail } from '../helpers/send-email'; import { sendInternalErrorResponse, validateEmail, validateFields, validatePassword } from '../validations'; +import { passwordEncrypt } from '../helpers/encrypt'; // Function for user signup export const signupUser = async (req: Request, res: Response) => { @@ -41,8 +41,7 @@ export const signupUser = async (req: Request, res: Response) => { return res.status(400).json({ ok: false, error: 'Email is already used, Login to continuue' }); } - const saltRound = await bcrypt.genSalt(10); - const hashPassword = await bcrypt.hash(password, saltRound); + const hashPassword = await passwordEncrypt(password); const newUser = await User.create({ firstName, diff --git a/src/database/seeders/20240427082911-create-default-role.js b/src/database/seeders/20240427082911-create-default-role.js index 3f0bb578..5cb9369d 100644 --- a/src/database/seeders/20240427082911-create-default-role.js +++ b/src/database/seeders/20240427082911-create-default-role.js @@ -13,6 +13,13 @@ module.exports = { createdAt: new Date(), updatedAt: new Date(), }, + { + id: '6ef1e121-304a-4f08-ad4e-cd07f9578b52', + name: 'admin', + displayName: 'Admin Role', + createdAt: new Date(), + updatedAt: new Date(), + }, ]); }, diff --git a/src/database/seeders/20240501163745-User.js b/src/database/seeders/20240501163745-User.js new file mode 100644 index 00000000..3f1ebd8d --- /dev/null +++ b/src/database/seeders/20240501163745-User.js @@ -0,0 +1,34 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +'use strict'; +const { v4: uuidv4 } = require('uuid'); + +/** @type {import('sequelize-cli').Seed} */ +module.exports = { + async up(queryInterface, Sequelize) { + return queryInterface.bulkInsert( + 'Users', + [ + { + id: uuidv4(), + firstName: 'admin', + lastName: '', + email: process.env.EMAIL, + password: + '$2b$10$ZCgzouXesg4Zqgj22u7ale5aAOJzmjfOchCpMlSgBMV8o2f.zdYUq', + gender: 'not specified', + phoneNumber: process.env.ADMIN_PHONE, + verified: true, + createdAt: new Date(), + updatedAt: new Date(), + status: 'active', + RoleId: '6ef1e121-304a-4f08-ad4e-cd07f9578b52', // Replace with the actual RoleId + }, + ], + {} + ); + }, + + async down(queryInterface, Sequelize) { + return queryInterface.bulkDelete('Users', null, {}); + }, +}; diff --git a/src/docs/manageUserStatusDocs.yaml b/src/docs/manageUserStatusDocs.yaml index a461b6e4..75ca8aa7 100644 --- a/src/docs/manageUserStatusDocs.yaml +++ b/src/docs/manageUserStatusDocs.yaml @@ -3,6 +3,8 @@ paths: put: summary: Deactivate a user account description: Deactivates a user account and sends an email notification. + security: + - bearerAuth: [] parameters: - in: path name: userId @@ -10,7 +12,7 @@ paths: description: The ID of the user to deactivate schema: type: string - example: "123456789" + example: '123456789' responses: '200': description: Success response @@ -26,8 +28,8 @@ paths: type: string description: The status of the operation ('OK' for success) example: - message: "User deactivated successfully" - status: "OK" + message: 'User deactivated successfully' + status: 'OK' '400': description: Bad request content: @@ -39,7 +41,7 @@ paths: type: string description: A message indicating the error example: - message: "Invalid request parameters" + message: 'Invalid request parameters' '500': description: Internal server error content: @@ -51,11 +53,13 @@ paths: type: string description: A message indicating the error example: - message: "Internal server error occurred" + message: 'Internal server error occurred' /api/users/activate/{userId}: put: summary: Activate a user account + sercurity: + - bearerAuth: [] description: Activates a user account and sends an email notification. parameters: - in: path @@ -64,7 +68,7 @@ paths: description: The ID of the user to activate schema: type: string - example: "123456789" + example: '123456789' responses: '200': description: Success response @@ -80,8 +84,8 @@ paths: type: string description: The status of the operation ('OK' for success) example: - message: "User activated successfully" - status: "OK" + message: 'User activated successfully' + status: 'OK' '400': description: Bad request content: @@ -93,7 +97,7 @@ paths: type: string description: A message indicating the error example: - message: "Invalid request parameters" + message: 'Invalid request parameters' '500': description: Internal server error content: @@ -105,4 +109,12 @@ paths: type: string description: A message indicating the error example: - message: "Internal server error occurred" + message: 'Internal server error occurred' +security: + - bearerAuth: [] +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT diff --git a/src/docs/roles.yaml b/src/docs/roles.yaml index 635f3757..b4780d7b 100644 --- a/src/docs/roles.yaml +++ b/src/docs/roles.yaml @@ -1,10 +1,12 @@ -tags: +tags: - name: Roles description: The role API test paths: /api/roles: get: summary: Get a list of all roles + security: + - bearerAuth: [] tags: - Roles description: This tests the get request of roles @@ -12,10 +14,12 @@ paths: 200: description: Role successfully found 500: - description: Role can't be found + description: Role can't be found post: summary: Add the role + security: + - bearerAuth: [] tags: - Roles description: This add a new role to the role list @@ -39,6 +43,8 @@ paths: /api/roles/{id}: get: summary: Get a single role + security: + - bearerAuth: [] tags: - Roles parameters: @@ -47,12 +53,14 @@ paths: required: true type: string responses: - 404: + 404: description: Role could not be found 200: description: Role successfully found delete: summary: Find a role and delete it by id + security: + - bearerAuth: [] tags: - Roles parameters: @@ -61,14 +69,16 @@ paths: required: true type: string responses: - 404: + 404: description: Role could not be found 200: description: Role successfully deleted 500: - description: Role can't be deleted successfully + description: Role can't be deleted successfully patch: summary: Find a role by id and update it + security: + - bearerAuth: [] tags: - Roles parameters: @@ -77,9 +87,17 @@ paths: required: true type: string responses: - 404: + 404: description: Role could not be found 200: description: Role successfully updated 500: - description: Role can't be updated successfully + description: Role can't be updated successfully +security: + - bearerAuth: [] +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT diff --git a/src/docs/users.yaml b/src/docs/users.yaml index 99c0ab00..a3d3cf0e 100644 --- a/src/docs/users.yaml +++ b/src/docs/users.yaml @@ -1,10 +1,10 @@ -tags: +tags: - name: User description: The role API test paths: /api/users/signup: post: - summary: "Create a new user" + summary: 'Create a new user' tags: - User description: This create a new user @@ -37,6 +37,8 @@ paths: /api/users/{page}: get: summary: Get all users + security: + - bearerAuth: [] tags: - User parameters: @@ -52,6 +54,8 @@ paths: /api/users/edit/{id}: patch: summary: Edit a user data + security: + - bearerAuth: [] tags: - User parameters: @@ -86,10 +90,11 @@ paths: 500: description: Internal Server Error - /api/users/{id}: delete: summary: Delete a user + security: + - bearerAuth: [] tags: - User description: Delete a user @@ -109,6 +114,8 @@ paths: /api/users/role{id}: put: summary: Change a user's role + security: + - bearerAuth: [] tags: - User description: Change a user role @@ -128,6 +135,8 @@ paths: /api/users/user/{id}: get: summary: Get a single user + security: + - bearerAuth: [] tags: - User parameters: @@ -137,11 +146,11 @@ paths: type: string responses: 200: - description: "Successfully Get User" + description: 'Successfully Get User' 404: - description: "User with this ID does not exits" + description: 'User with this ID does not exits' 500: - description: "Internal Server Error" + description: 'Internal Server Error' /api/users/{token}/verify-email: get: @@ -155,13 +164,13 @@ paths: type: string responses: 201: - description: "Account verified, Login to continue." + description: 'Account verified, Login to continue.' 400: - description: "Verification failed. Try again later" + description: 'Verification failed. Try again later' 403: - description: "Verification link has expired. Please request a new one." + description: 'Verification link has expired. Please request a new one.' 500: - description: "Internal Server Error" + description: 'Internal Server Error' /api/users/resend-verify: post: @@ -185,4 +194,12 @@ paths: 400: description: Email is already used, Login to continuue 500: - description: "Internal Server Error" \ No newline at end of file + description: 'Internal Server Error' +security: + - bearerAuth: [] +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT diff --git a/src/helpers/encrypt.ts b/src/helpers/encrypt.ts new file mode 100644 index 00000000..4d7a91ec --- /dev/null +++ b/src/helpers/encrypt.ts @@ -0,0 +1,10 @@ +import bcrypt from 'bcrypt'; + +export const passwordEncrypt = async (password: string) => { + const saltRound = await bcrypt.genSalt(12); + const hashedPwd = await bcrypt.hash(password, saltRound); + return hashedPwd; +}; +export const passwordCompare = async (password: string, inputPwd: string) => { + return await bcrypt.compare(password, inputPwd); +};