Fixed bug in auth handler.
This commit is contained in:
parent
4367fb0937
commit
367d4a1aeb
2 changed files with 74 additions and 43 deletions
|
|
@ -24,6 +24,8 @@ LOG: logging.Logger = logging.getLogger("http_api")
|
||||||
|
|
||||||
|
|
||||||
class HttpAPI:
|
class HttpAPI:
|
||||||
|
di_context: dict[str, Any] = {}
|
||||||
|
|
||||||
def __init__(self, root_path: Path, queue: DownloadQueue):
|
def __init__(self, root_path: Path, queue: DownloadQueue):
|
||||||
self.queue: DownloadQueue = queue or DownloadQueue.get_instance()
|
self.queue: DownloadQueue = queue or DownloadQueue.get_instance()
|
||||||
self.encoder: Encoder = Encoder()
|
self.encoder: Encoder = Encoder()
|
||||||
|
|
@ -33,6 +35,16 @@ class HttpAPI:
|
||||||
self.rootPath: Path = root_path
|
self.rootPath: Path = root_path
|
||||||
self.cache = Cache()
|
self.cache = Cache()
|
||||||
self.app: web.Application | None = None
|
self.app: web.Application | None = None
|
||||||
|
self.di_context: dict[str, Any] = {
|
||||||
|
"queue": self.queue,
|
||||||
|
"encoder": self.encoder,
|
||||||
|
"config": self.config,
|
||||||
|
"notify": self._notify,
|
||||||
|
"cache": self.cache,
|
||||||
|
"app": self.app,
|
||||||
|
"http_api": self,
|
||||||
|
"root_path": self.rootPath,
|
||||||
|
}
|
||||||
|
|
||||||
async def on_shutdown(self, _: web.Application):
|
async def on_shutdown(self, _: web.Application):
|
||||||
pass
|
pass
|
||||||
|
|
@ -55,7 +67,6 @@ class HttpAPI:
|
||||||
app=app,
|
app=app,
|
||||||
base_path=self.config.base_path.rstrip("/"),
|
base_path=self.config.base_path.rstrip("/"),
|
||||||
download_path=self.config.download_path,
|
download_path=self.config.download_path,
|
||||||
this=self,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -103,6 +114,12 @@ class HttpAPI:
|
||||||
async def options_handler(_: Request) -> Response:
|
async def options_handler(_: Request) -> Response:
|
||||||
return web.Response(status=204)
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
def _handle(handler):
|
||||||
|
async def wrapped(request):
|
||||||
|
return await self._handle(handler, request)
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
|
||||||
for route in get_routes(RouteType.HTTP).values():
|
for route in get_routes(RouteType.HTTP).values():
|
||||||
routePath: str = f"/{route.path.lstrip('/')}"
|
routePath: str = f"/{route.path.lstrip('/')}"
|
||||||
|
|
||||||
|
|
@ -113,7 +130,7 @@ class HttpAPI:
|
||||||
|
|
||||||
LOG.debug(f"Add ({route.name}) {route.method}: {route.path}.")
|
LOG.debug(f"Add ({route.name}) {route.method}: {route.path}.")
|
||||||
|
|
||||||
app.router.add_route(route.method, route.path, handler=route.handler, name=route.name)
|
app.router.add_route(route.method, route.path, handler=_handle(route.handler), name=route.name)
|
||||||
|
|
||||||
if route.path in registered_options:
|
if route.path in registered_options:
|
||||||
continue
|
continue
|
||||||
|
|
@ -131,6 +148,7 @@ class HttpAPI:
|
||||||
Args:
|
Args:
|
||||||
username (str): The username.
|
username (str): The username.
|
||||||
password (str): The password.
|
password (str): The password.
|
||||||
|
this (HttpAPI): The instance of the HttpAPI.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Awaitable: The middleware handler.
|
Awaitable: The middleware handler.
|
||||||
|
|
@ -138,9 +156,9 @@ class HttpAPI:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@web.middleware
|
@web.middleware
|
||||||
async def middleware_handler(request: Request, handler: RequestHandler) -> Response:
|
async def auth_handler(request: Request, handler: RequestHandler) -> Response:
|
||||||
# if OPTIONS request, skip auth
|
# if OPTIONS request, skip auth
|
||||||
if request.method == "OPTIONS":
|
if "OPTIONS" == request.method:
|
||||||
return await handler(request)
|
return await handler(request)
|
||||||
|
|
||||||
auth_header = request.headers.get("Authorization")
|
auth_header = request.headers.get("Authorization")
|
||||||
|
|
@ -183,10 +201,15 @@ class HttpAPI:
|
||||||
|
|
||||||
if not (user_match and pass_match):
|
if not (user_match and pass_match):
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
data={"error": "Unauthorized (Invalid credentials)."}, status=web.HTTPUnauthorized.status_code
|
data={"error": "Unauthorized (Invalid credentials)."},
|
||||||
|
status=web.HTTPUnauthorized.status_code,
|
||||||
|
headers={
|
||||||
|
"WWW-Authenticate": 'Basic realm="Authorization Required."',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
response = await handler(request)
|
response = await handler(request)
|
||||||
|
|
||||||
if request.path != "/":
|
if request.path != "/":
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
@ -207,10 +230,10 @@ class HttpAPI:
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
return middleware_handler
|
return auth_handler
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def middle_wares(app: web.Application, base_path: str, download_path: str, this: "HttpAPI") -> Awaitable:
|
def middle_wares(app: web.Application, base_path: str, download_path: str) -> Awaitable:
|
||||||
@web.middleware
|
@web.middleware
|
||||||
async def middleware_handler(request: Request, handler: RequestHandler) -> Response:
|
async def middleware_handler(request: Request, handler: RequestHandler) -> Response:
|
||||||
static_path = str(app.router["download_static"].url_for(filename=""))
|
static_path = str(app.router["download_static"].url_for(filename=""))
|
||||||
|
|
@ -231,41 +254,7 @@ class HttpAPI:
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
kwargs: dict[str, Any] = {
|
response = await handler(request)
|
||||||
"request": request,
|
|
||||||
"queue": this.queue,
|
|
||||||
"encoder": this.encoder,
|
|
||||||
"config": this.config,
|
|
||||||
"notify": this._notify,
|
|
||||||
"cache": this.cache,
|
|
||||||
"app": app,
|
|
||||||
"http_api": this,
|
|
||||||
"root_path": this.rootPath,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
sig = inspect.signature(handler)
|
|
||||||
expected_args = sig.parameters.keys()
|
|
||||||
|
|
||||||
try:
|
|
||||||
if 1 == len(expected_args) and "request" in expected_args:
|
|
||||||
response = await handler(request)
|
|
||||||
else:
|
|
||||||
filtered = {k: v for k, v in kwargs.items() if k in expected_args}
|
|
||||||
response = await handler(**filtered)
|
|
||||||
except TypeError as te:
|
|
||||||
if "missing 1 required positional argument" in str(te) and "request" in str(te):
|
|
||||||
response = await handler(request)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
except web.HTTPException as e:
|
|
||||||
return web.json_response(data={"error": str(e)}, status=e.status_code)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception(e)
|
|
||||||
response = web.json_response(
|
|
||||||
data={"error": "Internal Server Error"},
|
|
||||||
status=web.HTTPInternalServerError.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
contentType: str | None = response.headers.get("content-type", None)
|
contentType: str | None = response.headers.get("content-type", None)
|
||||||
if contentType and "/" != base_path and contentType.startswith("text/html"):
|
if contentType and "/" != base_path and contentType.startswith("text/html"):
|
||||||
|
|
@ -295,3 +284,44 @@ class HttpAPI:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
return middleware_handler
|
return middleware_handler
|
||||||
|
|
||||||
|
async def _handle(self, handler: RequestHandler, request: Request) -> Response:
|
||||||
|
"""
|
||||||
|
Call the handler with the request and return the response.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
handler (RequestHandler): The handler to call.
|
||||||
|
request (Request): The request object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: The response object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
context = {**self.di_context.copy(), "request": request}
|
||||||
|
|
||||||
|
try:
|
||||||
|
sig = inspect.signature(handler)
|
||||||
|
expected_args = sig.parameters.keys()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if 1 == len(expected_args) and "request" in expected_args:
|
||||||
|
response = await handler(request)
|
||||||
|
else:
|
||||||
|
filtered = {k: v for k, v in context.items() if k in expected_args}
|
||||||
|
response = await handler(**filtered)
|
||||||
|
except TypeError as te:
|
||||||
|
LOG.exception(te)
|
||||||
|
if "missing 1 required positional argument" in str(te) and "request" in str(te):
|
||||||
|
response = await handler(request)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
except web.HTTPException as e:
|
||||||
|
return web.json_response(data={"error": str(e)}, status=e.status_code)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception(e)
|
||||||
|
response = web.json_response(
|
||||||
|
data={"error": "Internal Server Error"},
|
||||||
|
status=web.HTTPInternalServerError.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import functools
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import socketio
|
import socketio
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
|
@ -26,7 +27,7 @@ class HttpSocket:
|
||||||
config: Config
|
config: Config
|
||||||
sio: socketio.AsyncServer
|
sio: socketio.AsyncServer
|
||||||
queue: DownloadQueue
|
queue: DownloadQueue
|
||||||
di_context: dict[str, object] = {}
|
di_context: dict[str, Any] = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue