stages/*: use bound logger (#3012)

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens L 2022-06-01 23:01:58 +02:00 committed by GitHub
parent 5a81ae956f
commit fc1c1a849a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 61 additions and 92 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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(

View file

@ -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)

View file

@ -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"""

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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"))

View file

@ -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"

View file

@ -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()

View file

@ -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,

View file

@ -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,

View file

@ -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,