Use correct extension for thumbnails, fixes #54

This commit is contained in:
Johannes Wilm 2022-08-16 13:28:09 +02:00
parent 210b54ca09
commit 39f8681e29
4 changed files with 27 additions and 14 deletions

View file

@ -34,22 +34,21 @@ def avatar_path_handler(
if not filename:
# Filename already stored in database
filename = instance.avatar.name
if ext and settings.AVATAR_HASH_FILENAMES:
# An extension was provided, probably because the thumbnail
# is in a different format than the file. Use it. Because it's
# only enabled if AVATAR_HASH_FILENAMES is true, we can trust
# it won't conflict with another filename
if ext:
(root, oldext) = os.path.splitext(filename)
filename = root + "." + ext.lower()
else:
# File doesn't exist yet
(root, oldext) = os.path.splitext(filename)
if settings.AVATAR_HASH_FILENAMES:
(root, ext) = os.path.splitext(filename)
if settings.AVATAR_RANDOMIZE_HASHES:
filename = binascii.hexlify(os.urandom(16)).decode("ascii")
root = binascii.hexlify(os.urandom(16)).decode("ascii")
else:
filename = hashlib.md5(force_bytes(filename)).hexdigest()
filename = filename + ext
root = hashlib.md5(force_bytes(root)).hexdigest()
if ext:
filename = root + "." + ext.lower()
else:
filename = root + oldext.lower()
if width or height:
tmppath.extend(["resized", str(width), str(height)])
tmppath.append(os.path.basename(filename))
@ -70,7 +69,7 @@ def find_extension(format):
class AvatarField(models.ImageField):
def __init__(self, *args, **kwargs):
super(AvatarField, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.max_length = 1024
self.upload_to = avatar_file_path
@ -78,7 +77,7 @@ class AvatarField(models.ImageField):
self.blank = True
def deconstruct(self):
name, path, args, kwargs = super(models.ImageField, self).deconstruct()
name, path, args, kwargs = super().deconstruct()
return name, path, (), {}
@ -116,7 +115,7 @@ class Avatar(models.Model):
avatars.update(primary=False)
else:
avatars.delete()
super(Avatar, self).save(*args, **kwargs)
super().save(*args, **kwargs)
def thumbnail_exists(self, width, height=None):
return self.avatar.storage.exists(self.avatar_name(width, height))
@ -162,8 +161,6 @@ class Avatar(models.Model):
else:
thumb_file = File(orig)
thumb_name = self.avatar_name(width, height)
if self.avatar.storage.exists(thumb_name):
self.avatar.storage.delete(thumb_name)
thumb = self.avatar.storage.save(thumb_name, thumb_file)
except IOError:
thumb_file = File(orig)

View file

@ -21,6 +21,7 @@ def avatar_url(user, width=settings.AVATAR_DEFAULT_SIZE, height=None):
avatar_url = provider.get_avatar_url(user, width, height)
if avatar_url:
return avatar_url
return get_default_avatar_url()
@cache_result()

View file

@ -254,6 +254,15 @@ appear on the site. Listed below are those settings:
Path to the Django template to use for confirming a delete of a user's
avatar. Defaults to ``avatar/avatar/confirm_delete.html``.
.. py:data:: AVATAR_ALLOWED_MIMETYPES
Limit allowed avatar image uploads by their actual content payload and what image codecs we wish to support.
This limits website user content site attack vectors against image codec buffer overflow and similar bugs.
`You must have python-imaging library installed <https://github.com/ahupp/python-magic>`_.
Suggested safe setting: ``("image/png", "image/gif", "image/jpeg")``.
When enabled you'll get the following error on the form upload *File content is invalid. Detected: image/tiff Allowed content types are: image/png, image/gif, image/jpg*.
Management Commands
-------------------

View file

@ -287,6 +287,12 @@ class AvatarTests(TestCase):
)
self.assertEqual(image.mode, "RGB")
def test_automatic_thumbnail_creation_image_type_conversion(self):
upload_helper(self, "django_pony_cmyk.jpg")
self.assertMediaFileExists(
f"/avatars/{self.user.id}/resized/80/80/django_pony_cmyk.png"
)
def test_thumbnail_transpose_based_on_exif(self):
upload_helper(self, "image_no_exif.jpg")
avatar = get_primary_avatar(self.user)