Edgewall Software

Ticket #4034: numcaptcha2.patch

File numcaptcha2.patch, 7.3 KB (added by sergeych <sergeych@…>, 6 years ago)

Num captcha integrated in Settings

  • templates/settings.html

     
    1515    <div id="content" class="settings"> 
    1616 
    1717      <h1>Settings and Session Management</h1> 
     18       
     19      <p class="error" py:if="settings.session.capmode == 'error'"> 
     20              Invalid answer to the test question, sorry. Please, try again. 
     21      </p> 
    1822 
    1923      <h2>User Settings</h2> 
    2024      <p>This page lets you customize your personal settings for this site. 
     
    9498          </div> 
    9599        </fieldset> 
    96100 
    97         <div class="buttons"> 
     101                <fieldset py:if="settings.session.capmode"> 
     102                        <legend>Accept changes</legend> 
     103                        <p class="hint">By answering this question you confirm that you are a human 
     104                        being as this page is not serving bots. This is one time operation;  
     105                        sorry for inconvenience.</p> 
     106                        <div class="field"> 
     107                        <label>How many is ${question}? 
     108                                <input type="text" name="answer" size="16" /> 
     109                        </label> 
     110                        </div> 
     111                <div class="buttons"> 
     112                  <input type="hidden" name="action" value="save" /> 
     113                  <input type="submit" value="Submit changes" /> 
     114                </div> 
     115        </fieldset> 
     116         
     117        <div class="buttons" py:if="not settings.session.capmode"> 
    98118          <input type="hidden" name="action" value="save" /> 
    99119          <input type="submit" value="Submit changes" /> 
    100         </div > 
     120        </div> 
     121       
    101122      </form> 
    102123 
    103124      <py:if test="settings.session_id"> 
  • trac/Settings.py

     
    2121from trac.util.datefmt import all_timezones, utc 
    2222from trac.web import IRequestHandler 
    2323from trac.web.chrome import INavigationContributor 
     24from trac.util.numcaptcha import numCaptchaQuestion 
    2425 
    25  
    2626class SettingsModule(Component): 
    2727 
    2828    implements(INavigationContributor, IRequestHandler) 
     
    4545 
    4646    def process_request(self, req): 
    4747        action = req.args.get('action') 
    48  
     48         
     49        question = None 
    4950        if req.method == 'POST': 
    5051            if action == 'save': 
    5152                self._do_save(req) 
    5253            elif action == 'load': 
    5354                self._do_load(req) 
     55        else: 
     56            # Get: consider whether to use captcha 
     57            if (req.session.has_key('name') and req.session['name']) or \ 
     58               (req.session.has_key('email') and req.session['email']): 
     59                capmode = req.session['capmode'] = None 
     60            else: 
     61                # Ask captcha question 
     62                question, req.session['capanswer'] = numCaptchaQuestion() 
     63                if not 'capmode' in req.session: 
     64                    if req.session['capmode'] != 'error': 
     65                        req.session['capmode'] = 'check' 
    5466 
    5567        data = {'session': req.session} 
    5668        if req.authname == 'anonymous': 
    5769            data['session_id'] = req.session.sid 
    58  
    59         return 'settings.html', {'settings': data, 
     70             
     71        return 'settings.html', {'settings': data, 'question':question, 
    6072                                 'timezones': all_timezones}, None 
    6173 
    6274    # Internal methods 
    6375 
    6476    def _do_save(self, req): 
    65         for field in self._form_fields: 
    66             val = req.args.get(field) 
    67             if val: 
    68                 if field == 'tz' and 'tz' in req.session and \ 
    69                         val not in all_timezones: 
    70                     del req.session['tz'] 
    71                 elif field == 'newsid' and val: 
    72                     req.session.change_sid(val) 
    73                 else: 
    74                     req.session[field] = val 
    75             elif field in req.session: 
    76                 del req.session[field] 
     77        self.error = req.session['capmode'] > 0 and req.session['capanswer'] != req.args.get('answer') 
     78        if self.error: 
     79            req.session['capmode'] = 'error' 
     80        else: 
     81            req.session['capmode'] = 'check' 
     82            for field in self._form_fields: 
     83                val = req.args.get(field) 
     84                if val: 
     85                    if field == 'tz' and 'tz' in req.session and \ 
     86                            val not in all_timezones: 
     87                        del req.session['tz'] 
     88                    elif field == 'newsid' and val: 
     89                        req.session.change_sid(val) 
     90                    else: 
     91                        req.session[field] = val 
     92                elif field in req.session: 
     93                    del req.session[field] 
    7794        req.redirect(req.href.settings()) 
    7895 
    7996    def _do_load(self, req): 
  • trac/util/numcaptcha.py

     
     1# 
     2# Arithmetic captcha, English only 
     3# 
     4# The idea is to generate some expression 
     5# like 5 * 7 or 3 * 11 + 2 and translate it 
     6# to the text form 
     7 
     8import re 
     9 
     10_numerals = ( 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 
     11              'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen',  
     12              'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen' ) 
     13 
     14_tens = ( 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety' ) 
     15 
     16# small positive numeral 0-99 
     17def numeral(number): 
     18    number = int(number) 
     19    if number < 20: 
     20        return _numerals[number] 
     21    import math 
     22    t, r = (number / 10) - 2, number % 10 
     23    if r: 
     24        return _tens[ t ] + '-' + _numerals[ r ] 
     25    return _tens[t] 
     26 
     27# basic operations 
     28def operation(opchar): 
     29    if opchar == '+': 
     30        return 'plus' 
     31    elif opchar == '-': 
     32        return 'minus' 
     33    elif opchar == '*': 
     34        return 'multiplied by' 
     35    elif opchar == '/': 
     36        return 'divided by' 
     37    raise NotImplementedError( 'Operation '+opchar+' is not supported' ) 
     38 
     39# numeral or operation 
     40def term( op_or_number ): 
     41    try: 
     42        return operation(op_or_number) 
     43    except NotImplementedError: 
     44        return numeral(op_or_number) 
     45 
     46# formulae parsing re 
     47_re_formulae = re.compile( '([0-9]+|[+\-*/])' ) 
     48 
     49# translate simple formulae to English 
     50def say( formulae ): 
     51    parts = re.findall( _re_formulae, formulae ) 
     52    return ' '.join( [ term(x) for x in parts ] ) 
     53     
     54from random import randint 
     55 
     56# Get numeric captcha question as English phrase and result as str 
     57def numCaptchaQuestion(): 
     58    expr, res = numCaptchaExpr() 
     59    return say(expr), str(res) 
     60 
     61# Build numeric captcha expression as formulae return it and result as int 
     62def numCaptchaExpr(): 
     63     
     64    total = randint(7,49) 
     65    m1 = randint( 2, total/2 ) 
     66    m2 = total / m1 
     67    rem = total - m1*m2 
     68    if rem: 
     69        return '%d * %d + %d' % (m1, m2, rem), total 
     70    return '%d * %d' % (m1, m2), total 
     71 
     72if __name__ == '__main__': 
     73 
     74    # parts of tests 
     75     
     76    def _test_buildNumExpr(): 
     77        print 'Testing buildNumExpr' 
     78        for cnt in xrange(100000): 
     79            ex, res = numCaptchaExpr() 
     80            if eval(ex) != res: 
     81                print 'Error: %s is %d not %d!' % (ex, eval(ex), res) 
     82                return False 
     83        print 'BuildNumExpr test passed' 
     84        return True 
     85             
     86    _test_buildNumExpr()