"""Gateway management API routes.""" from fastapi import APIRouter, Depends, HTTPException, status, Response from sqlalchemy.orm import Session from ..database import get_db from ..models.gateway import Gateway from ..models.user import User, UserRole from ..models.access import UserGatewayAccess from ..models.vpn_connection_log import VPNConnectionLog from ..schemas.gateway import GatewayCreate, GatewayUpdate, GatewayResponse, GatewayStatus from ..services.vpn_sync_service import VPNSyncService from .deps import get_current_user, require_admin router = APIRouter() def get_accessible_gateways_query(db: Session, user: User): """Get query for gateways accessible by user.""" if user.role == UserRole.SUPER_ADMIN: return db.query(Gateway) elif user.role == UserRole.ADMIN: return db.query(Gateway).filter(Gateway.tenant_id == user.tenant_id) else: # Technicians and viewers only see assigned gateways return db.query(Gateway).join( UserGatewayAccess, UserGatewayAccess.gateway_id == Gateway.id ).filter(UserGatewayAccess.user_id == user.id) @router.get("/", response_model=list[GatewayResponse]) def list_gateways( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """List gateways accessible by current user.""" query = get_accessible_gateways_query(db, current_user) gateways = query.offset(skip).limit(limit).all() return gateways @router.get("/status", response_model=list[GatewayStatus]) def get_gateways_status( db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """Get online status of all accessible gateways.""" query = get_accessible_gateways_query(db, current_user) gateways = query.all() return [ GatewayStatus( id=gw.id, name=gw.name, is_online=gw.is_online, last_seen=gw.last_seen, vpn_ip=gw.vpn_ip ) for gw in gateways ] @router.post("/", response_model=GatewayResponse, status_code=status.HTTP_201_CREATED) def create_gateway( gateway_data: GatewayCreate, db: Session = Depends(get_db), current_user: User = Depends(require_admin) ): """Create a new gateway (admin only).""" # Determine tenant_id if current_user.role == UserRole.SUPER_ADMIN: # Super admin must specify tenant_id via query param or we use a default tenant_id = current_user.tenant_id or 1 # Fallback else: tenant_id = current_user.tenant_id # Generate VPN certificate CN vpn_cert_cn = f"gateway-{gateway_data.name.lower().replace(' ', '-')}" gateway = Gateway( **gateway_data.model_dump(), tenant_id=tenant_id, vpn_cert_cn=vpn_cert_cn ) db.add(gateway) db.commit() db.refresh(gateway) return gateway @router.get("/{gateway_id}", response_model=GatewayResponse) def get_gateway( gateway_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """Get gateway by ID.""" query = get_accessible_gateways_query(db, current_user) gateway = query.filter(Gateway.id == gateway_id).first() if not gateway: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Gateway not found or access denied" ) return gateway @router.put("/{gateway_id}", response_model=GatewayResponse) def update_gateway( gateway_id: int, gateway_data: GatewayUpdate, db: Session = Depends(get_db), current_user: User = Depends(require_admin) ): """Update gateway (admin only).""" gateway = db.query(Gateway).filter(Gateway.id == gateway_id).first() if not gateway: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Gateway not found" ) # Check tenant access if current_user.role != UserRole.SUPER_ADMIN: if gateway.tenant_id != current_user.tenant_id: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Cannot modify gateways from other tenants" ) update_data = gateway_data.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(gateway, field, value) db.commit() db.refresh(gateway) return gateway @router.delete("/{gateway_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_gateway( gateway_id: int, db: Session = Depends(get_db), current_user: User = Depends(require_admin) ): """Delete gateway (admin only).""" gateway = db.query(Gateway).filter(Gateway.id == gateway_id).first() if not gateway: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Gateway not found" ) # Check tenant access if current_user.role != UserRole.SUPER_ADMIN: if gateway.tenant_id != current_user.tenant_id: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Cannot delete gateways from other tenants" ) db.delete(gateway) db.commit() @router.get("/{gateway_id}/provision") def download_provisioning_package( gateway_id: int, db: Session = Depends(get_db), current_user: User = Depends(require_admin) ): """Download provisioning package for gateway. DEPRECATED: Use VPN profiles for provisioning. GET /api/gateways/{gateway_id}/profiles/{profile_id}/provision """ gateway = db.query(Gateway).filter(Gateway.id == gateway_id).first() if not gateway: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Gateway not found" ) raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Provisioning is now done through VPN profiles. " "Please create a VPN profile for this gateway first, " "then use GET /api/gateways/{gateway_id}/profiles/{profile_id}/provision" ) @router.post("/{gateway_id}/access/{user_id}", status_code=status.HTTP_201_CREATED) def grant_gateway_access( gateway_id: int, user_id: int, db: Session = Depends(get_db), current_user: User = Depends(require_admin) ): """Grant user access to gateway.""" gateway = db.query(Gateway).filter(Gateway.id == gateway_id).first() if not gateway: raise HTTPException(status_code=404, detail="Gateway not found") user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException(status_code=404, detail="User not found") # Check existing access existing = db.query(UserGatewayAccess).filter( UserGatewayAccess.gateway_id == gateway_id, UserGatewayAccess.user_id == user_id ).first() if existing: raise HTTPException(status_code=400, detail="Access already granted") access = UserGatewayAccess( user_id=user_id, gateway_id=gateway_id, granted_by_id=current_user.id ) db.add(access) db.commit() return {"message": "Access granted"} @router.delete("/{gateway_id}/access/{user_id}", status_code=status.HTTP_204_NO_CONTENT) def revoke_gateway_access( gateway_id: int, user_id: int, db: Session = Depends(get_db), current_user: User = Depends(require_admin) ): """Revoke user access to gateway.""" access = db.query(UserGatewayAccess).filter( UserGatewayAccess.gateway_id == gateway_id, UserGatewayAccess.user_id == user_id ).first() if not access: raise HTTPException(status_code=404, detail="Access not found") db.delete(access) db.commit() @router.get("/{gateway_id}/vpn-logs") def get_gateway_vpn_logs( gateway_id: int, limit: int = 50, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """Get VPN connection logs for a gateway.""" # Check access query = get_accessible_gateways_query(db, current_user) gateway = query.filter(Gateway.id == gateway_id).first() if not gateway: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Gateway not found or access denied" ) sync_service = VPNSyncService(db) logs = sync_service.get_gateway_connection_logs(gateway_id, limit=limit) return [ { "id": log.id, "profile_id": log.vpn_profile_id, "profile_name": log.vpn_profile.name if log.vpn_profile else None, "server_id": log.vpn_server_id, "server_name": log.vpn_server.name if log.vpn_server else None, "common_name": log.common_name, "real_address": log.real_address, "vpn_ip": log.vpn_ip, "connected_at": log.connected_at.isoformat() if log.connected_at else None, "disconnected_at": log.disconnected_at.isoformat() if log.disconnected_at else None, "duration_seconds": log.duration_seconds, "bytes_received": log.bytes_received, "bytes_sent": log.bytes_sent, "is_active": log.is_active } for log in logs ] @router.get("/{gateway_id}/vpn-status") def get_gateway_vpn_status( gateway_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """Get current VPN connection status for a gateway.""" # Check access query = get_accessible_gateways_query(db, current_user) gateway = query.filter(Gateway.id == gateway_id).first() if not gateway: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Gateway not found or access denied" ) # Sync connections first sync_service = VPNSyncService(db) sync_service.sync_all_connections() # Get active connection active_log = db.query(VPNConnectionLog).filter( VPNConnectionLog.gateway_id == gateway_id, VPNConnectionLog.disconnected_at.is_(None) ).first() if active_log: return { "is_connected": True, "profile_name": active_log.vpn_profile.name if active_log.vpn_profile else None, "server_name": active_log.vpn_server.name if active_log.vpn_server else None, "real_address": active_log.real_address, "connected_since": active_log.connected_at.isoformat() if active_log.connected_at else None, "bytes_received": active_log.bytes_received, "bytes_sent": active_log.bytes_sent } else: return { "is_connected": False, "last_connection": gateway.last_seen.isoformat() if gateway.last_seen else None }