diff --git a/server/middleware/multi-tenant-auth.js b/server/middleware/multi-tenant-auth.js index 0d55cec..0c18ee7 100644 --- a/server/middleware/multi-tenant-auth.js +++ b/server/middleware/multi-tenant-auth.js @@ -279,6 +279,33 @@ class MultiTenantAuth { { expiresIn: '24h' } ); } + + /** + * Determine tenant from request (slug or subdomain) + */ + async determineTenant(req) { + try { + // Try to get tenant from subdomain first + const host = req.get('host') || ''; + const subdomain = host.split('.')[0]; + + // If subdomain is not localhost/IP, use it as tenant slug + if (subdomain && !subdomain.match(/^(localhost|127\.0\.0\.1|\d+\.\d+\.\d+\.\d+)$/)) { + return subdomain; + } + + // Fallback: get from user's tenant if authenticated + if (req.user && req.user.tenant_id) { + const tenant = await Tenant.findByPk(req.user.tenant_id); + return tenant ? tenant.slug : null; + } + + return null; + } catch (error) { + console.error('Error determining tenant:', error); + return null; + } + } } module.exports = MultiTenantAuth; diff --git a/server/routes/alert.js b/server/routes/alert.js index 1c95dce..e4a79cf 100644 --- a/server/routes/alert.js +++ b/server/routes/alert.js @@ -35,12 +35,20 @@ router.get('/rules', authenticateToken, async (req, res) => { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } @@ -53,7 +61,7 @@ router.get('/rules', authenticateToken, async (req, res) => { include: [{ model: User, as: 'user', - where: { tenant_id: tenantId }, + where: { tenant_id: tenant.id }, attributes: ['id', 'username', 'email'] }], limit: Math.min(parseInt(limit), 100), @@ -126,12 +134,20 @@ router.put('/rules/:id', authenticateToken, validateRequest(alertRuleSchema), as try { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } @@ -142,7 +158,7 @@ router.put('/rules/:id', authenticateToken, validateRequest(alertRuleSchema), as include: [{ model: User, as: 'user', - where: { tenant_id: tenantId }, + where: { tenant_id: tenant.id }, attributes: ['id'] }] }); @@ -177,12 +193,20 @@ router.delete('/rules/:id', authenticateToken, async (req, res) => { try { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } @@ -193,7 +217,7 @@ router.delete('/rules/:id', authenticateToken, async (req, res) => { include: [{ model: User, as: 'user', - where: { tenant_id: tenantId }, + where: { tenant_id: tenant.id }, attributes: ['id'] }] }); @@ -236,12 +260,20 @@ router.get('/logs', authenticateToken, async (req, res) => { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } @@ -263,7 +295,7 @@ router.get('/logs', authenticateToken, async (req, res) => { include: [{ model: User, as: 'user', - where: { tenant_id: tenantId }, + where: { tenant_id: tenant.id }, attributes: ['id', 'username'] }], attributes: ['id', 'name', 'priority'] @@ -302,12 +334,20 @@ router.get('/stats', authenticateToken, async (req, res) => { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } @@ -316,7 +356,7 @@ router.get('/stats', authenticateToken, async (req, res) => { include: [{ model: User, as: 'user', - where: { tenant_id: tenantId }, + where: { tenant_id: tenant.id }, attributes: [] }], attributes: ['id'] diff --git a/server/routes/dashboard.js b/server/routes/dashboard.js index 87f99b5..a26e536 100644 --- a/server/routes/dashboard.js +++ b/server/routes/dashboard.js @@ -14,17 +14,25 @@ router.get('/overview', authenticateToken, async (req, res) => { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } // Create base filter for tenant devices - const tenantDeviceFilter = { tenant_id: tenantId }; + const tenantDeviceFilter = { tenant_id: tenant.id }; // Get basic statistics - filtered by tenant const [ @@ -39,6 +47,7 @@ router.get('/overview', authenticateToken, async (req, res) => { DroneDetection.count({ include: [{ model: Device, + as: 'device', where: tenantDeviceFilter, attributes: [] }], @@ -47,6 +56,7 @@ router.get('/overview', authenticateToken, async (req, res) => { DroneDetection.count({ include: [{ model: Device, + as: 'device', where: tenantDeviceFilter, attributes: [] }], @@ -58,6 +68,7 @@ router.get('/overview', authenticateToken, async (req, res) => { DroneDetection.count({ include: [{ model: Device, + as: 'device', where: tenantDeviceFilter, attributes: [] }], @@ -145,12 +156,20 @@ router.get('/activity', authenticateToken, async (req, res) => { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } @@ -163,7 +182,7 @@ router.get('/activity', authenticateToken, async (req, res) => { include: [{ model: Device, as: 'device', - where: { tenant_id: tenantId }, + where: { tenant_id: tenant.id }, attributes: ['id', 'name', 'geo_lat', 'geo_lon', 'location_description'] }], limit: Math.min(parseInt(limit), 200), @@ -176,7 +195,7 @@ router.get('/activity', authenticateToken, async (req, res) => { include: [{ model: Device, as: 'device', - where: { tenant_id: tenantId }, + where: { tenant_id: tenant.id }, attributes: ['id', 'name', 'geo_lat', 'geo_lon'] }], limit: Math.min(parseInt(limit), 50), @@ -239,12 +258,20 @@ router.get('/charts/detections', authenticateToken, async (req, res) => { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } @@ -266,7 +293,8 @@ router.get('/charts/detections', authenticateToken, async (req, res) => { const detectionCounts = await DroneDetection.findAll({ include: [{ model: Device, - where: { tenant_id: tenantId }, + as: 'device', + where: { tenant_id: tenant.id }, attributes: [] }], where: { @@ -308,12 +336,20 @@ router.get('/charts/devices', authenticateToken, async (req, res) => { // Initialize multi-tenant auth to determine tenant const multiTenantAuth = new MultiTenantAuth(); - const tenantId = await multiTenantAuth.determineTenant(req); + const tenantSlug = await multiTenantAuth.determineTenant(req); - if (!tenantId) { - return res.status(403).json({ + if (!tenantSlug) { + return res.status(400).json({ success: false, - message: 'Access denied: No tenant context' + message: 'Unable to determine tenant' + }); + } + + const tenant = await Tenant.findOne({ where: { slug: tenantSlug } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' }); } @@ -329,7 +365,7 @@ router.get('/charts/devices', authenticateToken, async (req, res) => { include: [{ model: Device, as: 'device', - where: { tenant_id: tenantId }, + where: { tenant_id: tenant.id }, attributes: ['name', 'location_description'] }], group: ['device_id', 'device.id', 'device.name', 'device.location_description'],