From 578777b92d3e5835e3950a1ff80e9bcb07285bf6 Mon Sep 17 00:00:00 2001 From: Mmequignon Date: Wed, 28 Feb 2024 13:11:48 +0100 Subject: [PATCH 1/4] Add fastapi_auth_api_key --- fastapi_auth_api_key/README.rst | 0 fastapi_auth_api_key/__init__.py | 1 + fastapi_auth_api_key/__manifest__.py | 20 + fastapi_auth_api_key/dependencies.py | 60 +++ fastapi_auth_api_key/models/__init__.py | 1 + .../models/fastapi_endpoint.py | 10 + fastapi_auth_api_key/readme/CONTRIBUTORS.rst | 1 + fastapi_auth_api_key/readme/DESCRIPTION.rst | 1 + fastapi_auth_api_key/readme/USAGE.rst | 33 ++ .../static/description/index.html | 369 ++++++++++++++++++ fastapi_auth_api_key/tests/__init__.py | 1 + .../test_fastapi_api_key_dependencies.py | 88 +++++ .../views/fastapi_endpoint.xml | 17 + 13 files changed, 602 insertions(+) create mode 100644 fastapi_auth_api_key/README.rst create mode 100644 fastapi_auth_api_key/__init__.py create mode 100644 fastapi_auth_api_key/__manifest__.py create mode 100644 fastapi_auth_api_key/dependencies.py create mode 100644 fastapi_auth_api_key/models/__init__.py create mode 100644 fastapi_auth_api_key/models/fastapi_endpoint.py create mode 100644 fastapi_auth_api_key/readme/CONTRIBUTORS.rst create mode 100644 fastapi_auth_api_key/readme/DESCRIPTION.rst create mode 100644 fastapi_auth_api_key/readme/USAGE.rst create mode 100644 fastapi_auth_api_key/static/description/index.html create mode 100644 fastapi_auth_api_key/tests/__init__.py create mode 100644 fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py create mode 100644 fastapi_auth_api_key/views/fastapi_endpoint.xml diff --git a/fastapi_auth_api_key/README.rst b/fastapi_auth_api_key/README.rst new file mode 100644 index 00000000..e69de29b diff --git a/fastapi_auth_api_key/__init__.py b/fastapi_auth_api_key/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/fastapi_auth_api_key/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/fastapi_auth_api_key/__manifest__.py b/fastapi_auth_api_key/__manifest__.py new file mode 100644 index 00000000..1735c59b --- /dev/null +++ b/fastapi_auth_api_key/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +{ + "name": "Fastapi Auth Api Key", + "version": "16.0.1.0.0", + "category": "Others", + "website": "https://github.com/OCA/rest-framework", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["mmequignon"], + "license": "AGPL-3", + "installable": True, + "depends": [ + "fastapi", + "auth_api_key_group", + ], + "data": [ + "views/fastapi_endpoint.xml", + ], +} diff --git a/fastapi_auth_api_key/dependencies.py b/fastapi_auth_api_key/dependencies.py new file mode 100644 index 00000000..3b71e6d5 --- /dev/null +++ b/fastapi_auth_api_key/dependencies.py @@ -0,0 +1,60 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from typing_extensions import Annotated + +from odoo import SUPERUSER_ID +from odoo.api import Environment +from odoo.exceptions import ValidationError + +from odoo.addons.auth_api_key.models.auth_api_key import AuthApiKey +from odoo.addons.base.models.res_partner import Partner +from odoo.addons.fastapi.dependencies import fastapi_endpoint, odoo_env +from odoo.addons.fastapi.models.fastapi_endpoint import FastapiEndpoint + +from fastapi import Depends, status +from fastapi.exceptions import HTTPException +from fastapi.security import APIKeyHeader + + +def authenticated_auth_api_key( + key: Annotated[str, Depends(APIKeyHeader(name="HTTP-API-KEY"))], + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], +) -> AuthApiKey: + if not key: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="No HTTP-API-KEY provided", + headers={"WWW-Authenticate": "HTTP-API-KEY"}, + ) + admin_env = Environment(env.cr, SUPERUSER_ID, {}) + try: + auth_api_key = admin_env["auth.api.key"]._retrieve_api_key(key) + except ValidationError as error: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=error.name, + headers={"WWW-Authenticate": "HTTP-API-KEY"}, + ) from error + # Ensure the api key is authorized for the current endpoint. + if auth_api_key not in endpoint.sudo().auth_api_key_group_id.auth_api_key_ids: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Unauthorized", + headers={"WWW-Authenticate": "HTTP-API-KEY"}, + ) + return auth_api_key + + +def authenticated_partner_by_api_key( + auth_api_key: Annotated[AuthApiKey, Depends(authenticated_auth_api_key)] +) -> Partner: + return auth_api_key.user_id.partner_id + + +def authenticated_env_by_auth_api_key( + auth_api_key: Annotated[AuthApiKey, Depends(authenticated_auth_api_key)] +) -> Environment: + # set api key id in context + return auth_api_key.with_user(auth_api_key.user_id).env diff --git a/fastapi_auth_api_key/models/__init__.py b/fastapi_auth_api_key/models/__init__.py new file mode 100644 index 00000000..b825fab9 --- /dev/null +++ b/fastapi_auth_api_key/models/__init__.py @@ -0,0 +1 @@ +from . import fastapi_endpoint diff --git a/fastapi_auth_api_key/models/fastapi_endpoint.py b/fastapi_auth_api_key/models/fastapi_endpoint.py new file mode 100644 index 00000000..8da7ff66 --- /dev/null +++ b/fastapi_auth_api_key/models/fastapi_endpoint.py @@ -0,0 +1,10 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class FastapiEndpoint(models.Model): + _inherit = "fastapi.endpoint" + + auth_api_key_group_id = fields.Many2one("auth.api.key.group") diff --git a/fastapi_auth_api_key/readme/CONTRIBUTORS.rst b/fastapi_auth_api_key/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..bca4ee0c --- /dev/null +++ b/fastapi_auth_api_key/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Matthieu Méquignon diff --git a/fastapi_auth_api_key/readme/DESCRIPTION.rst b/fastapi_auth_api_key/readme/DESCRIPTION.rst new file mode 100644 index 00000000..22e772df --- /dev/null +++ b/fastapi_auth_api_key/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Provides `FastAPI` dependencies for Api Key authentication. diff --git a/fastapi_auth_api_key/readme/USAGE.rst b/fastapi_auth_api_key/readme/USAGE.rst new file mode 100644 index 00000000..356cb02d --- /dev/null +++ b/fastapi_auth_api_key/readme/USAGE.rst @@ -0,0 +1,33 @@ +Getting an odoo environment +=========================== + +If you need to get an odoo env based on the provided api key, you can use `authenticated_env_by_auth_api_key`. + +.. code-block:: python + + @router.get("/example_with_authenticated_env") + def example_with_authenticated_env( + env: Annotated[Environment, Depends(authenticated_env_by_auth_api_key)], + ) -> None: + # env.user is the user attached to the provided key + pass + +Getting the authenticated partner +================================= + +If want to get the partned related to the the provided api key, you can use `authenticated_partner_by_api_key` + +.. code-block:: python + + @router.get("/example_with_authenticated_partner") + def example_with_authenticated_partner( + partner: Annotated[Partner, Depends(authenticated_partner_by_api_key)], + ) -> None: + # partner is the partner related to the provided key key.user_id.partner_id + pass + +Configuration +============= + +For this to work, the api key must be defined on the `Endpoint`. +A new field `auth_api_key_group_id` has been added to the `Endpoint` model. diff --git a/fastapi_auth_api_key/static/description/index.html b/fastapi_auth_api_key/static/description/index.html new file mode 100644 index 00000000..ec3643de --- /dev/null +++ b/fastapi_auth_api_key/static/description/index.html @@ -0,0 +1,369 @@ + + + + + + +README.rst + + + +
+ + + +
+ + diff --git a/fastapi_auth_api_key/tests/__init__.py b/fastapi_auth_api_key/tests/__init__.py new file mode 100644 index 00000000..da7258cc --- /dev/null +++ b/fastapi_auth_api_key/tests/__init__.py @@ -0,0 +1 @@ +from . import test_fastapi_api_key_dependencies diff --git a/fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py b/fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py new file mode 100644 index 00000000..d746e1cc --- /dev/null +++ b/fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py @@ -0,0 +1,88 @@ +from odoo.tests import tagged +from odoo.tests.common import TransactionCase + +from fastapi.exceptions import HTTPException + +from ..dependencies import ( + authenticated_auth_api_key, + authenticated_env_by_auth_api_key, + authenticated_partner_by_api_key, +) + + +@tagged("-at_install", "post_install") +class TestFastapiAuthApiKey(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + # Is it valid? We need an env without superpowers + demo_user = cls.env.ref("base.partner_demo") + cls.demo_env = demo_user.with_user(demo_user).env + cls.setUpClassApiKey() + + @classmethod + def setUpClassApiKey(cls): + user_model = cls.env["res.users"].with_context(no_reset_password=True) + cls.authorized_user = user_model.create( + { + "name": "John Authorized", + "login": "johnauth", + } + ) + cls.unauthorized_user = user_model.create( + { + "name": "Bob Unauthorized", + "login": "bobunauth", + } + ) + api_key_model = cls.env["auth.api.key"] + cls.authorized_api_key = api_key_model.create( + { + "user_id": cls.authorized_user.id, + "name": "Authorized api key", + "key": "authorized_key", + } + ) + cls.unauthorized_api_key = api_key_model.create( + { + "user_id": cls.unauthorized_user.id, + "name": "Unauthorized api key", + "key": "unauthorized_key", + } + ) + api_key_group_model = cls.env["auth.api.key.group"] + cls.authorized_api_key_group = api_key_group_model.create( + { + "name": "Authorized api key group", + "code": "authorized_api_key_group", + "auth_api_key_ids": [(6, 0, cls.authorized_api_key.ids)], + } + ) + + def test_authenticated_auth_api_key(self): + # An exception is raised when no api key is used + with self.assertRaises(HTTPException) as error: + authenticated_auth_api_key(False, self.demo_env) + self.assertEqual(error.exception.detail, "No HTTP-API-KEY provided") + # An exception is raised when no api key record is found + with self.assertRaises(HTTPException) as error: + authenticated_auth_api_key("404", self.demo_env) + self.assertEqual(error.exception.detail, "The key 404 is not allowed") + # TODO enable this when we know how to filter keys based + # on endpoint's api key group. + # An exception is raised when unauthorized api key record is found + # with self.assertRaises(HTTPException) as error: + # authenticated_auth_api_key("not_authorized", self.demo_env) + result_key = authenticated_auth_api_key( + self.authorized_api_key.key, self.demo_env + ) + self.assertEqual(result_key, self.authorized_api_key) + + def test_authenticated_partner_by_api_key(self): + result_partner = authenticated_partner_by_api_key(self.authorized_api_key) + self.assertEqual(result_partner, self.authorized_user.partner_id) + + def test_authenticated_env_by_auth_api_key(self): + result_env = authenticated_env_by_auth_api_key(self.authorized_api_key) + self.assertEqual(result_env.user, self.authorized_user) diff --git a/fastapi_auth_api_key/views/fastapi_endpoint.xml b/fastapi_auth_api_key/views/fastapi_endpoint.xml new file mode 100644 index 00000000..bd8ba2a9 --- /dev/null +++ b/fastapi_auth_api_key/views/fastapi_endpoint.xml @@ -0,0 +1,17 @@ + + + + + + fastapi.endpoint.form.inherit + fastapi.endpoint + + + + + + + + + From 8e344b89529de468bdf8b5d55821a7a4fd36bc2d Mon Sep 17 00:00:00 2001 From: sonhd91 Date: Fri, 24 May 2024 18:30:22 +0700 Subject: [PATCH 2/4] [IMP] fastapi_auth_api_key: pre-commit auto fixes --- fastapi_auth_api_key/README.rst | 121 ++++++++++++++++++ .../odoo/addons/fastapi_auth_api_key | 1 + setup/fastapi_auth_api_key/setup.py | 6 + 3 files changed, 128 insertions(+) create mode 120000 setup/fastapi_auth_api_key/odoo/addons/fastapi_auth_api_key create mode 100644 setup/fastapi_auth_api_key/setup.py diff --git a/fastapi_auth_api_key/README.rst b/fastapi_auth_api_key/README.rst index e69de29b..bd0a9aaf 100644 --- a/fastapi_auth_api_key/README.rst +++ b/fastapi_auth_api_key/README.rst @@ -0,0 +1,121 @@ +==================== +Fastapi Auth Api Key +==================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a1a8681b1c3e7a13dc83e2e61a1d78ad8c8da1ddb684c8cf563607e96cf4f7e7 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/licence-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/16.0/fastapi_auth_api_key + :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-16-0/rest-framework-16-0-fastapi_auth_api_key + :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=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Provides `FastAPI` dependencies for Api Key authentication. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Getting an odoo environment +=========================== + +If you need to get an odoo env based on the provided api key, you can use `authenticated_env_by_auth_api_key`. + +.. code-block:: python + + @router.get("/example_with_authenticated_env") + def example_with_authenticated_env( + env: Annotated[Environment, Depends(authenticated_env_by_auth_api_key)], + ) -> None: + # env.user is the user attached to the provided key + pass + +Getting the authenticated partner +================================= + +If want to get the partned related to the the provided api key, you can use `authenticated_partner_by_api_key` + +.. code-block:: python + + @router.get("/example_with_authenticated_partner") + def example_with_authenticated_partner( + partner: Annotated[Partner, Depends(authenticated_partner_by_api_key)], + ) -> None: + # partner is the partner related to the provided key key.user_id.partner_id + pass + +Configuration +============= + +For this to work, the api key must be defined on the `Endpoint`. +A new field `auth_api_key_group_id` has been added to the `Endpoint` model. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* Matthieu Méquignon + +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-mmequignon| image:: https://github.com/mmequignon.png?size=40px + :target: https://github.com/mmequignon + :alt: mmequignon + +Current `maintainer `__: + +|maintainer-mmequignon| + +This module is part of the `OCA/rest-framework `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/setup/fastapi_auth_api_key/odoo/addons/fastapi_auth_api_key b/setup/fastapi_auth_api_key/odoo/addons/fastapi_auth_api_key new file mode 120000 index 00000000..498f5ddb --- /dev/null +++ b/setup/fastapi_auth_api_key/odoo/addons/fastapi_auth_api_key @@ -0,0 +1 @@ +../../../../fastapi_auth_api_key \ No newline at end of file diff --git a/setup/fastapi_auth_api_key/setup.py b/setup/fastapi_auth_api_key/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/fastapi_auth_api_key/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From c46ba66ff3124f42e4d962b127913eda918ffaaf Mon Sep 17 00:00:00 2001 From: sonhd91 Date: Fri, 24 May 2024 18:23:29 +0700 Subject: [PATCH 3/4] [MIG] fastapi_auth_api_key: Migration to 17.0 --- fastapi_auth_api_key/README.rst | 71 +++++++++++-------- fastapi_auth_api_key/__manifest__.py | 2 +- fastapi_auth_api_key/dependencies.py | 4 +- fastapi_auth_api_key/pyproject.toml | 3 + fastapi_auth_api_key/readme/CONTRIBUTORS.md | 2 + fastapi_auth_api_key/readme/CONTRIBUTORS.rst | 1 - fastapi_auth_api_key/readme/CREDITS.md | 2 + fastapi_auth_api_key/readme/DESCRIPTION.md | 1 + fastapi_auth_api_key/readme/DESCRIPTION.rst | 1 - fastapi_auth_api_key/readme/USAGE.md | 32 +++++++++ fastapi_auth_api_key/readme/USAGE.rst | 33 --------- .../test_fastapi_api_key_dependencies.py | 22 ++++-- .../views/fastapi_endpoint.xml | 2 +- requirements.txt | 1 - 14 files changed, 101 insertions(+), 76 deletions(-) create mode 100644 fastapi_auth_api_key/pyproject.toml create mode 100644 fastapi_auth_api_key/readme/CONTRIBUTORS.md delete mode 100644 fastapi_auth_api_key/readme/CONTRIBUTORS.rst create mode 100644 fastapi_auth_api_key/readme/CREDITS.md create mode 100644 fastapi_auth_api_key/readme/DESCRIPTION.md delete mode 100644 fastapi_auth_api_key/readme/DESCRIPTION.rst create mode 100644 fastapi_auth_api_key/readme/USAGE.md delete mode 100644 fastapi_auth_api_key/readme/USAGE.rst delete mode 100644 requirements.txt diff --git a/fastapi_auth_api_key/README.rst b/fastapi_auth_api_key/README.rst index bd0a9aaf..f97f63c5 100644 --- a/fastapi_auth_api_key/README.rst +++ b/fastapi_auth_api_key/README.rst @@ -17,18 +17,18 @@ Fastapi Auth Api Key :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/16.0/fastapi_auth_api_key + :target: https://github.com/OCA/rest-framework/tree/17.0/fastapi_auth_api_key :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-16-0/rest-framework-16-0-fastapi_auth_api_key + :target: https://translation.odoo-community.org/projects/rest-framework-17-0/rest-framework-17-0-fastapi_auth_api_key :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=16.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=17.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| -Provides `FastAPI` dependencies for Api Key authentication. +Provides FastAPI dependencies for Api Key authentication. **Table of contents** @@ -39,38 +39,40 @@ Usage ===== Getting an odoo environment -=========================== +--------------------------- -If you need to get an odoo env based on the provided api key, you can use `authenticated_env_by_auth_api_key`. +If you need to get an odoo env based on the provided api key, you can +use authenticated_env_by_auth_api_key. -.. code-block:: python +.. code:: python - @router.get("/example_with_authenticated_env") - def example_with_authenticated_env( - env: Annotated[Environment, Depends(authenticated_env_by_auth_api_key)], - ) -> None: - # env.user is the user attached to the provided key - pass + @router.get("/example_with_authenticated_env") + def example_with_authenticated_env( + env: Annotated[Environment, Depends(authenticated_env_by_auth_api_key)], + ) -> None: + # env.user is the user attached to the provided key + pass Getting the authenticated partner -================================= +--------------------------------- -If want to get the partned related to the the provided api key, you can use `authenticated_partner_by_api_key` +If want to get the partned related to the the provided api key, you can +use authenticated_partner_by_api_key -.. code-block:: python +.. code:: python - @router.get("/example_with_authenticated_partner") - def example_with_authenticated_partner( - partner: Annotated[Partner, Depends(authenticated_partner_by_api_key)], - ) -> None: - # partner is the partner related to the provided key key.user_id.partner_id - pass + @router.get("/example_with_authenticated_partner") + def example_with_authenticated_partner( + partner: Annotated[Partner, Depends(authenticated_partner_by_api_key)], + ) -> None: + # partner is the partner related to the provided key key.user_id.partner_id + pass Configuration -============= +------------- -For this to work, the api key must be defined on the `Endpoint`. -A new field `auth_api_key_group_id` has been added to the `Endpoint` model. +For this to work, the api key must be defined on the Endpoint. A new +field auth_api_key_group_id has been added to the Endpoint model. Bug Tracker =========== @@ -78,7 +80,7 @@ Bug Tracker Bugs are tracked on `GitHub 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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -86,17 +88,24 @@ Credits ======= Authors -~~~~~~~ +------- * Camptocamp Contributors -~~~~~~~~~~~~ +------------ -* Matthieu Méquignon +- Matthieu Méquignon +- Son Ho + +Other credits +------------- + +The migration of this module from 16.0 to 17.0 was financially supported +by Camptocamp Maintainers -~~~~~~~~~~~ +----------- This module is maintained by the OCA. @@ -116,6 +125,6 @@ Current `maintainer `__: |maintainer-mmequignon| -This module is part of the `OCA/rest-framework `_ project on GitHub. +This module is part of the `OCA/rest-framework `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fastapi_auth_api_key/__manifest__.py b/fastapi_auth_api_key/__manifest__.py index 1735c59b..539efdcc 100644 --- a/fastapi_auth_api_key/__manifest__.py +++ b/fastapi_auth_api_key/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Fastapi Auth Api Key", - "version": "16.0.1.0.0", + "version": "17.0.1.0.0", "category": "Others", "website": "https://github.com/OCA/rest-framework", "author": "Camptocamp, Odoo Community Association (OCA)", diff --git a/fastapi_auth_api_key/dependencies.py b/fastapi_auth_api_key/dependencies.py index 3b71e6d5..aa1838ab 100644 --- a/fastapi_auth_api_key/dependencies.py +++ b/fastapi_auth_api_key/dependencies.py @@ -1,7 +1,7 @@ # Copyright 2024 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from typing_extensions import Annotated +from typing import Annotated from odoo import SUPERUSER_ID from odoo.api import Environment @@ -34,7 +34,7 @@ def authenticated_auth_api_key( except ValidationError as error: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, - detail=error.name, + detail=error.args, headers={"WWW-Authenticate": "HTTP-API-KEY"}, ) from error # Ensure the api key is authorized for the current endpoint. diff --git a/fastapi_auth_api_key/pyproject.toml b/fastapi_auth_api_key/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/fastapi_auth_api_key/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fastapi_auth_api_key/readme/CONTRIBUTORS.md b/fastapi_auth_api_key/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..a6f2fed3 --- /dev/null +++ b/fastapi_auth_api_key/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Matthieu Méquignon \<\> +- Son Ho \<\> diff --git a/fastapi_auth_api_key/readme/CONTRIBUTORS.rst b/fastapi_auth_api_key/readme/CONTRIBUTORS.rst deleted file mode 100644 index bca4ee0c..00000000 --- a/fastapi_auth_api_key/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1 +0,0 @@ -* Matthieu Méquignon diff --git a/fastapi_auth_api_key/readme/CREDITS.md b/fastapi_auth_api_key/readme/CREDITS.md new file mode 100644 index 00000000..7c48f8e0 --- /dev/null +++ b/fastapi_auth_api_key/readme/CREDITS.md @@ -0,0 +1,2 @@ +The migration of this module from 16.0 to 17.0 was financially supported +by Camptocamp diff --git a/fastapi_auth_api_key/readme/DESCRIPTION.md b/fastapi_auth_api_key/readme/DESCRIPTION.md new file mode 100644 index 00000000..1e9aa691 --- /dev/null +++ b/fastapi_auth_api_key/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Provides FastAPI dependencies for Api Key authentication. diff --git a/fastapi_auth_api_key/readme/DESCRIPTION.rst b/fastapi_auth_api_key/readme/DESCRIPTION.rst deleted file mode 100644 index 22e772df..00000000 --- a/fastapi_auth_api_key/readme/DESCRIPTION.rst +++ /dev/null @@ -1 +0,0 @@ -Provides `FastAPI` dependencies for Api Key authentication. diff --git a/fastapi_auth_api_key/readme/USAGE.md b/fastapi_auth_api_key/readme/USAGE.md new file mode 100644 index 00000000..f60799c4 --- /dev/null +++ b/fastapi_auth_api_key/readme/USAGE.md @@ -0,0 +1,32 @@ +## Getting an odoo environment + +If you need to get an odoo env based on the provided api key, you can +use authenticated_env_by_auth_api_key. + +``` python +@router.get("/example_with_authenticated_env") +def example_with_authenticated_env( + env: Annotated[Environment, Depends(authenticated_env_by_auth_api_key)], +) -> None: + # env.user is the user attached to the provided key + pass +``` + +## Getting the authenticated partner + +If want to get the partned related to the the provided api key, you can +use authenticated_partner_by_api_key + +``` python +@router.get("/example_with_authenticated_partner") +def example_with_authenticated_partner( + partner: Annotated[Partner, Depends(authenticated_partner_by_api_key)], +) -> None: + # partner is the partner related to the provided key key.user_id.partner_id + pass +``` + +## Configuration + +For this to work, the api key must be defined on the Endpoint. A new +field auth_api_key_group_id has been added to the Endpoint model. diff --git a/fastapi_auth_api_key/readme/USAGE.rst b/fastapi_auth_api_key/readme/USAGE.rst deleted file mode 100644 index 356cb02d..00000000 --- a/fastapi_auth_api_key/readme/USAGE.rst +++ /dev/null @@ -1,33 +0,0 @@ -Getting an odoo environment -=========================== - -If you need to get an odoo env based on the provided api key, you can use `authenticated_env_by_auth_api_key`. - -.. code-block:: python - - @router.get("/example_with_authenticated_env") - def example_with_authenticated_env( - env: Annotated[Environment, Depends(authenticated_env_by_auth_api_key)], - ) -> None: - # env.user is the user attached to the provided key - pass - -Getting the authenticated partner -================================= - -If want to get the partned related to the the provided api key, you can use `authenticated_partner_by_api_key` - -.. code-block:: python - - @router.get("/example_with_authenticated_partner") - def example_with_authenticated_partner( - partner: Annotated[Partner, Depends(authenticated_partner_by_api_key)], - ) -> None: - # partner is the partner related to the provided key key.user_id.partner_id - pass - -Configuration -============= - -For this to work, the api key must be defined on the `Endpoint`. -A new field `auth_api_key_group_id` has been added to the `Endpoint` model. diff --git a/fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py b/fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py index d746e1cc..dde6088c 100644 --- a/fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py +++ b/fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py @@ -17,8 +17,17 @@ def setUpClass(cls): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) # Is it valid? We need an env without superpowers - demo_user = cls.env.ref("base.partner_demo") + demo_user = cls.env.ref("base.user_demo") cls.demo_env = demo_user.with_user(demo_user).env + cls.demo_endpoint = cls.env["fastapi.endpoint"].create( + { + "name": "Test Enpoint", + "app": "demo", + "root_path": "/path_demo", + "demo_auth_method": "api_key", + "user_id": demo_user.id, + } + ) cls.setUpClassApiKey() @classmethod @@ -63,19 +72,22 @@ def setUpClassApiKey(cls): def test_authenticated_auth_api_key(self): # An exception is raised when no api key is used with self.assertRaises(HTTPException) as error: - authenticated_auth_api_key(False, self.demo_env) + authenticated_auth_api_key(False, self.demo_env, self.demo_endpoint) self.assertEqual(error.exception.detail, "No HTTP-API-KEY provided") # An exception is raised when no api key record is found with self.assertRaises(HTTPException) as error: - authenticated_auth_api_key("404", self.demo_env) - self.assertEqual(error.exception.detail, "The key 404 is not allowed") + authenticated_auth_api_key("404", self.demo_env, self.demo_endpoint) + self.assertEqual(error.exception.detail, ("The key 404 is not allowed",)) # TODO enable this when we know how to filter keys based # on endpoint's api key group. # An exception is raised when unauthorized api key record is found # with self.assertRaises(HTTPException) as error: # authenticated_auth_api_key("not_authorized", self.demo_env) + self.demo_endpoint.auth_api_key_group_id = ( + self.authorized_api_key.auth_api_key_group_ids[0] + ) result_key = authenticated_auth_api_key( - self.authorized_api_key.key, self.demo_env + self.authorized_api_key.key, self.demo_env, self.demo_endpoint ) self.assertEqual(result_key, self.authorized_api_key) diff --git a/fastapi_auth_api_key/views/fastapi_endpoint.xml b/fastapi_auth_api_key/views/fastapi_endpoint.xml index bd8ba2a9..1005dd50 100644 --- a/fastapi_auth_api_key/views/fastapi_endpoint.xml +++ b/fastapi_auth_api_key/views/fastapi_endpoint.xml @@ -8,7 +8,7 @@ fastapi.endpoint - + diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 9cd16292..00000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -# generated from manifests external_dependencies From 06665a24a03b1b9fcc441cef20d65d856670ba58 Mon Sep 17 00:00:00 2001 From: sonhd91 Date: Tue, 4 Jun 2024 17:23:24 +0700 Subject: [PATCH 4/4] [DON'T MERGE] test-requirements.txt --- test-requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-requirements.txt b/test-requirements.txt index f7621d17..cd159839 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1,3 @@ httpx +odoo-addon-fastapi @ git+https://github.com/OCA/rest-framework.git@refs/pull/409/head#subdirectory=fastapi +odoo-addon-auth_api_key_group @ git+https://github.com/OCA/server-auth.git@refs/pull/654/head#subdirectory=auth_api_key_group