We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
OAuth source is added to user attributes each time user write is run
**Describe the bug**
The user attribute `goauthentik.io/user/sources` gets appended each time an user write stage is run.

```
goauthentik.io/user/sources:
  - Wordpress
  - Wordpress
  - Wordpress
  - Wordpress
  - Wordpress
  ...
```

**To Reproduce**
Steps to reproduce the behavior:

1. Configure OAuth source
2. Add User Write Stage to default-source-authentication flow
3. Login a few times
4. Each login adds a new entry under `goauthentik.io/user/sources`

**Expected behavior**
It should only add the source if it isn't there already

**Version and Deployment (please complete the following information):**

-   authentik version: 2023.5.4
-   Deployment: docker

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `authentik/stages/user_write/stage.py`
Content:
```
1 """Write stage logic"""
2 from typing import Any, Optional
3 
4 from django.contrib.auth import update_session_auth_hash
5 from django.db import transaction
6 from django.db.utils import IntegrityError, InternalError
7 from django.http import HttpRequest, HttpResponse
8 from django.utils.translation import gettext as _
9 from rest_framework.exceptions import ValidationError
10 
11 from authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER
12 from authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection
13 from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
14 from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
15 from authentik.flows.stage import StageView
16 from authentik.flows.views.executor import FlowExecutorView
17 from authentik.stages.password import BACKEND_INBUILT
18 from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
19 from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
20 from authentik.stages.user_write.models import UserCreationMode
21 from authentik.stages.user_write.signals import user_write
22 
23 PLAN_CONTEXT_GROUPS = "groups"
24 PLAN_CONTEXT_USER_PATH = "user_path"
25 
26 
27 class UserWriteStageView(StageView):
28     """Finalise Enrollment flow by creating a user object."""
29 
30     def __init__(self, executor: FlowExecutorView, **kwargs):
31         super().__init__(executor, **kwargs)
32         self.disallowed_user_attributes = [
33             "groups",
34         ]
35 
36     @staticmethod
37     def write_attribute(user: User, key: str, value: Any):
38         """Allow use of attributes.foo.bar when writing to a user, with full
39         recursion"""
40         parts = key.replace("_", ".").split(".")
41         if len(parts) < 1:  # pragma: no cover
42             return
43         # Function will always be called with a key like attributes.
44         # this is just a sanity check to ensure that is removed
45         if parts[0] == "attributes":
46             parts = parts[1:]
47         attrs = user.attributes
48         for comp in parts[:-1]:
49             if comp not in attrs:
50                 attrs[comp] = {}
51             attrs = attrs.get(comp)
52         attrs[parts[-1]] = value
53 
54     def post(self, request: HttpRequest) -> HttpResponse:
55         """Wrapper for post requests"""
56         return self.get(request)
57 
58     def ensure_user(self) -> tuple[Optional[User], bool]:
59         """Ensure a user exists"""
60         user_created = False
61         path = self.executor.plan.context.get(
62             PLAN_CONTEXT_USER_PATH, self.executor.current_stage.user_path_template
63         )
64         if path == "":
65             path = User.default_path()
66         if not self.request.user.is_anonymous:
67             self.executor.plan.context.setdefault(PLAN_CONTEXT_PENDING_USER, self.request.user)
68         if (
69             PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context
70             or self.executor.current_stage.user_creation_mode == UserCreationMode.ALWAYS_CREATE
71         ):
72             if self.executor.current_stage.user_creation_mode == UserCreationMode.NEVER_CREATE:
73                 return None, False
74             self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User(
75                 is_active=not self.executor.current_stage.create_users_as_inactive,
76                 path=path,
77             )
78             self.executor.plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_INBUILT
79             self.logger.debug(
80                 "Created new user",
81                 flow_slug=self.executor.flow.slug,
82             )
83             user_created = True
84         user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
85         return user, user_created
86 
87     def update_user(self, user: User):
88         """Update `user` with data from plan context
89 
90         Only simple attributes are updated, nothing which requires a foreign key or m2m"""
91         data: dict = self.executor.plan.context[PLAN_CONTEXT_PROMPT]
92         # This is always sent back but not written to the user
93         data.pop("component", None)
94         for key, value in data.items():
95             setter_name = f"set_{key}"
96             # Check if user has a setter for this key, like set_password
97             if hasattr(user, setter_name):
98                 setter = getattr(user, setter_name)
99                 if callable(setter):
100                     setter(value)
101             elif key in self.disallowed_user_attributes:
102                 self.logger.info("discarding key", key=key)
103                 continue
104             # For exact attributes match, update the dictionary in place
105             elif key == "attributes":
106                 user.attributes.update(value)
107             # If using dot notation, use the correct helper to update the nested value
108             elif key.startswith("attributes.") or key.startswith("attributes_"):
109                 UserWriteStageView.write_attribute(user, key, value)
110             # User has this key already
111             elif hasattr(user, key):
112                 setattr(user, key, value)
113             # If none of the cases above matched, we have an attribute that the user doesn't have,
114             # has no setter for, is not a nested attributes value and as such is invalid
115             else:
116                 self.logger.info("discarding key", key=key)
117                 continue
118         # Check if we're writing from a source, and save the source to the attributes
119         if PLAN_CONTEXT_SOURCES_CONNECTION in self.executor.plan.context:
120             if USER_ATTRIBUTE_SOURCES not in user.attributes or not isinstance(
121                 user.attributes.get(USER_ATTRIBUTE_SOURCES), list
122             ):
123                 user.attributes[USER_ATTRIBUTE_SOURCES] = []
124             connection: UserSourceConnection = self.executor.plan.context[
125                 PLAN_CONTEXT_SOURCES_CONNECTION
126             ]
127             user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)
128 
129     def get(self, request: HttpRequest) -> HttpResponse:
130         """Save data in the current flow to the currently pending user. If no user is pending,
131         a new user is created."""
132         if PLAN_CONTEXT_PROMPT not in self.executor.plan.context:
133             message = _("No Pending data.")
134             self.logger.debug(message)
135             return self.executor.stage_invalid(message)
136         data = self.executor.plan.context[PLAN_CONTEXT_PROMPT]
137         user, user_created = self.ensure_user()
138         if not user:
139             message = _("No user found and can't create new user.")
140             self.logger.info(message)
141             return self.executor.stage_invalid(message)
142         # Before we change anything, check if the user is the same as in the request
143         # and we're updating a password. In that case we need to update the session hash
144         # Also check that we're not currently impersonating, so we don't update the session
145         should_update_session = False
146         if (
147             any("password" in x for x in data.keys())
148             and self.request.user.pk == user.pk
149             and SESSION_KEY_IMPERSONATE_USER not in self.request.session
150         ):
151             should_update_session = True
152         try:
153             self.update_user(user)
154         except ValidationError as exc:
155             self.logger.warning("failed to update user", exc=exc)
156             return self.executor.stage_invalid(_("Failed to update user. Please try again later."))
157         # Extra check to prevent flows from saving a user with a blank username
158         if user.username == "":
159             self.logger.warning("Aborting write to empty username", user=user)
160             return self.executor.stage_invalid()
161         try:
162             with transaction.atomic():
163                 user.save()
164                 if self.executor.current_stage.create_users_group:
165                     user.ak_groups.add(self.executor.current_stage.create_users_group)
166                 if PLAN_CONTEXT_GROUPS in self.executor.plan.context:
167                     user.ak_groups.add(*self.executor.plan.context[PLAN_CONTEXT_GROUPS])
168         except (IntegrityError, ValueError, TypeError, InternalError) as exc:
169             self.logger.warning("Failed to save user", exc=exc)
170             return self.executor.stage_invalid(_("Failed to update user. Please try again later."))
171         user_write.send(sender=self, request=request, user=user, data=data, created=user_created)
172         # Check if the password has been updated, and update the session auth hash
173         if should_update_session:
174             update_session_auth_hash(self.request, user)
175             self.logger.debug("Updated session hash", user=user)
176         self.logger.debug(
177             "Updated existing user",
178             user=user,
179             flow_slug=self.executor.flow.slug,
180         )
181         return self.executor.stage_ok()
182
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/authentik/stages/user_write/stage.py b/authentik/stages/user_write/stage.py
--- a/authentik/stages/user_write/stage.py
+++ b/authentik/stages/user_write/stage.py
@@ -124,7 +124,8 @@
             connection: UserSourceConnection = self.executor.plan.context[
                 PLAN_CONTEXT_SOURCES_CONNECTION
             ]
-            user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)
+            if connection.source.name not in user.attributes[USER_ATTRIBUTE_SOURCES]:
+                user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)
 
     def get(self, request: HttpRequest) -> HttpResponse:
         """Save data in the current flow to the currently pending user. If no user is pending,

{"golden_diff": "diff --git a/authentik/stages/user_write/stage.py b/authentik/stages/user_write/stage.py\n--- a/authentik/stages/user_write/stage.py\n+++ b/authentik/stages/user_write/stage.py\n@@ -124,7 +124,8 @@\n             connection: UserSourceConnection = self.executor.plan.context[\n                 PLAN_CONTEXT_SOURCES_CONNECTION\n             ]\n-            user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)\n+            if connection.source.name not in user.attributes[USER_ATTRIBUTE_SOURCES]:\n+                user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)\n \n     def get(self, request: HttpRequest) -> HttpResponse:\n         \"\"\"Save data in the current flow to the currently pending user. If no user is pending,\n", "issue": "OAuth source is added to user attributes each time user write is run\n**Describe the bug**\r\nThe user attribute `goauthentik.io/user/sources` gets appended each time an user write stage is run.\r\n\r\n```\r\ngoauthentik.io/user/sources:\r\n  - Wordpress\r\n  - Wordpress\r\n  - Wordpress\r\n  - Wordpress\r\n  - Wordpress\r\n  ...\r\n```\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n\r\n1. Configure OAuth source\r\n2. Add User Write Stage to default-source-authentication flow\r\n3. Login a few times\r\n4. Each login adds a new entry under `goauthentik.io/user/sources`\r\n\r\n**Expected behavior**\r\nIt should only add the source if it isn't there already\r\n\r\n**Version and Deployment (please complete the following information):**\r\n\r\n-   authentik version: 2023.5.4\r\n-   Deployment: docker\n", "before_files": [{"content": "\"\"\"Write stage logic\"\"\"\nfrom typing import Any, Optional\n\nfrom django.contrib.auth import update_session_auth_hash\nfrom django.db import transaction\nfrom django.db.utils import IntegrityError, InternalError\nfrom django.http import HttpRequest, HttpResponse\nfrom django.utils.translation import gettext as _\nfrom rest_framework.exceptions import ValidationError\n\nfrom authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER\nfrom authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection\nfrom authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION\nfrom authentik.flows.planner import PLAN_CONTEXT_PENDING_USER\nfrom authentik.flows.stage import StageView\nfrom authentik.flows.views.executor import FlowExecutorView\nfrom authentik.stages.password import BACKEND_INBUILT\nfrom authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND\nfrom authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT\nfrom authentik.stages.user_write.models import UserCreationMode\nfrom authentik.stages.user_write.signals import user_write\n\nPLAN_CONTEXT_GROUPS = \"groups\"\nPLAN_CONTEXT_USER_PATH = \"user_path\"\n\n\nclass UserWriteStageView(StageView):\n    \"\"\"Finalise Enrollment flow by creating a user object.\"\"\"\n\n    def __init__(self, executor: FlowExecutorView, **kwargs):\n        super().__init__(executor, **kwargs)\n        self.disallowed_user_attributes = [\n            \"groups\",\n        ]\n\n    @staticmethod\n    def write_attribute(user: User, key: str, value: Any):\n        \"\"\"Allow use of attributes.foo.bar when writing to a user, with full\n        recursion\"\"\"\n        parts = key.replace(\"_\", \".\").split(\".\")\n        if len(parts) < 1:  # pragma: no cover\n            return\n        # Function will always be called with a key like attributes.\n        # this is just a sanity check to ensure that is removed\n        if parts[0] == \"attributes\":\n            parts = parts[1:]\n        attrs = user.attributes\n        for comp in parts[:-1]:\n            if comp not in attrs:\n                attrs[comp] = {}\n            attrs = attrs.get(comp)\n        attrs[parts[-1]] = value\n\n    def post(self, request: HttpRequest) -> HttpResponse:\n        \"\"\"Wrapper for post requests\"\"\"\n        return self.get(request)\n\n    def ensure_user(self) -> tuple[Optional[User], bool]:\n        \"\"\"Ensure a user exists\"\"\"\n        user_created = False\n        path = self.executor.plan.context.get(\n            PLAN_CONTEXT_USER_PATH, self.executor.current_stage.user_path_template\n        )\n        if path == \"\":\n            path = User.default_path()\n        if not self.request.user.is_anonymous:\n            self.executor.plan.context.setdefault(PLAN_CONTEXT_PENDING_USER, self.request.user)\n        if (\n            PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context\n            or self.executor.current_stage.user_creation_mode == UserCreationMode.ALWAYS_CREATE\n        ):\n            if self.executor.current_stage.user_creation_mode == UserCreationMode.NEVER_CREATE:\n                return None, False\n            self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User(\n                is_active=not self.executor.current_stage.create_users_as_inactive,\n                path=path,\n            )\n            self.executor.plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_INBUILT\n            self.logger.debug(\n                \"Created new user\",\n                flow_slug=self.executor.flow.slug,\n            )\n            user_created = True\n        user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]\n        return user, user_created\n\n    def update_user(self, user: User):\n        \"\"\"Update `user` with data from plan context\n\n        Only simple attributes are updated, nothing which requires a foreign key or m2m\"\"\"\n        data: dict = self.executor.plan.context[PLAN_CONTEXT_PROMPT]\n        # This is always sent back but not written to the user\n        data.pop(\"component\", None)\n        for key, value in data.items():\n            setter_name = f\"set_{key}\"\n            # Check if user has a setter for this key, like set_password\n            if hasattr(user, setter_name):\n                setter = getattr(user, setter_name)\n                if callable(setter):\n                    setter(value)\n            elif key in self.disallowed_user_attributes:\n                self.logger.info(\"discarding key\", key=key)\n                continue\n            # For exact attributes match, update the dictionary in place\n            elif key == \"attributes\":\n                user.attributes.update(value)\n            # If using dot notation, use the correct helper to update the nested value\n            elif key.startswith(\"attributes.\") or key.startswith(\"attributes_\"):\n                UserWriteStageView.write_attribute(user, key, value)\n            # User has this key already\n            elif hasattr(user, key):\n                setattr(user, key, value)\n            # If none of the cases above matched, we have an attribute that the user doesn't have,\n            # has no setter for, is not a nested attributes value and as such is invalid\n            else:\n                self.logger.info(\"discarding key\", key=key)\n                continue\n        # Check if we're writing from a source, and save the source to the attributes\n        if PLAN_CONTEXT_SOURCES_CONNECTION in self.executor.plan.context:\n            if USER_ATTRIBUTE_SOURCES not in user.attributes or not isinstance(\n                user.attributes.get(USER_ATTRIBUTE_SOURCES), list\n            ):\n                user.attributes[USER_ATTRIBUTE_SOURCES] = []\n            connection: UserSourceConnection = self.executor.plan.context[\n                PLAN_CONTEXT_SOURCES_CONNECTION\n            ]\n            user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)\n\n    def get(self, request: HttpRequest) -> HttpResponse:\n        \"\"\"Save data in the current flow to the currently pending user. If no user is pending,\n        a new user is created.\"\"\"\n        if PLAN_CONTEXT_PROMPT not in self.executor.plan.context:\n            message = _(\"No Pending data.\")\n            self.logger.debug(message)\n            return self.executor.stage_invalid(message)\n        data = self.executor.plan.context[PLAN_CONTEXT_PROMPT]\n        user, user_created = self.ensure_user()\n        if not user:\n            message = _(\"No user found and can't create new user.\")\n            self.logger.info(message)\n            return self.executor.stage_invalid(message)\n        # Before we change anything, check if the user is the same as in the request\n        # and we're updating a password. In that case we need to update the session hash\n        # Also check that we're not currently impersonating, so we don't update the session\n        should_update_session = False\n        if (\n            any(\"password\" in x for x in data.keys())\n            and self.request.user.pk == user.pk\n            and SESSION_KEY_IMPERSONATE_USER not in self.request.session\n        ):\n            should_update_session = True\n        try:\n            self.update_user(user)\n        except ValidationError as exc:\n            self.logger.warning(\"failed to update user\", exc=exc)\n            return self.executor.stage_invalid(_(\"Failed to update user. Please try again later.\"))\n        # Extra check to prevent flows from saving a user with a blank username\n        if user.username == \"\":\n            self.logger.warning(\"Aborting write to empty username\", user=user)\n            return self.executor.stage_invalid()\n        try:\n            with transaction.atomic():\n                user.save()\n                if self.executor.current_stage.create_users_group:\n                    user.ak_groups.add(self.executor.current_stage.create_users_group)\n                if PLAN_CONTEXT_GROUPS in self.executor.plan.context:\n                    user.ak_groups.add(*self.executor.plan.context[PLAN_CONTEXT_GROUPS])\n        except (IntegrityError, ValueError, TypeError, InternalError) as exc:\n            self.logger.warning(\"Failed to save user\", exc=exc)\n            return self.executor.stage_invalid(_(\"Failed to update user. Please try again later.\"))\n        user_write.send(sender=self, request=request, user=user, data=data, created=user_created)\n        # Check if the password has been updated, and update the session auth hash\n        if should_update_session:\n            update_session_auth_hash(self.request, user)\n            self.logger.debug(\"Updated session hash\", user=user)\n        self.logger.debug(\n            \"Updated existing user\",\n            user=user,\n            flow_slug=self.executor.flow.slug,\n        )\n        return self.executor.stage_ok()\n", "path": "authentik/stages/user_write/stage.py"}], "after_files": [{"content": "\"\"\"Write stage logic\"\"\"\nfrom typing import Any, Optional\n\nfrom django.contrib.auth import update_session_auth_hash\nfrom django.db import transaction\nfrom django.db.utils import IntegrityError, InternalError\nfrom django.http import HttpRequest, HttpResponse\nfrom django.utils.translation import gettext as _\nfrom rest_framework.exceptions import ValidationError\n\nfrom authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER\nfrom authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection\nfrom authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION\nfrom authentik.flows.planner import PLAN_CONTEXT_PENDING_USER\nfrom authentik.flows.stage import StageView\nfrom authentik.flows.views.executor import FlowExecutorView\nfrom authentik.stages.password import BACKEND_INBUILT\nfrom authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND\nfrom authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT\nfrom authentik.stages.user_write.models import UserCreationMode\nfrom authentik.stages.user_write.signals import user_write\n\nPLAN_CONTEXT_GROUPS = \"groups\"\nPLAN_CONTEXT_USER_PATH = \"user_path\"\n\n\nclass UserWriteStageView(StageView):\n    \"\"\"Finalise Enrollment flow by creating a user object.\"\"\"\n\n    def __init__(self, executor: FlowExecutorView, **kwargs):\n        super().__init__(executor, **kwargs)\n        self.disallowed_user_attributes = [\n            \"groups\",\n        ]\n\n    @staticmethod\n    def write_attribute(user: User, key: str, value: Any):\n        \"\"\"Allow use of attributes.foo.bar when writing to a user, with full\n        recursion\"\"\"\n        parts = key.replace(\"_\", \".\").split(\".\")\n        if len(parts) < 1:  # pragma: no cover\n            return\n        # Function will always be called with a key like attributes.\n        # this is just a sanity check to ensure that is removed\n        if parts[0] == \"attributes\":\n            parts = parts[1:]\n        attrs = user.attributes\n        for comp in parts[:-1]:\n            if comp not in attrs:\n                attrs[comp] = {}\n            attrs = attrs.get(comp)\n        attrs[parts[-1]] = value\n\n    def post(self, request: HttpRequest) -> HttpResponse:\n        \"\"\"Wrapper for post requests\"\"\"\n        return self.get(request)\n\n    def ensure_user(self) -> tuple[Optional[User], bool]:\n        \"\"\"Ensure a user exists\"\"\"\n        user_created = False\n        path = self.executor.plan.context.get(\n            PLAN_CONTEXT_USER_PATH, self.executor.current_stage.user_path_template\n        )\n        if path == \"\":\n            path = User.default_path()\n        if not self.request.user.is_anonymous:\n            self.executor.plan.context.setdefault(PLAN_CONTEXT_PENDING_USER, self.request.user)\n        if (\n            PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context\n            or self.executor.current_stage.user_creation_mode == UserCreationMode.ALWAYS_CREATE\n        ):\n            if self.executor.current_stage.user_creation_mode == UserCreationMode.NEVER_CREATE:\n                return None, False\n            self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User(\n                is_active=not self.executor.current_stage.create_users_as_inactive,\n                path=path,\n            )\n            self.executor.plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_INBUILT\n            self.logger.debug(\n                \"Created new user\",\n                flow_slug=self.executor.flow.slug,\n            )\n            user_created = True\n        user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]\n        return user, user_created\n\n    def update_user(self, user: User):\n        \"\"\"Update `user` with data from plan context\n\n        Only simple attributes are updated, nothing which requires a foreign key or m2m\"\"\"\n        data: dict = self.executor.plan.context[PLAN_CONTEXT_PROMPT]\n        # This is always sent back but not written to the user\n        data.pop(\"component\", None)\n        for key, value in data.items():\n            setter_name = f\"set_{key}\"\n            # Check if user has a setter for this key, like set_password\n            if hasattr(user, setter_name):\n                setter = getattr(user, setter_name)\n                if callable(setter):\n                    setter(value)\n            elif key in self.disallowed_user_attributes:\n                self.logger.info(\"discarding key\", key=key)\n                continue\n            # For exact attributes match, update the dictionary in place\n            elif key == \"attributes\":\n                user.attributes.update(value)\n            # If using dot notation, use the correct helper to update the nested value\n            elif key.startswith(\"attributes.\") or key.startswith(\"attributes_\"):\n                UserWriteStageView.write_attribute(user, key, value)\n            # User has this key already\n            elif hasattr(user, key):\n                setattr(user, key, value)\n            # If none of the cases above matched, we have an attribute that the user doesn't have,\n            # has no setter for, is not a nested attributes value and as such is invalid\n            else:\n                self.logger.info(\"discarding key\", key=key)\n                continue\n        # Check if we're writing from a source, and save the source to the attributes\n        if PLAN_CONTEXT_SOURCES_CONNECTION in self.executor.plan.context:\n            if USER_ATTRIBUTE_SOURCES not in user.attributes or not isinstance(\n                user.attributes.get(USER_ATTRIBUTE_SOURCES), list\n            ):\n                user.attributes[USER_ATTRIBUTE_SOURCES] = []\n            connection: UserSourceConnection = self.executor.plan.context[\n                PLAN_CONTEXT_SOURCES_CONNECTION\n            ]\n            if connection.source.name not in user.attributes[USER_ATTRIBUTE_SOURCES]:\n                user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)\n\n    def get(self, request: HttpRequest) -> HttpResponse:\n        \"\"\"Save data in the current flow to the currently pending user. If no user is pending,\n        a new user is created.\"\"\"\n        if PLAN_CONTEXT_PROMPT not in self.executor.plan.context:\n            message = _(\"No Pending data.\")\n            self.logger.debug(message)\n            return self.executor.stage_invalid(message)\n        data = self.executor.plan.context[PLAN_CONTEXT_PROMPT]\n        user, user_created = self.ensure_user()\n        if not user:\n            message = _(\"No user found and can't create new user.\")\n            self.logger.info(message)\n            return self.executor.stage_invalid(message)\n        # Before we change anything, check if the user is the same as in the request\n        # and we're updating a password. In that case we need to update the session hash\n        # Also check that we're not currently impersonating, so we don't update the session\n        should_update_session = False\n        if (\n            any(\"password\" in x for x in data.keys())\n            and self.request.user.pk == user.pk\n            and SESSION_KEY_IMPERSONATE_USER not in self.request.session\n        ):\n            should_update_session = True\n        try:\n            self.update_user(user)\n        except ValidationError as exc:\n            self.logger.warning(\"failed to update user\", exc=exc)\n            return self.executor.stage_invalid(_(\"Failed to update user. Please try again later.\"))\n        # Extra check to prevent flows from saving a user with a blank username\n        if user.username == \"\":\n            self.logger.warning(\"Aborting write to empty username\", user=user)\n            return self.executor.stage_invalid()\n        try:\n            with transaction.atomic():\n                user.save()\n                if self.executor.current_stage.create_users_group:\n                    user.ak_groups.add(self.executor.current_stage.create_users_group)\n                if PLAN_CONTEXT_GROUPS in self.executor.plan.context:\n                    user.ak_groups.add(*self.executor.plan.context[PLAN_CONTEXT_GROUPS])\n        except (IntegrityError, ValueError, TypeError, InternalError) as exc:\n            self.logger.warning(\"Failed to save user\", exc=exc)\n            return self.executor.stage_invalid(_(\"Failed to update user. Please try again later.\"))\n        user_write.send(sender=self, request=request, user=user, data=data, created=user_created)\n        # Check if the password has been updated, and update the session auth hash\n        if should_update_session:\n            update_session_auth_hash(self.request, user)\n            self.logger.debug(\"Updated session hash\", user=user)\n        self.logger.debug(\n            \"Updated existing user\",\n            user=user,\n            flow_slug=self.executor.flow.slug,\n        )\n        return self.executor.stage_ok()\n", "path": "authentik/stages/user_write/stage.py"}]}