author | Sylvain Thénault <sylvain.thenault@logilab.fr> |
Wed, 14 Apr 2010 17:31:41 +0200 | |
changeset 5250 | 1c0eb5f74fd4 |
parent 5223 | 6abd6e3599f4 |
child 5423 | e15abfdcce38 |
permissions | -rw-r--r-- |
4595
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
1 |
"""Simple captcha library, based on PIL. Monkey patch functions in this module |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
2 |
if you want something better... |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
3 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
4 |
:organization: Logilab |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
5 |
:copyright: 2009-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
6 |
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
7 |
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
8 |
""" |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
9 |
__docformat__ = "restructuredtext en" |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
10 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
11 |
from random import randint, choice |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
12 |
from cStringIO import StringIO |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
13 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
14 |
import Image, ImageFont, ImageDraw, ImageFilter |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
15 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
16 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
17 |
from time import time |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
18 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
19 |
from cubicweb import tags |
5037
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
20 |
from cubicweb.web import ProcessFormError, formwidgets as fw |
4595
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
21 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
22 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
23 |
def pil_captcha(text, fontfile, fontsize): |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
24 |
"""Generate a captcha image. Return a PIL image object. |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
25 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
26 |
adapted from http://code.activestate.com/recipes/440588/ |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
27 |
""" |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
28 |
# randomly select the foreground color |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
29 |
fgcolor = randint(0, 0xffff00) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
30 |
# make the background color the opposite of fgcolor |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
31 |
bgcolor = fgcolor ^ 0xffffff |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
32 |
# create a font object |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
33 |
font = ImageFont.truetype(fontfile, fontsize) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
34 |
# determine dimensions of the text |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
35 |
dim = font.getsize(text) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
36 |
# create a new image slightly larger that the text |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
37 |
img = Image.new('RGB', (dim[0]+5, dim[1]+5), bgcolor) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
38 |
draw = ImageDraw.Draw(img) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
39 |
# draw 100 random colored boxes on the background |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
40 |
x, y = img.size |
4722
9c13d5db03d9
pylint suggested refactorings
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4621
diff
changeset
|
41 |
for num in xrange(100): |
4595
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
42 |
draw.rectangle((randint(0, x), randint(0, y), |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
43 |
randint(0, x), randint(0, y)), |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
44 |
fill=randint(0, 0xffffff)) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
45 |
# add the text to the image |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
46 |
draw.text((3, 3), text, font=font, fill=fgcolor) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
47 |
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
48 |
return img |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
49 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
50 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
51 |
def captcha(fontfile, fontsize, size=5, format='JPEG'): |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
52 |
"""Generate an arbitrary text, return it together with a buffer containing |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
53 |
the captcha image for the text |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
54 |
""" |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
55 |
text = u''.join(choice('QWERTYUOPASDFGHJKLZXCVBNM') for i in range(size)) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
56 |
img = pil_captcha(text, fontfile, fontsize) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
57 |
out = StringIO() |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
58 |
img.save(out, format) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
59 |
out.seek(0) |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
60 |
return text, out |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
61 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
62 |
|
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
63 |
class CaptchaWidget(fw.TextInput): |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
64 |
def render(self, form, field, renderer=None): |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
65 |
# t=int(time()*100) to make sure img is not cached |
5037
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
66 |
src = form._cw.build_url('view', vid='captcha', t=int(time()*100), |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
67 |
captchakey=field.input_name(form)) |
4595
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
68 |
img = tags.img(src=src, alt=u'captcha') |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
69 |
img = u'<div class="captcha">%s</div>' % img |
bb08a75832e6
backport crypto/captcha utilities from the registration cube
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff
changeset
|
70 |
return img + super(CaptchaWidget, self).render(form, field, renderer) |
5037
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
71 |
|
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
72 |
def process_field_data(self, form, field): |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5037
diff
changeset
|
73 |
captcha = form._cw.session.data.pop(field.input_name(form), None) |
5037
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
74 |
val = super(CaptchaWidget, self).process_field_data(form, field) |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
75 |
if val is None: |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
76 |
return val # required will be checked by field |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
77 |
if captcha is None: |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
78 |
msg = form._cw._('unable to check captcha, please try again') |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
79 |
raise ProcessFormError(msg) |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
80 |
elif val.lower() != captcha.lower(): |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
81 |
msg = form._cw._('incorrect captcha value') |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
82 |
raise ProcessFormError(msg) |
7778a2bbdf9d
[captcha] handle captcha validation properly in the captcha widget
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4722
diff
changeset
|
83 |
return val |