diff --git a/pkgs/python-packages-overrides.nix b/pkgs/python-packages-overrides.nix --- a/pkgs/python-packages-overrides.nix +++ b/pkgs/python-packages-overrides.nix @@ -225,13 +225,6 @@ self: super: { }; }); - recaptcha-client = super.recaptcha-client.override (attrs: { - meta = { - # TODO: It is MIT/X11 - license = pkgs.lib.licenses.mit; - }; - }); - python-editor = super.python-editor.override (attrs: { meta = { license = pkgs.lib.licenses.asl20; diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix --- a/pkgs/python-packages.nix +++ b/pkgs/python-packages.nix @@ -1627,19 +1627,6 @@ license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ]; }; }; - recaptcha-client = super.buildPythonPackage { - name = "recaptcha-client-1.0.6"; - buildInputs = with self; []; - doCheck = false; - propagatedBuildInputs = with self; []; - src = fetchurl { - url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz"; - md5 = "74228180f7e1fb76c4d7089160b0d919"; - }; - meta = { - license = [ { fullName = "MIT/X11"; } ]; - }; - }; redis = super.buildPythonPackage { name = "redis-2.10.6"; buildInputs = with self; []; @@ -1683,7 +1670,7 @@ name = "rhodecode-enterprise-ce-4.12.0"; buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage configobj]; doCheck = true; - 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 recaptcha-client 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 transifex-client gevent greenlet gunicorn waitress uWSGI 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]; + 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 transifex-client gevent greenlet gunicorn waitress uWSGI 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]; src = ./.; meta = { license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ]; diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -65,7 +65,6 @@ pytz==2018.3 tzlocal==1.5.1 pyzmq==14.6.0 py-gfm==0.1.3 -recaptcha-client==1.0.6 redis==2.10.6 repoze.lru==0.7 requests==2.9.1 diff --git a/rhodecode/apps/login/tests/test_register_captcha.py b/rhodecode/apps/login/tests/test_register_captcha.py --- a/rhodecode/apps/login/tests/test_register_captcha.py +++ b/rhodecode/apps/login/tests/test_register_captcha.py @@ -97,16 +97,16 @@ class TestRegisterCaptcha(object): assertr.no_element_exists('#recaptcha_field') @pytest.mark.parametrize('valid', [False, True]) - @mock.patch('rhodecode.apps.login.views.submit') + @mock.patch.object(LoginView, 'validate_captcha') @mock.patch.object(LoginView, '_get_captcha_data') def test_register_with_active_captcha( - self, m_get_captcha_data, m_submit, valid, app, csrf_token): + self, m_get_captcha_data, m_validate_captcha, valid, app, csrf_token): captcha = CaptchaData( active=True, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY') m_get_captcha_data.return_value = captcha m_response = mock.Mock() m_response.is_valid = valid - m_submit.return_value = m_response + m_validate_captcha.return_value = valid, 'ok' params = { 'csrf_token': csrf_token, diff --git a/rhodecode/apps/login/views.py b/rhodecode/apps/login/views.py --- a/rhodecode/apps/login/views.py +++ b/rhodecode/apps/login/views.py @@ -25,10 +25,10 @@ import formencode import formencode.htmlfill import logging import urlparse +import requests from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config -from recaptcha.client.captcha import submit from rhodecode.apps._base import BaseAppView from rhodecode.authentication.base import authenticate, HTTP_TYPE @@ -124,6 +124,29 @@ class LoginView(BaseAppView): return CaptchaData( active=active, private_key=private_key, public_key=public_key) + def validate_captcha(self, private_key): + + captcha_rs = self.request.POST.get('g-recaptcha-response') + url = "https://www.google.com/recaptcha/api/siteverify" + params = { + 'secret': private_key, + 'response': captcha_rs, + 'remoteip': get_ip_addr(self.request.environ) + } + verify_rs = requests.get(url, params=params, verify=True) + verify_rs = verify_rs.json() + captcha_status = verify_rs.get('success', False) + captcha_errors = verify_rs.get('error-codes', []) + if not isinstance(captcha_errors, list): + captcha_errors = [captcha_errors] + captcha_errors = ', '.join(captcha_errors) + captcha_message = '' + if captcha_status is False: + captcha_message = "Bad captcha. Errors: {}".format( + captcha_errors) + + return captcha_status, captcha_message + @view_config( route_name='login', request_method='GET', renderer='rhodecode:templates/login.mako') @@ -262,17 +285,15 @@ class LoginView(BaseAppView): form_result['active'] = auto_active if captcha.active: - response = submit( - self.request.POST.get('recaptcha_challenge_field'), - self.request.POST.get('recaptcha_response_field'), - private_key=captcha.private_key, - remoteip=get_ip_addr(self.request.environ)) - if not response.is_valid: + captcha_status, captcha_message = self.validate_captcha( + captcha.private_key) + + if not captcha_status: _value = form_result _msg = _('Bad captcha') - error_dict = {'recaptcha_field': _msg} - raise formencode.Invalid(_msg, _value, None, - error_dict=error_dict) + error_dict = {'recaptcha_field': captcha_message} + raise formencode.Invalid( + _msg, _value, None, error_dict=error_dict) new_user = UserModel().create_registration(form_result) @@ -339,15 +360,13 @@ class LoginView(BaseAppView): user_email = form_result['email'] if captcha.active: - response = submit( - self.request.POST.get('recaptcha_challenge_field'), - self.request.POST.get('recaptcha_response_field'), - private_key=captcha.private_key, - remoteip=get_ip_addr(self.request.environ)) - if not response.is_valid: + captcha_status, captcha_message = self.validate_captcha( + captcha.private_key) + + if not captcha_status: _value = form_result _msg = _('Bad captcha') - error_dict = {'recaptcha_field': _msg} + error_dict = {'recaptcha_field': captcha_message} raise formencode.Invalid( _msg, _value, None, error_dict=error_dict) diff --git a/rhodecode/config/licenses.json b/rhodecode/config/licenses.json --- a/rhodecode/config/licenses.json +++ b/rhodecode/config/licenses.json @@ -305,9 +305,6 @@ "python2.7-pyzmq-14.6.0": { "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause" }, - "python2.7-recaptcha-client-1.0.6": { - "MIT License": "http://spdx.org/licenses/MIT" - }, "python2.7-repoze.lru-0.6": { "Repoze License": "http://www.repoze.org/LICENSE.txt" }, diff --git a/rhodecode/templates/admin/settings/settings_global.mako b/rhodecode/templates/admin/settings/settings_global.mako --- a/rhodecode/templates/admin/settings/settings_global.mako +++ b/rhodecode/templates/admin/settings/settings_global.mako @@ -65,26 +65,26 @@