Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions fastapi_captcha/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

===============
Fastapi Captcha
===============

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:9f521219b54944652afeaf18f64a122f01e1b6646171bc5212e48cb40d0906a0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github
:target: https://github.com/OCA/rest-framework/tree/19.0/fastapi_captcha
:alt: OCA/rest-framework
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/rest-framework-19-0/rest-framework-19-0-fastapi_captcha
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module provides a simple way to protect several fastapi endpoints
routes with a captcha.

It currently supports the following captcha providers:

- `Google reCAPTCHA <https://www.google.com/recaptcha>`__
- `hCaptcha <https://www.hcaptcha.com/>`__
- `Altcha <https://altcha.org/>`__

**Table of contents**

.. contents::
:local:

Usage
=====

Check the ``Use Captcha`` checkbox in your FastAPI endpoint to enable
captcha validation, then enter your captcha provider, secret key and an
array of route url regex.

Every matching route will now require a valid captcha token in the
X-Captcha-Token header.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/rest-framework/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/rest-framework/issues/new?body=module:%20fastapi_captcha%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Akretion

Contributors
------------

- Florian Mounier florian.mounier@akretion.com

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-paradoxxxzero| image:: https://github.com/paradoxxxzero.png?size=40px
:target: https://github.com/paradoxxxzero
:alt: paradoxxxzero

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-paradoxxxzero|

This module is part of the `OCA/rest-framework <https://github.com/OCA/rest-framework/tree/19.0/fastapi_captcha>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions fastapi_captcha/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
19 changes: 19 additions & 0 deletions fastapi_captcha/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2025 Akretion (http://www.akretion.com).
# @author Florian Mounier <florian.mounier@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Fastapi Captcha",
"version": "19.0.1.0.0",
"author": "Akretion, Odoo Community Association (OCA)",
"summary": "Add a captcha to your FastAPI routes",
"category": "Tools",
"depends": ["fastapi"],
"website": "https://github.com/OCA/rest-framework",
"data": [
"views/fastapi_endpoint_views.xml",
],
"maintainers": ["paradoxxxzero"],
"installable": True,
"license": "AGPL-3",
}
42 changes: 42 additions & 0 deletions fastapi_captcha/captcha_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2025 Akretion (http://www.akretion.com).
# @author Florian Mounier <florian.mounier@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from starlette.middleware.base import BaseHTTPMiddleware

from odoo.exceptions import AccessError

from odoo.addons.fastapi.context import odoo_env_ctx


class CaptchaMiddleware(BaseHTTPMiddleware):
def __init__(self, app, endpoint_id, root_path, routes_regex=None):
super().__init__(app)
self.endpoint_id = endpoint_id
self.root_path = root_path
self.routes_regex = routes_regex

async def dispatch(self, request, call_next):
url = request.url.path.replace(self.root_path, "", 1)
if self.routes_regex and not any(
rex.fullmatch(url) for rex in self.routes_regex
):
return await call_next(request)

env = odoo_env_ctx.get()
endpoint = env["fastapi.endpoint"].sudo().browse(self.endpoint_id)
token = request.headers.get("X-Captcha-Token")
if not token:
raise AccessError(
env._("Captcha token not found in headers"),
)
try:
endpoint.validate_captcha(token)
except AccessError as e:
raise e
except OSError as e:
raise AccessError(
env._("Captcha validation failed: %s", str(e)),
) from e
response = await call_next(request)
return response
153 changes: 153 additions & 0 deletions fastapi_captcha/i18n/fastapi_captcha.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fastapi_captcha
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: fastapi_captcha
#: model:ir.model.fields.selection,name:fastapi_captcha.selection__fastapi_endpoint__captcha_type__altcha
msgid "Altcha"
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/models/fastapi_endpoint.py:0
msgid "Altcha (%(url)s) validation failed: %(error)s"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,field_description:fastapi_captcha.field_fastapi_endpoint__captcha_custom_verify_url
msgid "Captcha Custom Verify Url"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,field_description:fastapi_captcha.field_fastapi_endpoint__captcha_minimum_score
msgid "Captcha Minimum Score"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,field_description:fastapi_captcha.field_fastapi_endpoint__captcha_routes_regex
msgid "Captcha Routes Regex"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,field_description:fastapi_captcha.field_fastapi_endpoint__captcha_secret_key
msgid "Captcha Secret Key"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,field_description:fastapi_captcha.field_fastapi_endpoint__captcha_type
msgid "Captcha Type"
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/captcha_middleware.py:0
msgid "Captcha token not found in headers"
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/captcha_middleware.py:0
msgid "Captcha validation failed: %s"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,help:fastapi_captcha.field_fastapi_endpoint__captcha_custom_verify_url
msgid "Custom URL to use for the captcha verification"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model,name:fastapi_captcha.model_fastapi_endpoint
msgid "FastAPI Endpoint"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields.selection,name:fastapi_captcha.selection__fastapi_endpoint__captcha_type__hcaptcha
msgid "Hcaptcha"
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/models/fastapi_endpoint.py:0
msgid "Hcaptcha validation failed: %s"
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/models/fastapi_endpoint.py:0
msgid ""
"Hcaptcha validation failed: score %(score)s < %(min_score)s (%(reason)s)"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,help:fastapi_captcha.field_fastapi_endpoint__use_captcha
msgid "If checked, this endpoint will be protected by a captcha"
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/models/fastapi_endpoint.py:0
msgid "Invalid regex for captcha routes: %(regex)s (error: %(error)s)"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,help:fastapi_captcha.field_fastapi_endpoint__captcha_minimum_score
msgid ""
"Minimum score to accept the captcha if a score is provided by the captcha "
"service."
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/models/fastapi_endpoint.py:0
msgid "No secret key found for this endpoint"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields.selection,name:fastapi_captcha.selection__fastapi_endpoint__captcha_type__recaptcha
msgid "Recaptcha"
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/models/fastapi_endpoint.py:0
msgid "Recaptcha validation failed: %s"
msgstr ""

#. module: fastapi_captcha
#. odoo-python
#: code:addons/fastapi_captcha/models/fastapi_endpoint.py:0
msgid "Recaptcha validation failed: score %(score)s < %(min_score)s"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,help:fastapi_captcha.field_fastapi_endpoint__captcha_routes_regex
msgid ""
"Regexes to match against routes url that should be protected by this "
"captcha, comma separated. If empty, all routes will be protected"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,help:fastapi_captcha.field_fastapi_endpoint__captcha_secret_key
msgid "Secret key to use for the captcha validation"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,help:fastapi_captcha.field_fastapi_endpoint__captcha_type
msgid "Type of captcha to use for this endpoint"
msgstr ""

#. module: fastapi_captcha
#: model:ir.model.fields,field_description:fastapi_captcha.field_fastapi_endpoint__use_captcha
msgid "Use Captcha"
msgstr ""
Loading
Loading