|
31 | 31 |
|
32 | 32 | import synapse.rest.admin |
33 | 33 | from synapse.api.constants import LoginType, Membership |
34 | | -from synapse.api.errors import Codes, HttpResponseException |
| 34 | +from synapse.api.errors import Codes, HttpResponseException, SynapseError |
35 | 35 | from synapse.appservice import ApplicationService |
36 | 36 | from synapse.rest import admin |
37 | 37 | from synapse.rest.client import account, login, register, room |
38 | 38 | from synapse.rest.synapse.client.password_reset import PasswordResetSubmitTokenResource |
39 | 39 | from synapse.server import HomeServer |
40 | 40 | from synapse.storage._base import db_to_json |
41 | | -from synapse.types import JsonDict, UserID |
| 41 | +from synapse.types import JsonDict, UserID, create_requester |
42 | 42 | from synapse.util.clock import Clock |
43 | 43 |
|
44 | 44 | from tests import unittest |
@@ -500,6 +500,123 @@ def test_deactivate_account(self) -> None: |
500 | 500 | channel = self.make_request("GET", "account/whoami", access_token=tok) |
501 | 501 | self.assertEqual(channel.code, 401) |
502 | 502 |
|
| 503 | + def test_deactivate_erase_account(self) -> None: |
| 504 | + """ |
| 505 | + Test that a user account can be signaled for erasure on the Matrix spec endpoint |
| 506 | + for client access, `/account/deactivate` and that profile data is erased as part |
| 507 | + of the process |
| 508 | + """ |
| 509 | + mxid = self.register_user("kermit", "test") |
| 510 | + user_id = UserID.from_string(mxid) |
| 511 | + tok = self.login("kermit", "test") |
| 512 | + |
| 513 | + profile_handler = self.hs.get_profile_handler() |
| 514 | + |
| 515 | + # Set some profile data that can be checked for after the user is erased |
| 516 | + self.get_success( |
| 517 | + profile_handler.set_displayname( |
| 518 | + user_id, create_requester(user_id), "Kermit the Frog" |
| 519 | + ) |
| 520 | + ) |
| 521 | + self.get_success( |
| 522 | + profile_handler.set_avatar_url( |
| 523 | + user_id, create_requester(user_id), "http://test/Kermit.jpg" |
| 524 | + ) |
| 525 | + ) |
| 526 | + # Verify it is set |
| 527 | + self.assertEqual( |
| 528 | + self.get_success(profile_handler.get_displayname(user_id)), |
| 529 | + "Kermit the Frog", |
| 530 | + ) |
| 531 | + self.assertEqual( |
| 532 | + self.get_success(profile_handler.get_avatar_url(user_id)), |
| 533 | + "http://test/Kermit.jpg", |
| 534 | + ) |
| 535 | + |
| 536 | + # Deactivate! |
| 537 | + self.deactivate(mxid, tok, erase=True) |
| 538 | + |
| 539 | + store = self.hs.get_datastores().main |
| 540 | + |
| 541 | + # Check that the user has been marked as deactivated. |
| 542 | + self.assertTrue(self.get_success(store.get_user_deactivated_status(mxid))) |
| 543 | + |
| 544 | + # On deactivation with 'erase', the entire database row is erased. Both of these |
| 545 | + # should raise a 404(Not Found) SynapseError |
| 546 | + display_name_failure = self.get_failure( |
| 547 | + profile_handler.get_displayname(user_id), SynapseError |
| 548 | + ) |
| 549 | + assert display_name_failure.value.code == HTTPStatus.NOT_FOUND |
| 550 | + |
| 551 | + avatar_url_failure = self.get_failure( |
| 552 | + profile_handler.get_avatar_url(user_id), SynapseError |
| 553 | + ) |
| 554 | + assert avatar_url_failure.value.code == HTTPStatus.NOT_FOUND |
| 555 | + |
| 556 | + # Check that this access token has been invalidated. |
| 557 | + channel = self.make_request("GET", "account/whoami", access_token=tok) |
| 558 | + self.assertEqual(channel.code, 401) |
| 559 | + |
| 560 | + @override_config({"enable_set_displayname": False, "enable_set_avatar_url": False}) |
| 561 | + def test_deactivate_erase_account_with_disabled_profile_changes(self) -> None: |
| 562 | + """ |
| 563 | + Test that deactivating the user with the 'erase' option will remove existing |
| 564 | + profile data, even with the Synapse configuration to forbid profile changes |
| 565 | + """ |
| 566 | + mxid = self.register_user("kermit", "test") |
| 567 | + user_id = UserID.from_string(mxid) |
| 568 | + tok = self.login("kermit", "test") |
| 569 | + |
| 570 | + profile_handler = self.hs.get_profile_handler() |
| 571 | + |
| 572 | + # Can not use the profile handler to set a display name when it is disabled. Use |
| 573 | + # the database directly |
| 574 | + store = self.hs.get_datastores().main |
| 575 | + self.get_success(store.set_profile_displayname(user_id, "Kermit the Frog")) |
| 576 | + self.get_success( |
| 577 | + store.set_profile_avatar_url(user_id, "http://test/Kermit.jpg") |
| 578 | + ) |
| 579 | + |
| 580 | + # Verify it is set |
| 581 | + self.assertEqual( |
| 582 | + (self.get_success(store.get_profile_displayname(user_id))), |
| 583 | + "Kermit the Frog", |
| 584 | + ) |
| 585 | + self.assertEqual( |
| 586 | + self.get_success(profile_handler.get_displayname(user_id)), |
| 587 | + "Kermit the Frog", |
| 588 | + ) |
| 589 | + self.assertEqual( |
| 590 | + (self.get_success(store.get_profile_avatar_url(user_id))), |
| 591 | + "http://test/Kermit.jpg", |
| 592 | + ) |
| 593 | + self.assertEqual( |
| 594 | + self.get_success(profile_handler.get_avatar_url(user_id)), |
| 595 | + "http://test/Kermit.jpg", |
| 596 | + ) |
| 597 | + |
| 598 | + # Deactivate! |
| 599 | + self.deactivate(mxid, tok, erase=True) |
| 600 | + |
| 601 | + # Check that the user has been marked as deactivated. |
| 602 | + self.assertTrue(self.get_success(store.get_user_deactivated_status(mxid))) |
| 603 | + |
| 604 | + # On deactivation with 'erase', the entire database row is erased. Both of these |
| 605 | + # should raise a 404(Not Found) SynapseError |
| 606 | + display_name_failure = self.get_failure( |
| 607 | + profile_handler.get_displayname(user_id), SynapseError |
| 608 | + ) |
| 609 | + assert display_name_failure.value.code == HTTPStatus.NOT_FOUND |
| 610 | + |
| 611 | + avatar_url_failure = self.get_failure( |
| 612 | + profile_handler.get_avatar_url(user_id), SynapseError |
| 613 | + ) |
| 614 | + assert avatar_url_failure.value.code == HTTPStatus.NOT_FOUND |
| 615 | + |
| 616 | + # Check that this access token has been invalidated. |
| 617 | + channel = self.make_request("GET", "account/whoami", access_token=tok) |
| 618 | + self.assertEqual(channel.code, 401) |
| 619 | + |
503 | 620 | def test_pending_invites(self) -> None: |
504 | 621 | """Tests that deactivating a user rejects every pending invite for them.""" |
505 | 622 | store = self.hs.get_datastores().main |
@@ -698,14 +815,23 @@ def test_background_update_deletes_deactivated_users_server_side_backup_keys( |
698 | 815 | ) |
699 | 816 | self.assertEqual(len(res2), 4) |
700 | 817 |
|
701 | | - def deactivate(self, user_id: str, tok: str) -> None: |
| 818 | + def deactivate(self, user_id: str, tok: str, erase: bool = False) -> None: |
| 819 | + """ |
| 820 | + Helper to deactivate a user using the /account/deactivate endpoint, optionally |
| 821 | + with erasure |
| 822 | +
|
| 823 | + Args: |
| 824 | + user_id: the string formatted mxid(not a UserID) |
| 825 | + tok: the user's access token |
| 826 | + erase: bool of if this should be a full erasure request |
| 827 | + """ |
702 | 828 | request_data = { |
703 | 829 | "auth": { |
704 | 830 | "type": "m.login.password", |
705 | 831 | "user": user_id, |
706 | 832 | "password": "test", |
707 | 833 | }, |
708 | | - "erase": False, |
| 834 | + "erase": erase, |
709 | 835 | } |
710 | 836 | channel = self.make_request( |
711 | 837 | "POST", "account/deactivate", request_data, access_token=tok |
|
0 commit comments