readme refreshed

This commit is contained in:
2026-02-05 08:50:11 +01:00
parent 1de7f5b593
commit 14b770af55
15 changed files with 1453 additions and 27 deletions
+3
View File
@@ -8,6 +8,7 @@ from .access import UserGatewayAccess, UserEndpointAccess, ConnectionLog
from .certificate_authority import CertificateAuthority, CAStatus, CAAlgorithm
from .vpn_server import VPNServer, VPNProtocol, VPNCipher, VPNAuth, VPNCompression, VPNServerStatus
from .vpn_profile import VPNProfile, VPNProfileStatus
from .client_vpn_profile import ClientVPNProfile, ClientVPNProfileStatus
from .vpn_connection_log import VPNConnectionLog
__all__ = [
@@ -32,5 +33,7 @@ __all__ = [
"VPNServerStatus",
"VPNProfile",
"VPNProfileStatus",
"ClientVPNProfile",
"ClientVPNProfileStatus",
"VPNConnectionLog",
]
@@ -69,6 +69,7 @@ class CertificateAuthority(Base):
created_by = relationship("User", foreign_keys=[created_by_id])
vpn_servers = relationship("VPNServer", back_populates="certificate_authority")
vpn_profiles = relationship("VPNProfile", back_populates="certificate_authority")
client_vpn_profiles = relationship("ClientVPNProfile", back_populates="certificate_authority")
def __repr__(self):
return f"<CertificateAuthority(id={self.id}, name='{self.name}', status='{self.status}')>"
+71
View File
@@ -0,0 +1,71 @@
"""Client VPN Profile model for user/technician VPN connections."""
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 ClientVPNProfileStatus(str, PyEnum):
"""Client VPN Profile status."""
PENDING = "pending" # Certificate being generated
ACTIVE = "active" # Ready to use
EXPIRED = "expired" # Certificate expired
REVOKED = "revoked" # Certificate revoked
class ClientVPNProfile(Base):
"""VPN Profile for a user/technician to connect to the VPN server."""
__tablename__ = "client_vpn_profiles"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
vpn_server_id = Column(Integer, ForeignKey("vpn_servers.id"), nullable=False)
ca_id = Column(Integer, ForeignKey("certificate_authorities.id"), nullable=False)
# Certificate data
cert_cn = Column(String(255), nullable=False, unique=True) # Common Name
client_cert = Column(Text, nullable=True) # Client certificate PEM
client_key = Column(Text, nullable=True) # Client private key PEM
# Status
status = Column(Enum(ClientVPNProfileStatus), default=ClientVPNProfileStatus.PENDING)
is_active = Column(Boolean, default=True)
# Validity
valid_from = Column(DateTime, nullable=True)
valid_until = Column(DateTime, nullable=True)
# VPN IP assigned (updated when connected)
vpn_ip = Column(String(15), nullable=True)
# Tracking
last_connection = Column(DateTime, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
user = relationship("User", back_populates="client_vpn_profile")
vpn_server = relationship("VPNServer", back_populates="client_vpn_profiles")
certificate_authority = relationship("CertificateAuthority", back_populates="client_vpn_profiles")
def __repr__(self):
return f"<ClientVPNProfile(id={self.id}, user_id={self.user_id}, cert_cn='{self.cert_cn}')>"
@property
def is_ready(self) -> bool:
"""Check if profile is ready for use."""
return (
self.status == ClientVPNProfileStatus.ACTIVE 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
+6
View File
@@ -47,6 +47,12 @@ class User(Base):
primaryjoin="User.id == UserEndpointAccess.user_id"
)
connection_logs = relationship("ConnectionLog", back_populates="user")
client_vpn_profile = relationship(
"ClientVPNProfile",
back_populates="user",
uselist=False, # One-to-one relationship
cascade="all, delete-orphan"
)
def __repr__(self):
return f"<User(id={self.id}, username='{self.username}', role='{self.role}')>"
+1
View File
@@ -106,6 +106,7 @@ class VPNServer(Base):
tenant = relationship("Tenant", back_populates="vpn_servers")
certificate_authority = relationship("CertificateAuthority", back_populates="vpn_servers")
vpn_profiles = relationship("VPNProfile", back_populates="vpn_server")
client_vpn_profiles = relationship("ClientVPNProfile", back_populates="vpn_server")
def __repr__(self):
return f"<VPNServer(id={self.id}, name='{self.name}', {self.hostname}:{self.port}/{self.protocol.value})>"