Skip to content

Commit

Permalink
work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
marsh_ committed Jul 8, 2015
1 parent b69d07e commit b1bf212
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 18 deletions.
6 changes: 4 additions & 2 deletions captcha/backends/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from captcha.conf import settings as captcha_settings


class DoesNotExist(Exception):
""" Can't find captcha in store """
pass


class BaseStore(object):
DoesNotExist = DoesNotExist

Expand All @@ -12,14 +14,14 @@ def generate_key(self):
Generate captcha with unique key
"""
return captcha_settings.get_challenge()()

def remove_expired(self):
"""
Remove expired captcha records
"""
pass

def get(self, response=None, hashkey=None, allow_expired = True):
def get(self, response=None, hashkey=None, allow_expired=True):
"""
Get captcha from store, or rise exception if captcha wasn't found
"""
Expand Down
16 changes: 8 additions & 8 deletions captcha/backends/db.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from captcha.conf import settings as captcha_settings
from ..models import CaptchaStore, get_safe_now
from .base import BaseStore


class DBStore(BaseStore):
def __init__(self, key = None):
def __init__(self, key=None):
self._captcha = {}
if key:
try:
cap = CaptchaStore.objects.get(hashkey = key)
cap = CaptchaStore.objects.get(hashkey=key)
self._captcha = {
'hashkey': cap.hashkey,
'challenge': cap.challenge,
Expand All @@ -16,23 +16,23 @@ def __init__(self, key = None):
}
except CaptchaStore.DoesNotExist:
raise self.DoesNotExist

def __getitem__(self, key):
return self._captcha[key]

def remove_expired(self):
CaptchaStore.objects.filter(expiration__lte=get_safe_now()).delete()

def generate_key(self):
return CaptchaStore.generate_key()

def get(self, response=None, hashkey=None, allow_expired = True):
def get(self, response=None, hashkey=None, allow_expired=True):
store = DBStore(hashkey)
if response and store['response'] != response:
raise self.DoesNotExist
if not allow_expired and store['expiration'] < get_safe_now():
raise self.DoesNotExist
return store

def delete(self):
CaptchaStore.objects.filter(hashkey = self['hashkey']).delete()
CaptchaStore.objects.filter(hashkey=self['hashkey']).delete()
13 changes: 8 additions & 5 deletions captcha/backends/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@
from importlib import import_module
Store = import_module(settings.SESSION_ENGINE).SessionStore
import django



class SessionStore(BaseStore):

def generate_key(self):
challenge, response = super(SessionStore, self).generate_key()
store = Store()
store.set_expiry(60 * int(captcha_settings.CAPTCHA_TIMEOUT))
store['challenge']=challenge
store['response']=response
store['challenge'] = challenge
store['response'] = response
store.save()
return store.session_key

def remove_expired(self):
if not django.get_version() < '1.5':
Store.clear_expired()

def get(self, response=None, hashkey=None, allow_expired = True):
def get(self, response=None, hashkey=None, allow_expired=True):
s = Store(session_key=hashkey)
if not s.get('response'):
raise self.DoesNotExist
if response:
if s['response'] != response:
raise self.DoesNotExist
Expand Down
2 changes: 1 addition & 1 deletion captcha/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def clean(self, value):
pass
else:
try:
CaptchaStore.get(response=response, hashkey=value[0], allow_expired = False).delete()
CaptchaStore.get(response=response, hashkey=value[0], allow_expired=False).delete()
except BaseStore.DoesNotExist:
raise ValidationError(getattr(self, 'error_messages', {}).get('invalid', ugettext_lazy('Invalid CAPTCHA')))
return value
101 changes: 100 additions & 1 deletion captcha/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
from captcha.conf import settings
from captcha.fields import CaptchaField, CaptchaTextInput
from captcha.models import CaptchaStore, get_safe_now
from captcha.backends.db import DBStore
from captcha.backends.session import SessionStore
from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.utils.translation import ugettext_lazy
from django.test.utils import override_settings
import django
import datetime
import json
import re
Expand All @@ -29,7 +33,6 @@ class CaptchaCase(TestCase):
urls = 'captcha.tests.urls'

def setUp(self):

self.stores = {}
self.__current_settings_output_format = settings.CAPTCHA_OUTPUT_FORMAT
self.__current_settings_dictionary = settings.CAPTCHA_WORDS_DICTIONARY
Expand Down Expand Up @@ -332,5 +335,101 @@ def test_image_size(self):
settings.CAPTCHA_IMAGE_SIZE = __current_test_mode_setting


@override_settings(CAPTCHA_STORE='SESSION')
class CaptchaCase2(TestCase):
urls = 'captcha.tests.urls'

# store tests
def test_db_store(self):
store = DBStore()
key = store.generate_key()
record = store.get(hashkey=key)
self.assertEqual(record['hashkey'], key)
self.assertNotEqual(record._captcha.get('challenge'), None)
self.assertNotEqual(record._captcha.get('response'), None)
record.delete()
try:
store.get(hashkey=key)
self.fail('Record deletion error')
except:
pass

key = store.generate_key()
cap = CaptchaStore.objects.get(hashkey=key)
cap.expiration = get_safe_now() - datetime.timedelta(minutes=1)
cap.save()
try:
store.get(hashkey=key, allow_expired=False)
self.fail()
except:
pass

store.remove_expired()
try:
store.get(hashkey=key)
self.fail('remove_expired failed')
except:
pass

def test_session_store(self):
store = SessionStore()
key = store.generate_key()
record = store.get(hashkey=key)
self.assertEqual(record.session_key, key)
self.assertNotEqual(record.get('challenge'), None)
self.assertNotEqual(record.get('response'), None)
record.delete()
try:
store.get(hashkey=key)
self.fail('Record deletion error')
except:
pass

key = store.generate_key()
cap = store.get(hashkey=key)
cap.set_expiry(-1 * 60)
cap.save()
try:
store.get(hashkey=key, allow_expired=False)
self.fail()
except:
pass

if not django.get_version() < '1.5':
# django lower than 1.5 can't remove expired sessions
store.remove_expired()
try:
store.get(hashkey=key)
self.fail('remove_expired failed')
except:
pass

# view tests
@override_settings(CAPTCHA_STORE='SESSION')
def testFormSubmit2(self):

r = self.client.get(reverse('captcha-test'))
self.assertEqual(r.status_code, 200)

store = SessionStore()
hash_ = re.findall(r'value="([0-9a-f]+)"', str(r.content))[0]
response = store.get(hashkey=hash_)['response']

r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='[email protected]'))
self.assertEqual(r.status_code, 200)
self.assertTrue(str(r.content).find('Form validated') > 0)

r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='[email protected]'))
self.assertEqual(r.status_code, 200)
self.assertFalse(str(r.content).find('Form validated') > 0)

def testWrongSubmit2(self):
for urlname in ('captcha-test', 'captcha-test-model-form'):
r = self.client.get(reverse(urlname))
self.assertEqual(r.status_code, 200)
r = self.client.post(reverse(urlname), dict(captcha_0='abc', captcha_1='wrong response', subject='xxx', sender='[email protected]'))
self.assertFormError(r, 'form', 'captcha', ugettext_lazy('Invalid CAPTCHA'))


def trivial_challenge():
return 'trivial', 'trivial'
2 changes: 2 additions & 0 deletions captcha/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from captcha.conf import settings
from captcha.helpers import captcha_image_url
from django.http import HttpResponse, Http404
from django.core.exceptions import ImproperlyConfigured
import random
import re
import tempfile
Expand Down Expand Up @@ -40,6 +41,7 @@
else:
raise ImproperlyConfigured


def getsize(font, text):
if hasattr(font, 'getoffset'):
return [x + y for x, y in zip(font.getsize(text), font.getoffset(text))]
Expand Down
2 changes: 1 addition & 1 deletion testproject/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
'captcha',
]

LANGUAGE_CODE = "en"
LANGUAGE_CODE = "en-us"

LANGUAGES = (
('en', 'English'),
Expand Down

0 comments on commit b1bf212

Please sign in to comment.