92 lines
3.5 KiB
Python
92 lines
3.5 KiB
Python
"""Gateway model for mGuard routers."""
|
|
|
|
from datetime import datetime
|
|
from enum import Enum as PyEnum
|
|
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Enum, Text
|
|
from sqlalchemy.orm import relationship
|
|
from ..database import Base
|
|
|
|
|
|
class RouterType(str, PyEnum):
|
|
"""Supported mGuard router types."""
|
|
FL_MGUARD_2000 = "FL_MGUARD_2000"
|
|
FL_MGUARD_4000 = "FL_MGUARD_4000"
|
|
FL_MGUARD_RS4000 = "FL_MGUARD_RS4000"
|
|
FL_MGUARD_1000 = "FL_MGUARD_1000"
|
|
|
|
|
|
class ProvisioningMethod(str, PyEnum):
|
|
"""Method used to provision the gateway."""
|
|
REST_API = "rest_api" # For firmware 10.5.x+
|
|
SSH = "ssh" # For older firmware
|
|
ATV_FILE = "atv_file" # Offline via config file
|
|
|
|
|
|
class Gateway(Base):
|
|
"""Gateway model representing an mGuard router."""
|
|
|
|
__tablename__ = "gateways"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
tenant_id = Column(Integer, ForeignKey("tenants.id"), nullable=False)
|
|
|
|
# Basic info
|
|
name = Column(String(255), nullable=False)
|
|
description = Column(Text, nullable=True)
|
|
location = Column(String(255), nullable=True) # Physical location
|
|
|
|
# Router details
|
|
serial_number = Column(String(100), nullable=True, unique=True)
|
|
router_type = Column(Enum(RouterType), nullable=False)
|
|
firmware_version = Column(String(50), nullable=True)
|
|
provisioning_method = Column(Enum(ProvisioningMethod), default=ProvisioningMethod.ATV_FILE)
|
|
|
|
# VPN configuration
|
|
vpn_ip = Column(String(45), nullable=True, unique=True) # IPv4/IPv6
|
|
vpn_cert_cn = Column(String(255), nullable=True, unique=True) # Certificate Common Name
|
|
vpn_subnet = Column(String(50), nullable=True) # Network behind gateway, e.g., "10.0.0.0/24"
|
|
|
|
# Status
|
|
is_online = Column(Boolean, default=False, nullable=False)
|
|
is_provisioned = Column(Boolean, default=False, nullable=False)
|
|
last_seen = Column(DateTime, nullable=True)
|
|
last_config_update = Column(DateTime, nullable=True)
|
|
|
|
# Timestamps
|
|
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
tenant = relationship("Tenant", back_populates="gateways")
|
|
endpoints = relationship("Endpoint", back_populates="gateway", cascade="all, delete-orphan")
|
|
user_access = relationship("UserGatewayAccess", back_populates="gateway", cascade="all, delete-orphan")
|
|
connection_logs = relationship("ConnectionLog", back_populates="gateway")
|
|
vpn_profiles = relationship("VPNProfile", back_populates="gateway", cascade="all, delete-orphan", order_by="VPNProfile.priority")
|
|
|
|
def __repr__(self):
|
|
return f"<Gateway(id={self.id}, name='{self.name}', type='{self.router_type}')>"
|
|
|
|
@property
|
|
def supports_rest_api(self) -> bool:
|
|
"""Check if gateway supports REST API (firmware 10.x+)."""
|
|
if not self.firmware_version:
|
|
return False
|
|
try:
|
|
major_version = int(self.firmware_version.split('.')[0])
|
|
return major_version >= 10
|
|
except (ValueError, IndexError):
|
|
return False
|
|
|
|
@property
|
|
def primary_profile(self):
|
|
"""Get the primary VPN profile (highest priority)."""
|
|
active_profiles = [p for p in self.vpn_profiles if p.is_active]
|
|
if active_profiles:
|
|
return min(active_profiles, key=lambda p: p.priority)
|
|
return None
|
|
|
|
@property
|
|
def has_vpn_profiles(self) -> bool:
|
|
"""Check if gateway has any VPN profiles."""
|
|
return len(self.vpn_profiles) > 0
|