openvpn-endpoint-server/server/app/models/vpn_profile.py

91 lines
3.3 KiB
Python

"""VPN Profile model for gateway VPN configurations."""
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 VPNProfileStatus(str, PyEnum):
"""VPN Profile status."""
PENDING = "pending" # Certificate being generated
ACTIVE = "active" # Ready to use
PROVISIONED = "provisioned" # Downloaded/deployed to gateway
EXPIRED = "expired" # Certificate expired
REVOKED = "revoked" # Certificate revoked
class VPNProfile(Base):
"""VPN Profile for a gateway - links gateway to VPN server."""
__tablename__ = "vpn_profiles"
id = Column(Integer, primary_key=True, index=True)
gateway_id = Column(Integer, ForeignKey("gateways.id"), nullable=False)
vpn_server_id = Column(Integer, ForeignKey("vpn_servers.id"), nullable=False)
ca_id = Column(Integer, ForeignKey("certificate_authorities.id"), nullable=False)
# Profile info
name = Column(String(255), nullable=False) # e.g., "Produktion", "Fallback"
description = Column(Text, nullable=True)
# Certificate data
cert_cn = Column(String(255), nullable=False) # Common Name
client_cert = Column(Text, nullable=True) # Client certificate PEM
client_key = Column(Text, nullable=True) # Client private key PEM
# Priority for failover (1 = highest priority)
priority = Column(Integer, default=1)
# Status
status = Column(Enum(VPNProfileStatus), default=VPNProfileStatus.PENDING)
is_active = Column(Boolean, default=True)
# Validity
valid_from = Column(DateTime, nullable=True)
valid_until = Column(DateTime, nullable=True)
# Provisioning tracking
provisioned_at = Column(DateTime, nullable=True)
last_connection = Column(DateTime, nullable=True)
# VPN IP assigned to this profile (if static)
vpn_ip = Column(String(15), nullable=True)
# Audit
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
gateway = relationship("Gateway", back_populates="vpn_profiles")
vpn_server = relationship("VPNServer", back_populates="vpn_profiles")
certificate_authority = relationship("CertificateAuthority", back_populates="vpn_profiles")
def __repr__(self):
return f"<VPNProfile(id={self.id}, name='{self.name}', gateway_id={self.gateway_id}, priority={self.priority})>"
@property
def is_ready(self) -> bool:
"""Check if profile is ready for provisioning."""
return (
self.status in (VPNProfileStatus.ACTIVE, VPNProfileStatus.PROVISIONED) and
self.client_cert is not None and
self.client_key is not None
)
@property
def is_expired(self) -> bool:
"""Check if certificate is expired."""
if self.valid_until:
return datetime.utcnow() > self.valid_until
return False
@property
def days_until_expiry(self) -> int | None:
"""Days until certificate expires."""
if self.valid_until:
delta = self.valid_until - datetime.utcnow()
return max(0, delta.days)
return None