Show user-uploaded avatars on the website.

Show user-uploaded avatars on the website for users who have
UserProfile.avatar_source == 'U'.  (Continue to show gravatars
for other users.)  This includes the home page, the visible-phone
div, and the settings page.

This fix does NOT address a few things:
* There is no GUI to actually upload user images yet on the website.
* The !gravatar syntax in bugdown will continue to show gravatar images
  only.
* We are not changing identicon behavior.

(imported from commit 9f5ac0bbe21ba56528048233aab2430e4dd431aa)
This commit is contained in:
Steve Howell
2013-06-10 15:35:48 -04:00
parent 01372b1f9a
commit d740d7c082
9 changed files with 38 additions and 10 deletions

View File

@@ -99,6 +99,12 @@ SECRET_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# username generation. # username generation.
HASH_SALT = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' HASH_SALT = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# Use this salt to hash a user's email into a filename for their user-uploaded
# avatar. If this salt is discovered, attackers will only be able to determine
# that the owner of an email account has uploaded an avatar to Humbug, which isn't
# the end of the world. Don't use the salt where there is more security exposure.
AVATAR_SALT = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# Tell the browser to never send our cookies without encryption, e.g. # Tell the browser to never send our cookies without encryption, e.g.
# when executing the initial http -> https redirect. # when executing the initial http -> https redirect.
# #

View File

@@ -3,7 +3,7 @@
<div class="upper_sidebar"> <div class="upper_sidebar">
<div class="brand"> <div class="brand">
<img class="img-rounded gravatar-profile" <img class="img-rounded gravatar-profile"
src="https://secure.gravatar.com/avatar/{{ email_hash }}?d=identicon&s=60" /> src="{{ avatar_url }}&s=60" />
<span id="my_information"> <span id="my_information">
<span class="my_fullname">{{ user_profile.full_name }}</span><br /> <span class="my_fullname">{{ user_profile.full_name }}</span><br />
<span class="my_email">{{ user_profile.email }}</span> <span class="my_email">{{ user_profile.email }}</span>

View File

@@ -9,7 +9,7 @@
<a class="brand skinny-user-gravatar visible-phone" href="#" <a class="brand skinny-user-gravatar visible-phone" href="#"
title="{{user_profile.full_name}} - {{user_profile.email}}"> title="{{user_profile.full_name}} - {{user_profile.email}}">
<img class="img-rounded gravatar-profile" <img class="img-rounded gravatar-profile"
src="https://secure.gravatar.com/avatar/{{ email_hash }}?d=identicon&s=30" /> src="{{ avatar_url }}&s=30" />
</a> </a>
<div id="searchbox"> <div id="searchbox">
<form id="searchbox_form" class="form-search navbar-search"> <form id="searchbox_form" class="form-search navbar-search">

View File

@@ -68,8 +68,8 @@
<label class="control-label">Your picture</label> <label class="control-label">Your picture</label>
<div class="controls"> <div class="controls">
<a href="https://en.gravatar.com/emails" target="_blank" class="change_gravatar_button"> <a href="https://en.gravatar.com/emails" target="_blank" class="change_gravatar_button">
<p><img class="img-rounded gravatar-profile" <p><img class="img-rounded gravatar-profile user-image-settings"
src="https://secure.gravatar.com/avatar/{{ email_hash }}?d=identicon&s=60" /></p> src="{{ avatar_url }}&s=60" /></p>
<p>Change at Gravatar.com</p> <p>Change at Gravatar.com</p>
</a> </a>
</div> </div>

View File

@@ -1,4 +1,5 @@
from __future__ import absolute_import from __future__ import absolute_import
from django.conf import settings
import hashlib import hashlib
from zephyr.lib.utils import make_safe_digest from zephyr.lib.utils import make_safe_digest
@@ -11,3 +12,21 @@ def gravatar_hash(email):
# typo an address or someone manages to give us a non-ASCII address, let's # typo an address or someone manages to give us a non-ASCII address, let's
# not error out on it. # not error out on it.
return make_safe_digest(email.lower(), hashlib.md5) return make_safe_digest(email.lower(), hashlib.md5)
def user_avatar_hash(email):
# Salting the user_key may be overkill, but it prevents us from
# basically mimicking Gravatar's hashing scheme, which could lead
# to some abuse scenarios like folks using us as a free Gravatar
# replacement.
user_key = email.lower() + settings.AVATAR_SALT
return make_safe_digest(user_key, hashlib.sha1)
def avatar_url(user_profile):
if user_profile.avatar_source == 'U':
bucket = settings.S3_AVATAR_BUCKET
hash_key = user_avatar_hash(user_profile.email)
# ?x=x allows templates to append additional parameters with &s
return "https://%s.s3.amazonaws.com/%s?x=x" % (bucket, hash_key)
else:
hash_key = gravatar_hash(user_profile.email)
return "https://secure.gravatar.com/avatar/%s?d=identicon" % (hash_key,)

