diff --git a/weave/decorators.py b/weave/decorators.py index 798b509..8b02e53 100644 --- a/weave/decorators.py +++ b/weave/decorators.py @@ -22,7 +22,6 @@ from datetime import datetime import base64 -import hashlib import pprint try: @@ -39,24 +38,14 @@ from django.core.exceptions import PermissionDenied from django.http import HttpResponse, HttpResponseBadRequest, \ HttpResponseForbidden -from django.contrib.auth import authenticate, login +from django.contrib.auth import authenticate, login, logout -from weave.utils import weave_timestamp +from weave.utils import weave_timestamp, make_sync_hash from weave import Logging logger = Logging.get_logger() -#def _fix_username(username): -# if len(username) <= 30: -# return username -# -# new_username = username[:30] -# -# logger.warn("Username %r cut to %r" % (username, new_username)) -# -# return new_username - def view_or_basicauth(view, request, test_func, realm="", *args, **kwargs): """ @@ -218,13 +207,16 @@ def wrapper(request, *args, **kwargs): # check new API email = request.user.email - sha1 = hashlib.sha1(email).digest() - base32encode = base64.b32encode(sha1).lower() - if url_username.startswith(base32encode): + sync_hash = make_sync_hash(email) + if url_username.startswith(sync_hash): logger.debug("Email hash %r from url is ok." % url_username) return func(request, *args, **kwargs) logger.debug("Url userdata %r doesn't fit to user %s" % (url_username, request.user.username)) + + logger.info("Logout user %s" % request.user) + logout(request) + raise PermissionDenied("URL userdata doesn't fit to user from HTTP authentication.") return wrapper @@ -246,11 +238,17 @@ def wrapper(request, *args, **kwargs): msg = "no version specified in URL" logger.error(msg) raise AssertionError(msg) - if kwargs['version'] != version: - msg = "unsupported weave client version: %r" % kwargs['version'] - logger.error(msg) - raise AssertionError(msg) - return func(request, *args, **kwargs) + + url_version = kwargs['version'] + if isinstance(version, (list, tuple)) and url_version in version: + return func(request, *args, **kwargs) + elif url_version == version: + return func(request, *args, **kwargs) + + msg = "unsupported weave client version: %r" % url_version + logger.error(msg) + raise AssertionError(msg) + return wrapper return decorator @@ -328,20 +326,3 @@ def wrapper(request, *args, **kwargs): return wrapper - -#def fix_username(func): -# """ -# Work-a-round for sync in Firefox v4 -# see: https://github.com/jedie/django-sync-server/issues/8 -# -# Firefox v4 doesn't use a username anymore. It send a SHA1 from the -# user email as the username. -# Here we use only the first 30 characters of the username, because the -# django user model allows only a username with a length of 30 characters. -# """ -# @wraps(func) -# def wrapper(request, *args, **kwargs): -# if "username" in kwargs: -# kwargs["username"] = _fix_username(kwargs["username"]) -# return func(request, *args, **kwargs) -# return wrapper diff --git a/weave/utils.py b/weave/utils.py index af2cd91..292abb6 100644 --- a/weave/utils.py +++ b/weave/utils.py @@ -6,13 +6,15 @@ Created on 15.03.2010 @license: GNU GPL v3 or above, see LICENSE for more details. - @copyright: 2010 see AUTHORS for more details. + @copyright: 2010-2011 see AUTHORS for more details. @author: Jens Diemer @author: FladischerMichael ''' -import time from datetime import datetime +import base64 +import hashlib +import time from weave import Logging @@ -78,3 +80,15 @@ def limit_wbo_queryset(request, queryset): queryset = queryset[:int(limit)] return queryset + + +def make_sync_hash(txt): + """ + make a base32 encoded SHA1 hash value. + Used in firefox sync for creating a username from the email address. + See also: + https://hg.mozilla.org/services/minimal-server/file/5ee9d9a4570a/weave_minimal/create_user#l87 + """ + sha1 = hashlib.sha1(txt).digest() + base32encode = base64.b32encode(sha1).lower() + return base32encode diff --git a/weave/views/misc.py b/weave/views/misc.py index 5764148..1997e53 100644 --- a/weave/views/misc.py +++ b/weave/views/misc.py @@ -20,7 +20,7 @@ logger = Logging.get_logger() @debug_sync_request -@weave_assert_version('1.1') +@weave_assert_version(['1.0', '1.1']) @csrf_exempt def captcha(request, version): if settings.WEAVE.DONT_USE_CAPTCHA == True: diff --git a/weave/views/user.py b/weave/views/user.py index cf2ce12..516cb7f 100644 --- a/weave/views/user.py +++ b/weave/views/user.py @@ -27,6 +27,7 @@ from weave import Logging from weave import constants from weave.decorators import logged_in_or_basicauth, weave_assert_version, debug_sync_request +from weave.utils import make_sync_hash logger = Logging.get_logger() @@ -76,7 +77,7 @@ def password_reset(request): @debug_sync_request -@weave_assert_version('1.1') +@weave_assert_version(['1.0', '1.1']) @csrf_exempt def node(request, version, username): """ @@ -110,7 +111,7 @@ def register_check(request, username): return HttpResponse(constants.ERR_UID_OR_EMAIL_IN_USE) -@weave_assert_version('1.1') +@weave_assert_version([u'1.0', u'1.1']) @csrf_exempt def exists(request, version, username): """ @@ -130,6 +131,7 @@ def exists(request, version, username): return HttpResponse(constants.USER_EXIST) elif request.method == 'PUT': # Handle user creation. + logger.debug("Raw post data: %r" % request.raw_post_data) data = json.loads(request.raw_post_data) if settings.WEAVE.DONT_USE_CAPTCHA == True: @@ -146,10 +148,6 @@ def exists(request, version, username): logger.error("Trying to create user but settings.WEAVE.RECAPTCHA_PRIVATE_KEY not set") raise ImproperlyConfigured - # Usernames are limited to a length of max. 30 chars. - # http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.models.User.username - if len(username) > 30 or len(data['password']) > 256: - return HttpResponseBadRequest() result = submit( data['captcha-challenge'], data['captcha-response'], @@ -160,12 +158,28 @@ def exists(request, version, username): # Captcha failed. return HttpResponseBadRequest() + if len(username) > 32 or len(data['password']) > 256: + return HttpResponseBadRequest() + + email = data['email'] + sync_hash = make_sync_hash(email) + if not sync_hash == username: + msg = "Error in sync hash: %r != %r" % (sync_hash, username) + logger.error(msg) + return HttpResponseBadRequest(msg) + + if len(username) <= 30: + # Cut the sync sha1 hash to 30 characters, because + # usernames are limited in django to a length of max. 30 chars. + # http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.models.User.username + username = username[:30] + try: - user = User.objects.create_user(username, data['email'], data['password']) + user = User.objects.create_user(username, email, data['password']) except Exception, err: logger.error("Can't create user: %s" % err) else: - logger.info("User %r with email %r created" % (user, data['email'])) + logger.info("User %r with email %r created" % (user, email)) return HttpResponse() else: raise NotImplemented()