Skip to content

Commit

Permalink
use recaptcha
Browse files Browse the repository at this point in the history
  • Loading branch information
whtsky committed Aug 3, 2012
1 parent 7897813 commit 2b4509a
Show file tree
Hide file tree
Showing 16 changed files with 136 additions and 123 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Expand Up @@ -3,4 +3,7 @@
.idea

#sass
.sass-cache
.sass-cache

#OS X
.DS_Store
23 changes: 2 additions & 21 deletions handlers/__init__.py
Expand Up @@ -7,12 +7,12 @@
import time
from bson.objectid import ObjectId
import hashlib
import ayah
from .recaptcha import RecaptchaMixin

_MENTION_FINDER_ = re.compile('class="mention">@(\w+)')


class BaseHandler(tornado.web.RequestHandler):
class BaseHandler(tornado.web.RequestHandler, RecaptchaMixin):
def prepare(self):
if self.request.remote_ip != '127.0.0.1' and \
self.request.host != self.settings['host']:
Expand Down Expand Up @@ -40,25 +40,6 @@ def get_user_locale(self):
def db(self):
return self.application.db

def get_ayah_html(self):
ayah_html = ''
if self.settings['use_ayah']:
ayah.configure(self.settings['ayah_public_key'],
self.settings['ayah_scoring_key'])
ayah_html = ayah.get_publisher_html()
return ayah_html

def verify_ayah(self):
if self.settings['use_ayah']:
ayah.configure(self.settings['ayah_public_key'],
self.settings['ayah_scoring_key'])
session_secret = self.get_argument('session_secret')
passed = ayah.score_result(session_secret)
if not passed:
self.flash('Are you human?')
self.redirect('/')
return True

def get_member(self, name):
name = name.lower()
member = self.db.members.find_one({'name_lower': name})
Expand Down
4 changes: 2 additions & 2 deletions handlers/account.py
Expand Up @@ -16,7 +16,7 @@ def get(self):
self.render('account/signup.html')

def post(self):
self.verify_ayah()
self.recaptcha_validate()
username = self.get_argument('username', None)
email = self.get_argument('email', '').lower()
password = self.get_argument('password', None)
Expand Down Expand Up @@ -68,7 +68,7 @@ def get(self):
self.render('account/signin.html')

def post(self):
self.verify_ayah()
self.recaptcha_validate()
username = self.get_argument('username', '').lower()
password = self.get_argument('password', None)
if not (username and password):
Expand Down
4 changes: 0 additions & 4 deletions handlers/ayah/__init__.py

This file was deleted.

85 changes: 0 additions & 85 deletions handlers/ayah/ayah.py

This file was deleted.

2 changes: 1 addition & 1 deletion handlers/node.py
Expand Up @@ -31,7 +31,7 @@ def get(self, node_name):

@tornado.web.authenticated
def post(self, node_name):
self.verify_ayah()
self.recaptcha_validate()
node = self.get_node(node_name)
title = self.get_argument('title', '')
content = self.get_argument('content', '')
Expand Down
110 changes: 110 additions & 0 deletions handlers/recaptcha.py
@@ -0,0 +1,110 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012, Hsiaoming Yang
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of the author nor the names of its contributors
# may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import urllib


class RecaptchaMixin(object):
"""RecaptchaMixin
You must define some options for this mixin. All information
can be found at http://www.google.com/recaptcha
A basic example::
from tornado.options import define
from tornado.web import RequestHandler, asynchronous
define('recaptcha_key', 'key')
define('recaptcha_secret', 'secret')
define('recaptcha_theme', 'clean')
class SignupHandler(RequestHandler, RecaptchaMixin):
def get(self):
self.write('<form method="post" action="">')
self.write(self.xsrf_form_html())
self.write(self.recaptcha_render())
self.write('<button type="submit">Submit</button>')
self.write('</form>')
@asynchronous
def post(self):
self.recaptcha_validate(self._on_validate)
def _on_validate(self, response):
if response:
self.write('success')
self.finish()
return
self.write('failed')
self.finish()
"""

RECAPTCHA_VERIFY_URL = "http://www.google.com/recaptcha/api/verify"

def recaptcha_render(self):
token = self._recaptcha_token()
html = (
'<div id="recaptcha_div"></div>'
'<script type="text/javascript" '
'src="https://www.google.com/recaptcha/api/js/recaptcha_ajax.js">'
'</script><script type="text/javascript">'
'Recaptcha.create("%(key)s", "recaptcha_div", '
'{theme: "%(theme)s",callback:Recaptcha.focus_response_field});'
'</script>'
)
return html % token

def recaptcha_validate(self):
if not self.settings['use_recaptcha']:
return
token = self._recaptcha_token()
challenge = self.get_argument('recaptcha_challenge_field', None)
response = self.get_argument('recaptcha_response_field', None)
post_args = {
'privatekey': token['secret'],
'remoteip': self.request.remote_ip,
'challenge': challenge,
'response': response
}
body = urllib.urlopen(self.RECAPTCHA_VERIFY_URL,
urllib.urlencode(post_args)).read()
verify, message = body.split()
if verify != 'true':
self.flash('Are you human?')
self.redirect('/')

def _recaptcha_token(self):
token = dict(
key=self.settings['recaptcha_key'],
secret=self.settings['recaptcha_secret'],
theme=self.settings['recaptcha_theme'],
)
return token
3 changes: 3 additions & 0 deletions locale/zh_CN.csv
Expand Up @@ -17,6 +17,7 @@
"Description",简介
"Website",网站
"Language",语言
"Avatar",头像
"Author",作者
"Code Block Example",代码块示例
"Choose a node",选择一个节点
Expand Down Expand Up @@ -111,3 +112,5 @@

"Already have an account?",已有账户?
"Don't have an account?",还没有账户?

"Change your avatar at Gravatar",在Gravatar更改头像
7 changes: 4 additions & 3 deletions settings.py
Expand Up @@ -24,9 +24,10 @@
google_analytics = ''
cookie_secret = 'hey reset me!'

use_ayah = False # If you use it,set to True
ayah_public_key = ''
ayah_scoring_key = ''
use_recaptcha = False # If you use it,set to True
recaptcha_key = ''
recaptcha_secret = ''
recaptcha_theme = 'clean'

gzip = False
debug = True
4 changes: 4 additions & 0 deletions templates/account/settings.html
Expand Up @@ -55,6 +55,10 @@
{% end %}

{% block sidebar %}
<div class="avatar">
<header>{{ _("Avatar") }}</header>
<a href="http://gravatar.com/">{{ _("Change your avatar at Gravatar") }}</a>
</div>
<div class="box">
<header>{{ _("Password") }}</header>
<form method="post" action="/account/password">
Expand Down
2 changes: 1 addition & 1 deletion templates/account/signin.html
Expand Up @@ -20,7 +20,7 @@
<input type="password" class="input-xlarge" id="password" name="password" required>
</div>
</div>
{% raw handler.get_ayah_html() %}
{% raw handler.recaptcha_render() %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ _("Signin") }}</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion templates/account/signup.html
Expand Up @@ -33,7 +33,7 @@
<input type="password" class="input-xlarge" id="password2" name="password2" required>
</div>
</div>
{% raw handler.get_ayah_html() %}
{% raw handler.recaptcha_render() %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ _("Signup") }}</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion templates/node/create.html
Expand Up @@ -15,7 +15,7 @@
<input type="text" class="input-xxlarge" id="title" name="title" placeholder="{{ _('Title') }}"
maxlength="100" required>
<textarea id="content" name="content" rows="20" required></textarea>
{% raw handler.get_ayah_html() %}
{% raw handler.recaptcha_render() %}
<div id="post_submit_div">
<button type="submit" class="btn btn-primary">{{ _("Post") }}</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion templates/node/list.html
Expand Up @@ -18,7 +18,7 @@
{% block sidebar %}
{% if handler.check_role(return_bool=True) %}
<div class="box">
<header>{{ _("Admin") }}</header>
<header>{{ _("Settings") }}</header>
<a href="/node/add" class="btn btn-warning">{{ _("Add Node") }}</a>
</div>
{% end %}
Expand Down
2 changes: 1 addition & 1 deletion templates/node/node.html
Expand Up @@ -19,7 +19,7 @@
{% module node_sitebar(node) %}
{% if handler.check_role(return_bool=True) %}
<div class="box">
<header>{{ _("Admin") }}</header>
<header>{{ _("Settings") }}</header>
<a href="/node/{{ node['name'] }}/edit" class="btn">{{ _("Edit Node") }}</a>
<a href="/node/add" class="btn btn-warning">{{ _("Add Node") }}</a>
<a href="/node/{{ node['name'] }}/remove" class="btn btn-danger">{{ _("Remove Node") }}</a>
Expand Down
2 changes: 1 addition & 1 deletion templates/topic/topic.html
Expand Up @@ -102,7 +102,7 @@ <h1>{{ topic['title'] }}
</div>
{% if handler.check_role(owner_name=topic['author'], return_bool=True) %}
<div class="box">
<header>{{ _("Admin") }}</header>
<header>{{ _("Settings") }}</header>
<a href="/topic/{{ topic['_id'] }}/edit" class="btn one_third">{{ _("Edit") }}</a>
<a href="/topic/{{ topic['_id'] }}/move" class="btn btn-warning one_third">{{ _("Move") }}</a>
<a href="/topic/{{ topic['_id'] }}/remove" class="btn btn-danger one_third">{{ _("Remove") }}</a>
Expand Down

0 comments on commit 2b4509a

Please sign in to comment.