Commit 3f285cd2 authored by Weblate's avatar Weblate

Merge remote-tracking branch 'origin/master'

parents f61eedba 3cd1c116
...@@ -23,8 +23,11 @@ from django.conf import settings ...@@ -23,8 +23,11 @@ from django.conf import settings
import hashlib import hashlib
import binascii import binascii
import time
from random import randint, choice from random import randint, choice
TIMEDELTA = 600
class MathCaptcha(object): class MathCaptcha(object):
''' '''
...@@ -38,11 +41,15 @@ class MathCaptcha(object): ...@@ -38,11 +41,15 @@ class MathCaptcha(object):
} }
interval = (1, 10) interval = (1, 10)
def __init__(self, question=None): def __init__(self, question=None, timestamp=None):
if question is None: if question is None:
self.question = self.generate_question() self.question = self.generate_question()
else: else:
self.question = question self.question = question
if timestamp is None:
self.timestamp = time.time()
else:
self.timestamp = timestamp
def generate_question(self): def generate_question(self):
''' '''
...@@ -56,7 +63,7 @@ class MathCaptcha(object): ...@@ -56,7 +63,7 @@ class MathCaptcha(object):
if operator == '-': if operator == '-':
first += self.interval[1] first += self.interval[1]
return '%d %s %d' % ( return '{d} {s} {d}'.format(
first, first,
operator, operator,
second second
...@@ -67,21 +74,24 @@ class MathCaptcha(object): ...@@ -67,21 +74,24 @@ class MathCaptcha(object):
''' '''
Creates object from hash. Creates object from hash.
''' '''
question = unhash_question(hashed) question, timestamp = unhash_question(hashed)
return MathCaptcha(question) return MathCaptcha(question, timestamp)
@property @property
def hashed(self): def hashed(self):
''' '''
Returns hashed question. Returns hashed question.
''' '''
return hash_question(self.question) return hash_question(self.question, self.timestamp)
def validate(self, answer): def validate(self, answer):
''' '''
Validates answer. Validates answer.
''' '''
return self.result == answer return (
self.result == answer
and self.timestamp + TIMEDELTA > time.time()
)
@property @property
def result(self): def result(self):
...@@ -96,28 +106,37 @@ class MathCaptcha(object): ...@@ -96,28 +106,37 @@ class MathCaptcha(object):
Gets unicode for display. Gets unicode for display.
''' '''
parts = self.question.split() parts = self.question.split()
return u'%s %s %s' % ( return u'{} {} {}'.format(
parts[0], parts[0],
self.operators_display[parts[1]], self.operators_display[parts[1]],
parts[2], parts[2],
) )
def checksum_question(question): def format_timestamp(timestamp):
'''
Formats timestamp in a form usable in captcha.
'''
return '{:>010x}'.format(int(timestamp))
def checksum_question(question, timestamp):
''' '''
Returns checksum for a question. Returns checksum for a question.
''' '''
sha = hashlib.sha1(settings.SECRET_KEY + question) sha = hashlib.sha1(settings.SECRET_KEY + question + timestamp)
return sha.hexdigest() return sha.hexdigest()
def hash_question(question): def hash_question(question, timestamp):
''' '''
Hashes question so that it can be later verified. Hashes question so that it can be later verified.
''' '''
hexsha = checksum_question(question) timestamp = format_timestamp(timestamp)
return '%s%s' % ( hexsha = checksum_question(question, timestamp)
return '{}{}{}'.format(
hexsha, hexsha,
timestamp,
question.encode('base64') question.encode('base64')
) )
...@@ -129,10 +148,11 @@ def unhash_question(question): ...@@ -129,10 +148,11 @@ def unhash_question(question):
if len(question) < 40: if len(question) < 40:
raise ValueError('Invalid data') raise ValueError('Invalid data')
hexsha = question[:40] hexsha = question[:40]
timestamp = question[40:50]
try: try:
question = question[40:].decode('base64') question = question[50:].decode('base64')
except binascii.Error: except binascii.Error:
raise ValueError('Invalid encoding') raise ValueError('Invalid encoding')
if hexsha != checksum_question(question): if hexsha != checksum_question(question, timestamp):
raise ValueError('Tampered question!') raise ValueError('Tampered question!')
return question return question, int(timestamp, 16)
...@@ -524,14 +524,15 @@ class NotificationTest(ViewTestCase): ...@@ -524,14 +524,15 @@ class NotificationTest(ViewTestCase):
class CaptchaTest(UnitTestCase): class CaptchaTest(UnitTestCase):
def test_decode(self): def test_decode(self):
question = '1 + 1' question = '1 + 1'
hashed = hash_question(question) timestamp = 1000
hashed = hash_question(question, timestamp)
self.assertEquals( self.assertEquals(
question, (question, timestamp),
unhash_question(hashed) unhash_question(hashed)
) )
def test_tamper(self): def test_tamper(self):
hashed = hash_question('') + '00' hashed = hash_question('', 0) + '00'
self.assertRaises( self.assertRaises(
ValueError, ValueError,
unhash_question, unhash_question,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment