annotate captchamiddleware/middleware.py @ 4:ce757057961c

flow contol now works; bumping version
author k0s <k0scist@gmail.com>
date Wed, 24 Feb 2010 20:11:25 -0500
parents b0ef5452a740
children 59245309f054
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
1 """
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
2 CAPTCHA middleware
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
3 """
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
4
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
5 import os
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
6 import random
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
7 import sys
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
8 from lxml import etree
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
9 from lxmlmiddleware import LXMLMiddleware
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
10 from skimpyGimpy import skimpyAPI
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
11 from urllib2 import urlopen
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
12 from webob import Request, exc
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
13
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
14
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
15 class CAPTCHAmiddleware(LXMLMiddleware):
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
16 """
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
17 put CAPTCHAs on forms for unauthorized users
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
18 """
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
19
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
20 ### class level variables
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
21 defaults = {
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
22 'dictionary': '/usr/share/dict/words',
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
23 'error': '<span class="error">Please type the CAPTCHA</span>',
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
24 'minimum_length': 5,
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
25 'path': "/input[@type='submit']",
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
26 }
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
27
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
28 def __init__(self, app, **kw):
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
29 self.app = app
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
30
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
31 # set instance parameters from kw and defaults
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
32 for key in self.defaults:
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
33 setattr(self, key, kw.get(key, self.defaults[key]))
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
34 self.minimum_length = int(self.minimum_length)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
35 assert os.path.exists(self.dictionary)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
36
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
37 # get dictionary
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
38 if self.dictionary.startswith('http://') or self.dictionary.startswith('https://'):
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
39 f = urlopen(self.dictionary)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
40 else:
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
41 f = file(self.dictionary)
2
c861518b2a44 fix bug whereby forbidden characters cause issues
k0s <k0scist@gmail.com>
parents: 1
diff changeset
42
c861518b2a44 fix bug whereby forbidden characters cause issues
k0s <k0scist@gmail.com>
parents: 1
diff changeset
43 # characters skimpygimpy doesnt know about
c861518b2a44 fix bug whereby forbidden characters cause issues
k0s <k0scist@gmail.com>
parents: 1
diff changeset
44 forbidden_characters = set(["'"])
c861518b2a44 fix bug whereby forbidden characters cause issues
k0s <k0scist@gmail.com>
parents: 1
diff changeset
45
3
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
46 self.words = [ i.strip().lower() for i in f.readlines()
2
c861518b2a44 fix bug whereby forbidden characters cause issues
k0s <k0scist@gmail.com>
parents: 1
diff changeset
47 if (len(i.strip()) > self.minimum_length)
c861518b2a44 fix bug whereby forbidden characters cause issues
k0s <k0scist@gmail.com>
parents: 1
diff changeset
48 and not forbidden_characters.intersection(i) ]
0
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
49 random.shuffle(self.words)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
50
3
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
51 def check_captcha(self, request):
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
52 captcha = request.POST.get('captcha', '').lower()
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
53 key = request.POST.get('key')
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
54 if not key: return False
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
55 try:
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
56 key = int(key)
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
57 except ValueError:
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
58 return False
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
59 try:
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
60 value = self.words[key]
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
61 except IndexError:
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
62 raise # TODO: better error handling
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
63 return value == captcha
0
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
64
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
65 def __call__(self, environ, start_response):
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
66 request = Request(environ)
2
c861518b2a44 fix bug whereby forbidden characters cause issues
k0s <k0scist@gmail.com>
parents: 1
diff changeset
67 if request.method == 'POST' and not request.remote_user:
4
ce757057961c flow contol now works; bumping version
k0s <k0scist@gmail.com>
parents: 3
diff changeset
68 if not self.check_captcha(request):
ce757057961c flow contol now works; bumping version
k0s <k0scist@gmail.com>
parents: 3
diff changeset
69 location = request.referrer
ce757057961c flow contol now works; bumping version
k0s <k0scist@gmail.com>
parents: 3
diff changeset
70 return exc.HTTPSeeOther(location=location)(environ, start_response)
ce757057961c flow contol now works; bumping version
k0s <k0scist@gmail.com>
parents: 3
diff changeset
71 # TODO: set a cookie to record an error
ce757057961c flow contol now works; bumping version
k0s <k0scist@gmail.com>
parents: 3
diff changeset
72 # stage 2: record form values from request.POST,
ce757057961c flow contol now works; bumping version
k0s <k0scist@gmail.com>
parents: 3
diff changeset
73 # and reinsert them into the form so that users
ce757057961c flow contol now works; bumping version
k0s <k0scist@gmail.com>
parents: 3
diff changeset
74 # don't hate me ;)
0
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
75
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
76 return LXMLMiddleware.__call__(self, environ, start_response)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
77
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
78 def manipulate(self, environ, tree):
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
79 """manipulate the DOM; should return an etree._Element"""
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
80
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
81 request = Request(environ)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
82
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
83 # don't use CAPTCHAs for authorized users
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
84 if request.remote_user:
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
85 return tree
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
86
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
87 for element in tree.findall(".//form[@method='post']"):
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
88 key = random.Random().randint(0, len(self.words))
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
89 word = self.words[key]
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
90 captcha = skimpyAPI.Pre(word).data()
3
b0ef5452a740 beginning of captcha checking
k0s <k0scist@gmail.com>
parents: 2
diff changeset
91 string = '<div class="captcha">%s<input type="hidden" name="key" value="%s"/><input type="text" name="captcha"/></div>' % (captcha, key)
0
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
92 addition = etree.fromstring(string)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
93 insertion_point = element.find('.' + self.path)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
94 insertion_point.addprevious(addition)
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
95
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
96 return tree
21ec6325ae0e initial import of CAPTCHA middleware; unfinished
k0s <k0scist@gmail.com>
parents:
diff changeset
97