from django.http import JsonResponse import logging from django.core.exceptions import ObjectDoesNotExist from .analyze import analyze_hashes, analyze_url from .models import * from .exception_handling import handle_exceptions from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status # Logger configuration logger = logging.getLogger(__name__) # Generic function for retrieving objects with error handling and JSON response. @handle_exceptions def generic_get_objects(model_class, filter_kwargs=None, select_related_fields=None, message="Objekte"): # Build queryset queryset = model_class.objects.all() if select_related_fields: queryset = queryset.select_related(*select_related_fields) if filter_kwargs: queryset = queryset.filter(**filter_kwargs) if not queryset.exists(): raise ObjectDoesNotExist(f'Keine {message} gefunden') data_list = [obj.to_json() for obj in queryset] return data_list #Corrected because we need a dictionary as input for Rest # return JsonResponse({ # 'status': 'success', # 'message': f'{len(data_list)} {message} gefunden', # 'data': data_list # }, status=200) # Generic function for retrieving a single object by its ID. @handle_exceptions def generic_get_object_by_id(model_class, object_id, select_related_fields=None, message="Objekt"): # Build queryset queryset = model_class.objects if select_related_fields: queryset = queryset.select_related(*select_related_fields) # Get call raises ObjectDoesNotExist when not found obj = queryset.get(id=object_id) return (obj.to_json()) # return { # 'status': 'success', # 'message': f'{message} gefunden', # 'data': [obj.to_json()] # } # @handle_exceptions does not need to be used for functions that only call generic functions, # since the generic functions already contain error handling. ### Manufacturer Functions ### # Returns all manufacturers. def get_all_manufacturers(): return generic_get_objects(Manufacturer, message="Hersteller") # Returns a single manufacturer by its ID. def get_manufacturer_by_id(manufacturer_id): return generic_get_object_by_id(Manufacturer, manufacturer_id, message="Hersteller") ### Device Type Functions ### # Returns all device types. def get_all_device_types(): return generic_get_objects(DeviceType, message="Gerätetypen") # Returns a single device type by its ID. def get_device_type_by_id(device_type_id): return generic_get_object_by_id(DeviceType, device_type_id, message="Gerätetyp") # Returns all device types of a specific manufacturer. def get_device_types_by_manufacturer(manufacturer_id): return generic_get_objects( DeviceType, filter_kwargs={'manufacturer': manufacturer_id}, select_related_fields=['manufacturer'], message="Gerätetypen" ) ### Product Functions ### # Returns all products. def get_all_products(): return generic_get_objects( Product, select_related_fields=['manufacturer', 'device_type'], message="Produkte" ) # Returns a single product by its ID. def get_product_by_id(product_id): return generic_get_object_by_id( Product, product_id, select_related_fields=['manufacturer', 'device_type'], message="Produkt" ) # Returns all products of a specific manufacturer. def get_products_by_manufacturer(manufacturer_id): return generic_get_objects( Product, filter_kwargs={'manufacturer': manufacturer_id}, select_related_fields=['manufacturer', 'device_type'], message="Produkte" ) # Returns all products of a specific device type. def get_products_by_device_type(device_type_id): return generic_get_objects( Product, filter_kwargs={'device_type': device_type_id}, select_related_fields=['manufacturer', 'device_type'], message="Produkte" ) ### Firmware Functions ### # Returns all firmwares. def get_all_firmwares(): return generic_get_objects( Firmware, select_related_fields=['product'], message="Firmwares" ) # Returns a single firmware by its ID. def get_firmware_by_id(firmware_id): return generic_get_object_by_id( Firmware, firmware_id, select_related_fields=['product'], message="Firmware" ) # Returns all firmwares of a specific product. def get_firmwares_by_product(product_id): return generic_get_objects( Firmware, filter_kwargs={'product': product_id}, select_related_fields=['product'], message="Firmwares" ) # Returns all firmwares of a specific device. def get_firmwares_by_device(device_id): return generic_get_objects( Firmware, filter_kwargs={'devices': device_id}, select_related_fields=['product'], message="Firmwares" ) ### Device Functions ### ##Returns all test devices including the complete manufacturer dataset. def get_all_devices(): devices = Device.objects.select_related('product', 'product__manufacturer', 'product__device_type') device_data = [] for device in devices: device_data.append({ 'id': device.id, 'manufacturer_name': device.product.manufacturer.name, 'device_type': device.product.device_type.device_type, 'device_name': device.product.product_name, 'serial': device.serial, 'comment:': device.comment, 'created:': device.created, }) return device_data # Returns a single test device by its ID. def get_devices_by_product(product_id): return generic_get_objects( Device, filter_kwargs={'product': product_id}, select_related_fields=['product'], message="Geräte" ) # Returns a single test device by its ID. def get_device_by_id(device_id): return generic_get_object_by_id( Device, device_id, select_related_fields=['product'], message="Gerät" ) ### File Functions ### # Returns all file entries. def get_all_files(): return generic_get_objects( File, select_related_fields=['firmware', 'device'], message="Dateien" ) # Returns a single file entry by its ID. def get_file_by_id(file_id): return generic_get_object_by_id( File, file_id, select_related_fields=['firmware', 'device'], message="Datei" ) # Returns all file entries of a specific device. def get_files_by_device(device_id: int): files = File.objects.filter(device_id=device_id).select_related('firmware', 'firmware__product') device_data = [] for file in files: device_data.append({ 'id': file.id, 'filename': file.filename, 'ip': file.ip_address, 'state': file.status, 'upload': file.upload_time, 'firmware_version': file.firmware.version, 'product': file.firmware.product.product_name }) return device_data # Returns all file entries of a specific firmware. def get_files_by_firmware(firmware_id): return generic_get_objects( File, filter_kwargs={'firmware': firmware_id}, select_related_fields=['firmware', 'device'], message="Dateien" ) ### Raw_Data Functions ### # Returns all raw data from all files. @handle_exceptions def get_all_raw_data(): raw_datasets = RawDataset.objects.prefetch_related( 'file', 'dnsraw_set', 'hashraw_set', 'urlraw_set' ).all() if not raw_datasets.exists(): raise ObjectDoesNotExist('Keine Rohdaten gefunden') # Extended to_json function for complete data def raw_dataset_to_detailed_json(dataset): base_json = dataset.to_json() base_json.update({ 'dns_data': [dns.to_json() for dns in dataset.dnsraw_set.all()], 'hash_data': [hash.to_json() for hash in dataset.hashraw_set.all()], 'url_data': [url.to_json() for url in dataset.urlraw_set.all()] }) return base_json data_list = [raw_dataset_to_detailed_json(obj) for obj in raw_datasets] return JsonResponse({ 'status': 'success', 'message': f'{len(data_list)} Rohdaten gefunden', 'data': data_list }, status=200) # Tools # Returns all used tools def get_all_tools(): return generic_get_objects( Tool, message="Tools") # Returns all raw data for the relevant file. @handle_exceptions def get_raw_data_by_file(file_id: int): raw_dataset = RawDataset.objects.select_related( 'file' ).prefetch_related( 'dnsraw_set', 'hashraw_set', 'urlraw_set' ).get(file_id=file_id) def raw_dataset_to_detailed_json(dataset): base_json = dataset.to_json() base_json.update({ 'dns_data': [dns.to_json() for dns in dataset.dnsraw_set.all()], 'hash_data': [hash.to_json() for hash in dataset.hashraw_set.all()], 'url_data': [url.to_json() for url in dataset.urlraw_set.all()] }) return base_json detailed_data = raw_dataset_to_detailed_json(raw_dataset) return { 'status': 'success', 'message': f'Rohdaten für File ID {file_id} gefunden', 'data': detailed_data } # Returns all found DNS raw data. def get_all_dns_raw_data_from_file(file_id: int): return generic_get_objects( model_class=DnsRaw, filter_kwargs={'raw_dataset__file_id': file_id}, select_related_fields=['raw_dataset'], message="DNS Rohdaten" ) # Returns all found hash raw data. def get_all_hash_raw_data_from_file(file_id: int): return generic_get_objects( model_class=HashRaw, filter_kwargs={'raw_dataset__file_id': file_id}, select_related_fields=['raw_dataset'], message="Hash Rohdaten" ) # Returns all found URL raw data. def get_all_url_raw_data_from_file(file_id: int): return generic_get_objects( model_class=UrlRaw, filter_kwargs={'raw_dataset__file_id': file_id}, select_related_fields=['raw_dataset'], message="URL Rohdaten" ) ### Analyzed_Data Functions ### # Returns all analyzed data from all files. @handle_exceptions def get_all_analyzed_data(): analyzed_datasets = AnalyzedDataset.objects.prefetch_related( 'file', 'dnsanalyzed_set', 'hashanalyzed_set', 'hashanalyzed_set__hash_type', 'urlanalyzed_set', 'urlanalyzed_set__ports', 'urlanalyzed_set__tls_versions', 'urlanalyzed_set__cipher_suites' ).all() if not analyzed_datasets.exists(): raise ObjectDoesNotExist('Keine analysierten Daten gefunden') def url_to_detailed_json(url): return url.to_json() def analyzed_dataset_to_detailed_json(dataset): base_json = dataset.to_json() base_json.update({ 'dns_data': [dns.to_json() for dns in dataset.dnsanalyzed_set.all()], 'hash_data': [hash.to_json() for hash in dataset.hashanalyzed_set.all()], 'url_data': [url_to_detailed_json(url) for url in dataset.urlanalyzed_set.all()] }) return base_json data_list = [analyzed_dataset_to_detailed_json(obj) for obj in analyzed_datasets] return{ 'status': 'success', 'message': f'{len(data_list)} analysierte Daten gefunden', 'data': data_list } # Returns all analyzed data for the relevant file. @handle_exceptions def get_analyzed_data_by_file(file_id: int): select_related_fields = ['file'] prefetch_related_fields = [ 'hashanalyzed_set', 'urlanalyzed_set', 'urlanalyzed_set__ports', 'urlanalyzed_set__tls_versions', 'urlanalyzed_set__cipher_suites', ] # Build queryset queryset = AnalyzedDataset.objects.select_related(*select_related_fields) for field in prefetch_related_fields: queryset = queryset.prefetch_related(field) # Apply filter queryset = queryset.filter(file_id=file_id) if not queryset.exists(): raise ObjectDoesNotExist(f'Keine analysierten Daten für File ID {file_id} gefunden') # Get single object, since we filter by ID analyzed_dataset = queryset.get() # Detail functions for the JSON conversion def url_to_detailed_json(url): return url.to_json() def analyzed_dataset_to_detailed_json(dataset): base_json = dataset.to_json() base_json.update({ 'dns_data': [dns.to_json() for dns in dataset.dnsanalyzed_set.all()], 'hash_data': [hash.to_json() for hash in dataset.hashanalyzed_set.all()], 'url_data': [url_to_detailed_json(url) for url in dataset.urlanalyzed_set.all()], }) return base_json detailed_data = analyzed_dataset_to_detailed_json(analyzed_dataset) return { 'status': 'success', 'message': f'Analysierte Daten für File ID {file_id} gefunden', 'data': detailed_data } # Returns all analyzed DNS data. def get_all_dns_analyzed_data_from_file(file_id: int): return generic_get_objects( model_class=DnsAnalyzed, filter_kwargs={'analyzed_dataset__file_id': file_id}, select_related_fields=['analyzed_dataset'], message="DNS analysierte Daten" ) # Returns all analyzed hash data. def get_all_hash_analyzed_data_from_file(file_id: int): return generic_get_objects( model_class=HashAnalyzed, filter_kwargs={'analyzed_dataset__file_id': file_id}, select_related_fields=['analyzed_dataset'], message="Hash analysierte Daten" ) # Returns all analyzed URL data. @handle_exceptions def get_all_url_analyzed_data_from_file(file_id: int): url_analyzed = UrlAnalyzed.objects.select_related( 'analyzed_dataset' ).prefetch_related( 'ports', 'tls_versions', 'cipher_suites' ).filter(analyzed_dataset__file_id=file_id) if not url_analyzed.exists(): raise ObjectDoesNotExist('Keine Daten zu analysierten URL gefunden') data_list = [url.to_json() for url in url_analyzed] return ({ 'status': 'success', 'message': f'Analysierte Daten zu {len(data_list)} URL(s) gefunden', 'data': data_list }) # Returns all analyzed URL data for a specific raw_url. @handle_exceptions def get_all_url_analyzed_by_raw_url_id(raw_url_id: int): ## TODO raw_url = UrlRaw.objects.get(id=raw_url_id) analyze_url.get_urls_from_db(raw_url_id) url_analyzed = UrlAnalyzed.objects.select_related( 'analyzed_dataset', 'tool' ).prefetch_related( 'ports', 'tls_versions', 'cipher_suites' ).filter( analyzed_dataset__file=raw_url.raw_dataset.file, ).filter( url=raw_url.url ) if not url_analyzed.exists(): raise ObjectDoesNotExist(f'Keine analysierten Daten gefunden für URL: {raw_url.url}') return { 'status': 'success', 'message': f'Analysierte Daten zu URL gefunden', 'data': [url.to_json() for url in url_analyzed] } # Returns all analyzed hash data for a specific raw_hash. @handle_exceptions def get_all_hash_analyzed_by_raw_hash_id(raw_hash_id: int): raw_hash = HashRaw.objects.get(id=raw_hash_id) analyze_hashes.crack_hashes_from_db(raw_hash_id) hash_analyzed = (HashAnalyzed.objects.select_related( 'analyzed_dataset', 'tool' ).prefetch_related( 'hash_type',) .filter( analyzed_dataset__file=raw_hash.raw_dataset.file, ).filter( hash_value=raw_hash.hash_value )) if not hash_analyzed.exists(): raise ObjectDoesNotExist(f'Keine analysierten Daten gefunden für Hash: {raw_hash.hash_value}') return { 'status': 'success', 'message': f'Analysierte Daten zu gefunden', 'data': [hash.to_json() for hash in hash_analyzed] } ### Firmware analysis READ functions ### # Returns all binwalk raw data of a file def get_all_binwalk_raw_data_from_file(file_id: int): """ Returns all binwalk raw data for a specific file. """ return generic_get_objects( model_class=BinwalkRaw, filter_kwargs={'raw_dataset__file_id': file_id}, select_related_fields=['raw_dataset'], message="Binwalk Rohdaten" ) # Returns all strings raw data of a file def get_all_strings_raw_data_from_file(file_id: int): """ Returns all strings raw data for a specific file. """ return generic_get_objects( model_class=StringsRaw, filter_kwargs={'raw_dataset__file_id': file_id}, select_related_fields=['raw_dataset'], message="Strings Rohdaten" ) # Returns filtered strings by type def get_strings_by_type(file_id: int, string_type: str): """ Returns filtered strings by type (url, ip, path, etc.). """ return generic_get_objects( model_class=StringsRaw, filter_kwargs={ 'raw_dataset__file_id': file_id, 'string_type': string_type }, select_related_fields=['raw_dataset'], message=f"Strings vom Typ {string_type}" ) # Returns all analyzed binwalk data of a file @handle_exceptions def get_all_binwalk_analyzed_data_from_file(file_id: int): """ Returns all analyzed binwalk data for a specific file. """ binwalk_analyzed = BinwalkAnalyzed.objects.select_related( 'analyzed_dataset', 'tool' ).filter(analyzed_dataset__file_id=file_id) if not binwalk_analyzed.exists(): raise ObjectDoesNotExist('Keine analysierten Binwalk-Daten gefunden') data_list = [entry.to_json() for entry in binwalk_analyzed] return { 'status': 'success', 'message': f'Analysierte Binwalk-Daten zu {len(data_list)} Einträgen gefunden', 'data': data_list } # Returns all analyzed strings data of a file @handle_exceptions def get_all_strings_analyzed_data_from_file(file_id: int): """ Returns all analyzed strings data for a specific file. """ strings_analyzed = StringsAnalyzed.objects.select_related( 'analyzed_dataset', 'tool' ).filter(analyzed_dataset__file_id=file_id) if not strings_analyzed.exists(): raise ObjectDoesNotExist('Keine analysierten Strings-Daten gefunden') data_list = [entry.to_json() for entry in strings_analyzed] return { 'status': 'success', 'message': f'Analysierte Strings-Daten zu {len(data_list)} Einträgen gefunden', 'data': data_list } # Returns critical strings by severity @handle_exceptions def get_critical_strings_by_severity(file_id: int, min_severity: str = 'medium'): """ Returns critical strings filtered by minimum severity. """ severity_order = {'info': 0, 'low': 1, 'medium': 2, 'high': 3, 'critical': 4} min_level = severity_order.get(min_severity, 0) strings_analyzed = StringsAnalyzed.objects.select_related( 'analyzed_dataset', 'tool' ).filter(analyzed_dataset__file_id=file_id) # Filter by severity filtered_strings = [ entry for entry in strings_analyzed if severity_order.get(entry.severity, 0) >= min_level ] if not filtered_strings: raise ObjectDoesNotExist(f'Keine Strings mit Severity >= {min_severity} gefunden') data_list = [entry.to_json() for entry in filtered_strings] return { 'status': 'success', 'message': f'{len(data_list)} kritische Strings gefunden', 'data': data_list } # Extended get_raw_data_by_file function (replaces the existing one) @handle_exceptions def get_raw_data_by_file_extended(file_id: int): """ Returns ALL raw data for a file (incl. firmware data). """ raw_dataset = RawDataset.objects.select_related( 'file' ).prefetch_related( 'dnsraw_set', 'hashraw_set', 'urlraw_set', 'binwalkraw_set', 'stringsraw_set' ).get(file_id=file_id) def raw_dataset_to_detailed_json(dataset): base_json = dataset.to_json() base_json.update({ 'dns_data': [dns.to_json() for dns in dataset.dnsraw_set.all()], 'hash_data': [hash.to_json() for hash in dataset.hashraw_set.all()], 'url_data': [url.to_json() for url in dataset.urlraw_set.all()], 'binwalk_data': [binwalk.to_json() for binwalk in dataset.binwalkraw_set.all()], 'strings_data': [string.to_json() for string in dataset.stringsraw_set.all()] }) return base_json detailed_data = raw_dataset_to_detailed_json(raw_dataset) return { 'status': 'success', 'message': f'Vollständige Rohdaten für File ID {file_id} gefunden', 'data': detailed_data } # Add import statements at the beginning of db_read.py: # from .models import BinwalkRaw, StringsRaw, BinwalkAnalyzed, StringsAnalyzed