kumidc/radius/models.py
Kumi 70b5235be8
Enhanced TOTP verification and added RADIUS support
Implement additional security and functionality in authentication with the introduction of docstrings, type hints, and extended verification logic in the TOTP model to prevent repeated token use, improving robustness against replay attacks. Simultaneously, established the groundwork for RADIUS (Remote Authentication Dial-In User Service) support by creating models and management commands essential for handling authentication, accounting packets, and web-based authentication challenges, broadening the system's capability to integrate with network access servers and services.

Resolves issues with token replay attacks and sets the stage for scalable network authentication mechanisms.
2024-01-28 22:23:05 +01:00

81 lines
2.7 KiB
Python

from django.db import models
from django.contrib.auth import get_user_model
from datetime import datetime, timedelta
from cidrfield.models import IPNetworkField
class RadiusChallengeChoice(models.TextChoices):
NONE = "none", "None"
TOTP = "totp", "Time-based One-time Password"
WEB = "web", "Web-based"
class RadiusUser(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
challenge = models.CharField(
max_length=255, choices=RadiusChallengeChoice.choices, default=RadiusChallengeChoice.NONE
)
class RadiusNetwork(models.Model):
name = models.CharField(max_length=255)
network = IPNetworkField()
class RadiusUserIP(models.Model):
user = models.ForeignKey(RadiusUser, on_delete=models.CASCADE)
network = models.ForeignKey(RadiusNetwork, on_delete=models.CASCADE)
ipv4_address = models.GenericIPAddressField(protocol="ipv4", null=True, blank=True)
ipv6_prefix = models.GenericIPAddressField(protocol="ipv6", null=True, blank=True)
class Meta:
unique_together = ("user", "network")
def save(self, *args, **kwargs):
if not self.ip_address in self.network.network:
raise ValueError(
f"IPv4 address {self.ip_address} not in network {self.network.network}"
)
if not self.ipv6_prefix in self.network.network:
raise ValueError(
f"IPv6 prefix {self.ipv6_prefix} not in network {self.network.network}"
)
super().save(*args, **kwargs)
class RadiusAccountingSession(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(RadiusUser, on_delete=models.CASCADE)
ip = models.ForeignKey(RadiusUserIP, on_delete=models.CASCADE)
start_time = models.DateTimeField(null=True, blank=True)
stop_time = models.DateTimeField(null=True, blank=True)
def start(self):
self.start_time = datetime.now()
self.save()
def stop(self):
self.stop_time = datetime.now()
self.save()
class RadiusAccountingEvent(models.Model):
session = models.ForeignKey(RadiusAccountingSession, on_delete=models.CASCADE)
event_type = models.CharField(max_length=255)
event_time = models.DateTimeField(auto_now_add=True)
raw_data = models.TextField()
class RadiusWebAuthentication(models.Model):
user = models.ForeignKey(RadiusUser, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
expiry = models.DateTimeField()
ip = models.IPAddressField()
def save(self, *args, **kwargs):
self.expiry = self.timestamp + timedelta(minutes=5)
super().save(*args, **kwargs)