root: add ASGI Error handler
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
85915905dc
commit
58712047e1
0
authentik/root/asgi/__init__.py
Normal file
0
authentik/root/asgi/__init__.py
Normal file
42
authentik/root/asgi/app.py
Normal file
42
authentik/root/asgi/app.py
Normal 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),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
31
authentik/root/asgi/error_handler.py
Normal file
31
authentik/root/asgi/error_handler.py
Normal 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,
|
||||||
|
}
|
||||||
|
)
|
|
@ -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),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
10
authentik/root/asgi/types.py
Normal file
10
authentik/root/asgi/types.py
Normal 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]]
|
|
@ -251,7 +251,7 @@ TEMPLATES = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
ASGI_APPLICATION = "authentik.root.asgi.application"
|
ASGI_APPLICATION = "authentik.root.asgi.app.application"
|
||||||
|
|
||||||
CHANNEL_LAYERS = {
|
CHANNEL_LAYERS = {
|
||||||
"default": {
|
"default": {
|
||||||
|
|
|
@ -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"}
|
||||||
|
|
Reference in a new issue