├── .gitignore ├── CHANGELOG ├── LICENSE ├── README.md ├── flask_recaptcha.py ├── setup.cfg ├── setup.py └── test_flask_recaptcha.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | __pycache__* 3 | 4 | # Packages 5 | *.egg 6 | *.egg-info 7 | dist 8 | build 9 | eggs 10 | parts 11 | bin 12 | var 13 | sdist 14 | develop-eggs 15 | .installed.cfg 16 | MANIFEST 17 | 18 | # PyCharm 19 | .idea/* 20 | .idea/libraries/sass_stdlib.xml 21 | 22 | # Distribution 23 | dist/* 24 | 25 | # Mac stuff 26 | .DS_Store 27 | 28 | docs/_build* -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | - 0.5.0 2 | - Added RECAPTCHA_LANGUAGE option. Defaults to english but can be updated according to preferred language. 3 | 4 | - 0.4.1 5 | - Added Py.Test 6 | - more refactoring 7 | 8 | - 0.4.0 9 | - Added RECAPTCHA_ENABLED option. When False it will bypass the validation 10 | - Some refactoring 11 | 12 | - 0.3.0 13 | - Implement the new google recaptcha 14 | - Make use of requests 15 | 16 | - Version 0.1.0 17 | - First version -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mardix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask-ReCaptcha 2 | 3 | The new Google ReCaptcha implementation for Flask without Flask-WTF. 4 | 5 | Can also be used as standalone 6 | 7 | --- 8 | 9 | ## Install 10 | 11 | pip install flask-recaptcha 12 | 13 | # Usage 14 | 15 | ### Implementation view.py 16 | 17 | from flask import Flask 18 | from flask_recaptcha import ReCaptcha 19 | 20 | app = Flask(__name__) 21 | recaptcha = ReCaptcha(app=app) 22 | 23 | #or 24 | 25 | recaptcha = Recaptcha() 26 | recaptcha.init_app(app) 27 | 28 | 29 | ### In your template: **{{ recaptcha }}** 30 | 31 | Inside of the form you want to protect, include the tag: **{{ recaptcha }}** 32 | 33 | It will insert the code automatically 34 | 35 | 36 |
44 | 45 | 46 | ### Verify the captcha 47 | 48 | In the view that's going to validate the captcha 49 | 50 | from flask import Flask 51 | from flask_recaptcha import ReCaptcha 52 | 53 | app = Flask(__name__) 54 | recaptcha = ReCaptcha(app=app) 55 | 56 | @route("/submit", methods=["POST"]) 57 | def submit(): 58 | 59 | if recaptcha.verify(): 60 | # SUCCESS 61 | pass 62 | else: 63 | # FAILED 64 | pass 65 | 66 | 67 | ## Api 68 | 69 | **reCaptcha.__init__(app, site_key, secret_key, is_enabled=True)** 70 | 71 | **reCaptcha.get_code()** 72 | 73 | Returns the HTML code to implement. But you can use 74 | **{{ recaptcha }}** directly in your template 75 | 76 | **reCaptcha.verfiy()** 77 | 78 | Returns bool 79 | 80 | ## In Template 81 | 82 | Just include **{{ recaptcha }}** wherever you want to show the recaptcha 83 | 84 | 85 | ## Config 86 | 87 | Flask-ReCaptcha is configured through the standard Flask config API. 88 | These are the available options: 89 | 90 | **RECAPTCHA_ENABLED**: Bool - True by default, when False it will bypass validation 91 | 92 | **RECAPTCHA_SITE_KEY** : Public key 93 | 94 | **RECAPTCHA_SECRET_KEY**: Private key 95 | 96 | The following are **Optional** arguments. 97 | 98 | **RECAPTCHA_THEME**: String - Theme can be 'light'(default) or 'dark' 99 | 100 | **RECAPTCHA_TYPE**: String - Type of recaptcha can be 'image'(default) or 'audio' 101 | 102 | **RECAPTCHA_SIZE**: String - Size of the image can be 'normal'(default) or 'compact' 103 | 104 | **RECAPTCHA_LANGUAGE**: String - Language of the recaptcha texts. Defaults to 'en' (english). You can use fa (persian), ar (arabic), fr (french), etc. 105 | 106 | **RECAPTCHA_TABINDEX**: Int - Tabindex of the widget can be used, if the page uses tabidex, to make navigation easier. Defaults to 0 107 | 108 | RECAPTCHA_ENABLED = True 109 | RECAPTCHA_SITE_KEY = "" 110 | RECAPTCHA_SECRET_KEY = "" 111 | RECAPTCHA_THEME = "dark" 112 | RECAPTCHA_TYPE = "image" 113 | RECAPTCHA_SIZE = "compact" 114 | RECAPTCHA_LANGUAGE = "en" 115 | RECAPTCHA_RTABINDEX = 10 116 | 117 | --- 118 | 119 | (c) 2015 Mardix 120 | 121 | -------------------------------------------------------------------------------- /flask_recaptcha.py: -------------------------------------------------------------------------------- 1 | """ 2 | The new Google ReCaptcha implementation for Flask without Flask-WTF 3 | Can be used as standalone 4 | """ 5 | 6 | __NAME__ = "Flask-ReCaptcha" 7 | __version__ = "0.5.0" 8 | __license__ = "MIT" 9 | __author__ = "Mardix" 10 | __copyright__ = "(c) 2015 Mardix" 11 | 12 | try: 13 | from flask import request 14 | try: 15 | from jinja2 import Markup 16 | except ImportError: 17 | from markupsafe import Markup 18 | import requests 19 | except ImportError as ex: 20 | print("Missing dependencies") 21 | 22 | 23 | class DEFAULTS(object): 24 | IS_ENABLED = True 25 | THEME = "light" 26 | TYPE = "image" 27 | SIZE = "normal" 28 | LANGUAGE = "en" 29 | TABINDEX = 0 30 | 31 | 32 | class ReCaptcha(object): 33 | 34 | VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify" 35 | site_key = None 36 | secret_key = None 37 | is_enabled = False 38 | 39 | def __init__(self, app=None, site_key=None, secret_key=None, is_enabled=True, **kwargs): 40 | if site_key: 41 | self.site_key = site_key 42 | self.secret_key = secret_key 43 | self.is_enabled = is_enabled 44 | self.theme = kwargs.get('theme', DEFAULTS.THEME) 45 | self.type = kwargs.get('type', DEFAULTS.TYPE) 46 | self.size = kwargs.get('size', DEFAULTS.SIZE) 47 | self.language = kwargs.get('language', DEFAULTS.LANGUAGE) 48 | self.tabindex = kwargs.get('tabindex', DEFAULTS.TABINDEX) 49 | 50 | elif app: 51 | self.init_app(app=app) 52 | 53 | def init_app(self, app=None): 54 | self.__init__(site_key=app.config.get("RECAPTCHA_SITE_KEY"), 55 | secret_key=app.config.get("RECAPTCHA_SECRET_KEY"), 56 | is_enabled=app.config.get("RECAPTCHA_ENABLED", DEFAULTS.IS_ENABLED), 57 | theme=app.config.get("RECAPTCHA_THEME", DEFAULTS.THEME), 58 | type=app.config.get("RECAPTCHA_TYPE", DEFAULTS.TYPE), 59 | size=app.config.get("RECAPTCHA_SIZE", DEFAULTS.SIZE), 60 | language=app.config.get("RECAPTCHA_LANGUAGE", DEFAULTS.LANGUAGE), 61 | tabindex=app.config.get("RECAPTCHA_TABINDEX", DEFAULTS.TABINDEX)) 62 | 63 | @app.context_processor 64 | def get_code(): 65 | return dict(recaptcha=Markup(self.get_code())) 66 | 67 | def get_code(self): 68 | """ 69 | Returns the new ReCaptcha code 70 | :return: 71 | """ 72 | return "" if not self.is_enabled else (""" 73 | 74 | 76 | """.format(SITE_KEY=self.site_key, THEME=self.theme, TYPE=self.type, SIZE=self.size, LANGUAGE=self.language, TABINDEX=self.tabindex)) 77 | 78 | def verify(self, response=None, remote_ip=None): 79 | if self.is_enabled: 80 | data = { 81 | "secret": self.secret_key, 82 | "response": response or request.form.get('g-recaptcha-response'), 83 | "remoteip": remote_ip or request.environ.get('REMOTE_ADDR') 84 | } 85 | 86 | r = requests.get(self.VERIFY_URL, params=data) 87 | return r.json()["success"] if r.status_code == 200 else False 88 | return True 89 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup, find_packages 3 | import flask_recaptcha 4 | 5 | PACKAGE = flask_recaptcha 6 | 7 | setup( 8 | name=PACKAGE.__NAME__, 9 | version=PACKAGE.__version__, 10 | license=PACKAGE.__license__, 11 | author=PACKAGE.__author__, 12 | author_email='mardix@pylot.io', 13 | description="The new Google ReCaptcha implementation for Flask without Flask-WTF", 14 | long_description=PACKAGE.__doc__, 15 | url='http://github.com/mardix/flask-recaptcha/', 16 | download_url='http://github.com/mardix/flask-recaptcha/tarball/master', 17 | py_modules=['flask_recaptcha'], 18 | include_package_data=True, 19 | install_requires=[ 20 | "flask", 21 | "requests", 22 | "MarkupSafe" 23 | ], 24 | keywords=['flask', 'recaptcha', "validate"], 25 | platforms='any', 26 | classifiers=[ 27 | 'Environment :: Web Environment', 28 | 'Intended Audience :: Developers', 29 | 'License :: OSI Approved :: BSD License', 30 | 'Operating System :: OS Independent', 31 | 'Programming Language :: Python', 32 | 'Programming Language :: Python :: 2', 33 | 'Programming Language :: Python :: 2.6', 34 | 'Programming Language :: Python :: 2.7', 35 | 'Programming Language :: Python :: 3', 36 | 'Programming Language :: Python :: 3.3', 37 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 38 | 'Topic :: Software Development :: Libraries :: Python Modules' 39 | ], 40 | zip_safe=False 41 | ) 42 | -------------------------------------------------------------------------------- /test_flask_recaptcha.py: -------------------------------------------------------------------------------- 1 | 2 | from flask import Flask 3 | from flask_recaptcha import ReCaptcha 4 | 5 | app = Flask(__name__) 6 | app.config.update({ 7 | "debug": True, 8 | "RECAPTCHA_SITE_KEY": "SITE_KEY", 9 | "RECAPTCHA_SITE_SECRET": "SECRET", 10 | "RECAPTCHA_ENABLED": True 11 | }) 12 | 13 | def test_recaptcha_enabled(): 14 | recaptcha = ReCaptcha(site_key="SITE_KEY", secret_key="SECRET_KEY") 15 | assert isinstance(recaptcha, ReCaptcha) 16 | assert recaptcha.is_enabled == True 17 | assert "script" in recaptcha.get_code() 18 | assert recaptcha.verify(response="None", remote_ip="0.0.0.0") == False 19 | 20 | def test_recaptcha_enabled_flask(): 21 | recaptcha = ReCaptcha(app=app) 22 | assert isinstance(recaptcha, ReCaptcha) 23 | assert recaptcha.is_enabled == True 24 | assert "script" in recaptcha.get_code() 25 | assert recaptcha.verify(response="None", remote_ip="0.0.0.0") == False 26 | 27 | def test_recaptcha_disabled(): 28 | recaptcha = ReCaptcha(site_key="SITE_KEY", secret_key="SECRET_KEY", is_enabled=False) 29 | assert recaptcha.is_enabled == False 30 | assert recaptcha.get_code() == "" 31 | assert recaptcha.verify(response="None", remote_ip="0.0.0.0") == True 32 | 33 | def test_recaptcha_disabled_flask(): 34 | app.config.update({ 35 | "RECAPTCHA_ENABLED": False 36 | }) 37 | recaptcha = ReCaptcha(app=app) 38 | assert recaptcha.is_enabled == False 39 | assert recaptcha.get_code() == "" 40 | assert recaptcha.verify(response="None", remote_ip="0.0.0.0") == True --------------------------------------------------------------------------------