Fix jwt-token
This commit is contained in:
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user