Skip to content

Commit

Permalink
Merge pull request #41 from atlp-rwanda/187454249-ft-middleware-fns
Browse files Browse the repository at this point in the history
187454249 ft middleware fns
  • Loading branch information
niyontwali authored Apr 29, 2024
2 parents 116a4a6 + 554b121 commit 01086fb
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 16 deletions.
4 changes: 4 additions & 0 deletions src/database/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ User.init(
RoleId: {
type: DataTypes.UUID,
defaultValue: UUIDV4,
references: {
model: Role,
key: 'id',
},
},
},
{ sequelize: sequelize, timestamps: true }
Expand Down
9 changes: 4 additions & 5 deletions src/helpers/token.generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import * as jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

dotenv.config();

export interface UserPayload {
id: string;
email: string;
}
// Function to generate token
export const userToken = async (userId: string, userEmail: string) => {
interface UserPayload {
id: string;
email: string;
}
const payload: UserPayload = {
id: userId,
email: userEmail,
Expand Down
71 changes: 71 additions & 0 deletions src/middlewares/authMiddlewares.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import { Request, Response, NextFunction } from 'express';
import { config } from 'dotenv';
import jwt from 'jsonwebtoken';

import logger from '../logs/config';
import User from '../database/models/user';
import Role from '../database/models/role';
import { sendInternalErrorResponse } from '../validations';

config();

export const isAuthenticated = (req: Request, res: Response, next: NextFunction) => {
try {
const authorization = req.headers.authorization;

if (!authorization) {
logger.error('Authentication required.');
return res.status(401).json({ message: 'Authentication required.' });
}
const token = authorization.split(' ')[1];
jwt.verify(token, process.env.SECRET_KEY!, async (err, decoded: any) => {
if (err) {
if (err.name === 'TokenExpiredError') {
logger.error('Token has expired.');
return res.status(401).json({ message: 'Token has expired.' });
}
logger.error('Invalid token.');
return res.status(401).json({ message: 'Invalid token.' });
}

const user = await User.findByPk(decoded.id, {
include: [
{
model: Role,
as: 'Role',
attributes: ['name'],
},
],
});

if (!user) {
logger.error('Authorized user not found');
return res.status(404).json({ message: 'Authorized user not found.' });
}
// Store the user and decoded token in the request for use in routes
req.user = user;

return next();
});
} catch (error) {
logger.error('Error while checking authentication.', error);
sendInternalErrorResponse(res, error);
}
};
// Middleware to check user roles
export const checkUserRoles = (requiredRole: string) => {
return async (req: Request, res: Response, next: NextFunction) => {
const user = (await req.user) as any;
const userRole = user.Role.name;

// Check if the user has all of the required roles
if (requiredRole !== userRole) {
logger.error('Access denied: Your role does not permit this action.');
return res.status(403).json({
message: 'Access denied: Your role does not permit this action.',
});
}
next();
};
};
7 changes: 4 additions & 3 deletions src/routes/roleRoute.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import { Router } from 'express';
import { createRole, deleteRole, getAllRoles, getSingleRole, updateRole } from '../controllers/roleControllers';

import { isAuthenticated, checkUserRoles } from '../middlewares/authMiddlewares';
const router = Router();

router.get('/', getAllRoles);
router.get('/', isAuthenticated, getAllRoles);
router.get('/:id', getSingleRole);
router.post('/', createRole);
router.post('/', isAuthenticated, checkUserRoles('admin'), createRole);
router.patch('/:id', updateRole);
router.delete('/:id', deleteRole);

Expand Down
16 changes: 9 additions & 7 deletions src/routes/userRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ import {
userVerify,
} from '../controllers/userController';
import multerUpload from '../helpers/multer';
import { checkUserRoles, isAuthenticated } from '../middlewares/authMiddlewares';

const router = Router();

router.post('/signup', signupUser);
router.get('/:page?', getAllUser);
router.get('/user/:id', getOneUser);
router.delete('/:id', deleteUser);
router.patch('/edit/:id', multerUpload.single('profileImage'), editUser); // remove id param
router.put('/role/:userId', editUserRole);
router.get('/:page?', isAuthenticated, getAllUser);
router.get('/user/:id', isAuthenticated, getOneUser);
router.delete('/:id', isAuthenticated, checkUserRoles('admin'), deleteUser);
router.patch('/edit/:id', isAuthenticated, multerUpload.single('profileImage'), editUser); // remove id param
router.put('/role/:userId', isAuthenticated, checkUserRoles('admin'), editUserRole);
router.get('/:token/verify-email', userVerify);
router.put('/deactivate/:userId', deactivateUserAccount);
router.put('/activate/:userId', activateUserAccount);
router.put('/deactivate/:userId', isAuthenticated, deactivateUserAccount);
router.put('/activate/:userId', isAuthenticated, checkUserRoles('admin'), activateUserAccount);

export default router;
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
"noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */,
"noImplicitReturns": false /* Enable error reporting for codepaths that do not explicitly return in a function. */,
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
Expand Down

0 comments on commit 01086fb

Please sign in to comment.