diff --git a/.env.example b/.env.example index ed60e95..935710c 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,7 @@ DEMO=true DEBUG=true ALLOWED_HOSTS=localhost,localhost:8000,127.0.0.1, +DEVICE_LOG_PATH=/tmp STATIC_ROOT=/tmp/static/ MEDIA_ROOT=/tmp/media/ EMAIL_HOST="mail.example.org" diff --git a/api/views.py b/api/views.py index be689e6..42d130b 100644 --- a/api/views.py +++ b/api/views.py @@ -21,7 +21,7 @@ from django.views.generic.edit import ( from utils.save_snapshots import move_json, save_in_disk from django.views.generic.edit import View from dashboard.mixins import DashboardView -from evidence.models import SystemProperty, UserProperty, Property +from evidence.models import SystemProperty, UserProperty from evidence.parse_details import ParseSnapshot from evidence.parse import Build from device.models import Device @@ -113,7 +113,6 @@ class NewSnapshotView(ApiMixing): property = SystemProperty.objects.filter( uuid=data['uuid'], - type=Property.Type.SYSTEM, # TODO this is hardcoded, it should select the user preferred algorithm key="hidalgo1", owner=self.tk.owner.institution @@ -281,7 +280,6 @@ class AddPropertyView(ApiMixing): self.property = SystemProperty.objects.filter( owner=institution, value=self.pk, - type=Property.Type.SYSTEM ).first() if not self.property: diff --git a/dashboard/views.py b/dashboard/views.py index cb36078..6b91674 100644 --- a/dashboard/views.py +++ b/dashboard/views.py @@ -6,7 +6,7 @@ from django.shortcuts import Http404 from django.db.models import Q from dashboard.mixins import InventaryMixin, DetailsMixin -from evidence.models import Property, SystemProperty +from evidence.models import SystemProperty from evidence.xapian import search from device.models import Device from lot.models import Lot @@ -96,7 +96,6 @@ class SearchView(InventaryMixin): qry |= Q(value__startswith=i) chids = SystemProperty.objects.filter( - type=Property.Type.SYSTEM, owner=self.request.user.institution ).filter( qry diff --git a/device/models.py b/device/models.py index 717af74..59ea09a 100644 --- a/device/models.py +++ b/device/models.py @@ -1,7 +1,7 @@ from django.db import models, connection from utils.constants import ALGOS -from evidence.models import SystemProperty, UserProperty, Property, Evidence +from evidence.models import SystemProperty, UserProperty, Evidence from lot.models import DeviceLot @@ -49,7 +49,6 @@ class Device: return self.properties self.properties = SystemProperty.objects.filter( - type=Property.Type.SYSTEM, value=self.id ).order_by("-created") @@ -66,6 +65,7 @@ class Device: user_properties = UserProperty.objects.filter( uuid__in=self.uuids, owner=self.owner, + type=UserProperty.Type.USER, ) return user_properties @@ -73,10 +73,10 @@ class Device: if not self.uuids: self.get_uuids() - properties = SystemProperty.objects.filter( + properties = UserProperty.objects.filter( uuid__in=self.uuids, owner=self.owner, - type=Property.Type.DOCUMENT + type=UserProperty.Type.DOCUMENT ) return properties @@ -91,7 +91,6 @@ class Device: algos = list(ALGOS.keys()) algos.append('CUSTOM_ID') self.hids = list(set(properties.filter( - type=Property.Type.SYSTEM, key__in=algos, ).values_list("value", flat=True))) @@ -115,10 +114,10 @@ class Device: if not self.uuids: return False - property = SystemProperty.objects.filter( + property = UserProperty.objects.filter( uuid__in=self.uuids, owner=self.owner, - type=Property.Type.ERASE_SERVER + type=UserProperty.Type.ERASE_SERVER ).first() if property: @@ -154,7 +153,6 @@ class Device: LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id WHERE t2.device_id IS NULL AND t1.owner_id = {institution} - AND t1.type = {type} ) SELECT DISTINCT value @@ -164,7 +162,6 @@ class Device: row_num = 1 """.format( institution=institution.id, - type=Property.Type.SYSTEM, ) if limit: sql += " limit {} offset {}".format(int(limit), int(offset)) @@ -202,7 +199,6 @@ class Device: LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id WHERE t2.device_id IS NULL AND t1.owner_id = {institution} - And t1.type = '{type}' ) SELECT COUNT(DISTINCT value) @@ -212,7 +208,6 @@ class Device: row_num = 1 """.format( institution=institution.id, - type=Property.Type.SYSTEM, ) with connection.cursor() as cursor: cursor.execute(sql) @@ -239,7 +234,6 @@ class Device: LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id WHERE t2.device_id IS NULL AND t1.owner_id = {institution} - AND t1.type = '{type}' AND t1.uuid = '{uuid}' ) SELECT DISTINCT @@ -251,7 +245,6 @@ class Device: """.format( uuid=uuid.replace("-", ""), institution=institution.id, - type=Property.Type.SYSTEM, ) properties = [] diff --git a/device/views.py b/device/views.py index 743c67e..60cfd81 100644 --- a/device/views.py +++ b/device/views.py @@ -16,7 +16,7 @@ from django.views.generic.edit import ( from django.views.generic.base import TemplateView from action.models import StateDefinition, State from dashboard.mixins import DashboardView, Http403 -from evidence.models import UserProperty, SystemProperty, Property +from evidence.models import UserProperty, SystemProperty from lot.models import LotTag from device.models import Device from device.forms import DeviceFormSet @@ -189,9 +189,14 @@ class AddUserPropertyView(DashboardView, CreateView): form.instance.owner = self.request.user.institution form.instance.user = self.request.user form.instance.uuid = self.property.uuid - form.instance.type = Property.Type.USER + form.instance.type = UserProperty.Type.USER messages.success(self.request, _("User property successfully added.")) + + device_logger.info( + f"Created user property (key='{form.instance.key}', value='{form.instance.value}') by user {self.request.user}, for evidence uuid: {self.property.uuid}." + ) + response = super().form_valid(form) return response @@ -201,7 +206,6 @@ class AddUserPropertyView(DashboardView, CreateView): self.property = SystemProperty.objects.filter( owner=institution, value=pk, - type=Property.Type.SYSTEM ).first() if not self.property: @@ -237,7 +241,7 @@ class UpdateUserPropertyView(DashboardView, UpdateView): form.instance.owner = self.request.user.institution form.instance.user = self.request.user - form.instance.type = Property.Type.USER + form.instance.type = UserProperty.Type.USER response = super().form_valid(form) messages.success(self.request, _("User property updated successfully.")) @@ -278,14 +282,14 @@ class AddDocumentView(DashboardView, CreateView): title = _("New Document") breadcrumb = "Device / New document" success_url = reverse_lazy('dashboard:unassigned_devices') - model = SystemProperty + model = UserProperty fields = ("key", "value") def form_valid(self, form): form.instance.owner = self.request.user.institution form.instance.user = self.request.user form.instance.uuid = self.property.uuid - form.instance.type = Property.Type.DOCUMENT + form.instance.type = UserProperty.Type.DOCUMENT response = super().form_valid(form) return response @@ -295,7 +299,6 @@ class AddDocumentView(DashboardView, CreateView): self.property = SystemProperty.objects.filter( owner=institution, value=pk, - type=Property.Type.SYSTEM ).first() if not self.property: diff --git a/dhub/settings.py b/dhub/settings.py index 433c057..0ad95f8 100644 --- a/dhub/settings.py +++ b/dhub/settings.py @@ -65,6 +65,8 @@ ENABLE_EMAIL = config("ENABLE_EMAIL", default=True, cast=bool) EVIDENCES_DIR = config("EVIDENCES_DIR", default=os.path.join(BASE_DIR, "db")) +DEVICE_LOG_PATH = config("DEVICE_LOG_PATH", default="/tmp") + # Application definition INSTALLED_APPS = [ @@ -224,7 +226,7 @@ LOGGING = { 'device_log_file': { 'level': 'INFO', 'class': 'logging.FileHandler', - 'filename': '/var/log/device_changes.log', + 'filename': DEVICE_LOG_PATH + "/device_changes.log", 'formatter': 'verbose', }, }, diff --git a/evidence/forms.py b/evidence/forms.py index c802b66..774362f 100644 --- a/evidence/forms.py +++ b/evidence/forms.py @@ -8,7 +8,7 @@ from utils.device import create_property, create_doc, create_index from utils.forms import MultipleFileField from device.models import Device from evidence.parse import Build -from evidence.models import SystemProperty, Property +from evidence.models import SystemProperty from utils.save_snapshots import move_json, save_in_disk @@ -70,7 +70,6 @@ class UserTagForm(forms.Form): self.user = kwargs.pop('user') instance = SystemProperty.objects.filter( uuid=self.uuid, - type=Property.Type.SYSTEM, key='CUSTOM_ID', owner=self.user.institution ).first() @@ -88,7 +87,6 @@ class UserTagForm(forms.Form): self.tag = data self.instance = SystemProperty.objects.filter( uuid=self.uuid, - type=Property.Type.SYSTEM, key='CUSTOM_ID', owner=self.user.institution ).first() @@ -108,7 +106,6 @@ class UserTagForm(forms.Form): SystemProperty.objects.create( uuid=self.uuid, - type=Property.Type.SYSTEM, key='CUSTOM_ID', value=self.tag, owner=self.user.institution, @@ -186,9 +183,9 @@ class EraseServerForm(forms.Form): self.pk = None self.uuid = kwargs.pop('uuid', None) self.user = kwargs.pop('user') - instance = SystemProperty.objects.filter( + instance = UserProperty.objects.filter( uuid=self.uuid, - type=SystemProperty.Type.ERASE_SERVER, + type=UserProperty.Type.ERASE_SERVER, key='ERASE_SERVER', owner=self.user.institution ).first() @@ -201,9 +198,9 @@ class EraseServerForm(forms.Form): def clean(self): self.erase_server = self.cleaned_data.get('erase_server', False) - self.instance = SystemProperty.objects.filter( + self.instance = UserProperty.objects.filter( uuid=self.uuid, - type=Property.Type.ERASE_SERVER, + type=UserProperty.Type.ERASE_SERVER, key='ERASE_SERVER', owner=self.user.institution ).first() @@ -222,9 +219,9 @@ class EraseServerForm(forms.Form): if self.instance: return - SystemProperty.objects.create( + UserProperty.objects.create( uuid=self.uuid, - type=Property.Type.ERASE_SERVER, + type=UserProperty.Type.ERASE_SERVER, key='ERASE_SERVER', value=self.erase_server, owner=self.user.institution, diff --git a/evidence/migrations/0003_systemproperty_userproperty_delete_annotation_and_more.py b/evidence/migrations/0003_systemproperty_userproperty_delete_annotation_and_more.py new file mode 100644 index 0000000..29e5e78 --- /dev/null +++ b/evidence/migrations/0003_systemproperty_userproperty_delete_annotation_and_more.py @@ -0,0 +1,107 @@ +# Generated by Django 5.0.6 on 2024-12-10 19:37 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("evidence", "0002_alter_annotation_type"), + ("user", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="SystemProperty", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("key", models.CharField(max_length=256)), + ("value", models.CharField(max_length=256)), + ("uuid", models.UUIDField()), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="user.institution", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.CreateModel( + name="UserProperty", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("key", models.CharField(max_length=256)), + ("value", models.CharField(max_length=256)), + ("uuid", models.UUIDField()), + ( + "type", + models.SmallIntegerField( + choices=[(1, "User"), (2, "Document"), (3, "EraseServer")], + default=1, + ), + ), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="user.institution", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.DeleteModel( + name="Annotation", + ), + migrations.AddConstraint( + model_name="systemproperty", + constraint=models.UniqueConstraint( + fields=("key", "uuid"), name="system_unique_type_key_uuid" + ), + ), + migrations.AddConstraint( + model_name="userproperty", + constraint=models.UniqueConstraint( + fields=("key", "uuid", "type"), name="user_unique_type_key_uuid" + ), + ), + ] diff --git a/evidence/models.py b/evidence/models.py index 4574f64..3457d2b 100644 --- a/evidence/models.py +++ b/evidence/models.py @@ -10,20 +10,12 @@ from evidence.xapian import search from evidence.parse_details import ParseSnapshot from user.models import User, Institution -#TODO: base class is abstract; revise if should be for query efficiency -class Property(models.Model): - class Type(models.IntegerChoices): - SYSTEM = 0, "System" - USER = 1, "User" - DOCUMENT = 2, "Document" - ERASE_SERVER = 3, "EraseServer" +class Property(models.Model): created = models.DateTimeField(auto_now_add=True) - uuid = models.UUIDField() owner = models.ForeignKey(Institution, on_delete=models.CASCADE) user = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True) - type = models.SmallIntegerField(choices=Type) key = models.CharField(max_length=STR_EXTEND_SIZE) value = models.CharField(max_length=STR_EXTEND_SIZE) @@ -31,40 +23,34 @@ class Property(models.Model): #Only for shared behaviour, it is not a table abstract = True + class SystemProperty(Property): + uuid = models.UUIDField() class Meta: - constraints = [ - models.CheckConstraint( - check=~Q(type=1), #Enforce that type is not User - name='property_cannot_be_user' - ), - ] - #Django orm wont inherit constraints to child - #TODO: check if this is needed constraints = [ models.UniqueConstraint( - fields=["type", "key", "uuid"], name="system_unique_type_key_uuid") + fields=["key", "uuid"], name="system_unique_type_key_uuid") ] + class UserProperty(Property): + uuid = models.UUIDField() - type = models.SmallIntegerField(default=Property.Type.USER) + class Type(models.IntegerChoices): + USER = 1, "User" + DOCUMENT = 2, "Document" + ERASE_SERVER = 3, "EraseServer" + + type = models.SmallIntegerField(choices=Type, default=Type.USER) class Meta: - constraints = [ - models.CheckConstraint( - check=Q(type=1), #Enforce that type is User - name='property_needs_to_be_user' - ), - ] constraints = [ models.UniqueConstraint( - fields=["type", "key", "uuid"], name="user_unique_type_key_uuid") + fields=["key", "uuid", "type"], name="user_unique_type_key_uuid") ] - class Evidence: def __init__(self, uuid): self.uuid = uuid @@ -166,7 +152,6 @@ class Evidence: def get_all(cls, user): return SystemProperty.objects.filter( owner=user.institution, - type=Property.Type.SYSTEM, key="hidalgo1", ).order_by("-created").values_list("uuid", "created").distinct() diff --git a/evidence/parse.py b/evidence/parse.py index b9c16c8..5a20896 100644 --- a/evidence/parse.py +++ b/evidence/parse.py @@ -6,7 +6,7 @@ from dmidecode import DMIParse from json_repair import repair_json from evidence.parse_details import get_lshw_child -from evidence.models import SystemProperty, Property +from evidence.models import SystemProperty from evidence.xapian import index from utils.constants import CHASSIS_DH @@ -76,7 +76,6 @@ class Build: property = SystemProperty.objects.filter( uuid=self.uuid, owner=self.user.institution, - type=Property.Type.SYSTEM, ) if property: @@ -89,7 +88,6 @@ class Build: uuid=self.uuid, owner=self.user.institution, user=self.user, - type=Property.Type.SYSTEM, key=k, value=v ) diff --git a/evidence/views.py b/evidence/views.py index c71ab68..833de03 100644 --- a/evidence/views.py +++ b/evidence/views.py @@ -13,7 +13,7 @@ from django.views.generic.edit import ( ) from dashboard.mixins import DashboardView, Http403 -from evidence.models import Property, SystemProperty, UserProperty, Evidence +from evidence.models import SystemProperty, UserProperty, Evidence from evidence.forms import ( UploadForm, UserTagForm, diff --git a/lot/migrations/0003_lotproperty_delete_lotannotation_and_more.py b/lot/migrations/0003_lotproperty_delete_lotannotation_and_more.py new file mode 100644 index 0000000..c10ce93 --- /dev/null +++ b/lot/migrations/0003_lotproperty_delete_lotannotation_and_more.py @@ -0,0 +1,77 @@ +# Generated by Django 5.0.6 on 2024-12-10 19:37 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("lot", "0002_alter_lot_closed"), + ("user", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="LotProperty", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("key", models.CharField(max_length=256)), + ("value", models.CharField(max_length=256)), + ( + "type", + models.SmallIntegerField( + choices=[ + (0, "System"), + (1, "User"), + (2, "Document"), + (3, "EraseServer"), + ], + default=1, + ), + ), + ( + "lot", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="lot.lot" + ), + ), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="user.institution", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.DeleteModel( + name="LotAnnotation", + ), + migrations.AddConstraint( + model_name="lotproperty", + constraint=models.UniqueConstraint( + fields=("key", "lot", "type"), name="lot_unique_type_key_lot" + ), + ), + ] diff --git a/lot/models.py b/lot/models.py index d9ef6ef..59ca155 100644 --- a/lot/models.py +++ b/lot/models.py @@ -6,8 +6,8 @@ from utils.constants import ( STR_EXTEND_SIZE, ) -from user.models import User, Institution -from device.models import Property +from user.models import User, Institution +from evidence.models import Property # from device.models import Device @@ -46,7 +46,19 @@ class Lot(models.Model): d.delete() class LotProperty (Property): - #uuid is not needed for id - uuid = None - #lot foreign key is lot = models.ForeignKey(Lot, on_delete=models.CASCADE) + + class Type(models.IntegerChoices): + SYSTEM = 0, "System" + USER = 1, "User" + DOCUMENT = 2, "Document" + ERASE_SERVER = 3, "EraseServer" + + type = models.SmallIntegerField(choices=Type.choices, default=Type.USER) + + class Meta: + constraints = [ + models.UniqueConstraint( + fields=["key", "lot", "type"], name="lot_unique_type_key_lot" + ) + ] diff --git a/lot/views.py b/lot/views.py index 36f582f..9ff0227 100644 --- a/lot/views.py +++ b/lot/views.py @@ -11,7 +11,6 @@ from django.views.generic.edit import ( from dashboard.mixins import DashboardView from lot.models import Lot, LotTag, LotProperty from lot.forms import LotsForm -from device.models import Property class NewLotView(DashboardView, CreateView): template_name = "new_lot.html" @@ -154,7 +153,7 @@ class LotAddDocumentView(DashboardView, CreateView): form.instance.owner = self.request.user.institution form.instance.user = self.request.user form.instance.lot = self.lot - form.instance.type = Property.Type.DOCUMENT + form.instance.type = LotProperty.Type.DOCUMENT response = super().form_valid(form) return response @@ -178,7 +177,7 @@ class LotDocumentsView(DashboardView, TemplateView): documents = LotProperty.objects.filter( lot=lot, owner=self.request.user.institution, - type=Property.Type.DOCUMENT, + type=LotProperty.Type.DOCUMENT, ) context.update({ 'lot': lot, @@ -201,7 +200,7 @@ class LotPropertiesView(DashboardView, TemplateView): properties = LotProperty.objects.filter( lot=lot, owner=self.request.user.institution, - type=Property.Type.USER, + type=LotProperty.Type.USER, ) context.update({ 'lot': lot, @@ -224,7 +223,7 @@ class LotAddPropertyView(DashboardView, CreateView): form.instance.owner = self.request.user.institution form.instance.user = self.request.user form.instance.lot = self.lot - form.instance.type = Property.Type.USER + form.instance.type = LotProperty.Type.USER response = super().form_valid(form) return response diff --git a/utils/device.py b/utils/device.py index 43d8e1b..865aff1 100644 --- a/utils/device.py +++ b/utils/device.py @@ -6,7 +6,7 @@ import logging from django.core.exceptions import ValidationError from evidence.xapian import index -from evidence.models import SystemProperty, Property +from evidence.models import SystemProperty from device.models import Device @@ -76,7 +76,6 @@ def create_property(doc, user, commit=False): 'uuid': doc['uuid'], 'owner': user.institution, 'user': user, - 'type': Property.Type.SYSTEM, 'key': 'CUSTOMER_ID', 'value': doc['CUSTOMER_ID'], } @@ -84,7 +83,6 @@ def create_property(doc, user, commit=False): property = SystemProperty.objects.filter( uuid=doc["uuid"], owner=user.institution, - type=Property.Type.SYSTEM, ) if property: