diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index 35b9c3ff61..2ca179bb40 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -5,8 +5,10 @@ Generic models which provide extra functionality over base Django model types. from __future__ import unicode_literals import os +import logging from django.db import models +from django.conf import settings from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ @@ -21,6 +23,9 @@ from mptt.exceptions import InvalidMove from .validators import validate_tree_name +logger = logging.getLogger('inventree') + + def rename_attachment(instance, filename): """ Function for renaming an attachment file. @@ -55,20 +60,6 @@ class InvenTreeAttachment(models.Model): return "attachments" - def get_filename(self): - - return os.path.basename(self.attachment.name) - - def rename(self, filename): - """ - Rename this attachment with the provided filename. - - - Filename cannot be empty - - Filename must have an extension - - Filename will have random data appended if a file exists with the same name - """ - pass - def __str__(self): return os.path.basename(self.attachment.name) @@ -91,6 +82,72 @@ class InvenTreeAttachment(models.Model): def basename(self): return os.path.basename(self.attachment.name) + @basename.setter + def basename(self, fn): + """ + Function to rename the attachment file. + + - Filename cannot be empty + - Filename cannot contain illegal characters + - Filename must specify an extension + - Filename cannot match an existing file + """ + + fn = fn.strip() + + if len(fn) == 0: + raise ValidationError(_('Filename must not be empty')) + + attachment_dir = os.path.join( + settings.MEDIA_ROOT, + self.getSubdir() + ) + + old_file = os.path.join( + settings.MEDIA_ROOT, + self.attachment.name + ) + + new_file = os.path.join( + settings.MEDIA_ROOT, + self.getSubdir(), + fn + ) + + new_file = os.path.abspath(new_file) + + # Check that there are no directory tricks going on... + if not os.path.dirname(new_file) == attachment_dir: + logger.error(f"Attempted to rename attachment outside valid directory: '{new_file}'") + raise ValidationError(_("Invalid attachment directory")) + + # Ignore further checks if the filename is not actually being renamed + if new_file == old_file: + return + + forbidden = ["'", '"', "#", "@", "!", "&", "^", "<", ">", ":", ";", "/", "\\", "|", "?", "*", "%", "~", "`"] + + for c in forbidden: + if c in fn: + raise ValidationError(_(f"Filename contains illegal character '{c}'")) + + if len(fn.split('.')) < 2: + raise ValidationError(_("Filename missing extension")) + + if not os.path.exists(old_file): + logger.error(f"Trying to rename attachment '{old_file}' which does not exist") + return + + if os.path.exists(new_file): + raise ValidationError(_("Attachment with this filename already exists")) + + try: + os.rename(old_file, new_file) + self.attachment.name = os.path.join(self.getSubdir(), fn) + self.save() + except: + raise ValidationError(_("Error renaming file")) + class Meta: abstract = True