Skip to content

Commit 0d7e53c

Browse files
committed
[REF] fastapi_auth_api_key: review improvements
1 parent edbe2ab commit 0d7e53c

5 files changed

Lines changed: 47 additions & 27 deletions

File tree

fastapi_auth_api_key/README.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
====================
2-
Fastapi Auth Api Key
2+
Fastapi Auth API Key
33
====================
44

55
..
@@ -95,8 +95,8 @@ Authors
9595
Contributors
9696
------------
9797

98-
- Matthieu Méquignon <matthieu.mequignon@camptocamp.com>
99-
- Son Ho <sonhd@trobz.com>
98+
- Matthieu Méquignon <matthieu.mequignon@camptocamp.com>
99+
- Son Ho <sonhd@trobz.com>
100100

101101
Other credits
102102
-------------

fastapi_auth_api_key/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
33

44
{
5-
"name": "Fastapi Auth Api Key",
5+
"name": "Fastapi Auth API Key",
66
"version": "17.0.1.0.0",
77
"category": "Others",
88
"website": "https://github.com/OCA/rest-framework",

fastapi_auth_api_key/dependencies.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Copyright 2024 Camptocamp SA
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3-
3+
import os
44
from typing import Annotated
55

6-
from odoo import SUPERUSER_ID
6+
from odoo import SUPERUSER_ID, _
77
from odoo.api import Environment
88
from odoo.exceptions import ValidationError
99

@@ -16,17 +16,20 @@
1616
from fastapi.exceptions import HTTPException
1717
from fastapi.security import APIKeyHeader
1818

19+
HTTP_API_KEY_HEADER = os.environ.get("FASTAPI_AUTH_HTTP_API_KEY_HEADER", "HTTP-API-KEY")
20+
1921

2022
def authenticated_auth_api_key(
21-
key: Annotated[str, Depends(APIKeyHeader(name="HTTP-API-KEY"))],
23+
key: Annotated[str, Depends(APIKeyHeader(name=HTTP_API_KEY_HEADER))],
2224
env: Annotated[Environment, Depends(odoo_env)],
2325
endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)],
2426
) -> AuthApiKey:
2527
if not key:
2628
raise HTTPException(
2729
status_code=status.HTTP_401_UNAUTHORIZED,
28-
detail="No HTTP-API-KEY provided",
29-
headers={"WWW-Authenticate": "HTTP-API-KEY"},
30+
detail=_("Missing %(HTTP_API_KEY_HEADER)s header")
31+
% {"HTTP_API_KEY_HEADER": HTTP_API_KEY_HEADER},
32+
headers={"WWW-Authenticate": HTTP_API_KEY_HEADER},
3033
)
3134
admin_env = Environment(env.cr, SUPERUSER_ID, {})
3235
try:
@@ -35,14 +38,17 @@ def authenticated_auth_api_key(
3538
raise HTTPException(
3639
status_code=status.HTTP_401_UNAUTHORIZED,
3740
detail=error.args,
38-
headers={"WWW-Authenticate": "HTTP-API-KEY"},
41+
headers={"WWW-Authenticate": HTTP_API_KEY_HEADER},
3942
) from error
4043
# Ensure the api key is authorized for the current endpoint.
41-
if auth_api_key not in endpoint.sudo().auth_api_key_group_id.auth_api_key_ids:
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+
):
4248
raise HTTPException(
4349
status_code=status.HTTP_401_UNAUTHORIZED,
44-
detail="Unauthorized",
45-
headers={"WWW-Authenticate": "HTTP-API-KEY"},
50+
detail=_("Unauthorized"),
51+
headers={"WWW-Authenticate": HTTP_API_KEY_HEADER},
4652
)
4753
return auth_api_key
4854

fastapi_auth_api_key/models/fastapi_endpoint.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@
77
class FastapiEndpoint(models.Model):
88
_inherit = "fastapi.endpoint"
99

