Implement MSC3391: Removing account data #14242
Description
Add an experimental implementation for MSC3391, which allows users to remove keys from their user and room account data.
TODO
-
Add an off-by-default experimental config option
msc3391_enabled
to guard the feature behind. -
Add two unstable endpoints for deleting account data:
-
DELETE /_matrix/client/unstable/org.matrix.msc3391/user/{userId}/account_data/{type}
-
DELETE /_matrix/client/unstable/org.matrix.msc3391/user/{userId}/rooms/{roomId}/account_data/{type}
- Upon calling the above endpoints, the provided
type
is removed from the user's user/room account data. This should be recorded in theaccount_data
androom_account_data
tables respectively, by setting the content of each to{}
. Ensure these entries are not included in initial syncs.
- Upon calling the above endpoints, the provided
-
-
Setting an account data type's content to
{}
when callingPUT /_matrix/client/v3/user/{userId}/account_data/{type}
andPUT /_matrix/client/v3/user/{userId}/rooms/{roomId}/account_data/{type}
will be equivalent to calling theDELETE
endpoint for that type. This is for backwards-compatibility with older clients which have used this to effectively "delete" keys in the past. -
Ensure that event entries with a content of
{}
appear down/sync
after an account data entry is deleted. -
Add a new table
account_data_unsynced_deletes
, which tracks whether a device has seen the deletion. Upon a delete one row for each known user device is added to this table. Upon a device sync'ing the change or hitting theGET
endpoint for the data, the relevant row will be removed. Once all devices have seen a deletion, we can delete the key from theaccount_data
orroom_account_data
tables. The device that caused the deletion should not have a row inserted.
Theaccount_data_unsynced_deletes
table has the following schema:CREATE TABLE IF NOT EXISTS account_data_unsynced_deletes ( -- The stream_id of the delete in `account_data` or `room_account_data`. Note that this value is -- unique across both `account_data` and `room_account_data` tables. stream_id BIGINT NOT NULL, user_id TEXT NOT NULL, // foreign key: users(name) account_data_type TEXT NOT NULL, -- The room ID if this is referring to `room_account_data`. room_id TEXT, // foreign key: rooms(room_id) -- A device ID that has not yet seen this delete. device_id TEXT NOT NULL, // foreign key: devices(device_id) ); -- This will be used to quickly look up a given (room) account_data entry for a given (user_id, device_id) pair CREATE UNIQUE INDEX IF NOT EXISTS account_data_unsynced_deletes_stream_id_user_id_device_id ON account_data_unsynced_deletes(stream_id, user_id, device_id); -- This is CREATE INDEX IF NOT EXISTS account_data_unsynced_deletes_user_id_device_id ON account_data_unsynced_deletes(user_id, device_id);
- /sync needs to delete a row using
(stream_id, user_id, device_id)
.GET
would use the same. Deleting all rows for a device needs(user_id, device_id)
.
- Delete entries in
account_data_unsynced_deletes
when a given device knows that delete has occurred. Either through/sync
orGET
endpoints. - Deleting a device should remove all relevant rows from the
account_data_unsynced_devices
table. - Write complement tests.