From 1197dbe739486f8c5e7974c2ddfb6bffbdccfd50 Mon Sep 17 00:00:00 2001 From: Victor Kotseruba Date: Fri, 12 Jun 2015 01:22:02 +0300 Subject: [PATCH 1/4] `EXPOSE_USERNAMES` prevents sensitive information leakage --- avatar/conf.py | 1 + avatar/models.py | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/avatar/conf.py b/avatar/conf.py index 4d7f01f..14a263a 100644 --- a/avatar/conf.py +++ b/avatar/conf.py @@ -18,6 +18,7 @@ class AvatarConf(AppConf): THUMB_QUALITY = 85 HASH_FILENAMES = False HASH_USERDIRNAMES = False + EXPOSE_USERNAMES = True ALLOWED_FILE_EXTS = None CACHE_TIMEOUT = 60 * 60 STORAGE = settings.DEFAULT_FILE_STORAGE diff --git a/avatar/models.py b/avatar/models.py index 5bc3a52..bca0918 100644 --- a/avatar/models.py +++ b/avatar/models.py @@ -8,6 +8,7 @@ from django.core.files import File from django.core.files.base import ContentFile from django.core.files.storage import get_storage_class from django.utils.translation import ugettext as _ +from django.utils.encoding import force_text from django.utils import six from django.db.models import signals @@ -26,10 +27,12 @@ avatar_storage = get_storage_class(settings.AVATAR_STORAGE)() def avatar_file_path(instance=None, filename=None, size=None, ext=None): tmppath = [settings.AVATAR_STORAGE_DIR] if settings.AVATAR_HASH_USERDIRNAMES: - tmp = hashlib.md5(get_username(instance.user)).hexdigest() - tmppath.extend([tmp[0], tmp[1], get_username(instance.user)]) - else: + tmp = hashlib.md5(force_bytes(get_username(instance.user))).hexdigest() + tmppath.extend(tmp[0:2]) + if settings.AVATAR_EXPOSE_USERNAMES: tmppath.append(get_username(instance.user)) + else: + tmppath.append(force_text(instance.user.pk)) if not filename: # Filename already stored in database filename = instance.avatar.name From c9c18733822e9869133131ccec9a6ad173e10327 Mon Sep 17 00:00:00 2001 From: Victor Kotseruba Date: Mon, 21 Dec 2015 19:44:43 +0800 Subject: [PATCH 2/4] facebook backup --- avatar/conf.py | 2 ++ avatar/templatetags/avatar_tags.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/avatar/conf.py b/avatar/conf.py index 14a263a..30b05a1 100644 --- a/avatar/conf.py +++ b/avatar/conf.py @@ -24,6 +24,8 @@ class AvatarConf(AppConf): STORAGE = settings.DEFAULT_FILE_STORAGE CLEANUP_DELETED = False AUTO_GENERATE_SIZES = (DEFAULT_SIZE,) + FACEBOOK_BACKUP = False + FACEBOOK_GET_ID = None def configure_auto_generate_avatar_sizes(self, value): return value or getattr(settings, 'AVATAR_AUTO_GENERATE_SIZES', diff --git a/avatar/templatetags/avatar_tags.py b/avatar/templatetags/avatar_tags.py index 75a1a4f..fdb3a5e 100644 --- a/avatar/templatetags/avatar_tags.py +++ b/avatar/templatetags/avatar_tags.py @@ -11,6 +11,7 @@ from django.core.urlresolvers import reverse from django.template.loader import render_to_string from django.utils import six from django.utils.translation import ugettext as _ +from django.utils.module_loading import import_string from avatar.conf import settings from avatar.util import (get_primary_avatar, get_default_avatar_url, @@ -19,6 +20,14 @@ from avatar.models import Avatar register = template.Library() +get_facebook_id = None + +if settings.AVATAR_FACEBOOK_BACKUP: + if callable(settings.AVATAR_FACEBOOK_GET_ID): + get_facebook_id = settings.AVATAR_FACEBOOK_GET_ID + else: + get_facebook_id = import_string(settings.AVATAR_FACEBOOK_GET_ID) + @cache_result() @register.simple_tag @@ -27,6 +36,13 @@ def avatar_url(user, size=settings.AVATAR_DEFAULT_SIZE): if avatar: return avatar.avatar_url(size) + if settings.AVATAR_FACEBOOK_BACKUP: + fb_id = get_facebook_id(user) + if fb_id: + return 'https://graph.facebook.com/{fb_id}/picture?type=square&width={size}&height={size}'.format( + fb_id=fb_id, size=size + ) + if settings.AVATAR_GRAVATAR_BACKUP: params = {'s': str(size)} if settings.AVATAR_GRAVATAR_DEFAULT: From 724a4787381d227fe26b9a93bad0145b7f51aada Mon Sep 17 00:00:00 2001 From: Victor Kotseruba Date: Mon, 18 Jan 2016 18:14:15 +0800 Subject: [PATCH 3/4] disable cache option --- avatar/conf.py | 1 + avatar/util.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/avatar/conf.py b/avatar/conf.py index 30b05a1..6c5d5c2 100644 --- a/avatar/conf.py +++ b/avatar/conf.py @@ -26,6 +26,7 @@ class AvatarConf(AppConf): AUTO_GENERATE_SIZES = (DEFAULT_SIZE,) FACEBOOK_BACKUP = False FACEBOOK_GET_ID = None + DISABLE_CACHE = False def configure_auto_generate_avatar_sizes(self, value): return value or getattr(settings, 'AVATAR_AUTO_GENERATE_SIZES', diff --git a/avatar/util.py b/avatar/util.py index 195bee6..2a14121 100644 --- a/avatar/util.py +++ b/avatar/util.py @@ -64,6 +64,12 @@ def cache_result(default_size=settings.AVATAR_DEFAULT_SIZE): Decorator to cache the result of functions that take a ``user`` and a ``size`` value. """ + + if settings.AVATAR_DISABLE_CACHE: + def decorator(func): + return func + return decorator + def decorator(func): def cached_func(user, size=None): prefix = func.__name__ From 0aaa59e0a775e6d5a3b90decbe9dbba0bf0e9877 Mon Sep 17 00:00:00 2001 From: Victor Kotseruba Date: Mon, 18 Jan 2016 18:57:41 +0800 Subject: [PATCH 4/4] RANDOMIZE_HASHES option --- avatar/conf.py | 1 + avatar/models.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/avatar/conf.py b/avatar/conf.py index 6c5d5c2..4e81b63 100644 --- a/avatar/conf.py +++ b/avatar/conf.py @@ -27,6 +27,7 @@ class AvatarConf(AppConf): FACEBOOK_BACKUP = False FACEBOOK_GET_ID = None DISABLE_CACHE = False + RANDOMIZE_HASHES = False def configure_auto_generate_avatar_sizes(self, value): return value or getattr(settings, 'AVATAR_AUTO_GENERATE_SIZES', diff --git a/avatar/models.py b/avatar/models.py index bca0918..bc29b83 100644 --- a/avatar/models.py +++ b/avatar/models.py @@ -1,3 +1,4 @@ +import binascii import datetime import os import hashlib @@ -47,7 +48,10 @@ def avatar_file_path(instance=None, filename=None, size=None, ext=None): # File doesn't exist yet if settings.AVATAR_HASH_FILENAMES: (root, ext) = os.path.splitext(filename) - filename = hashlib.md5(force_bytes(filename)).hexdigest() + if settings.AVATAR_RANDOMIZE_HASHES: + filename = binascii.hexlify(os.urandom(16)).decode('ascii') + else: + filename = hashlib.md5(force_bytes(filename)).hexdigest() filename = filename + ext if size: tmppath.extend(['resized', str(size)])