import logging from datetime import datetime from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.db import IntegrityError, DatabaseError, transaction from django.http import JsonResponse from .models import * from .exception_handling import handle_exceptions from .db_validate import * #Logger configuration logger = logging.getLogger(__name__) ### CREATE - functions ### # Update manufacturer @handle_exceptions @transaction.atomic def update_manufacturer_name_by_id(manufacturer_id: int, name: str) : # Check existence of ID and fetch old entry manufacturer=Manufacturer.objects.filter(id=manufacturer_id) if not manufacturer.exists(): raise ValidationError(f"Manufacturer mit Id'{manufacturer_id}' ist nicht vorhanden") # Input validation if not name or not isinstance(name, str): # Checks for None, empty strings and non-strings raise ValidationError("Ungültiger Herstellername") # Manufacturer must not consist only of whitespace name = name.strip() if not name: # Check again after stripping whitespace raise ValidationError("Herstellername darf nicht leer sein") # Check for duplicate if Manufacturer.objects.filter(name=name).exists(): raise ValidationError(f"Hersteller Name '{name}' existiert bereits") # Create manufacturer manufacturer = Manufacturer.objects.filter(id=manufacturer_id).update( name=name, created=datetime.now() ) # Return manufacturer object as JSON logging.info(f"Hersteller wurde erfolgreich aktualisiert: {name} (ID: {manufacturer.id})") return JsonResponse({ 'status': 'success', 'message': 'Hersteller Update wurde erfolgreich aktualisiert', 'data': manufacturer.to_json() }, status=201) # Update device type @handle_exceptions @transaction.atomic def update_device_type_by_id( device_type_id: int, device_type: str): # Check existence of ID and fetch old entry device_type=DeviceType.objects.filter(id=device_type_id) if not device_type.exists(): raise ValidationError(f"Devicetype mit Id'{device_type_id}' ist nicht vorhanden") # Input validation if not device_type or not isinstance(device_type, str): raise ValidationError("Ungültiger Gerätetyp") # Manufacturer must not consist only of whitespace device_type = device_type.strip() if not device_type: # Check again after stripping whitespace raise ValidationError("Gerätetyp darf nicht leer sein") # Check for duplicate if DeviceType.objects.filter(device_type=device_type).exists(): raise ValidationError(f"Gerätetyp '{device_type}' existiert bereits") # Create type device_type_obj = DeviceType.objects.filter(id=device_type_id).update( device_type=device_type, created=datetime.now() ) logging.info(f"Gerätetyp erfolgreich aktualisiert: {device_type} (ID: {device_type_obj.id})") return JsonResponse({ 'status': 'success', 'message': 'Gerätetyp erfolgreich aktualisiert', 'data': device_type_obj.to_json() }, status=201) # Update product// to be discussed @handle_exceptions @transaction.atomic def update_product_by_id(product_id: int, product_name: str ): # Check existence of ID and fetch old entry product=Product.objects.filter(id=product_id) if not product.exists(): raise ValidationError(f"Product mit Id'{product_id}' ist nicht vorhanden") # Input validation if not product_name or not isinstance(product_name, str): raise ValidationError("Ungültiger Produktname") # Product name must not be empty product_name = product_name.strip() if not product_name: raise ValidationError("Produktname darf nicht leer sein") # Check for duplicate if Product.objects.filter( manufacturer=product.manufacturer, device_type=product.device_type, product_name=product_name ).exists(): raise ValidationError(f"Produkt '{product_name}' existiert bereits für diesen Hersteller und Gerätetyp") # Create product product = Product.objects.update( id=product_id, product_name=product_name, created=datetime.now() ) logging.info( f"Produkt erfolgreich aktualisiert: {product_name} (ID: {product.id}, Hersteller: {product.manufacturer.name}, Typ: {product.device_type.device_type})") return JsonResponse({ 'status': 'success', 'message': 'Produkt wurde erfolgreich erstellt', 'data': product.to_json() }, status=201) # Update firmware @handle_exceptions @transaction.atomic def update_firmware_by_id(firmware_id: int, version: str): # Check existence of ID and fetch old entry firmware=Firmware.objects.filter(id=firmware_id) if not firmware.exists(): raise ValidationError(f"Firmware mit Id'{firmware_id}' ist nicht vorhanden") # Input validation if not version or not isinstance(version, str): raise ValidationError("Ungültige Firmware-Version") # Version must not be empty version = version.strip() if not version: raise ValidationError("Firmware-Version darf nicht leer sein") # Check for duplicate if Firmware.objects.filter( product=firmware.product, version=version ).exists(): raise ValidationError(f"Firmware-Version '{version}' existiert bereits für dieses Produkt") # Create firmware firmware = Firmware.objects.update( id=firmware_id, version=version, created=datetime.now() ) logging.info( f"Firmware erfolgreich aktualisiert: Version {version} (ID: {firmware.id}, Produkt: {firmware.product.product_name})") return JsonResponse({ 'status': 'success', 'message': 'Firmware wurde erfolgreich aktualisiert', 'data': firmware.to_json() }, status=201) # Update device @handle_exceptions @transaction.atomic def update_device_by_id(device_id: int, serial: str, comment: str = None ): # Check existence of ID and fetch old entry device=Device.objects.filter(id=device_id) if not device.exists(): raise ValidationError(f"Device mit Id'{device_id}' ist nicht vorhanden") # Input validation if not serial or not isinstance(serial, str): raise ValidationError("Ungültige Seriennummer") # Serial number must not be empty serial = serial.strip() if not serial: raise ValidationError("Seriennummer darf nicht leer sein") if comment and not isinstance(comment, str): raise ValidationError("Kommentar muss ein String sein") # Check for duplicate if Device.objects.filter( product=device.product, serial=serial ).exists(): raise ValidationError(f"Device mit Seriennummer '{serial}' existiert bereits für dieses Produkt") # Create device device = Device.objects.update( id=device_id, serial=serial, comment=comment if comment else '', created=datetime.now() ) logging.info( f"Device erfolgreich aktualisiert: Seriennummer {serial} (ID: {device.id}, Produkt: {device.product.product_name})") return JsonResponse({ 'status': 'success', 'message': 'Gerät wurde erfolgreich aktualisiert', 'data': device.to_json() }, status=201) # Update file @handle_exceptions @transaction.atomic def update_file_by_id(file_id: int, firmware_id: int, filename: str, ip_address: str, status: str, upload_time: datetime = None): # Check existence of ID and fetch old entry file=File.objects.filter(id=file_id) if not file.exists(): raise ValidationError(f"File mit Id'{file_id}' ist nicht vorhanden") # Input validation if not filename or not isinstance(filename, str): raise ValidationError("Ungültiger Dateiname") # File name must not be empty filename = filename.strip() if not filename: raise ValidationError("Dateiname darf nicht leer sein") if not ip_address or not isinstance(ip_address, str): raise ValidationError("Ungültige IP-Adresse") # IP address must not be empty ip_address = ip_address.strip() if not ip_address: raise ValidationError("IP-Adresse darf nicht leer sein") if not status or not isinstance(status, str): raise ValidationError("Ungültiger Status") if not re.match(r'^(\d{1,3}\.){3}\d{1,3}$', ip_address): raise ValidationError("Ungültige IP-Adresse") # Existence of ID if not File.objects.filter(id=file_id).exists(): raise ValidationError(f"File mit Id'{file_id}' ist nicht vorhanden") #fetch current file data file = File.objects.get(id=file_id) # Check device device = Device.objects.get(id=file.device) # Raises ObjectDoesNotExist # Check firmware firmware = Firmware.objects.get(id=firmware_id) # Raises ObjectDoesNotExist # Check for duplicate if File.objects.filter( device=device, firmware=firmware, filename=filename, ip_address=ip_address ).exists(): raise ValidationError(f"Datei '{filename}' existiert bereits für dieses Gerät, diese Firmware mit dieser IP-Adresse") # Create file file = File.objects.update( id=file_id, firmware=firmware, filename=filename, ip_address=ip_address, status=status, upload_time=upload_time if upload_time else datetime.now() ) logging.info( f"Datei erfolgreich erstellt: {filename} (ID: {file.id}, Gerät: {device.serial}, Firmware: {firmware.version})") return JsonResponse({ 'status': 'success', 'message': 'Datei wurde erfolgreich erstellt', 'data': file.to_json() }, status=201) # Update tool @handle_exceptions @transaction.atomic def update_tool_by_id(tool_id: int, tool_name: str, version: str): # Check existence of ID and fetch old entry tool=Tool.objects.filter(id=tool_id) if not tool.exists(): raise ValidationError(f"File mit Id'{tool_id}' ist nicht vorhanden") # Input validation if not tool_name or not isinstance(tool_name, str): raise ValidationError("Ungültiger Tool-Name") tool_name = tool_name.strip() if not tool_name: raise ValidationError("Tool-Name darf nicht leer sein") if not version or not isinstance(version, str): raise ValidationError("Ungültige Version") version = version.strip() if not version: raise ValidationError("Version darf nicht leer sein") # Check for duplicate if Tool.objects.filter( tool_name=tool_name, version=version ).exists(): raise ValidationError(f"Tool '{tool_name}' mit Version '{version}' existiert bereits") # Create tool tool = Tool.objects.update( id=tool_id, tool_name=tool_name, version=version, created=datetime.now() ) logging.info( f"Tool erfolgreich aktualisiert: {tool_name} Version {version} (ID: {tool.id})") return JsonResponse({ 'status': 'success', 'message': 'Tool wurde erfolgreich aktualisiert', 'data': tool.to_json() }, status=201) # Update raw dataset @handle_exceptions @transaction.atomic def update_raw_dataset_by_id(raw_dataset_id: int, file_id: int): # Check existence of ID and fetch old entry raw_dataset=RawDataset.objects.filter(id=raw_dataset_id) if not raw_dataset.exists(): raise ValidationError(f"Raw Dataset mit Id'{raw_dataset_id}' ist nicht vorhanden") #Check file file = File.objects.get(id=file_id) if not file.exists(): raise ValidationError(f"File mit Id '{file_id}' existiert nicht ") # Update raw dataset raw_dataset = RawDataset.objects.update( id=raw_dataset_id, file=file, created=datetime.now() ) logging.info(f"Raw Dataset erfolgreich aktualisert für File: {file.filename} (ID: {raw_dataset.id})") return JsonResponse({ 'status': 'success', 'message': 'Raw Dataset wurde erfolgreich aktualisiert', 'data': raw_dataset.to_json() }, status=201) # Update raw_url entry @handle_exceptions @transaction.atomic def update_raw_url_by_id(raw_url_id: int, url: str): # Check existence of ID and fetch old entry raw_url=UrlRaw.objects.filter(id=raw_url_id) if not raw_url.exists(): raise ValidationError(f"Raw Url mit Id'{raw_url_id}' ist nicht vorhanden") # Input validation if not url or not isinstance(url, str): raise ValidationError("Ungültige URL") url = url.strip() if not url: raise ValidationError("URL darf nicht leer sein") # Check for duplicate if UrlRaw.objects.filter( raw_dataset=raw_url.raw_dataset, url=url ).exists(): raise ValidationError(f"URL '{url}' existiert bereits für dieses Raw Dataset") # Create raw URL raw_url = UrlRaw.objects.create( id=raw_url_id, url=url, created=datetime.now() ) logging.info( f"Raw URL erfolgreich erstellt: {url} (ID: {raw_url.id}, Raw Dataset ID: {raw_url.raw_dataset.id})") return JsonResponse({ 'status': 'success', 'message': 'Raw URL wurde erfolgreich erstellt', 'data': raw_url.to_json() }, status=201) # Update raw_dns entry @handle_exceptions @transaction.atomic def update_raw_dns_by_id( dns_hostname: str, raw_dns_id: int): # Check existence of ID and fetch old entry raw_dns=DnsRaw.objects.filter(id=raw_dns_id) if not raw_dns.exists(): raise ValidationError(f"Raw Dns mit Id'{raw_dns_id}' ist nicht vorhanden") # Input validation if not dns_hostname or not isinstance(dns_hostname, str): raise ValidationError("Ungültiger DNS Hostname") dns_hostname = dns_hostname.strip() if not dns_hostname: raise ValidationError("DNS Hostname darf nicht leer sein") # Check for duplicate if DnsRaw.objects.filter( raw_dataset=raw_dns.raw_dataset, dns_hostname=dns_hostname ).exists(): raise ValidationError(f"DNS Hostname '{dns_hostname}' existiert bereits für dieses Raw Dataset") # Create raw DNS raw_dns = DnsRaw.objects.update( id=raw_dns_id, dns_hostname=dns_hostname, created=datetime.now() ) logging.info( f"Raw DNS erfolgreich erstellt: {dns_hostname} (ID: {raw_dns.id}, Raw Dataset ID: {raw_dns.raw_dataset.id})") return JsonResponse({ 'status': 'success', 'message': 'Raw DNS wurde erfolgreich erstellt', 'data': raw_dns.to_json() }, status=201) # Update raw_hash entry @handle_exceptions @transaction.atomic def update_raw_hash_by_id(hash_id: int, hash_value: str): # Check existence of ID and fetch old entry hash_raw=HashRaw.objects.filter(id=hash_id) if not hash_raw.exists(): raise ValidationError(f"Raw Dns mit Id'{hash_id}' ist nicht vorhanden") # Input validation if not hash_value or not isinstance(hash_value, str): raise ValidationError("Ungültiger Hash-Wert") hash_value = hash_value.strip() if not hash_value: raise ValidationError("Hash-Wert darf nicht leer sein") # Check for duplicate if HashRaw.objects.filter( raw_dataset=hash_raw.raw_dataset, hash_value=hash_value ).exists(): raise ValidationError(f"Hash '{hash_value}' existiert bereits für dieses Raw Dataset") # Create raw hash hash_raw = HashRaw.objects.update( id=hash_id, hash_value=hash_value, created=datetime.now() ) logging.info( f"Raw Hash erfolgreich aktualisiert: {hash_value} (ID: {hash_raw.id}, Raw Dataset ID: {hash_raw.raw_dataset.id})") return JsonResponse({ 'status': 'success', 'message': 'Raw Hash wurde erfolgreich aktualisiert', 'data': hash_raw.to_json() }, status=201) # Update analyzed dataset @handle_exceptions @transaction.atomic def update_analyzed_dataset_by_id(analyzed_dataset_id: int, tool_id: int): # Check existence of ID and fetch old entry analyzed_dataset=AnalyzedDataset.objects.filter(id=analyzed_dataset_id) if not analyzed_dataset.exists(): raise ValidationError(f"Raw Dns mit Id'{analyzed_dataset_id}' ist nicht vorhanden") # Check tool for existence tool = Tool.objects.get(id=tool_id) if not tool.exists(): raise ValidationError(f"Tool mit Id'{tool_id}' ist nicht vorhanden") # fetch file for dataset file= AnalyzedDataset.objects.get(id=analyzed_dataset_id) #Duplicate if AnalyzedDataset.objects.filter( id=analyzed_dataset_id, tool=tool ).exists(): raise ValidationError(f"Analyzed Dataset für File '{file.filename}' und Tool '{tool.tool_name}' existiert bereits") # Create analyzed dataset analyzed_dataset = AnalyzedDataset.objects.update( id=analyzed_dataset_id, tool=tool, created=datetime.now() ) logging.info( f"Analyzed Dataset erfolgreich aktualiseirt für File: {file.filename}, Tool: {tool.tool_name} (ID: {analyzed_dataset.id})") return JsonResponse({ 'status': 'success', 'message': 'Analyzed Dataset wurde erfolgreich aktualisiert', 'data': analyzed_dataset.to_json() }, status=201) # Update analyzed_url_id entry @handle_exceptions @transaction.atomic def update_analyzed_url_by_id(analyzed_url_id: int, url: str): # Check existence of ID and fetch old entry analyzed_url=UrlAnalyzed.objects.filter(id=analyzed_url_id) if not analyzed_url.exists(): raise ValidationError(f"Raw Dns mit Id'{analyzed_url_id}' ist nicht vorhanden") # Input validation if not url or not isinstance(url, str): raise ValidationError("Ungültige URL") url = url.strip() if not url: raise ValidationError("URL darf nicht leer sein") # Check for duplicate if UrlAnalyzed.objects.filter( analyzedl_dataset=analyzed_url.analyzed_dataset, url=url ).exists(): raise ValidationError(f"URL '{url}' existiert bereits für dieses Analyzed Dataset") # Create analyzed URL analyzed_url = UrlAnalyzed.objects.update( id=analyzed_url_id, url=url, created=datetime.now() ) logging.info( f"Analyzed URL erfolgreich aktualisiert: {url} (ID: {analyzed_url.id}, Detail Dataset ID: {analyzed_url.analyzed_dataset.id})") return JsonResponse({ 'status': 'success', 'message': 'Analyzed URL wurde erfolgreich aktualisiert', 'data': analyzed_url.to_json() }, status=201) #Update analyzed_dns entry @handle_exceptions @transaction.atomic def update_analyzed_dns_by_id(analyzed_dns_id: int, dns_hostname: str, record_type: str): # Check existence of ID and fetch old entry dns_analyzed=DnsAnalyzed.objects.filter(id=analyzed_dns_id) if not dns_analyzed.exists(): raise ValidationError(f"Analyzed Dns mit Id'{analyzed_dns_id}' ist nicht vorhanden") # Input validation if not dns_hostname or not isinstance(dns_hostname, str): raise ValidationError("Ungültiger DNS Hostname") dns_hostname = dns_hostname.strip() if not dns_hostname: raise ValidationError("DNS Hostname darf nicht leer sein") if not record_type or not isinstance(record_type, str): raise ValidationError("Ungültiger Record Type") record_type = record_type.strip() if not record_type: raise ValidationError("Record Type darf nicht leer sein") # Check for duplicate if DnsAnalyzed.objects.filter( analyzed_dataset=dns_analyzed.analyzed_dataset, dns_hostname=dns_hostname, record_type=record_type ).exists(): raise ValidationError( f"DNS '{dns_hostname}' mit Record Type '{record_type}' existiert bereits für dieses Analyzed Dataset") # Create analyzed DNS analyzed_dns = DnsAnalyzed.objects.update( id=analyzed_dns_id, dns_hostname=dns_hostname, record_type=record_type, created=datetime.now() ) logging.info( f"Analyzed DNS erfolgreich aktualisert: {dns_hostname} (ID: {analyzed_dns.id}, Detail Dataset ID: {dns_analyzed.analyzed_dataset.id})") return JsonResponse({ 'status': 'success', 'message': 'Analyzed DNS wurde erfolgreich aktulisiert', 'data': analyzed_dns.to_json() }, status=201) # Update analyzed_hash entry @handle_exceptions @transaction.atomic def update_analyzed_hash_by_id(analyzed_hash_id: int, hash_type: str, hash_value: str,cracked: bool = False, cracked_value: str = None, ): # Check existence of ID and fetch old entry hash_analyzed=HashAnalyzed.objects.filter(id=analyzed_hash_id) if not hash_analyzed.exists(): raise ValidationError(f"Analyzed Hash mit Id'{hash_analyzed}' ist nicht vorhanden") # Input validation if not hash_type or not isinstance(hash_type, str): raise ValidationError("Ungültiger Hash-Typ") hash_type = hash_type.strip() if not hash_type: raise ValidationError("Hash-Typ darf nicht leer sein") if not hash_value or not isinstance(hash_value, str): raise ValidationError("Ungültiger Hash-Wert") hash_value = hash_value.strip() if not hash_value: raise ValidationError("Hash-Wert darf nicht leer sein") if cracked and not cracked_value: raise ValidationError("Bei gecracktem Hash muss ein Wert angegeben werden") # Check for duplicate if HashAnalyzed.objects.filter( detail_dataset=hash_analyzed.analyzed_dataset, hash_type=hash_type, hash_value=hash_value ).exists(): raise ValidationError( f"Hash '{hash_value}' mit Typ '{hash_type}' existiert bereits für dieses Analyzed Dataset") # Create analyzed hash analyzed_hash = HashAnalyzed.objects.update( id=analyzed_hash_id, hash_type=hash_type, hash_value=hash_value, cracked=cracked, cracked_value=cracked_value if cracked else None, created=datetime.now() ) logging.info( f"Analyzed Hash erfolgreich aktualisiert: {hash_value} (ID: {analyzed_hash.id}, Detail Dataset ID: {hash_analyzed.analyzed_dataset.id})") return JsonResponse({ 'status': 'success', 'message': 'Analyzed Hash wurde erfolgreich aktualisiert', 'data': analyzed_hash.to_json() }, status=201) # Update port @handle_exceptions @transaction.atomic def update_port_by_id(port_id: int, port_number: int): # Check existence port = Port.objects.filter(id=port_id) if not port.exists(): raise ValidationError(f"Port mit Id '{port_id}' ist nicht vorhanden") # Input validation if not isinstance(port_number, int): raise ValidationError("Port muss eine Ganzzahl sein") # Check for duplicate (except for itself) if Port.objects.filter(port=port_number).exclude(id=port_id).exists(): raise ValidationError(f"Port {port_number} existiert bereits") # Update port port = Port.objects.filter(id=port_id).update( port=port_number, created=datetime.now() ) updated_port = Port.objects.get(id=port_id) logging.info(f"Port erfolgreich aktualisiert: {port_number} (ID: {port_id})") return JsonResponse({ 'status': 'success', 'message': 'Port wurde erfolgreich aktualisiert', 'data': updated_port.to_json() }, status=201) # Update TLS version @handle_exceptions @transaction.atomic def update_tls_version_by_id(tls_id: int, version: str): # Check existence tls = TlsVersion.objects.filter(id=tls_id) if not tls.exists(): raise ValidationError(f"TLS Version mit Id '{tls_id}' ist nicht vorhanden") # Input validation if not version or not isinstance(version, str): raise ValidationError("Ungültige TLS-Version") version = version.strip() if not version: raise ValidationError("TLS-Version darf nicht leer sein") # Check for duplicate (except for itself) if TlsVersion.objects.filter(tls_version=version).exclude(id=tls_id).exists(): raise ValidationError(f"TLS Version {version} existiert bereits") # Update TLS version tls = TlsVersion.objects.filter(id=tls_id).update( tls_version=version, created=datetime.now() ) updated_tls = TlsVersion.objects.get(id=tls_id) logging.info(f"TLS Version erfolgreich aktualisiert: {version} (ID: {tls_id})") return JsonResponse({ 'status': 'success', 'message': 'TLS Version wurde erfolgreich aktualisiert', 'data': updated_tls.to_json() }, status=201) # Update cipher suite @handle_exceptions @transaction.atomic def update_cipher_suite_by_id(cipher_id: int, name: str): # Check existence cipher = CipherSuite.objects.filter(id=cipher_id) if not cipher.exists(): raise ValidationError(f"Cipher Suite mit Id '{cipher_id}' ist nicht vorhanden") # Input validation if not name or not isinstance(name, str): raise ValidationError("Ungültige Cipher Suite") name = name.strip() if not name: raise ValidationError("Cipher Suite darf nicht leer sein") # Check for duplicate (except for itself) if CipherSuite.objects.filter(cipher_suite=name).exclude(id=cipher_id).exists(): raise ValidationError(f"Cipher Suite {name} existiert bereits") # Update cipher suite cipher = CipherSuite.objects.filter(id=cipher_id).update( cipher_suite=name, created=datetime.now() ) updated_cipher = CipherSuite.objects.get(id=cipher_id) logging.info(f"Cipher Suite erfolgreich aktualisiert: {name} (ID: {cipher_id})") return JsonResponse({ 'status': 'success', 'message': 'Cipher Suite wurde erfolgreich aktualisiert', 'data': updated_cipher.to_json() }, status=201) # Functions for updating all associations of URL features @handle_exceptions @transaction.atomic def update_url_features(url_id: int, ports: list = None, tls_versions: list = None, cipher_suites: list = None): url = UrlAnalyzed.objects.get(id=url_id) if ports is not None: # Remove all existing ports and add new ones url.ports.clear() for port_number in ports: port, _ = Port.objects.get_or_create(port=port_number) url.ports.add(port) if tls_versions is not None: # Remove all existing TLS versions and add new ones url.tls_versions.clear() for version in tls_versions: tls, _ = TlsVersion.objects.get_or_create(tls_version=version) url.tls_versions.add(tls) if cipher_suites is not None: # Remove all existing cipher suites and add new ones url.cipher_suites.clear() for suite in cipher_suites: cipher, _ = CipherSuite.objects.get_or_create(cipher_suite=suite) url.cipher_suites.add(cipher) logging.info(f"Features für URL {url_id} erfolgreich aktualisiert") return JsonResponse({ 'status': 'success', 'message': 'URL Features wurden erfolgreich aktualisiert', 'data': url.to_json() }, status=201)