Show More
@@ -225,13 +225,6 b' self: super: {' | |||
|
225 | 225 | }; |
|
226 | 226 | }); |
|
227 | 227 | |
|
228 | recaptcha-client = super.recaptcha-client.override (attrs: { | |
|
229 | meta = { | |
|
230 | # TODO: It is MIT/X11 | |
|
231 | license = pkgs.lib.licenses.mit; | |
|
232 | }; | |
|
233 | }); | |
|
234 | ||
|
235 | 228 | python-editor = super.python-editor.override (attrs: { |
|
236 | 229 | meta = { |
|
237 | 230 | license = pkgs.lib.licenses.asl20; |
@@ -1497,19 +1497,6 b'' | |||
|
1497 | 1497 | license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ]; |
|
1498 | 1498 | }; |
|
1499 | 1499 | }; |
|
1500 | recaptcha-client = super.buildPythonPackage { | |
|
1501 | name = "recaptcha-client-1.0.6"; | |
|
1502 | buildInputs = with self; []; | |
|
1503 | doCheck = false; | |
|
1504 | propagatedBuildInputs = with self; []; | |
|
1505 | src = fetchurl { | |
|
1506 | url = "https://files.pythonhosted.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz"; | |
|
1507 | sha256 = "28c6853c1d13d365b7dc71a6b05e5ffb56471f70a850de318af50d3d7c0dea2f"; | |
|
1508 | }; | |
|
1509 | meta = { | |
|
1510 | license = [ { fullName = "MIT/X11"; } ]; | |
|
1511 | }; | |
|
1512 | }; | |
|
1513 | 1500 | redis = super.buildPythonPackage { |
|
1514 | 1501 | name = "redis-2.10.6"; |
|
1515 | 1502 | buildInputs = with self; []; |
@@ -1553,7 +1540,7 b'' | |||
|
1553 | 1540 | name = "rhodecode-enterprise-ce-4.13.0"; |
|
1554 | 1541 | buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock webtest cov-core coverage configobj]; |
|
1555 | 1542 | doCheck = true; |
|
1556 |
propagatedBuildInputs = with self; [setuptools-scm amqp authomatic babel beaker celery chameleon channelstream click colander configobj cssselect decorator deform docutils dogpile.cache dogpile.core ecdsa formencode future futures gnureadline infrae.cache iso8601 itsdangerous jinja2 billiard kombu lxml mako markdown markupsafe msgpack-python mysql-python objgraph packaging paste pastedeploy pastescript pathlib2 peppercorn psutil psycopg2 py-bcrypt pycrypto pycurl pyflakes pygments-markdown-lexer pygments pyparsing pyramid-beaker pyramid-debugtoolbar pyramid-jinja2 pyramid-mako pyramid pysqlite python-dateutil python-ldap python-memcached python-pam pytz tzlocal pyzmq py-gfm |
|
|
1543 | propagatedBuildInputs = with self; [setuptools-scm amqp authomatic babel beaker celery chameleon channelstream click colander configobj cssselect decorator deform docutils dogpile.cache dogpile.core ecdsa formencode future futures gnureadline infrae.cache iso8601 itsdangerous jinja2 billiard kombu lxml mako markdown markupsafe msgpack-python mysql-python objgraph packaging paste pastedeploy pastescript pathlib2 peppercorn psutil psycopg2 py-bcrypt pycrypto pycurl pyflakes pygments-markdown-lexer pygments pyparsing pyramid-beaker pyramid-debugtoolbar pyramid-jinja2 pyramid-mako pyramid pysqlite python-dateutil python-ldap python-memcached python-pam pytz tzlocal pyzmq py-gfm redis repoze.lru requests routes setproctitle simplejson six sqlalchemy sshpubkeys subprocess32 supervisor tempita translationstring trollius urllib3 urlobject venusian weberror webhelpers2 webhelpers webob whoosh wsgiref zope.cachedescriptors zope.deprecation zope.event zope.interface nbconvert bleach nbformat jupyter-client alembic invoke bumpversion gevent greenlet gunicorn waitress ipdb ipython cprofilev bottle rhodecode-tools appenlight-client pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock webtest cov-core coverage]; | |
|
1557 | 1544 | src = ./.; |
|
1558 | 1545 | meta = { |
|
1559 | 1546 | license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ]; |
@@ -65,7 +65,6 b' pytz==2018.4' | |||
|
65 | 65 | tzlocal==1.5.1 |
|
66 | 66 | pyzmq==14.6.0 |
|
67 | 67 | py-gfm==0.1.3 |
|
68 | recaptcha-client==1.0.6 | |
|
69 | 68 | redis==2.10.6 |
|
70 | 69 | repoze.lru==0.7 |
|
71 | 70 | requests==2.9.1 |
@@ -97,16 +97,16 b' class TestRegisterCaptcha(object):' | |||
|
97 | 97 | assertr.no_element_exists('#recaptcha_field') |
|
98 | 98 | |
|
99 | 99 | @pytest.mark.parametrize('valid', [False, True]) |
|
100 | @mock.patch('rhodecode.apps.login.views.submit') | |
|
100 | @mock.patch.object(LoginView, 'validate_captcha') | |
|
101 | 101 | @mock.patch.object(LoginView, '_get_captcha_data') |
|
102 | 102 | def test_register_with_active_captcha( |
|
103 |
self, m_get_captcha_data, m_ |
|
|
103 | self, m_get_captcha_data, m_validate_captcha, valid, app, csrf_token): | |
|
104 | 104 | captcha = CaptchaData( |
|
105 | 105 | active=True, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY') |
|
106 | 106 | m_get_captcha_data.return_value = captcha |
|
107 | 107 | m_response = mock.Mock() |
|
108 | 108 | m_response.is_valid = valid |
|
109 |
m_ |
|
|
109 | m_validate_captcha.return_value = valid, 'ok' | |
|
110 | 110 | |
|
111 | 111 | params = { |
|
112 | 112 | 'csrf_token': csrf_token, |
@@ -25,10 +25,10 b' import formencode' | |||
|
25 | 25 | import formencode.htmlfill |
|
26 | 26 | import logging |
|
27 | 27 | import urlparse |
|
28 | import requests | |
|
28 | 29 | |
|
29 | 30 | from pyramid.httpexceptions import HTTPFound |
|
30 | 31 | from pyramid.view import view_config |
|
31 | from recaptcha.client.captcha import submit | |
|
32 | 32 | |
|
33 | 33 | from rhodecode.apps._base import BaseAppView |
|
34 | 34 | from rhodecode.authentication.base import authenticate, HTTP_TYPE |
@@ -124,6 +124,29 b' class LoginView(BaseAppView):' | |||
|
124 | 124 | return CaptchaData( |
|
125 | 125 | active=active, private_key=private_key, public_key=public_key) |
|
126 | 126 | |
|
127 | def validate_captcha(self, private_key): | |
|
128 | ||
|
129 | captcha_rs = self.request.POST.get('g-recaptcha-response') | |
|
130 | url = "https://www.google.com/recaptcha/api/siteverify" | |
|
131 | params = { | |
|
132 | 'secret': private_key, | |
|
133 | 'response': captcha_rs, | |
|
134 | 'remoteip': get_ip_addr(self.request.environ) | |
|
135 | } | |
|
136 | verify_rs = requests.get(url, params=params, verify=True) | |
|
137 | verify_rs = verify_rs.json() | |
|
138 | captcha_status = verify_rs.get('success', False) | |
|
139 | captcha_errors = verify_rs.get('error-codes', []) | |
|
140 | if not isinstance(captcha_errors, list): | |
|
141 | captcha_errors = [captcha_errors] | |
|
142 | captcha_errors = ', '.join(captcha_errors) | |
|
143 | captcha_message = '' | |
|
144 | if captcha_status is False: | |
|
145 | captcha_message = "Bad captcha. Errors: {}".format( | |
|
146 | captcha_errors) | |
|
147 | ||
|
148 | return captcha_status, captcha_message | |
|
149 | ||
|
127 | 150 | @view_config( |
|
128 | 151 | route_name='login', request_method='GET', |
|
129 | 152 | renderer='rhodecode:templates/login.mako') |
@@ -262,17 +285,15 b' class LoginView(BaseAppView):' | |||
|
262 | 285 | form_result['active'] = auto_active |
|
263 | 286 | |
|
264 | 287 | if captcha.active: |
|
265 | response = submit( | |
|
266 | self.request.POST.get('recaptcha_challenge_field'), | |
|
267 | self.request.POST.get('recaptcha_response_field'), | |
|
268 |
|
|
|
269 | remoteip=get_ip_addr(self.request.environ)) | |
|
270 | if not response.is_valid: | |
|
288 | captcha_status, captcha_message = self.validate_captcha( | |
|
289 | captcha.private_key) | |
|
290 | ||
|
291 | if not captcha_status: | |
|
271 | 292 | _value = form_result |
|
272 | 293 | _msg = _('Bad captcha') |
|
273 | error_dict = {'recaptcha_field': _msg} | |
|
274 |
raise formencode.Invalid( |
|
|
275 |
|
|
|
294 | error_dict = {'recaptcha_field': captcha_message} | |
|
295 | raise formencode.Invalid( | |
|
296 | _msg, _value, None, error_dict=error_dict) | |
|
276 | 297 | |
|
277 | 298 | new_user = UserModel().create_registration(form_result) |
|
278 | 299 | |
@@ -339,15 +360,13 b' class LoginView(BaseAppView):' | |||
|
339 | 360 | user_email = form_result['email'] |
|
340 | 361 | |
|
341 | 362 | if captcha.active: |
|
342 | response = submit( | |
|
343 |
|
|
|
344 | self.request.POST.get('recaptcha_response_field'), | |
|
345 |
|
|
|
346 | remoteip=get_ip_addr(self.request.environ)) | |
|
347 | if not response.is_valid: | |
|
363 | captcha_status, captcha_message = self.validate_captcha( | |
|
364 | captcha.private_key) | |
|
365 | ||
|
366 | if not captcha_status: | |
|
348 | 367 | _value = form_result |
|
349 | 368 | _msg = _('Bad captcha') |
|
350 | error_dict = {'recaptcha_field': _msg} | |
|
369 | error_dict = {'recaptcha_field': captcha_message} | |
|
351 | 370 | raise formencode.Invalid( |
|
352 | 371 | _msg, _value, None, error_dict=error_dict) |
|
353 | 372 |
@@ -305,9 +305,6 b'' | |||
|
305 | 305 | "python2.7-pyzmq-14.6.0": { |
|
306 | 306 | "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause" |
|
307 | 307 | }, |
|
308 | "python2.7-recaptcha-client-1.0.6": { | |
|
309 | "MIT License": "http://spdx.org/licenses/MIT" | |
|
310 | }, | |
|
311 | 308 | "python2.7-repoze.lru-0.6": { |
|
312 | 309 | "Repoze License": "http://www.repoze.org/LICENSE.txt" |
|
313 | 310 | }, |
@@ -65,26 +65,26 b'' | |||
|
65 | 65 | </div> |
|
66 | 66 | <div class="panel-body"> |
|
67 | 67 | <div class="label"> |
|
68 |
<label for="rhodecode_captcha_public_key">${_('Google |
|
|
68 | <label for="rhodecode_captcha_public_key">${_('Google reCaptcha v2 site key.')}</label> | |
|
69 | 69 | </div> |
|
70 | 70 | <div class="field input"> |
|
71 | 71 | ${h.text('rhodecode_captcha_public_key',size=60)} |
|
72 | 72 | </div> |
|
73 | 73 | <div class="field"> |
|
74 | 74 | <span class="help-block"> |
|
75 |
${_(' |
|
|
75 | ${_('Site key for reCaptcha v2 system.')} | |
|
76 | 76 | </span> |
|
77 | 77 | </div> |
|
78 | 78 | |
|
79 | 79 | <div class="label"> |
|
80 |
<label for="rhodecode_captcha_private_key">${_('Google |
|
|
80 | <label for="rhodecode_captcha_private_key">${_('Google reCaptcha v2 secret key.')}</label> | |
|
81 | 81 | </div> |
|
82 | 82 | <div class="field input"> |
|
83 | 83 | ${h.text('rhodecode_captcha_private_key',size=60)} |
|
84 | 84 | </div> |
|
85 | 85 | <div class="field"> |
|
86 | 86 | <span class="help-block"> |
|
87 |
${_(' |
|
|
87 | ${_('Secret key for reCaptcha v2 system. Setting this value will enable captcha on registration and password reset forms.')} | |
|
88 | 88 | </span> |
|
89 | 89 | </div> |
|
90 | 90 | </div> |
@@ -61,6 +61,7 b'' | |||
|
61 | 61 | <label for="email">${_('Captcha')}:</label> |
|
62 | 62 | ${h.hidden('recaptcha_field')} |
|
63 | 63 | <div id="recaptcha"></div> |
|
64 | ||
|
64 | 65 |
|
|
65 | 66 | <span class="error-message">${errors.get('recaptcha_field')}</span> |
|
66 | 67 | <br /> |
@@ -78,14 +79,20 b'' | |||
|
78 | 79 | </div> |
|
79 | 80 | </div> |
|
80 | 81 |
|
|
81 | %if captcha_active: | |
|
82 | <script type="text/javascript" src="https://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script> | |
|
83 | %endif | |
|
84 | 82 | <script type="text/javascript"> |
|
85 | 83 | $(document).ready(function(){ |
|
86 | 84 | $('#email').focus(); |
|
87 | %if captcha_active: | |
|
88 | Recaptcha.create("${captcha_public_key}", "recaptcha", {theme: "white"}); | |
|
89 | %endif | |
|
90 | 85 | }); |
|
91 | 86 | </script> |
|
87 | ||
|
88 | % if captcha_active: | |
|
89 | <script type="text/javascript"> | |
|
90 | var onloadCallback = function() { | |
|
91 | grecaptcha.render('recaptcha', { | |
|
92 | 'sitekey' : "${captcha_public_key}" | |
|
93 | }); | |
|
94 | }; | |
|
95 | </script> | |
|
96 | <script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script> | |
|
97 | % endif | |
|
98 |
@@ -110,14 +110,21 b'' | |||
|
110 | 110 | </div> |
|
111 | 111 | </div> |
|
112 | 112 |
|
|
113 | %if captcha_active: | |
|
114 | <script type="text/javascript" src="https://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script> | |
|
115 | %endif | |
|
113 | ||
|
116 | 114 | <script type="text/javascript"> |
|
117 | $(document).ready(function(){ | |
|
115 | $(document).ready(function(){ | |
|
118 | 116 | $('#username').focus(); |
|
119 | %if captcha_active: | |
|
120 | Recaptcha.create("${captcha_public_key}", "recaptcha", {theme: "white"}); | |
|
121 | %endif | |
|
122 | }); | |
|
117 | }); | |
|
123 | 118 | </script> |
|
119 | ||
|
120 | % if captcha_active: | |
|
121 | <script type="text/javascript"> | |
|
122 | var onloadCallback = function() { | |
|
123 | grecaptcha.render('recaptcha', { | |
|
124 | 'sitekey' : "${captcha_public_key}" | |
|
125 | }); | |
|
126 | }; | |
|
127 | </script> | |
|
128 | <script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script> | |
|
129 | % endif | |
|
130 |
General Comments 0
You need to be logged in to leave comments.
Login now