Skip to content

Commit 2682e36

Browse files
author
marsh_
committed
minor changes and tests
1 parent b69d07e commit 2682e36

File tree

7 files changed

+145
-29
lines changed

7 files changed

+145
-29
lines changed

captcha/backends/base.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from captcha.conf import settings as captcha_settings
22

3+
34
class DoesNotExist(Exception):
45
""" Can't find captcha in store """
56
pass
67

8+
79
class BaseStore(object):
810
DoesNotExist = DoesNotExist
911

@@ -12,14 +14,14 @@ def generate_key(self):
1214
Generate captcha with unique key
1315
"""
1416
return captcha_settings.get_challenge()()
15-
17+
1618
def remove_expired(self):
1719
"""
1820
Remove expired captcha records
1921
"""
2022
pass
2123

22-
def get(self, response=None, hashkey=None, allow_expired = True):
24+
def get(self, response=None, hashkey=None, allow_expired=True):
2325
"""
2426
Get captcha from store, or rise exception if captcha wasn't found
2527
"""

captcha/backends/db.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
from captcha.conf import settings as captcha_settings
21
from ..models import CaptchaStore, get_safe_now
32
from .base import BaseStore
43

4+
55
class DBStore(BaseStore):
6-
def __init__(self, key = None):
6+
def __init__(self, key=None):
77
self._captcha = {}
88
if key:
99
try:
10-
cap = CaptchaStore.objects.get(hashkey = key)
10+
cap = CaptchaStore.objects.get(hashkey=key)
1111
self._captcha = {
1212
'hashkey': cap.hashkey,
1313
'challenge': cap.challenge,
@@ -16,23 +16,23 @@ def __init__(self, key = None):
1616
}
1717
except CaptchaStore.DoesNotExist:
1818
raise self.DoesNotExist
19-
19+
2020
def __getitem__(self, key):
2121
return self._captcha[key]
22-
22+
2323
def remove_expired(self):
2424
CaptchaStore.objects.filter(expiration__lte=get_safe_now()).delete()
2525

2626
def generate_key(self):
2727
return CaptchaStore.generate_key()
2828

29-
def get(self, response=None, hashkey=None, allow_expired = True):
29+
def get(self, response=None, hashkey=None, allow_expired=True):
3030
store = DBStore(hashkey)
3131
if response and store['response'] != response:
3232
raise self.DoesNotExist
3333
if not allow_expired and store['expiration'] < get_safe_now():
3434
raise self.DoesNotExist
3535
return store
36-
36+
3737
def delete(self):
38-
CaptchaStore.objects.filter(hashkey = self['hashkey']).delete()
38+
CaptchaStore.objects.filter(hashkey=self['hashkey']).delete()

captcha/backends/session.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,27 @@
44
from importlib import import_module
55
Store = import_module(settings.SESSION_ENGINE).SessionStore
66
import django
7-
7+
8+
89
class SessionStore(BaseStore):
910

1011
def generate_key(self):
1112
challenge, response = super(SessionStore, self).generate_key()
1213
store = Store()
1314
store.set_expiry(60 * int(captcha_settings.CAPTCHA_TIMEOUT))
14-
store['challenge']=challenge
15-
store['response']=response
15+
store['challenge'] = challenge
16+
store['response'] = response
1617
store.save()
1718
return store.session_key
18-
19+
1920
def remove_expired(self):
2021
if not django.get_version() < '1.5':
2122
Store.clear_expired()
2223

23-
def get(self, response=None, hashkey=None, allow_expired = True):
24+
def get(self, response=None, hashkey=None, allow_expired=True):
2425
s = Store(session_key=hashkey)
26+
if not s.get('response'):
27+
raise self.DoesNotExist
2528
if response:
2629
if s['response'] != response:
2730
raise self.DoesNotExist

captcha/fields.py

+22-11
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,6 @@
88
from six import u
99
from .backends.base import BaseStore
1010

11-
CaptchaStore = None
12-
if settings.CAPTCHA_STORE == 'SESSION':
13-
from .backends.session import SessionStore
14-
CaptchaStore = SessionStore()
15-
elif settings.CAPTCHA_STORE == 'DB':
16-
from .backends.db import DBStore
17-
CaptchaStore = DBStore()
18-
else:
19-
raise ImproperlyConfigured
20-
2111

2212
class BaseCaptchaTextInput(MultiWidget):
2313
"""
@@ -40,6 +30,17 @@ def fetch_captcha_store(self, name, value, attrs=None):
4030
Fetches a new CaptchaStore
4131
This has to be called inside render
4232
"""
33+
34+
CaptchaStore = None
35+
if settings.CAPTCHA_STORE == 'SESSION':
36+
from .backends.session import SessionStore
37+
CaptchaStore = SessionStore()
38+
elif settings.CAPTCHA_STORE == 'DB':
39+
from .backends.db import DBStore
40+
CaptchaStore = DBStore()
41+
else:
42+
raise ImproperlyConfigured
43+
4344
try:
4445
reverse('captcha-image', args=('dummy',))
4546
except NoReverseMatch:
@@ -135,6 +136,16 @@ def compress(self, data_list):
135136
return None
136137

137138
def clean(self, value):
139+
CaptchaStore = None
140+
if settings.CAPTCHA_STORE == 'SESSION':
141+
from .backends.session import SessionStore
142+
CaptchaStore = SessionStore()
143+
elif settings.CAPTCHA_STORE == 'DB':
144+
from .backends.db import DBStore
145+
CaptchaStore = DBStore()
146+
else:
147+
raise ImproperlyConfigured
148+
138149
super(CaptchaField, self).clean(value)
139150
response, value[1] = (value[1] or '').strip().lower(), ''
140151
CaptchaStore.remove_expired()
@@ -150,7 +161,7 @@ def clean(self, value):
150161
pass
151162
else:
152163
try:
153-
CaptchaStore.get(response=response, hashkey=value[0], allow_expired = False).delete()
164+
CaptchaStore.get(response=response, hashkey=value[0], allow_expired=False).delete()
154165
except BaseStore.DoesNotExist:
155166
raise ValidationError(getattr(self, 'error_messages', {}).get('invalid', ugettext_lazy('Invalid CAPTCHA')))
156167
return value

captcha/tests/tests.py

+100-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
from captcha.conf import settings
33
from captcha.fields import CaptchaField, CaptchaTextInput
44
from captcha.models import CaptchaStore, get_safe_now
5+
from captcha.backends.db import DBStore
6+
from captcha.backends.session import SessionStore
57
from django.conf import settings as django_settings
68
from django.core.exceptions import ImproperlyConfigured
79
from django.core.urlresolvers import reverse
810
from django.test import TestCase
911
from django.utils.translation import ugettext_lazy
12+
from django.test.utils import override_settings
13+
import django
1014
import datetime
1115
import json
1216
import re
@@ -27,9 +31,8 @@
2731

2832
class CaptchaCase(TestCase):
2933
urls = 'captcha.tests.urls'
30-
34+
3135
def setUp(self):
32-
3336
self.stores = {}
3437
self.__current_settings_output_format = settings.CAPTCHA_OUTPUT_FORMAT
3538
self.__current_settings_dictionary = settings.CAPTCHA_WORDS_DICTIONARY
@@ -332,5 +335,100 @@ def test_image_size(self):
332335
settings.CAPTCHA_IMAGE_SIZE = __current_test_mode_setting
333336

334337

338+
class StoresCase(TestCase):
339+
urls = 'captcha.tests.urls'
340+
341+
# store tests
342+
def test_db_store(self):
343+
store = DBStore()
344+
key = store.generate_key()
345+
record = store.get(hashkey=key)
346+
self.assertEqual(record['hashkey'], key)
347+
self.assertNotEqual(record._captcha.get('challenge'), None)
348+
self.assertNotEqual(record._captcha.get('response'), None)
349+
record.delete()
350+
try:
351+
store.get(hashkey=key)
352+
self.fail('Record deletion error')
353+
except:
354+
pass
355+
356+
key = store.generate_key()
357+
cap = CaptchaStore.objects.get(hashkey=key)
358+
cap.expiration = get_safe_now() - datetime.timedelta(minutes=1)
359+
cap.save()
360+
try:
361+
store.get(hashkey=key, allow_expired=False)
362+
self.fail()
363+
except:
364+
pass
365+
366+
store.remove_expired()
367+
try:
368+
store.get(hashkey=key)
369+
self.fail('remove_expired failed')
370+
except:
371+
pass
372+
373+
def test_session_store(self):
374+
store = SessionStore()
375+
key = store.generate_key()
376+
record = store.get(hashkey=key)
377+
self.assertEqual(record.session_key, key)
378+
self.assertNotEqual(record.get('challenge'), None)
379+
self.assertNotEqual(record.get('response'), None)
380+
record.delete()
381+
try:
382+
store.get(hashkey=key)
383+
self.fail('Record deletion error')
384+
except:
385+
pass
386+
387+
key = store.generate_key()
388+
cap = store.get(hashkey=key)
389+
cap.set_expiry(-1 * 60)
390+
cap.save()
391+
try:
392+
store.get(hashkey=key, allow_expired=False)
393+
self.fail()
394+
except:
395+
pass
396+
397+
if not django.get_version() < '1.5':
398+
# django lower than 1.5 can't remove expired sessions
399+
store.remove_expired()
400+
try:
401+
store.get(hashkey=key)
402+
self.fail('remove_expired failed')
403+
except:
404+
pass
405+
406+
# view tests
407+
def testFormSubmit(self):
408+
settings.CAPTCHA_STORE='SESSION'
409+
r = self.client.get(reverse('captcha-test'))
410+
self.assertEqual(r.status_code, 200)
411+
412+
store = SessionStore()
413+
hash_ = re.findall(r'value="([0-9a-z]+)"', str(r.content))[0]
414+
response = store.get(hashkey=hash_)['response']
415+
416+
r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='[email protected]'))
417+
self.assertEqual(r.status_code, 200)
418+
self.assertTrue(str(r.content).find('Form validated') > 0)
419+
420+
r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='[email protected]'))
421+
self.assertEqual(r.status_code, 200)
422+
self.assertFalse(str(r.content).find('Form validated') > 0)
423+
424+
def testWrongSubmit(self):
425+
settings.CAPTCHA_STORE='SESSION'
426+
for urlname in ('captcha-test', 'captcha-test-model-form'):
427+
r = self.client.get(reverse(urlname))
428+
self.assertEqual(r.status_code, 200)
429+
r = self.client.post(reverse(urlname), dict(captcha_0='abc', captcha_1='wrong response', subject='xxx', sender='[email protected]'))
430+
self.assertFormError(r, 'form', 'captcha', ugettext_lazy('Invalid CAPTCHA'))
431+
432+
335433
def trivial_challenge():
336434
return 'trivial', 'trivial'

captcha/views.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from captcha.conf import settings
22
from captcha.helpers import captcha_image_url
33
from django.http import HttpResponse, Http404
4+
from django.core.exceptions import ImproperlyConfigured
45
import random
56
import re
67
import tempfile
@@ -40,6 +41,7 @@
4041
else:
4142
raise ImproperlyConfigured
4243

44+
4345
def getsize(font, text):
4446
if hasattr(font, 'getoffset'):
4547
return [x + y for x, y in zip(font.getsize(text), font.getoffset(text))]

testproject/settings.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
'captcha',
3434
]
3535

36-
LANGUAGE_CODE = "en"
36+
LANGUAGE_CODE = "en-us"
3737

3838
LANGUAGES = (
3939
('en', 'English'),

0 commit comments

Comments
 (0)