const { describe, it, beforeEach, afterEach, before, after } = require('mocha'); const { expect } = require('chai'); const sinon = require('sinon'); const IPRestrictionMiddleware = require('../../middleware/ip-restriction'); const { setupTestEnvironment, teardownTestEnvironment, cleanDatabase, mockRequest, mockResponse, mockNext, createTestUser, createTestTenant } = require('../setup'); describe('IP Restriction Middleware', () => { let models, sequelize, ipRestriction; before(async () => { ({ models, sequelize } = await setupTestEnvironment()); ipRestriction = new IPRestrictionMiddleware(); ipRestriction.setModels(models); }); after(async () => { await teardownTestEnvironment(); }); beforeEach(async () => { await cleanDatabase(); }); describe('checkIPRestriction', () => { it('should allow access when IP restrictions disabled', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: false, ip_whitelist: '192.168.1.1,10.0.0.1' }); const req = mockRequest({ ip: '127.0.0.1', tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(next.errors).to.have.length(0); }); it('should allow access from allowed IP', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '192.168.1.1,10.0.0.1,127.0.0.1' }); const req = mockRequest({ ip: '192.168.1.1', tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(next.errors).to.have.length(0); }); it('should block access from non-allowed IP', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '192.168.1.1,10.0.0.1' }); const req = mockRequest({ ip: '192.168.2.1', tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(res.statusCode).to.equal(403); expect(res.data).to.have.property('success', false); expect(res.data).to.have.property('message'); expect(res.data).to.have.property('code', 'IP_RESTRICTED'); expect(res.data).to.have.property('timestamp'); }); it('should allow access when tenant not found', async () => { const req = mockRequest({ ip: '192.168.1.1', tenant: 'nonexistent-tenant' }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(next.errors).to.have.length(0); }); it('should extract IP from x-forwarded-for header', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '203.0.113.1' }); const req = mockRequest({ ip: '127.0.0.1', // Local proxy IP headers: { 'x-forwarded-for': '203.0.113.1, 198.51.100.1' }, tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(next.errors).to.have.length(0); }); it('should extract IP from x-real-ip header', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '203.0.113.2' }); const req = mockRequest({ ip: '127.0.0.1', headers: { 'x-real-ip': '203.0.113.2' }, tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(next.errors).to.have.length(0); }); it('should handle CIDR notation in allowed IPs', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '192.168.1.0/24,10.0.0.0/8' }); const req = mockRequest({ ip: '192.168.1.100', tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(next.errors).to.have.length(0); }); it('should block IP outside CIDR range', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '192.168.1.0/24' }); const req = mockRequest({ ip: '192.168.2.1', tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(res.statusCode).to.equal(403); }); it('should allow access from Docker container networks', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '192.168.1.1' }); const dockerIPs = ['172.17.0.1', '172.18.0.1', '172.19.0.1']; for (const ip of dockerIPs) { const req = mockRequest({ ip: ip, tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(next.errors).to.have.length(0); } }); it('should allow management routes regardless of IP restrictions', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, allowed_ips: '192.168.1.1' }); const managementPaths = [ '/api/management/status', '/api/management/health', '/api/management/system-info' ]; for (const path of managementPaths) { const req = mockRequest({ ip: '192.168.2.1', // Not in allowed IPs path: path, tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(next.errors).to.have.length(0); } }); it('should handle empty ip_whitelist', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '' }); const req = mockRequest({ ip: '192.168.1.1', tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(res.statusCode).to.equal(403); }); it('should handle null ip_whitelist', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: null }); const req = mockRequest({ ip: '192.168.1.1', tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(res.statusCode).to.equal(403); }); it('should log IP restriction events', async () => { const consoleLogSpy = sinon.spy(console, 'log'); const tenant = await createTestTenant({ slug: 'test-tenant', ip_restriction_enabled: true, ip_whitelist: '192.168.1.1' }); const req = mockRequest({ ip: '192.168.2.1', tenant: tenant.slug }); const res = mockResponse(); const next = mockNext(); await ipRestriction.checkIPRestriction(req, res, next); expect(consoleLogSpy.calledWith(sinon.match(/🚫.*IP Access Denied/))).to.be.true; consoleLogSpy.restore(); }); }); });