Skip to content

Commit 554bb0d

Browse files
committed
🐛 AuthenticationMiddleware continue execution on 404 or 405
1 parent d47e38f commit 554bb0d

File tree

4 files changed

+46
-22
lines changed

4 files changed

+46
-22
lines changed

flama/authentication/middleware.py

+10-9
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
import logging
33
import typing as t
44

5-
from flama import authentication
5+
from flama import authentication, exceptions
66
from flama.exceptions import HTTPException
77
from flama.http import APIErrorResponse, Request
88

99
if t.TYPE_CHECKING:
1010
from flama import Flama, types
1111
from flama.http import Response
12-
from flama.routing import BaseRoute
1312

1413
__all__ = ["AuthenticationMiddleware"]
1514

@@ -30,17 +29,19 @@ async def __call__(self, scope: "types.Scope", receive: "types.Receive", send: "
3029

3130
await response(scope, receive, send)
3231

33-
def _get_permissions(self, route: "BaseRoute") -> set[str]:
34-
return set(route.tags.get("permissions", []))
32+
def _get_permissions(self, app: "Flama", scope: "types.Scope") -> set[str]:
33+
try:
34+
route, _ = app.router.resolve_route(scope)
35+
permissions = set(route.tags.get("permissions", []))
36+
except (exceptions.MethodNotAllowedException, exceptions.NotFoundException):
37+
permissions = []
38+
39+
return set(permissions)
3540

3641
async def _get_response(self, scope: "types.Scope", receive: "types.Receive") -> t.Union["Response", "Flama"]:
3742
app: Flama = scope["app"]
3843

39-
route, _ = app.router.resolve_route(scope)
40-
41-
required_permissions = self._get_permissions(route)
42-
43-
if not required_permissions:
44+
if not (required_permissions := self._get_permissions(app, scope)):
4445
return self.app
4546

4647
try:

flama/routing/router.py

+2
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ def resolve_route(self, scope: types.Scope) -> tuple[BaseRoute, types.Scope]:
255255
"""Look for a route that matches given ASGI scope.
256256
257257
:param scope: ASGI scope.
258+
:raise MethodNotAllowedException: If route is resolved but http method is not valid.
259+
:raise NotFoundException: If route cannot be resolved.
258260
:return: Route and its scope.
259261
"""
260262
partial = None

tests/authentication/test_middleware.py

+33-12
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,24 @@ def cookies(self, request):
6262
raise ValueError(f"Invalid token {request.param}")
6363

6464
@pytest.mark.parametrize(
65-
["path", "headers", "cookies", "status_code", "result"],
65+
["path", "method", "headers", "cookies", "status_code", "result"],
6666
(
67-
pytest.param("/auth/", "permission", None, 200, {"foo": "auth"}, id="auth_header_token_permission"),
68-
pytest.param("/auth/", "role", None, 200, {"foo": "auth"}, id="auth_header_token_role"),
67+
pytest.param("/auth/", "get", "permission", None, 200, {"foo": "auth"}, id="auth_header_token_permission"),
68+
pytest.param("/auth/", "get", "role", None, 200, {"foo": "auth"}, id="auth_header_token_role"),
6969
pytest.param(
7070
"/auth/",
71+
"get",
7172
"empty",
7273
None,
7374
403,
7475
{"detail": "Insufficient permissions", "error": None, "status_code": 403},
7576
id="auth_header_token_empty",
7677
),
77-
pytest.param("/auth/", None, "permission", 200, {"foo": "auth"}, id="auth_cookie_token_permission"),
78-
pytest.param("/auth/", None, "role", 200, {"foo": "auth"}, id="auth_cookie_token_role"),
78+
pytest.param("/auth/", "get", None, "permission", 200, {"foo": "auth"}, id="auth_cookie_token_permission"),
79+
pytest.param("/auth/", "get", None, "role", 200, {"foo": "auth"}, id="auth_cookie_token_role"),
7980
pytest.param(
8081
"/auth/",
82+
"get",
8183
None,
8284
"empty",
8385
403,
@@ -86,28 +88,47 @@ def cookies(self, request):
8688
),
8789
pytest.param(
8890
"/auth/",
91+
"get",
8992
None,
9093
None,
9194
401,
9295
{"detail": "Unauthorized", "error": None, "status_code": 401},
9396
id="auth_no_token",
9497
),
9598
pytest.param(
96-
"/no-auth/", "permission", None, 200, {"foo": "no-auth"}, id="no_auth_header_token_permission"
99+
"/no-auth/", "get", "permission", None, 200, {"foo": "no-auth"}, id="no_auth_header_token_permission"
97100
),
98-
pytest.param("/no-auth/", "role", None, 200, {"foo": "no-auth"}, id="no_auth_header_token_role"),
101+
pytest.param("/no-auth/", "get", "role", None, 200, {"foo": "no-auth"}, id="no_auth_header_token_role"),
99102
pytest.param(
100-
"/no-auth/", None, "permission", 200, {"foo": "no-auth"}, id="no_auth_cookie_token_permission"
103+
"/no-auth/", "get", None, "permission", 200, {"foo": "no-auth"}, id="no_auth_cookie_token_permission"
104+
),
105+
pytest.param("/no-auth/", "get", None, "role", 200, {"foo": "no-auth"}, id="no_auth_cookie_token_role"),
106+
pytest.param("/no-auth/", "get", None, None, 200, {"foo": "no-auth"}, id="no_auth_no_token"),
107+
pytest.param(
108+
"/not-found/",
109+
"get",
110+
None,
111+
None,
112+
404,
113+
{"detail": "Not Found", "error": "HTTPException", "status_code": 404},
114+
id="not_found",
115+
),
116+
pytest.param(
117+
"/auth/",
118+
"post",
119+
None,
120+
None,
121+
405,
122+
{"detail": "Method Not Allowed", "error": "HTTPException", "status_code": 405},
123+
id="method_not_allowed",
101124
),
102-
pytest.param("/no-auth/", None, "role", 200, {"foo": "no-auth"}, id="no_auth_cookie_token_role"),
103-
pytest.param("/no-auth/", None, None, 200, {"foo": "no-auth"}, id="no_auth_no_token"),
104125
),
105126
indirect=["headers", "cookies"],
106127
)
107-
async def test_request(self, client, path, headers, cookies, status_code, result):
128+
async def test_request(self, client, path, method, headers, cookies, status_code, result):
108129
client.headers = headers
109130
client.cookies = cookies
110-
response = await client.request("get", path)
131+
response = await client.request(method, path)
111132

112133
assert response.status_code == status_code
113134
assert response.json() == result

uv.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)