Make 'event.redacts' never raise. (#6771)
There are quite a few places that we assume that a redaction event has a corresponding `redacts` key, which is not always the case. So lets cheekily make it so that event.redacts just returns None instead.pull/6775/head
parent
51fc3f693e
commit
fa4d609e20
|
@ -0,0 +1 @@
|
|||
Fix persisting redaction events that have been redacted (or otherwise don't have a redacts key).
|
|
@ -116,16 +116,32 @@ class _EventInternalMetadata(object):
|
|||
return getattr(self, "redacted", False)
|
||||
|
||||
|
||||
def _event_dict_property(key):
|
||||
_SENTINEL = object()
|
||||
|
||||
|
||||
def _event_dict_property(key, default=_SENTINEL):
|
||||
"""Creates a new property for the given key that delegates access to
|
||||
`self._event_dict`.
|
||||
|
||||
The default is used if the key is missing from the `_event_dict`, if given,
|
||||
otherwise an AttributeError will be raised.
|
||||
|
||||
Note: If a default is given then `hasattr` will always return true.
|
||||
"""
|
||||
|
||||
# We want to be able to use hasattr with the event dict properties.
|
||||
# However, (on python3) hasattr expects AttributeError to be raised. Hence,
|
||||
# we need to transform the KeyError into an AttributeError
|
||||
def getter(self):
|
||||
|
||||
def getter_raises(self):
|
||||
try:
|
||||
return self._event_dict[key]
|
||||
except KeyError:
|
||||
raise AttributeError(key)
|
||||
|
||||
def getter_default(self):
|
||||
return self._event_dict.get(key, default)
|
||||
|
||||
def setter(self, v):
|
||||
try:
|
||||
self._event_dict[key] = v
|
||||
|
@ -138,7 +154,11 @@ def _event_dict_property(key):
|
|||
except KeyError:
|
||||
raise AttributeError(key)
|
||||
|
||||
return property(getter, setter, delete)
|
||||
if default is _SENTINEL:
|
||||
# No default given, so use the getter that raises
|
||||
return property(getter_raises, setter, delete)
|
||||
else:
|
||||
return property(getter_default, setter, delete)
|
||||
|
||||
|
||||
class EventBase(object):
|
||||
|
@ -165,7 +185,7 @@ class EventBase(object):
|
|||
origin = _event_dict_property("origin")
|
||||
origin_server_ts = _event_dict_property("origin_server_ts")
|
||||
prev_events = _event_dict_property("prev_events")
|
||||
redacts = _event_dict_property("redacts")
|
||||
redacts = _event_dict_property("redacts", None)
|
||||
room_id = _event_dict_property("room_id")
|
||||
sender = _event_dict_property("sender")
|
||||
user_id = _event_dict_property("sender")
|
||||
|
|
|
@ -951,7 +951,7 @@ class EventsStore(
|
|||
elif event.type == EventTypes.Message:
|
||||
# Insert into the event_search table.
|
||||
self._store_room_message_txn(txn, event)
|
||||
elif event.type == EventTypes.Redaction:
|
||||
elif event.type == EventTypes.Redaction and event.redacts is not None:
|
||||
# Insert into the redactions table.
|
||||
self._store_redaction(txn, event)
|
||||
elif event.type == EventTypes.Retention:
|
||||
|
|
|
@ -287,7 +287,7 @@ class EventsWorkerStore(SQLBaseStore):
|
|||
# we have to recheck auth now.
|
||||
|
||||
if not allow_rejected and entry.event.type == EventTypes.Redaction:
|
||||
if not hasattr(entry.event, "redacts"):
|
||||
if entry.event.redacts is None:
|
||||
# A redacted redaction doesn't have a `redacts` key, in
|
||||
# which case lets just withhold the event.
|
||||
#
|
||||
|
|
|
@ -398,3 +398,38 @@ class RedactionTestCase(unittest.HomeserverTestCase):
|
|||
self.get_success(
|
||||
self.store.get_event(first_redact_event.event_id, allow_none=True)
|
||||
)
|
||||
|
||||
def test_store_redacted_redaction(self):
|
||||
"""Tests that we can store a redacted redaction.
|
||||
"""
|
||||
|
||||
self.get_success(
|
||||
self.inject_room_member(self.room1, self.u_alice, Membership.JOIN)
|
||||
)
|
||||
|
||||
builder = self.event_builder_factory.for_room_version(
|
||||
RoomVersions.V1,
|
||||
{
|
||||
"type": EventTypes.Redaction,
|
||||
"sender": self.u_alice.to_string(),
|
||||
"room_id": self.room1.to_string(),
|
||||
"content": {"reason": "foo"},
|
||||
},
|
||||
)
|
||||
|
||||
redaction_event, context = self.get_success(
|
||||
self.event_creation_handler.create_new_client_event(builder)
|
||||
)
|
||||
|
||||
self.get_success(
|
||||
self.storage.persistence.persist_event(redaction_event, context)
|
||||
)
|
||||
|
||||
# Now lets jump to the future where we have censored the redaction event
|
||||
# in the DB.
|
||||
self.reactor.advance(60 * 60 * 24 * 31)
|
||||
|
||||
# We just want to check that fetching the event doesn't raise an exception.
|
||||
self.get_success(
|
||||
self.store.get_event(redaction_event.event_id, allow_none=True)
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue