Skip to content

Commit da414c9

Browse files
committed
Merge PR #568 into 18.0
Signed-off-by etobella
2 parents 34c06aa + a5bae2b commit da414c9

17 files changed

Lines changed: 770 additions & 2 deletions

fastapi/error_handlers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def convert_exception_to_status_body(exc: Exception) -> tuple[int, dict]:
2929
status_code = exc.status_code
3030
details = exc.detail
3131
elif isinstance(exc, RequestValidationError):
32-
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
32+
status_code = status.HTTP_422_UNPROCESSABLE_CONTENT
3333
details = jsonable_encoder(exc.errors())
3434
elif isinstance(exc, WebSocketRequestValidationError):
3535
status_code = status.WS_1008_POLICY_VIOLATION

fastapi/tests/test_fastapi.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ def test_request_validation_error(self) -> None:
156156
route = "/fastapi_demo/demo/exception?exception_type=BAD&error_message="
157157
response = self.url_open(route, timeout=200)
158158
mocked_commit.assert_not_called()
159-
self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY)
159+
self.assertEqual(
160+
response.status_code, status.HTTP_422_UNPROCESSABLE_CONTENT
161+
)
160162

161163
def test_no_commit_on_exception(self) -> None:
162164
# this test check that the way we mock the cursor is working as expected

fastapi_auth_api_key/README.rst

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
====================
2+
Fastapi Auth API Key
3+
====================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:a1a8681b1c3e7a13dc83e2e61a1d78ad8c8da1ddb684c8cf563607e96cf4f7e7
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github
20+
:target: https://github.com/OCA/rest-framework/tree/18.0/fastapi_auth_api_key
21+
:alt: OCA/rest-framework
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/rest-framework-18-0/rest-framework-18-0-fastapi_auth_api_key
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
Provides FastAPI dependencies for Api Key authentication.
32+
33+
**Table of contents**
34+
35+
.. contents::
36+
:local:
37+
38+
Usage
39+
=====
40+
41+
Getting an odoo environment
42+
---------------------------
43+
44+
If you need to get an odoo env based on the provided api key, you can
45+
use authenticated_env_by_auth_api_key.
46+
47+
.. code:: python
48+
49+
@router.get("/example_with_authenticated_env")
50+
def example_with_authenticated_env(
51+
env: Annotated[Environment, Depends(authenticated_env_by_auth_api_key)],
52+
) -> None:
53+
# env.user is the user attached to the provided key
54+
pass
55+
56+
Getting the authenticated partner
57+
---------------------------------
58+
59+
If want to get the partned related to the the provided api key, you can
60+
use authenticated_partner_by_api_key
61+
62+
.. code:: python
63+
64+
@router.get("/example_with_authenticated_partner")
65+
def example_with_authenticated_partner(
66+
partner: Annotated[Partner, Depends(authenticated_partner_by_api_key)],
67+
) -> None:
68+
# partner is the partner related to the provided key key.user_id.partner_id
69+
pass
70+
71+
Configuration
72+
-------------
73+
74+
For this to work, the api key must be defined on the Endpoint. A new
75+
field auth_api_key_group_id has been added to the Endpoint model.
76+
77+
Bug Tracker
78+
===========
79+
80+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/rest-framework/issues>`_.
81+
In case of trouble, please check there if your issue has already been reported.
82+
If you spotted it first, help us to smash it by providing a detailed and welcomed
83+
`feedback <https://github.com/OCA/rest-framework/issues/new?body=module:%20fastapi_auth_api_key%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
84+
85+
Do not contact contributors directly about support or help with technical issues.
86+
87+
Credits
88+
=======
89+
90+
Authors
91+
-------
92+
93+
* Camptocamp
94+
95+
Contributors
96+
------------
97+
98+
- Matthieu Méquignon <matthieu.mequignon@camptocamp.com>
99+
- Son Ho <sonhd@trobz.com>
100+
101+
Other credits
102+
-------------
103+
104+
The migration of this module from 17.0 to 18.0 was financially supported
105+
by Camptocamp
106+
107+
Maintainers
108+
-----------
109+
110+
This module is maintained by the OCA.
111+
112+
.. image:: https://odoo-community.org/logo.png
113+
:alt: Odoo Community Association
114+
:target: https://odoo-community.org
115+
116+
OCA, or the Odoo Community Association, is a nonprofit organization whose
117+
mission is to support the collaborative development of Odoo features and
118+
promote its widespread use.
119+
120+
.. |maintainer-mmequignon| image:: https://github.com/mmequignon.png?size=40px
121+
:target: https://github.com/mmequignon
122+
:alt: mmequignon
123+
124+
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
125+
126+
|maintainer-mmequignon|
127+
128+
This module is part of the `OCA/rest-framework <https://github.com/OCA/rest-framework/tree/18.0/fastapi_auth_api_key>`_ project on GitHub.
129+
130+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

