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)
|
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.
|
# We want to be able to use hasattr with the event dict properties.
|
||||||
# However, (on python3) hasattr expects AttributeError to be raised. Hence,
|
# However, (on python3) hasattr expects AttributeError to be raised. Hence,
|
||||||
# we need to transform the KeyError into an AttributeError
|
# we need to transform the KeyError into an AttributeError
|
||||||
def getter(self):
|
|
||||||
|
def getter_raises(self):
|
||||||
try:
|
try:
|
||||||
return self._event_dict[key]
|
return self._event_dict[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise AttributeError(key)
|
raise AttributeError(key)
|
||||||
|
|
||||||
|
def getter_default(self):
|
||||||
|
return self._event_dict.get(key, default)
|
||||||
|
|
||||||
def setter(self, v):
|
def setter(self, v):
|
||||||
try:
|
try:
|
||||||
self._event_dict[key] = v
|
self._event_dict[key] = v
|
||||||
|
@ -138,7 +154,11 @@ def _event_dict_property(key):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise AttributeError(key)
|
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):
|
class EventBase(object):
|
||||||
|
@ -165,7 +185,7 @@ class EventBase(object):
|
||||||
origin = _event_dict_property("origin")
|
origin = _event_dict_property("origin")
|
||||||
origin_server_ts = _event_dict_property("origin_server_ts")
|
origin_server_ts = _event_dict_property("origin_server_ts")
|
||||||
prev_events = _event_dict_property("prev_events")
|
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")
|
room_id = _event_dict_property("room_id")
|
||||||
sender = _event_dict_property("sender")
|
sender = _event_dict_property("sender")
|
||||||
user_id = _event_dict_property("sender")
|
user_id = _event_dict_property("sender")
|
||||||
|
|
|
@ -951,7 +951,7 @@ class EventsStore(
|
||||||
elif event.type == EventTypes.Message:
|
elif event.type == EventTypes.Message:
|
||||||
# Insert into the event_search table.
|
# Insert into the event_search table.
|
||||||
self._store_room_message_txn(txn, event)
|
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.
|
# Insert into the redactions table.
|
||||||
self._store_redaction(txn, event)
|
self._store_redaction(txn, event)
|
||||||
elif event.type == EventTypes.Retention:
|
elif event.type == EventTypes.Retention:
|
||||||
|
|
|
@ -287,7 +287,7 @@ class EventsWorkerStore(SQLBaseStore):
|
||||||
# we have to recheck auth now.
|
# we have to recheck auth now.
|
||||||
|
|
||||||
if not allow_rejected and entry.event.type == EventTypes.Redaction:
|
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
|
# A redacted redaction doesn't have a `redacts` key, in
|
||||||
# which case lets just withhold the event.
|
# which case lets just withhold the event.
|
||||||
#
|
#
|
||||||
|
|
|
@ -398,3 +398,38 @@ class RedactionTestCase(unittest.HomeserverTestCase):
|
||||||
self.get_success(
|
self.get_success(
|
||||||
self.store.get_event(first_redact_event.event_id, allow_none=True)
|
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