stages/*: use bound logger (#3012)
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
5a81ae956f
commit
fc1c1a849a
|
@ -9,7 +9,7 @@ from django.urls import reverse
|
|||
from django.views.generic.base import View
|
||||
from rest_framework.request import Request
|
||||
from sentry_sdk.hub import Hub
|
||||
from structlog.stdlib import get_logger
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.core.models import DEFAULT_AVATAR, User
|
||||
from authentik.flows.challenge import (
|
||||
|
@ -28,7 +28,6 @@ if TYPE_CHECKING:
|
|||
from authentik.flows.views.executor import FlowExecutorView
|
||||
|
||||
PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier"
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class StageView(View):
|
||||
|
@ -38,8 +37,15 @@ class StageView(View):
|
|||
|
||||
request: HttpRequest = None
|
||||
|
||||
logger: BoundLogger
|
||||
|
||||
def __init__(self, executor: "FlowExecutorView", **kwargs):
|
||||
self.executor = executor
|
||||
current_stage = getattr(self.executor, "current_stage", None)
|
||||
self.logger = get_logger().bind(
|
||||
stage=getattr(current_stage, "name", None),
|
||||
stage_view=self,
|
||||
)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def get_pending_user(self, for_display=False) -> User:
|
||||
|
@ -77,12 +83,9 @@ class ChallengeStageView(StageView):
|
|||
"""Return a challenge for the frontend to solve"""
|
||||
challenge = self._get_challenge(*args, **kwargs)
|
||||
if not challenge.is_valid():
|
||||
LOGGER.warning(
|
||||
self.logger.warning(
|
||||
"f(ch): Invalid challenge",
|
||||
binding=self.executor.current_binding,
|
||||
errors=challenge.errors,
|
||||
stage_view=self,
|
||||
challenge=challenge,
|
||||
)
|
||||
return HttpChallengeResponse(challenge)
|
||||
|
||||
|
@ -99,10 +102,8 @@ class ChallengeStageView(StageView):
|
|||
self.executor.current_binding.invalid_response_action
|
||||
== InvalidResponseAction.RESTART_WITH_CONTEXT
|
||||
)
|
||||
LOGGER.debug(
|
||||
self.logger.debug(
|
||||
"f(ch): Invalid response, restarting flow",
|
||||
binding=self.executor.current_binding,
|
||||
stage_view=self,
|
||||
keep_context=keep_context,
|
||||
)
|
||||
return self.executor.restart_flow(keep_context)
|
||||
|
@ -128,7 +129,7 @@ class ChallengeStageView(StageView):
|
|||
}
|
||||
# pylint: disable=broad-except
|
||||
except Exception as exc:
|
||||
LOGGER.warning("failed to template title", exc=exc)
|
||||
self.logger.warning("failed to template title", exc=exc)
|
||||
return self.executor.flow.title
|
||||
|
||||
def _get_challenge(self, *args, **kwargs) -> Challenge:
|
||||
|
@ -188,11 +189,9 @@ class ChallengeStageView(StageView):
|
|||
)
|
||||
challenge_response.initial_data["response_errors"] = full_errors
|
||||
if not challenge_response.is_valid():
|
||||
LOGGER.error(
|
||||
self.logger.error(
|
||||
"f(ch): invalid challenge response",
|
||||
binding=self.executor.current_binding,
|
||||
errors=challenge_response.errors,
|
||||
stage_view=self,
|
||||
)
|
||||
return HttpChallengeResponse(challenge_response)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.timezone import now
|
||||
from rest_framework.fields import CharField
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.flows.challenge import (
|
||||
|
@ -16,8 +15,6 @@ from authentik.flows.stage import ChallengeStageView
|
|||
from authentik.flows.views.executor import InvalidStageError
|
||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
SESSION_KEY_DUO_USER_ID = "authentik/stages/authenticator_duo/user_id"
|
||||
SESSION_KEY_DUO_ACTIVATION_CODE = "authentik/stages/authenticator_duo/activation_code"
|
||||
|
||||
|
@ -69,7 +66,7 @@ class AuthenticatorDuoStageView(ChallengeStageView):
|
|||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
|
||||
if not user:
|
||||
LOGGER.debug("No pending user, continuing")
|
||||
self.logger.debug("No pending user, continuing")
|
||||
return self.executor.stage_ok()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ from django.http.request import QueryDict
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import BooleanField, CharField, IntegerField
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.challenge import (
|
||||
Challenge,
|
||||
|
@ -19,7 +18,6 @@ from authentik.flows.stage import ChallengeStageView
|
|||
from authentik.stages.authenticator_sms.models import AuthenticatorSMSStage, SMSDevice
|
||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
|
||||
LOGGER = get_logger()
|
||||
SESSION_KEY_SMS_DEVICE = "authentik/stages/authenticator_sms/sms_device"
|
||||
|
||||
|
||||
|
@ -64,10 +62,10 @@ class AuthenticatorSMSStageView(ChallengeStageView):
|
|||
def _has_phone_number(self) -> Optional[str]:
|
||||
context = self.executor.plan.context
|
||||
if "phone" in context.get(PLAN_CONTEXT_PROMPT, {}):
|
||||
LOGGER.debug("got phone number from plan context")
|
||||
self.logger.debug("got phone number from plan context")
|
||||
return context.get(PLAN_CONTEXT_PROMPT, {}).get("phone")
|
||||
if SESSION_KEY_SMS_DEVICE in self.request.session:
|
||||
LOGGER.debug("got phone number from device in session")
|
||||
self.logger.debug("got phone number from device in session")
|
||||
device: SMSDevice = self.request.session[SESSION_KEY_SMS_DEVICE]
|
||||
if device.phone_number == "":
|
||||
return None
|
||||
|
@ -90,7 +88,7 @@ class AuthenticatorSMSStageView(ChallengeStageView):
|
|||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
|
||||
if not user:
|
||||
LOGGER.debug("No pending user, continuing")
|
||||
self.logger.debug("No pending user, continuing")
|
||||
return self.executor.stage_ok()
|
||||
|
||||
# Currently, this stage only supports one device per user. If the user already
|
||||
|
|
|
@ -2,14 +2,11 @@
|
|||
from django.http import HttpRequest, HttpResponse
|
||||
from django_otp.plugins.otp_static.models import StaticDevice, StaticToken
|
||||
from rest_framework.fields import CharField, ListField
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.challenge import ChallengeResponse, ChallengeTypes, WithUserInfoChallenge
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.stages.authenticator_static.models import AuthenticatorStaticStage
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class AuthenticatorStaticChallenge(WithUserInfoChallenge):
|
||||
"""Static authenticator challenge"""
|
||||
|
@ -42,7 +39,7 @@ class AuthenticatorStaticStageView(ChallengeStageView):
|
|||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
user = self.get_pending_user()
|
||||
if not user.is_authenticated:
|
||||
LOGGER.debug("No pending user, continuing")
|
||||
self.logger.debug("No pending user, continuing")
|
||||
return self.executor.stage_ok()
|
||||
|
||||
stage: AuthenticatorStaticStage = self.executor.current_stage
|
||||
|
|
|
@ -6,7 +6,6 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||
from rest_framework.fields import CharField, IntegerField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.challenge import (
|
||||
Challenge,
|
||||
|
@ -18,8 +17,6 @@ from authentik.flows.stage import ChallengeStageView
|
|||
from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage
|
||||
from authentik.stages.authenticator_totp.settings import OTP_TOTP_ISSUER
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class AuthenticatorTOTPChallenge(WithUserInfoChallenge):
|
||||
"""TOTP Setup challenge"""
|
||||
|
@ -72,7 +69,7 @@ class AuthenticatorTOTPStageView(ChallengeStageView):
|
|||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
user = self.get_pending_user()
|
||||
if not user.is_authenticated:
|
||||
LOGGER.debug("No pending user, continuing")
|
||||
self.logger.debug("No pending user, continuing")
|
||||
return self.executor.stage_ok()
|
||||
|
||||
stage: AuthenticatorTOTPStage = self.executor.current_stage
|
||||
|
|
|
@ -37,6 +37,7 @@ from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
|||
from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
COOKIE_NAME_MFA = "authentik_mfa"
|
||||
|
||||
SESSION_KEY_STAGES = "authentik/stages/authenticator_validate/stages"
|
||||
|
@ -151,7 +152,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
challenges = []
|
||||
# Convert to a list to have usable log output instead of just <generator ...>
|
||||
user_devices = list(devices_for_user(self.get_pending_user()))
|
||||
LOGGER.debug("Got devices for user", devices=user_devices)
|
||||
self.logger.debug("Got devices for user", devices=user_devices)
|
||||
|
||||
# static and totp are only shown once
|
||||
# since their challenges are device-independent
|
||||
|
@ -165,7 +166,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
for device in user_devices:
|
||||
device_class = device.__class__.__name__.lower().replace("device", "")
|
||||
if device_class not in stage.device_classes:
|
||||
LOGGER.debug("device class not allowed", device_class=device_class)
|
||||
self.logger.debug("device class not allowed", device_class=device_class)
|
||||
continue
|
||||
allowed_devices.append(device)
|
||||
# Ensure only one challenge per device class
|
||||
|
@ -183,7 +184,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
)
|
||||
challenge.is_valid()
|
||||
challenges.append(challenge.data)
|
||||
LOGGER.debug("adding challenge for device", challenge=challenge)
|
||||
self.logger.debug("adding challenge for device", challenge=challenge)
|
||||
# check if we have an MFA cookie and if it's valid
|
||||
if threshold.total_seconds() > 0:
|
||||
self.check_mfa_cookie(allowed_devices)
|
||||
|
@ -214,27 +215,27 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
return self.executor.stage_ok()
|
||||
else:
|
||||
if self.executor.flow.designation != FlowDesignation.AUTHENTICATION:
|
||||
LOGGER.debug("Refusing passwordless flow in non-authentication flow")
|
||||
self.logger.debug("Refusing passwordless flow in non-authentication flow")
|
||||
return self.executor.stage_ok()
|
||||
# Passwordless auth, with just webauthn
|
||||
if DeviceClasses.WEBAUTHN in stage.device_classes:
|
||||
LOGGER.debug("Flow without user, getting generic webauthn challenge")
|
||||
self.logger.debug("Flow without user, getting generic webauthn challenge")
|
||||
challenges = self.get_webauthn_challenge_without_user()
|
||||
else:
|
||||
LOGGER.debug("No pending user, continuing")
|
||||
self.logger.debug("No pending user, continuing")
|
||||
return self.executor.stage_ok()
|
||||
self.request.session[SESSION_KEY_DEVICE_CHALLENGES] = challenges
|
||||
|
||||
# No allowed devices
|
||||
if len(challenges) < 1:
|
||||
if stage.not_configured_action == NotConfiguredAction.SKIP:
|
||||
LOGGER.debug("Authenticator not configured, skipping stage")
|
||||
self.logger.debug("Authenticator not configured, skipping stage")
|
||||
return self.executor.stage_ok()
|
||||
if stage.not_configured_action == NotConfiguredAction.DENY:
|
||||
LOGGER.debug("Authenticator not configured, denying")
|
||||
self.logger.debug("Authenticator not configured, denying")
|
||||
return self.executor.stage_invalid()
|
||||
if stage.not_configured_action == NotConfiguredAction.CONFIGURE:
|
||||
LOGGER.debug("Authenticator not configured, forcing configure")
|
||||
self.logger.debug("Authenticator not configured, forcing configure")
|
||||
return self.prepare_stages(user)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
@ -255,7 +256,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
return self.executor.stage_invalid()
|
||||
if stage.configuration_stages.count() == 1:
|
||||
next_stage = Stage.objects.get_subclass(pk=stage.configuration_stages.first().pk)
|
||||
LOGGER.debug("Single stage configured, auto-selecting", stage=next_stage)
|
||||
self.logger.debug("Single stage configured, auto-selecting", stage=next_stage)
|
||||
self.request.session[SESSION_KEY_SELECTED_STAGE] = next_stage
|
||||
# Because that normal execution only happens on post, we directly inject it here and
|
||||
# return it
|
||||
|
@ -271,7 +272,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
SESSION_KEY_SELECTED_STAGE in self.request.session
|
||||
and self.executor.current_stage.not_configured_action == NotConfiguredAction.CONFIGURE
|
||||
):
|
||||
LOGGER.debug("Got selected stage in session, running that")
|
||||
self.logger.debug("Got selected stage in session, running that")
|
||||
stage_pk = self.request.session.get(SESSION_KEY_SELECTED_STAGE)
|
||||
# Because the foreign key to stage.configuration_stage points to
|
||||
# a base stage class, we need to do another lookup
|
||||
|
@ -326,18 +327,18 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
try:
|
||||
payload = decode(self.request.COOKIES[COOKIE_NAME_MFA], self.cookie_jwt_key, ["HS256"])
|
||||
if payload["stage"] != stage.pk.hex:
|
||||
LOGGER.warning("Invalid stage PK")
|
||||
self.logger.warning("Invalid stage PK")
|
||||
return
|
||||
if datetime.fromtimestamp(payload["exp"]) > latest_allowed:
|
||||
LOGGER.warning("Expired MFA cookie")
|
||||
self.logger.warning("Expired MFA cookie")
|
||||
return
|
||||
if not any(device.pk == payload["device"] for device in allowed_devices):
|
||||
LOGGER.warning("Invalid device PK")
|
||||
self.logger.warning("Invalid device PK")
|
||||
return
|
||||
LOGGER.info("MFA has been used within threshold")
|
||||
self.logger.info("MFA has been used within threshold")
|
||||
raise FlowSkipStageException()
|
||||
except (PyJWTError, ValueError, TypeError) as exc:
|
||||
LOGGER.info("Invalid mfa cookie for device", exc=exc)
|
||||
self.logger.info("Invalid mfa cookie for device", exc=exc)
|
||||
|
||||
def set_valid_mfa_cookie(self, device: Device) -> HttpResponse:
|
||||
"""Set an MFA cookie to allow users to skip MFA validation in this context (browser)
|
||||
|
@ -346,7 +347,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
stage: AuthenticatorValidateStage = self.executor.current_stage
|
||||
delta = timedelta_from_string(stage.last_auth_threshold)
|
||||
if delta.total_seconds() < 1:
|
||||
LOGGER.info("Not setting MFA cookie since threshold is not set.")
|
||||
self.logger.info("Not setting MFA cookie since threshold is not set.")
|
||||
return self.executor.stage_ok()
|
||||
expiry = datetime.now() + delta
|
||||
cookie_payload = {
|
||||
|
@ -375,7 +376,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
webauthn_device: WebAuthnDevice = response.data.get("webauthn", None)
|
||||
if not webauthn_device:
|
||||
return self.executor.stage_ok()
|
||||
LOGGER.debug("Set user from user-less flow", user=webauthn_device.user)
|
||||
self.logger.debug("Set user from user-less flow", user=webauthn_device.user)
|
||||
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = webauthn_device.user
|
||||
self.executor.plan.context[PLAN_CONTEXT_METHOD] = "auth_webauthn_pwl"
|
||||
self.executor.plan.context[PLAN_CONTEXT_METHOD_ARGS] = cleanse_dict(
|
||||
|
|
|
@ -29,7 +29,6 @@ from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnS
|
|||
from authentik.stages.authenticator_webauthn.utils import get_origin, get_rp_id
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
SESSION_KEY_WEBAUTHN_CHALLENGE = "authentik/stages/authenticator_webauthn/challenge"
|
||||
|
||||
|
||||
|
@ -115,7 +114,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
|
|||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
|
||||
if not user:
|
||||
LOGGER.debug("No pending user, continuing")
|
||||
self.logger.debug("No pending user, continuing")
|
||||
return self.executor.stage_ok()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
"""Deny stage logic"""
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.stage import StageView
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class DenyStageView(StageView):
|
||||
"""Cancells the current flow"""
|
||||
|
|
|
@ -10,7 +10,6 @@ from django.utils.timezone import now
|
|||
from django.utils.translation import gettext as _
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
|
||||
from authentik.flows.models import FlowToken
|
||||
|
@ -21,7 +20,6 @@ from authentik.stages.email.models import EmailStage
|
|||
from authentik.stages.email.tasks import send_mails
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
|
||||
LOGGER = get_logger()
|
||||
PLAN_CONTEXT_EMAIL_SENT = "email_sent"
|
||||
PLAN_CONTEXT_EMAIL_OVERRIDE = "email"
|
||||
|
||||
|
@ -113,7 +111,7 @@ class EmailStageView(ChallengeStageView):
|
|||
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER].save()
|
||||
return self.executor.stage_ok()
|
||||
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
||||
LOGGER.debug("No pending user")
|
||||
self.logger.debug("No pending user")
|
||||
messages.error(self.request, _("No pending user."))
|
||||
return self.executor.stage_invalid()
|
||||
# Check if we've already sent the initial e-mail
|
||||
|
|
|
@ -159,11 +159,11 @@ class IdentificationStageView(ChallengeStageView):
|
|||
model_field += "__exact"
|
||||
query |= Q(**{model_field: uid_value})
|
||||
if not query:
|
||||
LOGGER.debug("Empty user query", query=query)
|
||||
self.logger.debug("Empty user query", query=query)
|
||||
return None
|
||||
users = User.objects.filter(query, is_active=True)
|
||||
if users.exists():
|
||||
LOGGER.debug("Found user", user=users.first(), query=query)
|
||||
self.logger.debug("Found user", user=users.first(), query=query)
|
||||
return users.first()
|
||||
return None
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ from typing import Optional
|
|||
from deepmerge import always_merger
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.http.response import HttpResponseBadRequest
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.models import in_memory_stage
|
||||
from authentik.flows.stage import StageView
|
||||
|
@ -13,7 +12,6 @@ from authentik.stages.invitation.models import Invitation, InvitationStage
|
|||
from authentik.stages.invitation.signals import invitation_used
|
||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
|
||||
LOGGER = get_logger()
|
||||
INVITATION_TOKEN_KEY_CONTEXT = "token" # nosec
|
||||
INVITATION_TOKEN_KEY = "itoken" # nosec
|
||||
INVITATION_IN_EFFECT = "invitation_in_effect"
|
||||
|
@ -51,7 +49,7 @@ class InvitationStageView(StageView):
|
|||
|
||||
invite: Invitation = Invitation.objects.filter(pk=token).first()
|
||||
if not invite:
|
||||
LOGGER.debug("invalid invitation", token=token)
|
||||
self.logger.debug("invalid invitation", token=token)
|
||||
if stage.continue_flow_without_invitation:
|
||||
return self.executor.stage_ok()
|
||||
return self.executor.stage_invalid()
|
||||
|
@ -81,11 +79,11 @@ class InvitationFinalStageView(StageView):
|
|||
"""Delete invitation if single_use is active"""
|
||||
invitation: Invitation = self.executor.plan.context.get(INVITATION, None)
|
||||
if not invitation:
|
||||
LOGGER.warning("InvitationFinalStageView stage called without invitation")
|
||||
self.logger.warning("InvitationFinalStageView stage called without invitation")
|
||||
return HttpResponseBadRequest
|
||||
token = invitation.invite_uuid.hex
|
||||
if invitation.single_use:
|
||||
invitation.delete()
|
||||
LOGGER.debug("Deleted invitation", token=token)
|
||||
self.logger.debug("Deleted invitation", token=token)
|
||||
del self.executor.plan.context[INVITATION]
|
||||
return self.executor.stage_ok()
|
||||
|
|
|
@ -108,7 +108,7 @@ class PasswordStageView(ChallengeStageView):
|
|||
self.request.session[SESSION_KEY_INVALID_TRIES]
|
||||
> current_stage.failed_attempts_before_cancel
|
||||
):
|
||||
LOGGER.debug("User has exceeded maximum tries")
|
||||
self.logger.debug("User has exceeded maximum tries")
|
||||
del self.request.session[SESSION_KEY_INVALID_TRIES]
|
||||
return self.executor.stage_invalid()
|
||||
return super().challenge_invalid(response)
|
||||
|
@ -135,18 +135,18 @@ class PasswordStageView(ChallengeStageView):
|
|||
except PermissionDenied:
|
||||
del auth_kwargs["password"]
|
||||
# User was found, but permission was denied (i.e. user is not active)
|
||||
LOGGER.debug("Denied access", **auth_kwargs)
|
||||
self.logger.debug("Denied access", **auth_kwargs)
|
||||
return self.executor.stage_invalid()
|
||||
except ValidationError as exc:
|
||||
del auth_kwargs["password"]
|
||||
# User was found, authentication succeeded, but another signal raised an error
|
||||
# (most likely LDAP)
|
||||
LOGGER.debug("Validation error from signal", exc=exc, **auth_kwargs)
|
||||
self.logger.debug("Validation error from signal", exc=exc, **auth_kwargs)
|
||||
return self.executor.stage_invalid()
|
||||
else:
|
||||
if not user:
|
||||
# No user was found -> invalid credentials
|
||||
LOGGER.debug("Invalid credentials")
|
||||
self.logger.debug("Invalid credentials")
|
||||
# Manually inject error into form
|
||||
response._errors.setdefault("password", [])
|
||||
response._errors["password"].append(ErrorDetail(_("Invalid password"), "invalid"))
|
||||
|
|
|
@ -10,7 +10,6 @@ from django.utils.translation import gettext_lazy as _
|
|||
from guardian.shortcuts import get_anonymous_user
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, IntegerField, empty
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.core.models import User
|
||||
|
@ -22,7 +21,6 @@ from authentik.policies.models import PolicyBinding, PolicyBindingModel, PolicyE
|
|||
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
||||
from authentik.stages.prompt.signals import password_validate
|
||||
|
||||
LOGGER = get_logger()
|
||||
PLAN_CONTEXT_PROMPT = "prompt_data"
|
||||
|
||||
|
||||
|
|
|
@ -3,13 +3,10 @@ from django.contrib import messages
|
|||
from django.contrib.auth import logout
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.translation import gettext as _
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import StageView
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class UserDeleteStageView(StageView):
|
||||
"""Finalise unenrollment flow by deleting the user object."""
|
||||
|
@ -24,11 +21,11 @@ class UserDeleteStageView(StageView):
|
|||
if not user.is_authenticated:
|
||||
message = _("No Pending User.")
|
||||
messages.error(request, message)
|
||||
LOGGER.debug(message)
|
||||
self.logger.debug(message)
|
||||
return self.executor.stage_invalid()
|
||||
logout(self.request)
|
||||
user.delete()
|
||||
LOGGER.debug("Deleted user", user=user)
|
||||
self.logger.debug("Deleted user", user=user)
|
||||
if PLAN_CONTEXT_PENDING_USER in self.executor.plan.context:
|
||||
del self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
|
||||
return self.executor.stage_ok()
|
||||
|
|
|
@ -3,7 +3,6 @@ from django.contrib import messages
|
|||
from django.contrib.auth import login
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.translation import gettext as _
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
|
@ -12,7 +11,6 @@ from authentik.lib.utils.time import timedelta_from_string
|
|||
from authentik.stages.password import BACKEND_INBUILT
|
||||
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||
|
||||
LOGGER = get_logger()
|
||||
USER_LOGIN_AUTHENTICATED = "user_login_authenticated"
|
||||
|
||||
|
||||
|
@ -28,14 +26,14 @@ class UserLoginStageView(StageView):
|
|||
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
||||
message = _("No Pending user to login.")
|
||||
messages.error(request, message)
|
||||
LOGGER.debug(message)
|
||||
self.logger.debug(message)
|
||||
return self.executor.stage_invalid()
|
||||
backend = self.executor.plan.context.get(
|
||||
PLAN_CONTEXT_AUTHENTICATION_BACKEND, BACKEND_INBUILT
|
||||
)
|
||||
user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
|
||||
if not user.is_active:
|
||||
LOGGER.warning("User is not active, login will not work.")
|
||||
self.logger.warning("User is not active, login will not work.")
|
||||
login(
|
||||
self.request,
|
||||
user,
|
||||
|
@ -46,7 +44,7 @@ class UserLoginStageView(StageView):
|
|||
self.request.session.set_expiry(0)
|
||||
else:
|
||||
self.request.session.set_expiry(delta)
|
||||
LOGGER.debug(
|
||||
self.logger.debug(
|
||||
"Logged in",
|
||||
backend=backend,
|
||||
user=user,
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
"""Logout stage logic"""
|
||||
from django.contrib.auth import logout
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.stage import StageView
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class UserLogoutStageView(StageView):
|
||||
"""Finalise Authentication flow by logging the user in"""
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Remove the user from the current session"""
|
||||
LOGGER.debug(
|
||||
self.logger.debug(
|
||||
"Logged out",
|
||||
user=request.user,
|
||||
flow_slug=self.executor.flow.slug,
|
||||
|
|
|
@ -7,7 +7,6 @@ from django.db import transaction
|
|||
from django.db.utils import IntegrityError
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.translation import gettext as _
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER
|
||||
from authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection
|
||||
|
@ -19,7 +18,6 @@ from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
|||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
from authentik.stages.user_write.signals import user_write
|
||||
|
||||
LOGGER = get_logger()
|
||||
PLAN_CONTEXT_GROUPS = "groups"
|
||||
|
||||
|
||||
|
@ -56,7 +54,7 @@ class UserWriteStageView(StageView):
|
|||
is_active=not self.executor.current_stage.create_users_as_inactive
|
||||
)
|
||||
self.executor.plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_INBUILT
|
||||
LOGGER.debug(
|
||||
self.logger.debug(
|
||||
"Created new user",
|
||||
flow_slug=self.executor.flow.slug,
|
||||
)
|
||||
|
@ -86,7 +84,7 @@ class UserWriteStageView(StageView):
|
|||
# `attribute_`, to prevent accidentally saving values
|
||||
else:
|
||||
if not key.startswith("attributes.") and not key.startswith("attributes_"):
|
||||
LOGGER.debug("discarding key", key=key)
|
||||
self.logger.debug("discarding key", key=key)
|
||||
continue
|
||||
UserWriteStageView.write_attribute(user, key, value)
|
||||
# Check if we're writing from a source, and save the source to the attributes
|
||||
|
@ -106,7 +104,7 @@ class UserWriteStageView(StageView):
|
|||
if PLAN_CONTEXT_PROMPT not in self.executor.plan.context:
|
||||
message = _("No Pending data.")
|
||||
messages.error(request, message)
|
||||
LOGGER.debug(message)
|
||||
self.logger.debug(message)
|
||||
return self.executor.stage_invalid()
|
||||
data = self.executor.plan.context[PLAN_CONTEXT_PROMPT]
|
||||
user, user_created = self.ensure_user()
|
||||
|
@ -123,7 +121,7 @@ class UserWriteStageView(StageView):
|
|||
self.update_user(user)
|
||||
# Extra check to prevent flows from saving a user with a blank username
|
||||
if user.username == "":
|
||||
LOGGER.warning("Aborting write to empty username", user=user)
|
||||
self.logger.warning("Aborting write to empty username", user=user)
|
||||
return self.executor.stage_invalid()
|
||||
try:
|
||||
with transaction.atomic():
|
||||
|
@ -133,14 +131,14 @@ class UserWriteStageView(StageView):
|
|||
if PLAN_CONTEXT_GROUPS in self.executor.plan.context:
|
||||
user.ak_groups.add(*self.executor.plan.context[PLAN_CONTEXT_GROUPS])
|
||||
except (IntegrityError, ValueError, TypeError) as exc:
|
||||
LOGGER.warning("Failed to save user", exc=exc)
|
||||
self.logger.warning("Failed to save user", exc=exc)
|
||||
return self.executor.stage_invalid()
|
||||
user_write.send(sender=self, request=request, user=user, data=data, created=user_created)
|
||||
# Check if the password has been updated, and update the session auth hash
|
||||
if should_update_session:
|
||||
update_session_auth_hash(self.request, user)
|
||||
LOGGER.debug("Updated session hash", user=user)
|
||||
LOGGER.debug(
|
||||
self.logger.debug("Updated session hash", user=user)
|
||||
self.logger.debug(
|
||||
"Updated existing user",
|
||||
user=user,
|
||||
flow_slug=self.executor.flow.slug,
|
||||
|
|
Reference in New Issue