fastapi_auth_api_key/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2024 Camptocamp SA
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3+
4+
{
5+
"name": "Fastapi Auth API Key",
6+
"version": "18.0.1.0.0",
7+
"category": "Others",
8+
"website": "https://github.com/OCA/rest-framework",
9+
"author": "Camptocamp, Odoo Community Association (OCA)",
10+
"maintainers": ["mmequignon"],
11+
"license": "AGPL-3",
12+
"installable": True,
13+
"depends": [
14+
"fastapi",
15+
"auth_api_key_group",
16+
],
17+
"data": [
18+
"views/fastapi_endpoint.xml",
19+
],
20+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Copyright 2024 Camptocamp SA
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3+
import os
4+
from typing import Annotated
5+
6+
from odoo import SUPERUSER_ID
7+
from odoo.api import Environment
8+
from odoo.exceptions import ValidationError
9+
10+
from odoo.addons.auth_api_key.models.auth_api_key import AuthApiKey
11+
from odoo.addons.base.models.res_partner import Partner
12+
from odoo.addons.fastapi.dependencies import fastapi_endpoint, odoo_env
13+
from odoo.addons.fastapi.models.fastapi_endpoint import FastapiEndpoint
14+
15+
from fastapi import Depends, status
16+
from fastapi.exceptions import HTTPException
17+
from fastapi.security import APIKeyHeader
18+
19+
HTTP_API_KEY_HEADER = os.environ.get("FASTAPI_AUTH_HTTP_API_KEY_HEADER", "HTTP-API-KEY")
20+
21+
22+
def authenticated_auth_api_key(
23+
key: Annotated[str, Depends(APIKeyHeader(name=HTTP_API_KEY_HEADER))],
24+
env: Annotated[Environment, Depends(odoo_env)],
25+
endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)],
26+
) -> AuthApiKey:
27+
if not key:
28+
raise HTTPException(
29+
status_code=status.HTTP_401_UNAUTHORIZED,
30+
detail=env._("Missing %(HTTP_API_KEY_HEADER)s header")
31+
% {"HTTP_API_KEY_HEADER": HTTP_API_KEY_HEADER},
32+
headers={"WWW-Authenticate": HTTP_API_KEY_HEADER},
33+
)
34+
admin_env = Environment(env.cr, SUPERUSER_ID, {})
35+
try:
36+
auth_api_key = admin_env["auth.api.key"]._retrieve_api_key(key)
37+
except ValidationError as error:
38+
raise HTTPException(
39+
status_code=status.HTTP_401_UNAUTHORIZED,
40+
detail=error.args,
41+
headers={"WWW-Authenticate": HTTP_API_KEY_HEADER},
42+
) from error
43+
# Ensure the api key is authorized for the current endpoint.
44+
if (
45+
endpoint.sudo().auth_api_key_group_id
46+
and auth_api_key not in endpoint.sudo().auth_api_key_group_id.auth_api_key_ids
47+
):
48+
raise HTTPException(
49+
status_code=status.HTTP_401_UNAUTHORIZED,
50+
detail=env._("Unauthorized"),
51+
headers={"WWW-Authenticate": HTTP_API_KEY_HEADER},
52+
)
53+
return auth_api_key
54+
55+
56+
def authenticated_partner_by_api_key(
57+
auth_api_key: Annotated[AuthApiKey, Depends(authenticated_auth_api_key)],
58+
) -> Partner:
59+
return auth_api_key.user_id.partner_id
60+
61+
62+
def authenticated_env_by_auth_api_key(
63+
auth_api_key: Annotated[AuthApiKey, Depends(authenticated_auth_api_key)],
64+
) -> Environment:
65+
# set api key id in context
66+
return auth_api_key.with_user(auth_api_key.user_id).env
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import fastapi_endpoint
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2024 Camptocamp SA
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3+
4+
from odoo import fields, models
5+
6+
7+
class FastapiEndpoint(models.Model):
8+
_inherit = "fastapi.endpoint"
9+
10+
auth_api_key_group_id = fields.Many2one(
11+
"auth.api.key.group",
12+
help="If not set, all 'auth.api.key' are allowed to access to endpoints",
13+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Matthieu Méquignon \<<matthieu.mequignon@camptocamp.com>\>
2+
- Son Ho \<<sonhd@trobz.com>\>

0 commit comments

Comments
 (0)