Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.d/19902.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a bug introduced in Synapse v1.150.0 where reactivating a deactivated and erased user did not restore their profile, breaking login, name changes, and invitations.
Contributed by @m4us1ne.
3 changes: 3 additions & 0 deletions synapse/handlers/deactivate_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,9 @@ async def activate_account(self, user_id: str) -> None:
# Ensure the user is not marked as erased.
await self.store.mark_user_not_erased(user_id)

# The profile row is deleted on erasure, so recreate it if missing.
await self.store.create_profile(user)

# Mark the user as active.
await self.store.set_user_deactivated_status(user_id, False)

Expand Down
8 changes: 5 additions & 3 deletions synapse/storage/databases/main/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,15 +293,17 @@ async def get_profile_fields(self, user_id: UserID) -> dict[str, str]:

async def create_profile(self, user_id: UserID) -> None:
"""
Create a blank profile for a user.
Create a blank profile for a user, if one does not already exist.

Args:
user_id: The user to create the profile for.
"""
user_localpart = user_id.localpart
await self.db_pool.simple_insert(
await self.db_pool.simple_upsert(
table="profiles",
values={"user_id": user_localpart, "full_user_id": user_id.to_string()},
keyvalues={"user_id": user_localpart},
values={},
insertion_values={"full_user_id": user_id.to_string()},
desc="create_profile",
)

Expand Down
67 changes: 67 additions & 0 deletions tests/handlers/test_deactivate_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,3 +483,70 @@ def test_rooms_forgotten_upon_deactivation(self) -> None:

# Validate that the created room is forgotten
self.assertTrue(room_id in forgotten_rooms)

def _get_profile_row(self) -> str | None:
"""Return the `user_id` of `self.user`'s `profiles` row, or None if absent."""
return self.get_success(
self._store.db_pool.simple_select_one_onecol(
table="profiles",
keyvalues={"full_user_id": self.user},
retcol="user_id",
allow_none=True,
desc="_get_profile_row",
)
)

def test_reactivation_recreates_profile(self) -> None:
"""
Tests that reactivating an erased user recreates their profile row, so
that subsequent profile operations work.
"""
self.assertIsNotNone(self._get_profile_row())

# Erasure deletes the whole profiles row.
self._deactivate_my_account()
self.assertIsNone(self._get_profile_row())

# Reactivating recreates a blank profile row.
deactivate_handler = self.hs.get_deactivate_account_handler()
self.get_success(deactivate_handler.activate_account(self.user))
self.assertIsNotNone(self._get_profile_row())

# Setting a display name now works again.
user = UserID.from_string(self.user)
self.get_success(
self.hs.get_profile_handler().set_displayname(
user, create_requester(user), "Reactivated", by_admin=True
)
)
self.assertEqual(
self.get_success(self._store.get_profile_displayname(user)),
"Reactivated",
)

def test_reactivation_without_erasure_keeps_profile(self) -> None:
"""
Reactivating a user whose profile row still exists leaves it untouched.
"""
user = UserID.from_string(self.user)
self.get_success(
self.hs.get_profile_handler().set_displayname(
user, create_requester(user), "Original", by_admin=True
)
)

# Deactivate without erasure, so the profile row is left intact.
deactivate_handler = self.hs.get_deactivate_account_handler()
self.get_success(
deactivate_handler.deactivate_account(
self.user, erase_data=False, requester=create_requester(user)
)
)
self.assertIsNotNone(self._get_profile_row())

# Reactivating must not raise despite the existing profile row.
self.get_success(deactivate_handler.activate_account(self.user))
self.assertEqual(
self.get_success(self._store.get_profile_displayname(user)),
"Original",
)