Files
IoT_PreTester_V46_Demo/django_project/IoTAnalyzer/db_create.py
T
2026-08-19 08:04:59 +02:00

123 lines
4.0 KiB
Python

"""Focused creation services used by the demo API."""
import ipaddress
import logging
import os
from django.core.exceptions import ValidationError
from django.db import transaction
from .models import (
Device,
DeviceType,
File,
Firmware,
FirmwareDevices,
Manufacturer,
Product,
RawDataset,
)
logger = logging.getLogger(__name__)
CAPTURE_EXTENSIONS = {".pcap", ".pcapng"}
def _required_text(value, label: str, max_length: int) -> str:
if not isinstance(value, str) or not value.strip():
raise ValidationError(f"{label} is required.")
value = value.strip()
if len(value) > max_length:
raise ValidationError(f"{label} must not exceed {max_length} characters.")
return value
@transaction.atomic
def create_manufacturer(name: str) -> Manufacturer:
name = _required_text(name, "Manufacturer name", 50)
manufacturer, created = Manufacturer.objects.get_or_create(name=name)
if not created:
raise ValidationError("This manufacturer already exists.")
return manufacturer
@transaction.atomic
def create_device_type(device_type: str) -> DeviceType:
device_type = _required_text(device_type, "Device type", 50)
instance, created = DeviceType.objects.get_or_create(device_type=device_type)
if not created:
raise ValidationError("This device type already exists.")
return instance
@transaction.atomic
def create_new_device(
manufacturer_id: int,
device_type_id: int,
device_name: str,
serial: str,
comment: str = "",
) -> Device:
manufacturer = Manufacturer.objects.get(id=manufacturer_id)
device_type = DeviceType.objects.get(id=device_type_id)
device_name = _required_text(device_name, "Device name", 100)
serial = _required_text(serial, "Serial number", 100)
comment = (comment or "").strip()[:255]
product, _ = Product.objects.get_or_create(
manufacturer=manufacturer,
device_type=device_type,
product_name=device_name,
)
device, created = Device.objects.get_or_create(
product=product,
serial=serial,
defaults={"comment": comment},
)
if not created:
raise ValidationError("A device with this product and serial number already exists.")
return device
def create_capture(
device_id: int,
firmware_version: str,
filename: str,
ip_address: str,
) -> File:
"""Create capture metadata, analyze the stored file, and update its status."""
firmware_version = _required_text(firmware_version, "Firmware version", 100)
filename = os.path.basename(_required_text(filename, "Capture filename", 255))
if os.path.splitext(filename)[1].lower() not in CAPTURE_EXTENSIONS:
raise ValidationError("Only .pcap and .pcapng captures are supported.")
try:
normalized_ip = str(ipaddress.ip_address(ip_address))
except ValueError as exc:
raise ValidationError("A valid device IP address is required.") from exc
with transaction.atomic():
device = Device.objects.select_related("product").get(id=device_id)
firmware, _ = Firmware.objects.get_or_create(
product=device.product,
version=firmware_version,
)
FirmwareDevices.objects.get_or_create(device=device, firmware=firmware)
file_entry, created = File.objects.get_or_create(
device=device,
firmware=firmware,
filename=filename,
ip_address=normalized_ip,
defaults={"status": "analyzing"},
)
if not created:
raise ValidationError("This capture is already registered for the device.")
raw_dataset = RawDataset.objects.create(file=file_entry)
from .analyze.security_analysis import analyze_capture
result = analyze_capture(filename, file_entry.id, raw_dataset.id)
file_entry.status = "analyzed" if result is not None else "analysis_failed"
file_entry.save(update_fields=["status"])
logger.info("Capture %s completed with status %s", file_entry.id, file_entry.status)
return file_entry