flows: add additional sentry spans
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
b5685ec072
commit
54f893b84f
|
@ -6,6 +6,7 @@ from django.http.response import HttpResponse
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic.base import View
|
from django.views.generic.base import View
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
|
from sentry_sdk.hub import Hub
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.core.models import DEFAULT_AVATAR, User
|
from authentik.core.models import DEFAULT_AVATAR, User
|
||||||
|
@ -94,8 +95,16 @@ class ChallengeStageView(StageView):
|
||||||
keep_context=keep_context,
|
keep_context=keep_context,
|
||||||
)
|
)
|
||||||
return self.executor.restart_flow(keep_context)
|
return self.executor.restart_flow(keep_context)
|
||||||
return self.challenge_invalid(challenge)
|
with Hub.current.start_span(
|
||||||
return self.challenge_valid(challenge)
|
op="authentik.flow.stage.challenge_invalid",
|
||||||
|
description=self.__class__.__name__,
|
||||||
|
):
|
||||||
|
return self.challenge_invalid(challenge)
|
||||||
|
with Hub.current.start_span(
|
||||||
|
op="authentik.flow.stage.challenge_valid",
|
||||||
|
description=self.__class__.__name__,
|
||||||
|
):
|
||||||
|
return self.challenge_valid(challenge)
|
||||||
|
|
||||||
def format_title(self) -> str:
|
def format_title(self) -> str:
|
||||||
"""Allow usage of placeholder in flow title."""
|
"""Allow usage of placeholder in flow title."""
|
||||||
|
@ -104,7 +113,11 @@ class ChallengeStageView(StageView):
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_challenge(self, *args, **kwargs) -> Challenge:
|
def _get_challenge(self, *args, **kwargs) -> Challenge:
|
||||||
challenge = self.get_challenge(*args, **kwargs)
|
with Hub.current.start_span(
|
||||||
|
op="authentik.flow.stage.get_challenge",
|
||||||
|
description=self.__class__.__name__,
|
||||||
|
):
|
||||||
|
challenge = self.get_challenge(*args, **kwargs)
|
||||||
if "flow_info" not in challenge.initial_data:
|
if "flow_info" not in challenge.initial_data:
|
||||||
flow_info = ContextualFlowInfo(
|
flow_info = ContextualFlowInfo(
|
||||||
data={
|
data={
|
||||||
|
|
|
@ -12,6 +12,7 @@ from django.utils.translation import gettext as _
|
||||||
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema_field
|
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema_field
|
||||||
from rest_framework.fields import BooleanField, CharField, DictField, ListField
|
from rest_framework.fields import BooleanField, CharField, DictField, ListField
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
from sentry_sdk.hub import Hub
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
|
@ -90,8 +91,12 @@ class IdentificationChallengeResponse(ChallengeResponse):
|
||||||
|
|
||||||
pre_user = self.stage.get_user(uid_field)
|
pre_user = self.stage.get_user(uid_field)
|
||||||
if not pre_user:
|
if not pre_user:
|
||||||
# Sleep a random time (between 90 and 210ms) to "prevent" user enumeration attacks
|
with Hub.current.start_span(
|
||||||
sleep(0.30 * SystemRandom().randint(3, 7))
|
op="authentik.stages.identification.validate_invalid_wait",
|
||||||
|
description="Sleep random time on invalid user identifier",
|
||||||
|
):
|
||||||
|
# Sleep a random time (between 90 and 210ms) to "prevent" user enumeration attacks
|
||||||
|
sleep(0.30 * SystemRandom().randint(3, 7))
|
||||||
LOGGER.debug("invalid_login", identifier=uid_field)
|
LOGGER.debug("invalid_login", identifier=uid_field)
|
||||||
identification_failed.send(sender=self, request=self.stage.request, uid_field=uid_field)
|
identification_failed.send(sender=self, request=self.stage.request, uid_field=uid_field)
|
||||||
# We set the pending_user even on failure so it's part of the context, even
|
# We set the pending_user even on failure so it's part of the context, even
|
||||||
|
@ -114,12 +119,16 @@ class IdentificationChallengeResponse(ChallengeResponse):
|
||||||
if not password:
|
if not password:
|
||||||
LOGGER.warning("Password not set for ident+auth attempt")
|
LOGGER.warning("Password not set for ident+auth attempt")
|
||||||
try:
|
try:
|
||||||
user = authenticate(
|
with Hub.current.start_span(
|
||||||
self.stage.request,
|
op="authentik.stages.identification.authenticate",
|
||||||
current_stage.password_stage.backends,
|
description="User authenticate call (combo stage)",
|
||||||
username=self.pre_user.username,
|
):
|
||||||
password=password,
|
user = authenticate(
|
||||||
)
|
self.stage.request,
|
||||||
|
current_stage.password_stage.backends,
|
||||||
|
username=self.pre_user.username,
|
||||||
|
password=password,
|
||||||
|
)
|
||||||
if not user:
|
if not user:
|
||||||
raise ValidationError("Failed to authenticate.")
|
raise ValidationError("Failed to authenticate.")
|
||||||
self.pre_user = user
|
self.pre_user = user
|
||||||
|
|
|
@ -10,6 +10,7 @@ from django.urls import reverse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from rest_framework.exceptions import ErrorDetail, ValidationError
|
from rest_framework.exceptions import ErrorDetail, ValidationError
|
||||||
from rest_framework.fields import CharField
|
from rest_framework.fields import CharField
|
||||||
|
from sentry_sdk.hub import Hub
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
|
@ -43,7 +44,11 @@ def authenticate(request: HttpRequest, backends: list[str], **credentials: Any)
|
||||||
LOGGER.warning("Failed to import backend", path=backend_path)
|
LOGGER.warning("Failed to import backend", path=backend_path)
|
||||||
continue
|
continue
|
||||||
LOGGER.debug("Attempting authentication...", backend=backend_path)
|
LOGGER.debug("Attempting authentication...", backend=backend_path)
|
||||||
user = backend.authenticate(request, **credentials)
|
with Hub.current.start_span(
|
||||||
|
op="authentik.stages.password.authenticate",
|
||||||
|
description=backend_path,
|
||||||
|
):
|
||||||
|
user = backend.authenticate(request, **credentials)
|
||||||
if user is None:
|
if user is None:
|
||||||
LOGGER.debug("Backend returned nothing, continuing", backend=backend_path)
|
LOGGER.debug("Backend returned nothing, continuing", backend=backend_path)
|
||||||
continue
|
continue
|
||||||
|
@ -120,7 +125,13 @@ class PasswordStageView(ChallengeStageView):
|
||||||
"username": pending_user.username,
|
"username": pending_user.username,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
user = authenticate(self.request, self.executor.current_stage.backends, **auth_kwargs)
|
with Hub.current.start_span(
|
||||||
|
op="authentik.stages.password.authenticate",
|
||||||
|
description="User authenticate call",
|
||||||
|
):
|
||||||
|
user = authenticate(
|
||||||
|
self.request, self.executor.current_stage.backends, **auth_kwargs
|
||||||
|
)
|
||||||
except PermissionDenied:
|
except PermissionDenied:
|
||||||
del auth_kwargs["password"]
|
del auth_kwargs["password"]
|
||||||
# User was found, but permission was denied (i.e. user is not active)
|
# User was found, but permission was denied (i.e. user is not active)
|
||||||
|
|
Reference in New Issue