Initial commit: IT Nexus Web-App
This commit is contained in:
62
backend/src/middleware/auth.js
Normal file
62
backend/src/middleware/auth.js
Normal file
@@ -0,0 +1,62 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const JWT_CONFIG = require('../config/jwt');
|
||||
|
||||
/**
|
||||
* Middleware to verify JWT token and attach user to request
|
||||
*/
|
||||
function authenticateToken(req, res, next) {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
message: 'Access token required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, JWT_CONFIG.secret);
|
||||
req.user = decoded; // { id, username, email, role, roleId }
|
||||
next();
|
||||
} catch (error) {
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
message: 'Token expired'
|
||||
});
|
||||
}
|
||||
return res.status(403).json({
|
||||
status: 'error',
|
||||
message: 'Invalid token'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional authentication - doesn't fail if no token provided
|
||||
* But validates token if present
|
||||
*/
|
||||
function optionalAuth(req, res, next) {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
|
||||
if (!token) {
|
||||
req.user = null;
|
||||
return next();
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, JWT_CONFIG.secret);
|
||||
req.user = decoded;
|
||||
next();
|
||||
} catch (error) {
|
||||
req.user = null;
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
authenticateToken,
|
||||
optionalAuth
|
||||
};
|
||||
67
backend/src/middleware/errorHandler.js
Normal file
67
backend/src/middleware/errorHandler.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Custom error class for application errors
|
||||
*/
|
||||
class AppError extends Error {
|
||||
constructor(message, statusCode = 500, isOperational = true) {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
this.isOperational = isOperational;
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global error handling middleware
|
||||
*/
|
||||
function errorHandler(err, req, res, next) {
|
||||
let { statusCode = 500, message } = err;
|
||||
|
||||
// Log error for debugging
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.error('❌ Error:', err);
|
||||
}
|
||||
|
||||
// Don't leak error details in production for operational errors
|
||||
if (process.env.NODE_ENV === 'production' && !err.isOperational) {
|
||||
message = 'Something went wrong';
|
||||
}
|
||||
|
||||
// Prepare response
|
||||
const response = {
|
||||
status: 'error',
|
||||
message
|
||||
};
|
||||
|
||||
// Add stack trace in development
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
response.stack = err.stack;
|
||||
}
|
||||
|
||||
res.status(statusCode).json(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle 404 errors
|
||||
*/
|
||||
function notFoundHandler(req, res, next) {
|
||||
res.status(404).json({
|
||||
status: 'error',
|
||||
message: 'Route not found'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Async wrapper to catch errors in async route handlers
|
||||
*/
|
||||
function asyncHandler(fn) {
|
||||
return (req, res, next) => {
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AppError,
|
||||
errorHandler,
|
||||
notFoundHandler,
|
||||
asyncHandler
|
||||
};
|
||||
124
backend/src/middleware/roleCheck.js
Normal file
124
backend/src/middleware/roleCheck.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Role-based access control middleware
|
||||
*
|
||||
* Usage: requireRole(['super_admin', 'admin'])
|
||||
*/
|
||||
|
||||
const ROLES = {
|
||||
SUPER_ADMIN: 'super_admin',
|
||||
ADMIN: 'admin',
|
||||
BEARBEITER: 'bearbeiter',
|
||||
BENUTZER: 'benutzer',
|
||||
SUPPORT: 'support',
|
||||
HR_PERSONAL: 'hr_personal',
|
||||
BUCHHALTUNG: 'buchhaltung',
|
||||
TECHNIKER: 'produktion',
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware to check if user has one of the required roles
|
||||
* @param {Array<string>} allowedRoles - Array of role names that are allowed
|
||||
*/
|
||||
function requireRole(allowedRoles) {
|
||||
return (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
message: 'Authentication required'
|
||||
});
|
||||
}
|
||||
|
||||
if (!allowedRoles.includes(req.user.role)) {
|
||||
return res.status(403).json({
|
||||
status: 'error',
|
||||
message: 'Insufficient permissions'
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is Super Admin
|
||||
*/
|
||||
function requireSuperAdmin(req, res, next) {
|
||||
return requireRole([ROLES.SUPER_ADMIN])(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is Super Admin or Admin
|
||||
*/
|
||||
function requireAdmin(req, res, next) {
|
||||
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN])(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can modify FIDO keys (Super Admin, Admin, or Bearbeiter)
|
||||
*/
|
||||
function canModifyFidoKeys(req, res, next) {
|
||||
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.BEARBEITER])(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can view FIDO keys (all authenticated users)
|
||||
*/
|
||||
function canViewFidoKeys(req, res, next) {
|
||||
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.BEARBEITER, ROLES.BENUTZER])(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can view assets (Super Admin, Admin, Bearbeiter)
|
||||
*/
|
||||
function canViewAssets(req, res, next) {
|
||||
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.TECHNIKER])(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can modify assets (Super Admin, Admin only)
|
||||
*/
|
||||
function canModifyAssets(req, res, next) {
|
||||
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.TECHNIKER])(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can view tickets (all authenticated users)
|
||||
*/
|
||||
function canViewTickets(req, res, next) {
|
||||
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.SUPPORT, ROLES.BEARBEITER, ROLES.BENUTZER, ROLES.HR_PERSONAL, ROLES.BUCHHALTUNG, ROLES.TECHNIKER])(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can modify tickets (Super Admin, Admin, Support)
|
||||
*/
|
||||
function canModifyTickets(req, res, next) {
|
||||
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.SUPPORT])(req, res, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can view onboarding: admins + dept roles (hr_personal, buchhaltung, support used for IT)
|
||||
*/
|
||||
function canViewOnboarding(req, res, next) {
|
||||
return requireRole([
|
||||
ROLES.SUPER_ADMIN, ROLES.ADMIN,
|
||||
ROLES.HR_PERSONAL, ROLES.BUCHHALTUNG, ROLES.SUPPORT,
|
||||
])(req, res, next);
|
||||
}
|
||||
|
||||
// Departments visible per role
|
||||
const TECHNIKER_DEPARTMENTS = ['Produktion', 'Techniker'];
|
||||
|
||||
module.exports = {
|
||||
ROLES,
|
||||
TECHNIKER_DEPARTMENTS,
|
||||
requireRole,
|
||||
requireSuperAdmin,
|
||||
requireAdmin,
|
||||
canModifyFidoKeys,
|
||||
canViewFidoKeys,
|
||||
canViewAssets,
|
||||
canModifyAssets,
|
||||
canViewTickets,
|
||||
canModifyTickets,
|
||||
canViewOnboarding,
|
||||
};
|
||||
Reference in New Issue
Block a user