changeset 0:51a744916b4f default tip

Basic dictionary CAPTCHA
author IBBoard <dev@ibboard.co.uk>
date Sat, 10 Aug 2013 11:00:42 +0000
parents
children
files ibbcaptcha/__init__.py ibbcaptcha/register.py ibbcaptcha/templates/captcha.html ibbcaptcha/utils.py setup.py
diffstat 5 files changed, 218 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ibbcaptcha/__init__.py	Sat Aug 10 11:00:42 2013 +0000
@@ -0,0 +1,2 @@
+#
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ibbcaptcha/register.py	Sat Aug 10 11:00:42 2013 +0000
@@ -0,0 +1,124 @@
+"""
+A quick and dirty captcha for use with AccountManagerPlugin registration
+"""
+# Plugin for trac 0.11
+
+from acct_mgr.web_ui import RegistrationModule
+
+from componentdependencies import IRequireComponents
+
+from genshi.builder import Markup
+from genshi.builder import tag
+from genshi.filters.transform import Transformer
+
+from trac.config import Option
+from trac.core import *
+from trac.web import IRequestFilter
+from trac.web import ITemplateStreamFilter
+from trac.web.api import IRequestHandler
+from trac.web.chrome import add_warning 
+
+from utils import random_word
+
+class RegistrationCaptcha(Component):
+
+    ### class data
+    implements(IRequestFilter, ITemplateStreamFilter, IRequireComponents)
+    dict_file = Option('ibbcaptcha', 'dictionary_file',
+                           default="http://java.sun.com/docs/books/tutorial/collections/interfaces/examples/dictionary.txt")
+
+
+    ### IRequestFilter methods
+
+    def pre_process_request(self, req, handler):
+        """Called after initial handler selection, and can be used to change
+        the selected handler or redirect request.
+        
+        Always returns the request handler, even if unchanged.
+        """
+
+        if req.path_info.strip('/') == "register":
+
+            if req.method == "POST":
+                correct_answer = req.session.pop('captcha', None)
+                req.session.save()
+                if req.args['captcha'].lower() != correct_answer:
+                    req.session['ibbcaptcha_message'] = "You typed the wrong word. Please try again."
+                    req.session.save()
+                    req.redirect(req.href('register'))
+            if req.method == "GET":
+                message = req.session.pop('ibbcaptcha_message', None)
+                if message:
+                    add_warning(req, message)
+                                    
+        return handler
+
+    # for ClearSilver templates
+    def post_process_request(self, req, template, content_type):
+        """Do any post-processing the request might need; typically adding
+        values to req.hdf, or changing template or mime type.
+        
+        Always returns a tuple of (template, content_type), even if
+        unchanged.
+
+        Note that `template`, `content_type` will be `None` if:
+         - called when processing an error page
+         - the default request handler did not return any result
+
+        (for 0.10 compatibility; only used together with ClearSilver templates)
+        """
+        return (template, content_type)
+
+    # for Genshi templates
+    def post_process_request(self, req, template, data, content_type):
+        """Do any post-processing the request might need; typically adding
+        values to the template `data` dictionary, or changing template or
+        mime type.
+        
+        `data` may be update in place.
+
+        Always returns a tuple of (template, data, content_type), even if
+        unchanged.
+
+        Note that `template`, `data`, `content_type` will be `None` if:
+         - called when processing an error page
+         - the default request handler did not return any result
+
+        (Since 0.11)
+        """
+        return (template, data, content_type)
+
+
+    ### ITemplateStreamFilter method
+
+    def filter_stream(self, req, method, filename, stream, data):
+        """Return a filtered Genshi event stream, or the original unfiltered
+        stream if no match.
+
+        `req` is the current request object, `method` is the Genshi render
+        method (xml, xhtml or text), `filename` is the filename of the template
+        to be rendered, `stream` is the event stream and `data` is the data for
+        the current template.
+
+        See the Genshi documentation for more information.
+        """
+        # move these someplace sensible?
+        form_id = "acctmgr_registerform" # id of the registration form
+        msg = "Please enter the text below to prove you're not a machine."
+
+        if filename == "register.html":
+            word = random_word(self.dict_file)
+            req.session['captcha'] = word
+            req.session.save()
+            captcha = word
+            content = "<p>%s</p><p>%s</p>" % (msg, captcha)
+            content += '<label>Confirm: <input type="text" name="captcha" class="textwidget" size="20"/></label>'
+            stream |= Transformer('//form[@id="%s"]/fieldset[1]' % form_id).append(tag.div(Markup(content)))
+
+        return stream
+
+    ### method for IRequireComponents
+
+    def requires(self):
+        """list of component classes that this component depends on"""
+        return [RegistrationModule]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ibbcaptcha/templates/captcha.html	Sat Aug 10 11:00:42 2013 +0000
@@ -0,0 +1,29 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      py:strip="True">
+  <fieldset>
+    
+    <div>
+      <label>Name:
+        <input type="text" name="name" class="textwidget" size="20" value="${name}"/>
+      </label>
+    </div>
+
+    <div>
+      <label>Email:
+        <input type="text" name="email" class="textwidget" size="20" value="${email}"/>
+      </label>
+    </div>
+
+    <div>
+      <p>
+        Please enter the text below to prove you're not a machine.
+      </p>
+      <p>
+        ${captcha}
+      </p>
+      <label>Confirm: <input type="text" name="ibbcaptcha" class="textwidget" size="20"/></label>
+    </div>
+    <input type="hidden" name="captchaid" value="${captchaid}"/>
+  </fieldset>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ibbcaptcha/utils.py	Sat Aug 10 11:00:42 2013 +0000
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+import random
+import urllib
+
+class WordChooser(object):
+    """chose a random word given a dict file"""
+    wordlist = []
+    def __init__(self, dictfile, min_len=5):
+        if not getattr(WordChooser, 'dictfile', '') == dictfile:
+            if dictfile.startswith("http://"):
+                f = urllib.urlopen(dictfile)
+            else:
+                f = open(dictfile, 'r')
+            _dict = f.read()
+            f.close()
+            _dict = _dict.lower().split()
+            _dict = [word for word in _dict if word.isalpha() and len(word) > min_len]
+            WordChooser.wordlist = _dict
+            WordChooser.dictfile = dictfile
+
+    def __call__(self):
+        return random.Random().choice(self.wordlist)
+
+def random_word(dictfile):
+    chooser = WordChooser(dictfile)
+    return chooser()
+    
+
+if __name__ == '__main__':
+    foo = WordChooser('http://java.sun.com/docs/books/tutorial/collections/interfaces/examples/dictionary.txt')
+    print foo()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/setup.py	Sat Aug 10 11:00:42 2013 +0000
@@ -0,0 +1,30 @@
+from setuptools import find_packages, setup
+
+version='0.1'
+
+setup(name='IBBCaptcha',
+      version=version,
+      description="Simple text-based CAPTCHA check for registration",
+      author='IBBoard',
+      author_email='dev@ibboard.co.uk',
+      url='http://ibboard.co.uk',
+      keywords='trac plugin',
+      license="GPL",
+      packages=find_packages(exclude=['ez_setup', 'examples', 'tests*']),
+      include_package_data=True,
+      package_data={ 'ibbcaptcha': ['templates/*', ] },
+      zip_safe=False,
+      install_requires=[
+        'Trac',
+        'TracAccountManager',
+        'ComponentDependencyPlugin',
+        ],
+      dependency_links=[
+        "http://trac-hacks.org/svn/componentdependencyplugin/0.11#egg=ComponentDependencyPlugin",
+        ],
+      entry_points = """
+      [trac.plugins]
+      registration_captcha = ibbcaptcha.register
+      """,
+      )
+