10-
auth_api_key_group_id = fields.Many2one("auth.api.key.group")
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+
)

fastapi_auth_api_key/tests/test_fastapi_api_key_dependencies.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
from odoo.tests import tagged
22
from odoo.tests.common import TransactionCase
33

4-
from fastapi.exceptions import HTTPException
5-
6-
from ..dependencies import (
4+
from odoo.addons.fastapi_auth_api_key.dependencies import (
75
authenticated_auth_api_key,
86
authenticated_env_by_auth_api_key,
97
authenticated_partner_by_api_key,
108
)
119

10+
from fastapi.exceptions import HTTPException
11+
1212

1313
@tagged("-at_install", "post_install")
1414
class TestFastapiAuthApiKey(TransactionCase):
1515
@classmethod
1616
def setUpClass(cls):
1717
super().setUpClass()
1818
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
19-
# Is it valid? We need an env without superpowers
2019
demo_user = cls.env.ref("base.user_demo")
2120
cls.demo_env = demo_user.with_user(demo_user).env
2221
cls.demo_endpoint = cls.env["fastapi.endpoint"].create(
@@ -68,29 +67,41 @@ def setUpClassApiKey(cls):
6867
"auth_api_key_ids": [(6, 0, cls.authorized_api_key.ids)],
6968
}
7069
)
70+
cls.demo_endpoint.auth_api_key_group_id = cls.authorized_api_key_group
7171

72-
def test_authenticated_auth_api_key(self):
72+
def test_authenticated_auth_api_key_no_api_key(self):
7373
# An exception is raised when no api key is used
7474
with self.assertRaises(HTTPException) as error:
7575
authenticated_auth_api_key(False, self.demo_env, self.demo_endpoint)
7676
self.assertEqual(error.exception.detail, "No HTTP-API-KEY provided")
77+
78+
def test_authenticated_auth_api_key_no_api_key_found(self):
7779
# An exception is raised when no api key record is found
7880
with self.assertRaises(HTTPException) as error:
7981
authenticated_auth_api_key("404", self.demo_env, self.demo_endpoint)
8082
self.assertEqual(error.exception.detail, ("The key 404 is not allowed",))
81-
# TODO enable this when we know how to filter keys based
82-
# on endpoint's api key group.
83+
84+
def test_authenticated_auth_api_key_unauthorized_key(self):
8385
# An exception is raised when unauthorized api key record is found
84-
# with self.assertRaises(HTTPException) as error:
85-
# authenticated_auth_api_key("not_authorized", self.demo_env)
86-
self.demo_endpoint.auth_api_key_group_id = (
87-
self.authorized_api_key.auth_api_key_group_ids[0]
88-
)
86+
with self.assertRaisesRegex(HTTPException, r"Unauthorized"):
87+
authenticated_auth_api_key(
88+
"unauthorized_key", self.demo_env, self.demo_endpoint
89+
)
90+
91+
def test_authenticated_auth_api_key(self):
8992
result_key = authenticated_auth_api_key(
90-
self.authorized_api_key.key, self.demo_env, self.demo_endpoint
93+
"authorized_key", self.demo_env, self.demo_endpoint
9194
)
9295
self.assertEqual(result_key, self.authorized_api_key)
9396

97+
def test_authenticated_auth_api_key_without_group(self):
98+
# test with no group set unauthorized is allow to access
99+
self.demo_endpoint.auth_api_key_group_id = False
100+
result_key = authenticated_auth_api_key(
101+
"unauthorized_key", self.demo_env, self.demo_endpoint
102+
)
103+
self.assertEqual(result_key, self.unauthorized_api_key)
104+
94105
def test_authenticated_partner_by_api_key(self):
95106
result_partner = authenticated_partner_by_api_key(self.authorized_api_key)
96107
self.assertEqual(result_partner, self.authorized_user.partner_id)

0 commit comments

Comments
 (0)