Fix jwt-token

This commit is contained in:
2025-09-20 06:46:48 +02:00
parent ec41acca48
commit 1031f77ff6
2 changed files with 207 additions and 30 deletions

View File

@@ -12,9 +12,11 @@ const { Tenant, User } = require('../models');
const { authenticateToken } = require('../middleware/auth');
const { requirePermissions, requireAnyPermission, hasPermission } = require('../middleware/rbac');
const MultiTenantAuth = require('../middleware/multi-tenant-auth');
const { SecurityLogger } = require('../middleware/logger');
// Initialize multi-tenant auth
// Initialize multi-tenant auth and security logger
const multiAuth = new MultiTenantAuth();
const securityLogger = new SecurityLogger();
// Configure multer for logo uploads
const storage = multer.diskStorage({
@@ -108,6 +110,93 @@ router.get('/info', authenticateToken, requirePermissions(['tenant.view']), asyn
}
});
/**
* Validates user has access to tenant and logs the validation
*/
async function validateTenantAccess(req, res, action) {
try {
// Validate user is authenticated
if (!req.user || !req.user.id) {
securityLogger.logSecurityEvent('error', 'Tenant logo access attempt without valid user authentication', {
action,
ip: req.ip,
userAgent: req.get('User-Agent'),
path: req.path
});
return { success: false, message: 'Authentication required', status: 401 };
}
// Determine tenant from request
const tenantId = await multiAuth.determineTenant(req);
if (!tenantId) {
securityLogger.logSecurityEvent('error', `Failed to determine tenant for ${action}`, {
action,
userId: req.user.id,
username: req.user.username,
ip: req.ip,
userAgent: req.get('User-Agent'),
path: req.path
});
return { success: false, message: 'Invalid tenant', status: 400 };
}
// Get tenant by slug
const tenant = await Tenant.findOne({ where: { slug: tenantId } });
if (!tenant) {
securityLogger.logSecurityEvent('error', `Tenant not found for ${action}`, {
action,
tenantSlug: tenantId,
userId: req.user.id,
username: req.user.username,
ip: req.ip,
userAgent: req.get('User-Agent'),
path: req.path
});
return { success: false, message: 'Tenant not found', status: 404 };
}
// Validate user belongs to this tenant
if (req.user.tenant_id !== tenant.id) {
securityLogger.logSecurityEvent('warning', `User attempted ${action} on different tenant`, {
action,
userId: req.user.id,
username: req.user.username,
userTenantId: req.user.tenant_id,
requestedTenantId: tenant.id,
tenantSlug: tenantId,
ip: req.ip,
userAgent: req.get('User-Agent'),
path: req.path
});
return { success: false, message: 'Access denied: User not member of tenant', status: 403 };
}
securityLogger.logSecurityEvent('info', `${action} access validated`, {
action,
userId: req.user.id,
username: req.user.username,
tenantId: tenant.id,
tenantSlug: tenantId,
ip: req.ip,
userAgent: req.get('User-Agent'),
path: req.path
});
return { success: true, tenant };
} catch (error) {
securityLogger.logSecurityEvent('error', `Error validating tenant access for ${action}`, {
action,
error: error.message,
userId: req.user?.id,
username: req.user?.username,
ip: req.ip,
userAgent: req.get('User-Agent'),
path: req.path
});
return { success: false, message: 'Internal server error', status: 500 };
}
}
/**
* POST /tenant/logo-upload
* Upload tenant logo (branding admin or higher)
@@ -115,29 +204,33 @@ router.get('/info', authenticateToken, requirePermissions(['tenant.view']), asyn
router.post('/logo-upload', authenticateToken, requirePermissions(['branding.edit']), upload.single('logo'), async (req, res) => {
try {
if (!req.file) {
securityLogger.logSecurityEvent('warning', 'Logo upload attempted without file', {
action: 'logo_upload',
userId: req.user?.id,
username: req.user?.username,
ip: req.ip,
userAgent: req.get('User-Agent')
});
return res.status(400).json({
success: false,
message: 'No file uploaded'
});
}
// Determine tenant from request
const tenantId = await multiAuth.determineTenant(req);
if (!tenantId) {
return res.status(400).json({
// Enhanced security validation
const validation = await validateTenantAccess(req, res, 'logo_upload');
if (!validation.success) {
// Clean up uploaded file on validation failure
if (fs.existsSync(req.file.path)) {
fs.unlinkSync(req.file.path);
}
return res.status(validation.status).json({
success: false,
message: 'Invalid tenant'
message: validation.message
});
}
// Get tenant by slug
const tenant = await Tenant.findOne({ where: { slug: tenantId } });
if (!tenant) {
return res.status(404).json({
success: false,
message: 'Tenant not found'
});
}
const { tenant } = validation;
// Clean up old logo file if it exists
if (tenant.branding?.logo_url) {
@@ -145,6 +238,14 @@ router.post('/logo-upload', authenticateToken, requirePermissions(['branding.edi
const oldFilePath = path.join(__dirname, '../uploads/logos', oldLogoPath);
if (fs.existsSync(oldFilePath)) {
fs.unlinkSync(oldFilePath);
securityLogger.logSecurityEvent('info', 'Old logo file cleaned up', {
action: 'logo_upload',
oldLogoPath,
userId: req.user.id,
username: req.user.username,
tenantId: tenant.id,
tenantSlug: tenant.slug
});
}
}
@@ -159,7 +260,21 @@ router.post('/logo-upload', authenticateToken, requirePermissions(['branding.edi
await tenant.update({ branding: updatedBranding });
console.log(`✅ Tenant "${tenantId}" logo uploaded by user "${req.user.username}"`);
// Audit log successful upload
securityLogger.logSecurityEvent('info', 'Tenant logo uploaded successfully', {
action: 'logo_upload',
userId: req.user.id,
username: req.user.username,
tenantId: tenant.id,
tenantSlug: tenant.slug,
fileName: req.file.filename,
fileSize: req.file.size,
logoUrl,
ip: req.ip,
userAgent: req.get('User-Agent')
});
console.log(`✅ Tenant "${tenant.slug}" logo uploaded by user "${req.user.username}"`);
res.json({
success: true,
@@ -173,6 +288,17 @@ router.post('/logo-upload', authenticateToken, requirePermissions(['branding.edi
} catch (error) {
console.error('Error uploading logo:', error);
// Audit log the error
securityLogger.logSecurityEvent('error', 'Logo upload failed with error', {
action: 'logo_upload',
error: error.message,
userId: req.user?.id,
username: req.user?.username,
fileName: req.file?.filename,
ip: req.ip,
userAgent: req.get('User-Agent')
});
// Clean up uploaded file on error
if (req.file && fs.existsSync(req.file.path)) {
fs.unlinkSync(req.file.path);
@@ -191,37 +317,49 @@ router.post('/logo-upload', authenticateToken, requirePermissions(['branding.edi
*/
router.delete('/logo', authenticateToken, requirePermissions(['branding.edit']), async (req, res) => {
try {
// Determine tenant from request
const tenantId = await multiAuth.determineTenant(req);
if (!tenantId) {
return res.status(400).json({
// Enhanced security validation
const validation = await validateTenantAccess(req, res, 'logo_removal');
if (!validation.success) {
return res.status(validation.status).json({
success: false,
message: 'Unable to determine tenant from request'
message: validation.message
});
}
// Get tenant
const tenant = await Tenant.findOne({ where: { slug: tenantId } });
if (!tenant) {
return res.status(404).json({
success: false,
message: 'Tenant not found'
});
}
const { tenant } = validation;
// Check if tenant has a logo to remove
if (!tenant.branding?.logo_url) {
securityLogger.logSecurityEvent('warning', 'Logo removal attempted when no logo exists', {
action: 'logo_removal',
userId: req.user.id,
username: req.user.username,
tenantId: tenant.id,
tenantSlug: tenant.slug,
ip: req.ip,
userAgent: req.get('User-Agent')
});
return res.status(400).json({
success: false,
message: 'No logo to remove'
});
}
const oldLogoUrl = tenant.branding.logo_url;
// Delete the physical logo file
const logoPath = tenant.branding.logo_url.replace('/uploads/logos/', '');
const filePath = path.join(__dirname, '../uploads/logos', logoPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
securityLogger.logSecurityEvent('info', 'Logo file deleted from filesystem', {
action: 'logo_removal',
logoPath,
userId: req.user.id,
username: req.user.username,
tenantId: tenant.id,
tenantSlug: tenant.slug
});
}
// Update tenant branding to remove logo
@@ -232,7 +370,19 @@ router.delete('/logo', authenticateToken, requirePermissions(['branding.edit']),
await tenant.update({ branding: updatedBranding });
console.log(`✅ Tenant "${tenantId}" logo removed by user "${req.user.username}"`);
// Audit log successful removal
securityLogger.logSecurityEvent('info', 'Tenant logo removed successfully', {
action: 'logo_removal',
userId: req.user.id,
username: req.user.username,
tenantId: tenant.id,
tenantSlug: tenant.slug,
removedLogoUrl: oldLogoUrl,
ip: req.ip,
userAgent: req.get('User-Agent')
});
console.log(`✅ Tenant "${tenant.slug}" logo removed by user "${req.user.username}"`);
res.json({
success: true,
@@ -244,6 +394,17 @@ router.delete('/logo', authenticateToken, requirePermissions(['branding.edit']),
} catch (error) {
console.error('Error removing logo:', error);
// Audit log the error
securityLogger.logSecurityEvent('error', 'Logo removal failed with error', {
action: 'logo_removal',
error: error.message,
userId: req.user?.id,
username: req.user?.username,
ip: req.ip,
userAgent: req.get('User-Agent')
});
res.status(500).json({
success: false,
message: 'Failed to remove logo'