root: add ASGI Error handler

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-08-23 15:15:12 +02:00
parent 85915905dc
commit 58712047e1
7 changed files with 88 additions and 50 deletions

View file

View file

@ -0,0 +1,42 @@
"""
ASGI config for authentik project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
"""
from time import time
import django
from asgiref.compatibility import guarantee_single_callable
from channels.routing import ProtocolTypeRouter, URLRouter
from defusedxml import defuse_stdlib
from django.core.asgi import get_asgi_application
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from authentik.root.asgi.error_handler import ASGIErrorHandler
from authentik.root.asgi.logger import ASGILogger
# DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py
defuse_stdlib()
django.setup()
# pylint: disable=wrong-import-position
from authentik.root import websocket # noqa # isort:skip
application = ASGIErrorHandler(
ASGILogger(
guarantee_single_callable(
SentryAsgiMiddleware(
ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": URLRouter(websocket.websocket_urlpatterns),
}
)
)
)
)
)

View file

@ -0,0 +1,31 @@
"""ASGI Error handler"""
from structlog.stdlib import get_logger
from authentik.root.asgi.types import ASGIApp, Receive, Scope, Send
LOGGER = get_logger("authentik.asgi")
class ASGIErrorHandler:
"""ASGI Error handler"""
app: ASGIApp
def __init__(self, app: ASGIApp):
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
try:
return await self.app(scope, receive, send)
except Exception as exc: # pylint: disable=
LOGGER.warning("Fatal ASGI exception", exc=exc)
return await self.error_handler(send)
async def error_handler(self, send: Send) -> None:
return await send(
{
"type": "http.request",
"body": b"Internal server error",
"more_body": False,
}
)

View file

@ -1,41 +1,10 @@
""" """ASGI Logger"""
ASGI config for authentik project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
"""
import typing
from time import time from time import time
import django
from asgiref.compatibility import guarantee_single_callable
from channels.routing import ProtocolTypeRouter, URLRouter
from defusedxml import defuse_stdlib
from django.core.asgi import get_asgi_application
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.middleware import RESPONSE_HEADER_ID from authentik.core.middleware import RESPONSE_HEADER_ID
from authentik.root.asgi.types import ASGIApp, Message, Receive, Scope, Send
# DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py
defuse_stdlib()
django.setup()
# pylint: disable=wrong-import-position
from authentik.root import websocket # noqa # isort:skip
# See https://github.com/encode/starlette/blob/master/starlette/types.py
Scope = typing.MutableMapping[str, typing.Any]
Message = typing.MutableMapping[str, typing.Any]
Receive = typing.Callable[[], typing.Awaitable[Message]]
Send = typing.Callable[[Message], typing.Awaitable[None]]
ASGIApp = typing.Callable[[Scope, Receive, Send], typing.Awaitable[None]]
ASGI_IP_HEADERS = ( ASGI_IP_HEADERS = (
b"x-forwarded-for", b"x-forwarded-for",
@ -86,7 +55,7 @@ class ASGILogger:
# https://code.djangoproject.com/ticket/31508 # https://code.djangoproject.com/ticket/31508
# https://github.com/encode/uvicorn/issues/266 # https://github.com/encode/uvicorn/issues/266
return return
await self.app(scope, receive, send_hooked) return await self.app(scope, receive, send_hooked)
def _get_ip(self, scope: Scope) -> str: def _get_ip(self, scope: Scope) -> str:
client_ip = None client_ip = None
@ -115,17 +84,3 @@ class ASGILogger:
runtime=runtime, runtime=runtime,
**kwargs, **kwargs,
) )
application = ASGILogger(
guarantee_single_callable(
SentryAsgiMiddleware(
ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": URLRouter(websocket.websocket_urlpatterns),
}
)
)
)
)

View file

@ -0,0 +1,10 @@
import typing
# See https://github.com/encode/starlette/blob/master/starlette/types.py
Scope = typing.MutableMapping[str, typing.Any]
Message = typing.MutableMapping[str, typing.Any]
Receive = typing.Callable[[], typing.Awaitable[Message]]
Send = typing.Callable[[Message], typing.Awaitable[None]]
ASGIApp = typing.Callable[[Scope, Receive, Send], typing.Awaitable[None]]

View file

@ -251,7 +251,7 @@ TEMPLATES = [
}, },
] ]
ASGI_APPLICATION = "authentik.root.asgi.application" ASGI_APPLICATION = "authentik.root.asgi.app.application"
CHANNEL_LAYERS = { CHANNEL_LAYERS = {
"default": { "default": {

View file

@ -28,7 +28,7 @@ func NewGoUnicorn() *GoUnicorn {
func (g *GoUnicorn) initCmd() { func (g *GoUnicorn) initCmd() {
command := "gunicorn" command := "gunicorn"
args := []string{"-c", "./lifecycle/gunicorn.conf.py", "authentik.root.asgi:application"} args := []string{"-c", "./lifecycle/gunicorn.conf.py", "authentik.root.asgi.app:application"}
if config.G.Debug { if config.G.Debug {
command = "python" command = "python"
args = []string{"manage.py", "runserver", "localhost:8000"} args = []string{"manage.py", "runserver", "localhost:8000"}