Skip to content

Instantly share code, notes, and snippets.

@tfoote
Last active January 15, 2016 19:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tfoote/675b98df53369e199dea to your computer and use it in GitHub Desktop.
Save tfoote/675b98df53369e199dea to your computer and use it in GitHub Desktop.
This is the diff to add recaptcha to moin moin 1.9.7
diff --git a/PageEditor.py b/PageEditor.py
index 83a6505..a6dab02 100755
--- a/PageEditor.py
+++ b/PageEditor.py
@@ -420,6 +420,9 @@ If you don't want that, hit '''%(cancel_button_text)s''' to cancel your changes.
from MoinMoin.security.textcha import TextCha
request.write(TextCha(request).render())
+ from MoinMoin.security.sec_recaptcha import ReCaptcha
+ request.write(ReCaptcha(request).render())
+
# Add textarea with page text
self.sendconfirmleaving()
diff --git a/PageGraphicalEditor.py b/PageGraphicalEditor.py
index 0a39fb8..7e40c17 100755
--- a/PageGraphicalEditor.py
+++ b/PageGraphicalEditor.py
@@ -305,6 +305,9 @@ If you don't want that, hit '''%(cancel_button_text)s''' to cancel your changes.
from MoinMoin.security.textcha import TextCha
request.write(TextCha(request).render())
+ from MoinMoin.security.sec_recaptcha import ReCaptcha
+ request.write(ReCaptcha(request).render())
+
self.sendconfirmleaving() # TODO update state of flgChange to make this work, see PageEditor
# Add textarea with page text
diff --git a/action/AttachFile.py b/action/AttachFile.py
index 2df164b..b7fd63f 100755
--- a/action/AttachFile.py
+++ b/action/AttachFile.py
@@ -43,6 +43,7 @@ from MoinMoin import config, packages
from MoinMoin.Page import Page
from MoinMoin.util import filesys, timefuncs
from MoinMoin.security.textcha import TextCha
+from MoinMoin.security.sec_recaptcha import ReCaptcha
from MoinMoin.events import FileAttachedEvent, FileRemovedEvent, send_event
from MoinMoin.support import tarfile
@@ -484,6 +485,7 @@ def send_uploadform(pagename, request):
<dd><input type="checkbox" name="overwrite" value="1" %(overwrite_checked)s></dd>
</dl>
%(textcha)s
+%(recaptcha)s
<p>
<input type="hidden" name="action" value="%(action_name)s">
<input type="hidden" name="do" value="upload">
@@ -501,6 +503,7 @@ def send_uploadform(pagename, request):
'overwrite_checked': ('', 'checked')[request.form.get('overwrite', '0') == '1'],
'upload_button': _('Upload'),
'textcha': TextCha(request).render(),
+ 'recaptcha': ReCaptcha(request).render(),
'ticket': wikiutil.createTicket(request),
})
@@ -558,6 +561,8 @@ def _do_upload(pagename, request):
# but it could be extended to more/all attachment write access
if not TextCha(request).check_answer_from_form():
return _('TextCha: Wrong answer! Go back and try again...')
+ if not ReCaptcha(request).check_answer_from_form():
+ return _('ReCaptcha: Wrong answer! Go back and try again...')
form = request.form
diff --git a/action/CopyPage.py b/action/CopyPage.py
index c2b151a..d47879b 100755
--- a/action/CopyPage.py
+++ b/action/CopyPage.py
@@ -14,6 +14,7 @@ from MoinMoin.Page import Page
from MoinMoin.PageEditor import PageEditor
from MoinMoin.action import ActionBase
from MoinMoin.security.textcha import TextCha
+from MoinMoin.security.sec_recaptcha import ReCaptcha
class CopyPage(ActionBase):
""" Copy page action
@@ -45,11 +46,14 @@ class CopyPage(ActionBase):
def do_action(self):
""" copy this page to "pagename" """
+ status = False
_ = self._
# Currently we only check TextCha for upload (this is what spammers ususally do),
# but it could be extended to more/all attachment write access
if not TextCha(self.request).check_answer_from_form():
return status, _('TextCha: Wrong answer! Go back and try again...')
+ if not ReCaptcha(self.request).check_answer_from_form():
+ return status, _('ReCaptcha: Wrong answer! Go back and try again...')
form = self.form
newpagename = form.get('newpagename', u'')
@@ -90,6 +94,7 @@ class CopyPage(ActionBase):
d = {
'textcha': TextCha(self.request).render(),
+ 'recaptcha': ReCaptcha(self.request).render(),
'subpage': subpages,
'subpages_checked': ('', 'checked')[self.request.args.get('subpages_checked', '0') == '1'],
'subpage_label': _('Copy all /subpages too?'),
@@ -105,6 +110,7 @@ class CopyPage(ActionBase):
<br>
<br>
%(textcha)s
+%(recaptcha)s
<table>
<tr>
<dd>
@@ -140,6 +146,7 @@ class CopyPage(ActionBase):
else:
d = {
'textcha': TextCha(self.request).render(),
+ 'recaptcha': ReCaptcha(self.request).render(),
'pagename': wikiutil.escape(self.pagename, True),
'newname_label': _("New name"),
'comment_label': _("Optional reason for the copying"),
@@ -147,6 +154,7 @@ class CopyPage(ActionBase):
}
return '''
%(textcha)s
+%(recaptcha)s
<table>
<tr>
<td class="label"><label>%(newname_label)s</label></td>
diff --git a/action/Load.py b/action/Load.py
index 2d71f8b..1b168dc 100755
--- a/action/Load.py
+++ b/action/Load.py
@@ -14,6 +14,7 @@ from MoinMoin.action import ActionBase, AttachFile
from MoinMoin.PageEditor import PageEditor
from MoinMoin.Page import Page
from MoinMoin.security.textcha import TextCha
+from MoinMoin.security.sec_recaptcha import ReCaptcha
class Load(ActionBase):
""" Load page action
@@ -40,6 +41,8 @@ class Load(ActionBase):
# but it could be extended to more/all attachment write access
if not TextCha(request).check_answer_from_form():
return status, _('TextCha: Wrong answer! Go back and try again...')
+ if not ReCaptcha(request).check_answer_from_form():
+ return _('ReCaptcha: Wrong answer! Go back and try again...')
comment = form.get('comment', u'')
comment = wikiutil.clean_input(comment)
@@ -97,6 +100,7 @@ class Load(ActionBase):
<dd><input type="text" name="comment" size="80" maxlength="200"></dd>
</dl>
%(textcha)s
+%(recaptcha)s
<p>
<input type="hidden" name="action" value="%(action_name)s">
<input type="hidden" name="do" value="upload">
@@ -115,6 +119,7 @@ class Load(ActionBase):
'buttons_html': buttons_html,
'action_name': self.form_trigger,
'textcha': TextCha(self.request).render(),
+ 'recaptcha': ReCaptcha(self.request).render(),
}
def execute(pagename, request):
diff --git a/action/edit.py b/action/edit.py
index e8fc23a..ea5b08b 100755
--- a/action/edit.py
+++ b/action/edit.py
@@ -161,6 +161,9 @@ def execute(pagename, request):
from MoinMoin.security.textcha import TextCha
if not TextCha(request).check_answer_from_form():
raise pg.SaveError(_('TextCha: Wrong answer! Try again below...'))
+ from MoinMoin.security.sec_recaptcha import ReCaptcha
+ if not ReCaptcha(request).check_answer_from_form():
+ raise pg.SaveError(_('ReCaptcha: Wrong answer! Try again below...'))
if request.cfg.comment_required and not comment:
raise pg.SaveError(_('Supplying a comment is mandatory. Write a comment below and try again...'))
savemsg = pg.saveText(savetext, rev, trivial=trivial, comment=comment)
diff --git a/action/newaccount.py b/action/newaccount.py
index 4643b50..a5fae37 100755
--- a/action/newaccount.py
+++ b/action/newaccount.py
@@ -10,6 +10,7 @@ from MoinMoin import user, wikiutil
from MoinMoin.Page import Page
from MoinMoin.widget import html
from MoinMoin.security.textcha import TextCha
+from MoinMoin.security.sec_recaptcha import ReCaptcha
from MoinMoin.auth import MoinAuth
@@ -26,6 +27,9 @@ def _create_user(request):
if not TextCha(request).check_answer_from_form():
return _('TextCha: Wrong answer! Go back and try again...')
+ if not ReCaptcha(request).check_answer_from_form():
+ return _('ReCaptcha: Wrong answer! Go back and try again...')
+
# Create user profile
theuser = user.User(request, auth_method="new-user")
@@ -134,7 +138,7 @@ def _create_form(request):
textcha = TextCha(request)
if textcha.is_enabled():
row = html.TR()
- tbl.append(row)
+ tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_('TextCha (required)')))))
td = html.TD()
@@ -142,6 +146,17 @@ def _create_form(request):
td.append(textcha.render())
row.append(td)
+ recaptcha = ReCaptcha(request)
+ if recaptcha.is_enabled():
+ row = html.TR()
+ tbl.append(row)
+ row.append(html.TD().append(html.STRONG().append(
+ html.Text(_('ReCaptcha (required)')))))
+ td = html.TD()
+ if recaptcha:
+ td.append(recaptcha.render())
+ row.append(td)
+
row = html.TR()
tbl.append(row)
row.append(html.TD())
diff --git a/security/sec_recaptcha.py b/security/sec_recaptcha.py
new file mode 100644
index 0000000..37dfdd7
--- /dev/null
+++ b/security/sec_recaptcha.py
@@ -0,0 +1,74 @@
+# -*- coding: iso-8859-1 -*-
+"""
+ MoinMoin - recaptcha support
+
+ Based heavily on the textcha support in textcha.py
+
+ @copyright: 2011 by Steve McIntyre
+ @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin import log
+from recaptcha.client import captcha
+import sys
+
+logging = log.getLogger(__name__)
+
+from MoinMoin import wikiutil
+
+class ReCaptcha(object):
+ """ Recaptcha support """
+
+ def __init__(self, request):
+ """ Initialize the Recaptcha setup.
+
+ @param request: the request object
+ """
+ self.request = request
+ self.user_info = request.user.valid and request.user.name or request.remote_addr
+ cfg = request.cfg
+
+ try:
+ if cfg.recaptcha_public_key:
+ self.public_key = cfg.recaptcha_public_key
+ if cfg.recaptcha_private_key:
+ self.private_key = cfg.recaptcha_private_key
+ except:
+ self.public_key = None
+ self.private_key = None
+
+ def is_enabled(self):
+ """ check if we're configured, i.e. we have a key
+ """
+ if (self.public_key and self.private_key):
+ return True
+ return False
+
+ def check_answer_from_form(self, form=None):
+ if self.is_enabled():
+ if form is None:
+ form = self.request.form
+ challenge = form.get('recaptcha_challenge_field')
+ response = form.get('recaptcha_response_field')
+ captcha_result = captcha.submit(challenge, response, self.private_key, self.request.remote_addr)
+ if captcha_result.is_valid:
+ logging.info(u"ReCaptcha: OK.")
+ return True
+ else:
+ logging.info(u"ReCaptcha: failed, error code %s." % captcha_result.error_code)
+ return False
+ else:
+ return True
+
+ def render(self, form=None):
+ """ Checks if ReCaptchas are enabled and returns HTML for one,
+ or an empty string if they are not enabled.
+
+ @return: unicode result html
+ """
+ if self.is_enabled():
+ result = captcha.displayhtml(self.public_key, use_ssl = True)
+ else:
+ result = u''
+ return result
+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment