mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-11-13 12:23:44 +01:00
ImportProfile and ImportRun had no owner, and neither the web views nor the REST viewsets filtered by the requesting user, so any authenticated user could read, edit and delete every other user's import profiles and runs (GHSA-q668-h56g-mfp4). - ImportProfile is now a SharedObject, with share and take-ownership views like transaction rules; the global unique constraint on name is dropped. - ImportRun is now an OwnedObject owned by the uploader, so runs made with a shared profile stay private to whoever ran them. - Existing runs are backfilled with the owner of their imported transactions; existing profiles stay unowned and usable by everyone.
100 lines
3.0 KiB
Python
100 lines
3.0 KiB
Python
import yaml
|
|
|
|
from django.core.exceptions import ValidationError
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from apps.common.models import (
|
|
OwnedObject,
|
|
OwnedObjectManager,
|
|
SharedObject,
|
|
SharedObjectManager,
|
|
)
|
|
from apps.import_app.schemas import version_1
|
|
|
|
|
|
class ImportProfile(SharedObject):
|
|
class Versions(models.IntegerChoices):
|
|
VERSION_1 = 1, "Version 1"
|
|
|
|
name = models.CharField(max_length=100, verbose_name=_("Name"))
|
|
yaml_config = models.TextField(verbose_name=_("YAML Configuration"))
|
|
version = models.IntegerField(
|
|
choices=Versions,
|
|
default=Versions.VERSION_1,
|
|
verbose_name=_("Version"),
|
|
)
|
|
|
|
objects = SharedObjectManager()
|
|
all_objects = models.Manager() # Unfiltered manager
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Meta:
|
|
ordering = ["name"]
|
|
|
|
def get_version_display(self):
|
|
version_number = self.Versions(self.version).name.split("_")[1]
|
|
return _("Version {number}").format(number=version_number)
|
|
|
|
def clean(self):
|
|
if self.version and self.version == self.Versions.VERSION_1:
|
|
try:
|
|
yaml_data = yaml.safe_load(self.yaml_config)
|
|
version_1.ImportProfileSchema(**yaml_data)
|
|
except Exception as e:
|
|
raise ValidationError(
|
|
{"yaml_config": _("Invalid YAML Configuration: ") + str(e)}
|
|
)
|
|
|
|
|
|
class ImportRun(OwnedObject):
|
|
class Status(models.TextChoices):
|
|
QUEUED = "QUEUED", _("Queued")
|
|
PROCESSING = "PROCESSING", _("Processing")
|
|
FAILED = "FAILED", _("Failed")
|
|
FINISHED = "FINISHED", _("Finished")
|
|
|
|
status = models.CharField(
|
|
max_length=10,
|
|
choices=Status,
|
|
default=Status.QUEUED,
|
|
verbose_name=_("Status"),
|
|
)
|
|
profile = models.ForeignKey(
|
|
ImportProfile,
|
|
on_delete=models.CASCADE,
|
|
)
|
|
file_name = models.CharField(
|
|
max_length=10000,
|
|
help_text=_("File name"),
|
|
)
|
|
transactions = models.ManyToManyField(
|
|
"transactions.Transaction", related_name="import_runs"
|
|
)
|
|
tags = models.ManyToManyField(
|
|
"transactions.TransactionTag", related_name="import_runs"
|
|
)
|
|
categories = models.ManyToManyField(
|
|
"transactions.TransactionCategory", related_name="import_runs"
|
|
)
|
|
entities = models.ManyToManyField(
|
|
"transactions.TransactionEntity", related_name="import_runs"
|
|
)
|
|
currencies = models.ManyToManyField(
|
|
"currencies.Currency", related_name="import_runs"
|
|
)
|
|
|
|
logs = models.TextField(blank=True)
|
|
processed_rows = models.IntegerField(default=0)
|
|
total_rows = models.IntegerField(default=0)
|
|
successful_rows = models.IntegerField(default=0)
|
|
skipped_rows = models.IntegerField(default=0)
|
|
failed_rows = models.IntegerField(default=0)
|
|
started_at = models.DateTimeField(null=True)
|
|
finished_at = models.DateTimeField(null=True)
|
|
|
|
objects = OwnedObjectManager()
|
|
all_objects = models.Manager() # Unfiltered manager
|