core: bump structlog from 21.5.0 to 22.1.0 (#3294)
* core: bump structlog from 21.5.0 to 22.1.0 Bumps [structlog](https://github.com/hynek/structlog) from 21.5.0 to 22.1.0. - [Release notes](https://github.com/hynek/structlog/releases) - [Changelog](https://github.com/hynek/structlog/blob/main/CHANGELOG.md) - [Commits](https://github.com/hynek/structlog/compare/21.5.0...22.1.0) --- updated-dependencies: - dependency-name: structlog dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * migrate threaedlocal to contextvars Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
e0adcd3277
commit
bd8794f646
|
@ -7,7 +7,7 @@ from rest_framework.exceptions import AuthenticationFailed
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.core.middleware import KEY_AUTH_VIA, LOCAL
|
from authentik.core.middleware import CTX_AUTH_VIA
|
||||||
from authentik.core.models import Token, TokenIntents, User
|
from authentik.core.models import Token, TokenIntents, User
|
||||||
from authentik.outposts.models import Outpost
|
from authentik.outposts.models import Outpost
|
||||||
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
|
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
|
||||||
|
@ -36,14 +36,12 @@ def bearer_auth(raw_header: bytes) -> Optional[User]:
|
||||||
auth_credentials = validate_auth(raw_header)
|
auth_credentials = validate_auth(raw_header)
|
||||||
if not auth_credentials:
|
if not auth_credentials:
|
||||||
return None
|
return None
|
||||||
if not hasattr(LOCAL, "authentik"):
|
|
||||||
LOCAL.authentik = {}
|
|
||||||
# first, check traditional tokens
|
# first, check traditional tokens
|
||||||
key_token = Token.filter_not_expired(
|
key_token = Token.filter_not_expired(
|
||||||
key=auth_credentials, intent=TokenIntents.INTENT_API
|
key=auth_credentials, intent=TokenIntents.INTENT_API
|
||||||
).first()
|
).first()
|
||||||
if key_token:
|
if key_token:
|
||||||
LOCAL.authentik[KEY_AUTH_VIA] = "api_token"
|
CTX_AUTH_VIA.set("api_token")
|
||||||
return key_token.user
|
return key_token.user
|
||||||
# then try to auth via JWT
|
# then try to auth via JWT
|
||||||
jwt_token = RefreshToken.filter_not_expired(
|
jwt_token = RefreshToken.filter_not_expired(
|
||||||
|
@ -54,12 +52,12 @@ def bearer_auth(raw_header: bytes) -> Optional[User]:
|
||||||
# we want to check the parsed version too
|
# we want to check the parsed version too
|
||||||
if SCOPE_AUTHENTIK_API not in jwt_token.scope:
|
if SCOPE_AUTHENTIK_API not in jwt_token.scope:
|
||||||
raise AuthenticationFailed("Token invalid/expired")
|
raise AuthenticationFailed("Token invalid/expired")
|
||||||
LOCAL.authentik[KEY_AUTH_VIA] = "jwt"
|
CTX_AUTH_VIA.set("jwt")
|
||||||
return jwt_token.user
|
return jwt_token.user
|
||||||
# then try to auth via secret key (for embedded outpost/etc)
|
# then try to auth via secret key (for embedded outpost/etc)
|
||||||
user = token_secret_key(auth_credentials)
|
user = token_secret_key(auth_credentials)
|
||||||
if user:
|
if user:
|
||||||
LOCAL.authentik[KEY_AUTH_VIA] = "secret_key"
|
CTX_AUTH_VIA.set("secret_key")
|
||||||
return user
|
return user
|
||||||
raise AuthenticationFailed("Token invalid/expired")
|
raise AuthenticationFailed("Token invalid/expired")
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
"""authentik admin Middleware to impersonate users"""
|
"""authentik admin Middleware to impersonate users"""
|
||||||
from logging import Logger
|
from contextvars import ContextVar
|
||||||
from threading import local
|
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from sentry_sdk.api import set_tag
|
from sentry_sdk.api import set_tag
|
||||||
|
from structlog.contextvars import STRUCTLOG_KEY_PREFIX
|
||||||
|
|
||||||
SESSION_KEY_IMPERSONATE_USER = "authentik/impersonate/user"
|
SESSION_KEY_IMPERSONATE_USER = "authentik/impersonate/user"
|
||||||
SESSION_KEY_IMPERSONATE_ORIGINAL_USER = "authentik/impersonate/original_user"
|
SESSION_KEY_IMPERSONATE_ORIGINAL_USER = "authentik/impersonate/original_user"
|
||||||
LOCAL = local()
|
|
||||||
RESPONSE_HEADER_ID = "X-authentik-id"
|
RESPONSE_HEADER_ID = "X-authentik-id"
|
||||||
KEY_AUTH_VIA = "auth_via"
|
KEY_AUTH_VIA = "auth_via"
|
||||||
KEY_USER = "user"
|
KEY_USER = "user"
|
||||||
|
|
||||||
|
CTX_REQUEST_ID = ContextVar(STRUCTLOG_KEY_PREFIX + "request_id", default=None)
|
||||||
|
CTX_HOST = ContextVar(STRUCTLOG_KEY_PREFIX + "host", default=None)
|
||||||
|
CTX_AUTH_VIA = ContextVar(STRUCTLOG_KEY_PREFIX + KEY_AUTH_VIA, default=None)
|
||||||
|
|
||||||
|
|
||||||
class ImpersonateMiddleware:
|
class ImpersonateMiddleware:
|
||||||
"""Middleware to impersonate users"""
|
"""Middleware to impersonate users"""
|
||||||
|
@ -47,26 +50,20 @@ class RequestIDMiddleware:
|
||||||
if not hasattr(request, "request_id"):
|
if not hasattr(request, "request_id"):
|
||||||
request_id = uuid4().hex
|
request_id = uuid4().hex
|
||||||
setattr(request, "request_id", request_id)
|
setattr(request, "request_id", request_id)
|
||||||
LOCAL.authentik = {
|
CTX_REQUEST_ID.set(request_id)
|
||||||
"request_id": request_id,
|
CTX_HOST.set(request.get_host())
|
||||||
"host": request.get_host(),
|
|
||||||
}
|
|
||||||
set_tag("authentik.request_id", request_id)
|
set_tag("authentik.request_id", request_id)
|
||||||
|
if hasattr(request, "user") and getattr(request.user, "is_authenticated", False):
|
||||||
|
CTX_AUTH_VIA.set("session")
|
||||||
|
else:
|
||||||
|
CTX_AUTH_VIA.set("unauthenticated")
|
||||||
|
|
||||||
response = self.get_response(request)
|
response = self.get_response(request)
|
||||||
|
|
||||||
response[RESPONSE_HEADER_ID] = request.request_id
|
response[RESPONSE_HEADER_ID] = request.request_id
|
||||||
setattr(response, "ak_context", {})
|
setattr(response, "ak_context", {})
|
||||||
response.ak_context.update(LOCAL.authentik)
|
response.ak_context["request_id"] = CTX_REQUEST_ID.get()
|
||||||
response.ak_context.setdefault(KEY_USER, request.user.username)
|
response.ak_context["host"] = CTX_HOST.get()
|
||||||
for key in list(LOCAL.authentik.keys()):
|
response.ak_context[KEY_AUTH_VIA] = CTX_AUTH_VIA.get()
|
||||||
del LOCAL.authentik[key]
|
response.ak_context[KEY_USER] = request.user.username
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def structlog_add_request_id(logger: Logger, method_name: str, event_dict: dict):
|
|
||||||
"""If threadlocal has authentik defined, add request_id to log"""
|
|
||||||
if hasattr(LOCAL, "authentik"):
|
|
||||||
event_dict.update(LOCAL.authentik)
|
|
||||||
if hasattr(LOCAL, "authentik_task"):
|
|
||||||
event_dict.update(LOCAL.authentik_task)
|
|
||||||
return event_dict
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ from django.http import HttpRequest, HttpResponse
|
||||||
from django_otp.plugins.otp_static.models import StaticToken
|
from django_otp.plugins.otp_static.models import StaticToken
|
||||||
from guardian.models import UserObjectPermission
|
from guardian.models import UserObjectPermission
|
||||||
|
|
||||||
from authentik.core.middleware import LOCAL
|
|
||||||
from authentik.core.models import AuthenticatedSession, User
|
from authentik.core.models import AuthenticatedSession, User
|
||||||
from authentik.events.models import Event, EventAction, Notification
|
from authentik.events.models import Event, EventAction, Notification
|
||||||
from authentik.events.signals import EventNewThread
|
from authentik.events.signals import EventNewThread
|
||||||
|
@ -45,36 +44,46 @@ class AuditMiddleware:
|
||||||
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
|
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
|
|
||||||
def __call__(self, request: HttpRequest) -> HttpResponse:
|
def connect(self, request: HttpRequest):
|
||||||
# Connect signal for automatic logging
|
"""Connect signal for automatic logging"""
|
||||||
if hasattr(request, "user") and getattr(request.user, "is_authenticated", False):
|
if not hasattr(request, "user"):
|
||||||
|
return
|
||||||
|
if not getattr(request.user, "is_authenticated", False):
|
||||||
|
return
|
||||||
|
if not hasattr(request, "request_id"):
|
||||||
|
return
|
||||||
post_save_handler = partial(self.post_save_handler, user=request.user, request=request)
|
post_save_handler = partial(self.post_save_handler, user=request.user, request=request)
|
||||||
pre_delete_handler = partial(
|
pre_delete_handler = partial(self.pre_delete_handler, user=request.user, request=request)
|
||||||
self.pre_delete_handler, user=request.user, request=request
|
|
||||||
)
|
|
||||||
post_save.connect(
|
post_save.connect(
|
||||||
post_save_handler,
|
post_save_handler,
|
||||||
dispatch_uid=LOCAL.authentik["request_id"],
|
dispatch_uid=request.request_id,
|
||||||
weak=False,
|
weak=False,
|
||||||
)
|
)
|
||||||
pre_delete.connect(
|
pre_delete.connect(
|
||||||
pre_delete_handler,
|
pre_delete_handler,
|
||||||
dispatch_uid=LOCAL.authentik["request_id"],
|
dispatch_uid=request.request_id,
|
||||||
weak=False,
|
weak=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def disconnect(self, request: HttpRequest):
|
||||||
|
"""Disconnect signals"""
|
||||||
|
if not hasattr(request, "request_id"):
|
||||||
|
return
|
||||||
|
post_save.disconnect(dispatch_uid=request.request_id)
|
||||||
|
pre_delete.disconnect(dispatch_uid=request.request_id)
|
||||||
|
|
||||||
|
def __call__(self, request: HttpRequest) -> HttpResponse:
|
||||||
|
self.connect(request)
|
||||||
|
|
||||||
response = self.get_response(request)
|
response = self.get_response(request)
|
||||||
|
|
||||||
post_save.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
|
self.disconnect(request)
|
||||||
pre_delete.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def process_exception(self, request: HttpRequest, exception: Exception):
|
def process_exception(self, request: HttpRequest, exception: Exception):
|
||||||
"""Disconnect handlers in case of exception"""
|
"""Disconnect handlers in case of exception"""
|
||||||
post_save.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
|
self.disconnect(request)
|
||||||
pre_delete.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
|
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
return
|
return
|
||||||
|
|
|
@ -17,7 +17,7 @@ from django.conf import settings
|
||||||
from django.db import ProgrammingError
|
from django.db import ProgrammingError
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.core.middleware import LOCAL
|
from authentik.core.middleware import CTX_AUTH_VIA, CTX_HOST, CTX_REQUEST_ID
|
||||||
from authentik.lib.sentry import before_send
|
from authentik.lib.sentry import before_send
|
||||||
from authentik.lib.utils.errors import exception_to_string
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@ def after_task_publish_hook(sender=None, headers=None, body=None, **kwargs):
|
||||||
def task_prerun_hook(task_id: str, task, *args, **kwargs):
|
def task_prerun_hook(task_id: str, task, *args, **kwargs):
|
||||||
"""Log task_id on worker"""
|
"""Log task_id on worker"""
|
||||||
request_id = "task-" + task_id.replace("-", "")
|
request_id = "task-" + task_id.replace("-", "")
|
||||||
LOCAL.authentik_task = {
|
CTX_REQUEST_ID.set(request_id)
|
||||||
"request_id": request_id,
|
CTX_AUTH_VIA.set(Ellipsis)
|
||||||
}
|
CTX_HOST.set(Ellipsis)
|
||||||
LOGGER.info("Task started", task_id=task_id, task_name=task.__name__)
|
LOGGER.info("Task started", task_id=task_id, task_name=task.__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,10 +59,6 @@ def task_prerun_hook(task_id: str, task, *args, **kwargs):
|
||||||
def task_postrun_hook(task_id, task, *args, retval=None, state=None, **kwargs):
|
def task_postrun_hook(task_id, task, *args, retval=None, state=None, **kwargs):
|
||||||
"""Log task_id on worker"""
|
"""Log task_id on worker"""
|
||||||
LOGGER.info("Task finished", task_id=task_id, task_name=task.__name__, state=state)
|
LOGGER.info("Task finished", task_id=task_id, task_name=task.__name__, state=state)
|
||||||
if not hasattr(LOCAL, "authentik_task"):
|
|
||||||
return
|
|
||||||
for key in list(LOCAL.authentik_task.keys()):
|
|
||||||
del LOCAL.authentik_task[key]
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
|
|
|
@ -14,7 +14,6 @@ from celery.schedules import crontab
|
||||||
from sentry_sdk import set_tag
|
from sentry_sdk import set_tag
|
||||||
|
|
||||||
from authentik import ENV_GIT_HASH_KEY, __version__
|
from authentik import ENV_GIT_HASH_KEY, __version__
|
||||||
from authentik.core.middleware import structlog_add_request_id
|
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.logging import add_process_id
|
from authentik.lib.logging import add_process_id
|
||||||
from authentik.lib.sentry import sentry_init
|
from authentik.lib.sentry import sentry_init
|
||||||
|
@ -380,12 +379,12 @@ structlog.configure_once(
|
||||||
processors=[
|
processors=[
|
||||||
structlog.stdlib.add_log_level,
|
structlog.stdlib.add_log_level,
|
||||||
structlog.stdlib.add_logger_name,
|
structlog.stdlib.add_logger_name,
|
||||||
structlog.threadlocal.merge_threadlocal_context,
|
structlog.contextvars.merge_contextvars,
|
||||||
add_process_id,
|
add_process_id,
|
||||||
structlog_add_request_id,
|
|
||||||
structlog.stdlib.PositionalArgumentsFormatter(),
|
structlog.stdlib.PositionalArgumentsFormatter(),
|
||||||
structlog.processors.TimeStamper(fmt="iso", utc=False),
|
structlog.processors.TimeStamper(fmt="iso", utc=False),
|
||||||
structlog.processors.StackInfoRenderer(),
|
structlog.processors.StackInfoRenderer(),
|
||||||
|
structlog.processors.dict_tracebacks,
|
||||||
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
||||||
],
|
],
|
||||||
logger_factory=structlog.stdlib.LoggerFactory(),
|
logger_factory=structlog.stdlib.LoggerFactory(),
|
||||||
|
@ -400,6 +399,7 @@ LOG_PRE_CHAIN = [
|
||||||
# is not from structlog.
|
# is not from structlog.
|
||||||
structlog.stdlib.add_log_level,
|
structlog.stdlib.add_log_level,
|
||||||
structlog.stdlib.add_logger_name,
|
structlog.stdlib.add_logger_name,
|
||||||
|
structlog.processors.dict_tracebacks,
|
||||||
structlog.processors.TimeStamper(),
|
structlog.processors.TimeStamper(),
|
||||||
structlog.processors.StackInfoRenderer(),
|
structlog.processors.StackInfoRenderer(),
|
||||||
]
|
]
|
||||||
|
|
14
poetry.lock
generated
14
poetry.lock
generated
|
@ -1715,16 +1715,16 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "structlog"
|
name = "structlog"
|
||||||
version = "21.5.0"
|
version = "22.1.0"
|
||||||
description = "Structured Logging for Python"
|
description = "Structured Logging for Python"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["pre-commit", "rich", "cogapp", "tomli", "coverage", "freezegun (>=0.2.8)", "pretend", "pytest-asyncio", "pytest (>=6.0)", "simplejson", "furo", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "twisted"]
|
dev = ["pre-commit", "rich", "cogapp", "tomli", "coverage", "freezegun (>=0.2.8)", "pretend", "pytest-asyncio (>=0.17)", "pytest (>=6.0)", "simplejson", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "twisted"]
|
||||||
docs = ["furo", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "twisted"]
|
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "twisted"]
|
||||||
tests = ["coverage", "freezegun (>=0.2.8)", "pretend", "pytest-asyncio", "pytest (>=6.0)", "simplejson"]
|
tests = ["coverage", "freezegun (>=0.2.8)", "pretend", "pytest-asyncio (>=0.17)", "pytest (>=6.0)", "simplejson"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swagger-spec-validator"
|
name = "swagger-spec-validator"
|
||||||
|
@ -3433,8 +3433,8 @@ stevedore = [
|
||||||
{file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"},
|
{file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"},
|
||||||
]
|
]
|
||||||
structlog = [
|
structlog = [
|
||||||
{file = "structlog-21.5.0-py3-none-any.whl", hash = "sha256:fd7922e195262b337da85c2a91c84be94ccab1f8fd1957bd6986f6904e3761c8"},
|
{file = "structlog-22.1.0-py3-none-any.whl", hash = "sha256:760d37b8839bd4fe1747bed7b80f7f4de160078405f4b6a1db9270ccbfce6c30"},
|
||||||
{file = "structlog-21.5.0.tar.gz", hash = "sha256:68c4c29c003714fe86834f347cb107452847ba52414390a7ee583472bde00fc9"},
|
{file = "structlog-22.1.0.tar.gz", hash = "sha256:94b29b1d62b2659db154f67a9379ec1770183933d6115d21f21aa25cfc9a7393"},
|
||||||
]
|
]
|
||||||
swagger-spec-validator = [
|
swagger-spec-validator = [
|
||||||
{file = "swagger-spec-validator-2.7.4.tar.gz", hash = "sha256:2aee5e1fc0503be9f8299378b10c92169572781573c6de3315e831fd0559ba73"},
|
{file = "swagger-spec-validator-2.7.4.tar.gz", hash = "sha256:2aee5e1fc0503be9f8299378b10c92169572781573c6de3315e831fd0559ba73"},
|
||||||
|
|
Reference in a new issue