Fix jwt-token

This commit is contained in:
2025-09-13 12:13:16 +02:00
parent 65b7e0d965
commit 04e9f548e6
5 changed files with 582 additions and 8 deletions

View File

@@ -2,6 +2,7 @@ const express = require('express');
const router = express.Router();
// Import route modules
const managementRoutes = require('./management');
const deviceRoutes = require('./device');
const userRoutes = require('./user');
const alertRoutes = require('./alert');
@@ -13,6 +14,9 @@ const detectorsRoutes = require('./detectors');
const detectionsRoutes = require('./detections');
const droneTypesRoutes = require('./droneTypes');
// Management portal routes (before API versioning)
router.use('/management', managementRoutes);
// API versioning
router.use('/v1/devices', deviceRoutes);
router.use('/v1/users', userRoutes);

469
server/routes/management.js Normal file
View File

@@ -0,0 +1,469 @@
/**
* Management Portal API Routes
* Completely separate authentication system for security isolation
*/
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const { Tenant, User } = require('../models');
// Management-specific authentication middleware - NO shared auth with tenants
const requireManagementAuth = (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({
success: false,
message: 'Management authentication required'
});
}
try {
// Use separate JWT secret for management
const MANAGEMENT_SECRET = process.env.MANAGEMENT_JWT_SECRET || 'mgmt-super-secret-change-in-production';
const decoded = jwt.verify(token, MANAGEMENT_SECRET);
// Verify this is a management token with proper role
if (!decoded.isManagement || !['super_admin', 'platform_admin'].includes(decoded.role)) {
return res.status(403).json({
success: false,
message: 'Insufficient management privileges'
});
}
req.managementUser = {
id: decoded.userId,
username: decoded.username,
role: decoded.role,
isManagement: true
};
next();
} catch (error) {
return res.status(403).json({
success: false,
message: 'Invalid management token',
error: error.message
});
}
};
// Management login endpoint - separate from tenant auth
router.post('/auth/login', async (req, res) => {
try {
const { username, password } = req.body;
// Hardcoded management users for now (should be in separate DB table)
const MANAGEMENT_USERS = {
'admin': {
password: await bcrypt.hash('admin123', 10), // Change this!
role: 'super_admin'
},
'platform_admin': {
password: await bcrypt.hash('platform123', 10), // Change this!
role: 'platform_admin'
}
};
const managementUser = MANAGEMENT_USERS[username];
if (!managementUser || !await bcrypt.compare(password, managementUser.password)) {
return res.status(401).json({
success: false,
message: 'Invalid management credentials'
});
}
const MANAGEMENT_SECRET = process.env.MANAGEMENT_JWT_SECRET || 'mgmt-super-secret-change-in-production';
const token = jwt.sign({
userId: username,
username: username,
role: managementUser.role,
isManagement: true
}, MANAGEMENT_SECRET, { expiresIn: '8h' });
res.json({
success: true,
token,
user: {
username,
role: managementUser.role
}
});
} catch (error) {
res.status(500).json({
success: false,
message: 'Management authentication error',
error: error.message
});
}
});
// Apply management authentication to all other routes
router.use(requireManagementAuth);
// Security audit logging for all management operations
router.use((req, res, next) => {
const auditLog = {
timestamp: new Date().toISOString(),
user: req.managementUser.username,
role: req.managementUser.role,
method: req.method,
path: req.path,
ip: req.ip,
userAgent: req.headers['user-agent']
};
console.log('[MANAGEMENT AUDIT]', JSON.stringify(auditLog));
next();
});
/**
* GET /api/management/system-info - Platform system information
*/
router.get('/system-info', async (req, res) => {
try {
const tenantCount = await Tenant.count();
const userCount = await User.count();
res.json({
success: true,
data: {
platform: {
name: 'UAMILS Platform',
version: '1.0.0',
environment: process.env.NODE_ENV || 'development'
},
statistics: {
tenants: tenantCount,
total_users: userCount,
uptime: process.uptime()
},
security: {
management_access_level: req.managementUser.role,
last_backup: process.env.LAST_BACKUP_DATE || 'Not configured'
}
}
});
} catch (error) {
console.error('Management: Error fetching system info:', error);
res.status(500).json({
success: false,
message: 'Failed to fetch system information',
error: error.message
});
}
});
/**
* GET /api/management/tenants - List all tenants with admin details
*/
router.get('/tenants', async (req, res) => {
try {
const { limit = 50, offset = 0, search, auth_provider } = req.query;
const whereClause = {};
if (search) {
whereClause[Op.or] = [
{ name: { [Op.iLike]: `%${search}%` } },
{ slug: { [Op.iLike]: `%${search}%` } },
{ domain: { [Op.iLike]: `%${search}%` } }
];
}
if (auth_provider) {
whereClause.auth_provider = auth_provider;
}
const tenants = await Tenant.findAndCountAll({
where: whereClause,
include: [{
model: User,
as: 'users',
attributes: ['id', 'username', 'email', 'role', 'last_login', 'created_at'],
limit: 10
}],
limit: Math.min(parseInt(limit), 100),
offset: parseInt(offset),
order: [['created_at', 'DESC']]
});
res.json({
success: true,
data: tenants.rows,
pagination: {
total: tenants.count,
limit: parseInt(limit),
offset: parseInt(offset),
pages: Math.ceil(tenants.count / parseInt(limit))
}
});
} catch (error) {
console.error('Management: Error fetching tenants:', error);
res.status(500).json({
success: false,
message: 'Failed to fetch tenants'
});
}
});
/**
* POST /api/management/tenants - Create new tenant
*/
router.post('/tenants', async (req, res) => {
try {
const tenantData = req.body;
// Enhanced validation for management portal
if (!tenantData.name || !tenantData.slug) {
return res.status(400).json({
success: false,
message: 'Name and slug are required'
});
}
// Check for unique slug
const existingTenant = await Tenant.findOne({ where: { slug: tenantData.slug } });
if (existingTenant) {
return res.status(409).json({
success: false,
message: 'Tenant slug already exists'
});
}
// Log management action
console.log(`Management: Admin ${req.user.username} creating tenant: ${tenantData.name}`);
const tenant = await Tenant.create(tenantData);
res.status(201).json({
success: true,
data: tenant,
message: 'Tenant created successfully'
});
} catch (error) {
console.error('Management: Error creating tenant:', error);
res.status(500).json({
success: false,
message: 'Failed to create tenant'
});
}
});
/**
* GET /api/management/tenants/:id - Get tenant details
*/
router.get('/tenants/:id', async (req, res) => {
try {
const tenant = await Tenant.findByPk(req.params.id, {
include: [{
model: User,
as: 'users',
attributes: ['id', 'username', 'email', 'role', 'last_login', 'created_at']
}]
});
if (!tenant) {
return res.status(404).json({
success: false,
message: 'Tenant not found'
});
}
res.json({
success: true,
data: tenant
});
} catch (error) {
console.error('Management: Error fetching tenant:', error);
res.status(500).json({
success: false,
message: 'Failed to fetch tenant'
});
}
});
/**
* PUT /api/management/tenants/:id - Update tenant
*/
router.put('/tenants/:id', async (req, res) => {
try {
const tenant = await Tenant.findByPk(req.params.id);
if (!tenant) {
return res.status(404).json({
success: false,
message: 'Tenant not found'
});
}
// Log management action
console.log(`Management: Admin ${req.user.username} updating tenant: ${tenant.name}`);
await tenant.update(req.body);
res.json({
success: true,
data: tenant,
message: 'Tenant updated successfully'
});
} catch (error) {
console.error('Management: Error updating tenant:', error);
res.status(500).json({
success: false,
message: 'Failed to update tenant'
});
}
});
/**
* DELETE /api/management/tenants/:id - Delete tenant
*/
router.delete('/tenants/:id', async (req, res) => {
try {
const tenant = await Tenant.findByPk(req.params.id);
if (!tenant) {
return res.status(404).json({
success: false,
message: 'Tenant not found'
});
}
// Prevent deletion of default tenant
if (tenant.slug === 'default') {
return res.status(403).json({
success: false,
message: 'Cannot delete default tenant'
});
}
// Log management action
console.log(`Management: Admin ${req.user.username} deleting tenant: ${tenant.name}`);
await tenant.destroy();
res.json({
success: true,
message: 'Tenant deleted successfully'
});
} catch (error) {
console.error('Management: Error deleting tenant:', error);
res.status(500).json({
success: false,
message: 'Failed to delete tenant'
});
}
});
/**
* GET /api/management/users - List all users across tenants
*/
router.get('/users', async (req, res) => {
try {
const { limit = 50, offset = 0, search, role, tenant_id } = req.query;
const whereClause = {};
if (search) {
whereClause[Op.or] = [
{ username: { [Op.iLike]: `%${search}%` } },
{ email: { [Op.iLike]: `%${search}%` } }
];
}
if (role) {
whereClause.role = role;
}
if (tenant_id) {
whereClause.tenant_id = tenant_id;
}
const users = await User.findAndCountAll({
where: whereClause,
include: [{
model: Tenant,
as: 'tenant',
attributes: ['id', 'name', 'slug']
}],
attributes: { exclude: ['password'] }, // Never expose passwords
limit: Math.min(parseInt(limit), 100),
offset: parseInt(offset),
order: [['created_at', 'DESC']]
});
res.json({
success: true,
data: users.rows,
pagination: {
total: users.count,
limit: parseInt(limit),
offset: parseInt(offset),
pages: Math.ceil(users.count / parseInt(limit))
}
});
} catch (error) {
console.error('Management: Error fetching users:', error);
res.status(500).json({
success: false,
message: 'Failed to fetch users'
});
}
});
/**
* GET /api/management/system/info - System information
*/
router.get('/system/info', async (req, res) => {
try {
// Gather system statistics
const tenantCount = await Tenant.count();
const userCount = await User.count();
const activeTenantsCount = await Tenant.count({ where: { is_active: true } });
const systemInfo = {
version: process.env.APP_VERSION || '1.0.0',
environment: process.env.NODE_ENV || 'production',
uptime: process.uptime(),
database: {
status: 'connected',
version: 'PostgreSQL 15',
connections: 5, // You can get this from pg pool
maxConnections: 100
},
statistics: {
tenants: tenantCount,
users: userCount,
activeTenants: activeTenantsCount
},
memory: {
used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024) + 'MB',
total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024) + 'MB',
percentage: Math.round((process.memoryUsage().heapUsed / process.memoryUsage().heapTotal) * 100)
},
lastBackup: new Date(Date.now() - 24*60*60*1000).toISOString(), // Mock
ssl: {
status: 'valid',
expiresAt: new Date(Date.now() + 90*24*60*60*1000).toISOString() // Mock 90 days
}
};
res.json({
success: true,
data: systemInfo
});
} catch (error) {
console.error('Management: Error fetching system info:', error);
res.status(500).json({
success: false,
message: 'Failed to fetch system information'
});
}
});
module.exports = router;