View File

@@ -10,7 +10,7 @@ from zephyr.lib.utils import make_safe_digest
import os import os
from django.db import transaction, IntegrityError from django.db import transaction, IntegrityError
from zephyr.lib import bugdown from zephyr.lib import bugdown
from zephyr.lib.avatar import gravatar_hash from zephyr.lib.avatar import gravatar_hash, avatar_url
from django.utils import timezone from django.utils import timezone
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from django.utils.html import escape from django.utils.html import escape
@@ -333,7 +333,8 @@ class Message(models.Model):
recipient_id = self.recipient.id, recipient_id = self.recipient.id,
subject = self.subject, subject = self.subject,
timestamp = datetime_to_timestamp(self.pub_date), timestamp = datetime_to_timestamp(self.pub_date),
gravatar_hash = gravatar_hash(self.sender.email), gravatar_hash = gravatar_hash(self.sender.email), # Deprecated June 2013
avatar_url = avatar_url(self.sender),
client = self.sending_client.name) client = self.sending_client.name)
if self.last_edit_time != None: if self.last_edit_time != None:

View File

@@ -59,7 +59,7 @@
{{#include_sender}} {{#include_sender}}
<span class="message_sender actions_hover"> <span class="message_sender actions_hover">
<div class="inline_profile_picture" <div class="inline_profile_picture"
style="background-image: url('https://secure.gravatar.com/avatar/{{gravatar_hash}}?d=identicon&s=30?stamp={{stamp}}');"/> style="background-image: url('{{avatar_url}}&s=30&stamp={{stamp}}');"/>
<span class="sender_name">{{sender_full_name}}</span> <span class="sender_name">{{sender_full_name}}</span>
</span> </span>
{{/include_sender}} {{/include_sender}}

View File

@@ -1046,9 +1046,11 @@ class GetOldMessagesTest(AuthedTestCase):
self.assertIsInstance(result["messages"], list) self.assertIsInstance(result["messages"], list)
for message in result["messages"]: for message in result["messages"]:
for field in ("content", "content_type", "display_recipient", for field in ("content", "content_type", "display_recipient",
"gravatar_hash", "recipient_id", "sender_full_name", "avatar_url", "recipient_id", "sender_full_name",
"sender_short_name", "timestamp"): "sender_short_name", "timestamp"):
self.assertIn(field, message) self.assertIn(field, message)
# TODO: deprecate soon in favor of avatar_url
self.assertIn('gravatar_hash', message)
def test_successful_get_old_messages(self): def test_successful_get_old_messages(self):
""" """

View File

@@ -46,7 +46,7 @@ from zephyr.decorator import require_post, \
JsonableError, RequestVariableMissingError, get_user_profile_by_email, \ JsonableError, RequestVariableMissingError, get_user_profile_by_email, \
authenticated_rest_api_view, process_as_post, REQ, rate_limit, rate_limit_user authenticated_rest_api_view, process_as_post, REQ, rate_limit, rate_limit_user
from zephyr.lib.query import last_n from zephyr.lib.query import last_n
from zephyr.lib.avatar import gravatar_hash from zephyr.lib.avatar import avatar_url
from zephyr.lib.response import json_success, json_error, json_response, json_method_not_allowed, \ from zephyr.lib.response import json_success, json_error, json_response, json_method_not_allowed, \
render_to_response render_to_response
from zephyr.lib.timestamp import datetime_to_timestamp from zephyr.lib.timestamp import datetime_to_timestamp
@@ -580,7 +580,7 @@ def home(request):
return render_to_response('zephyr/index.html', return render_to_response('zephyr/index.html',
{'user_profile': user_profile, {'user_profile': user_profile,
'page_params' : page_params, 'page_params' : page_params,
'email_hash' : gravatar_hash(user_profile.email), 'avatar_url': avatar_url(user_profile),
'show_debug': 'show_debug':
settings.DEBUG and ('show_debug' in request.GET), settings.DEBUG and ('show_debug' in request.GET),
'show_invites': show_invites 'show_invites': show_invites