Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Modify the auth rules and redaction rules for aliases. #7037

Merged
merged 4 commits into from
Mar 9, 2020
Merged
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
1 change: 1 addition & 0 deletions changelog.d/7037.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement updated authorization rules and redaction rules for aliases events, from [MSC2261](https://github.com/matrix-org/matrix-doc/pull/2261) and [MSC2432](https://github.com/matrix-org/matrix-doc/pull/2432).
9 changes: 5 additions & 4 deletions synapse/api/room_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class RoomVersion(object):
state_res = attr.ib() # int; one of the StateResolutionVersions
enforce_key_validity = attr.ib() # bool

# bool: before MSC2260, anyone was allowed to send an aliases event
# bool: before MSC2261/MSC2432, m.room.aliases had special auth rules and redaction rules
special_case_aliases_auth = attr.ib(type=bool, default=False)


Expand Down Expand Up @@ -102,12 +102,13 @@ class RoomVersions(object):
enforce_key_validity=True,
special_case_aliases_auth=True,
)
MSC2260_DEV = RoomVersion(
"org.matrix.msc2260",
MSC2432_DEV = RoomVersion(
clokep marked this conversation as resolved.
Show resolved Hide resolved
"org.matrix.msc2432",
RoomDisposition.UNSTABLE,
EventFormatVersions.V3,
StateResolutionVersions.V2,
enforce_key_validity=True,
special_case_aliases_auth=False,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is equivalent, but setting up these rooms seems like it should be explicit instead of using defaults. I can remove this again though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have strong feelings.

)


Expand All @@ -119,6 +120,6 @@ class RoomVersions(object):
RoomVersions.V3,
RoomVersions.V4,
RoomVersions.V5,
RoomVersions.MSC2260_DEV,
RoomVersions.MSC2432_DEV,
)
} # type: Dict[str, RoomVersion]
2 changes: 1 addition & 1 deletion synapse/crypto/event_signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def compute_event_signature(
Returns:
a dictionary in the same format of an event's signatures field.
"""
redact_json = prune_event_dict(event_dict)
redact_json = prune_event_dict(room_version, event_dict)
redact_json.pop("age_ts", None)
redact_json.pop("unsigned", None)
if logger.isEnabledFor(logging.DEBUG):
Expand Down
8 changes: 3 additions & 5 deletions synapse/event_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def check(
raise AuthError(403, "This room has been marked as unfederatable.")

# 4. If type is m.room.aliases
if event.type == EventTypes.Aliases:
if event.type == EventTypes.Aliases and room_version_obj.special_case_aliases_auth:
# 4a. If event has no state_key, reject
if not event.is_state():
raise AuthError(403, "Alias event must be a state event")
Expand All @@ -152,10 +152,8 @@ def check(
)

# 4c. Otherwise, allow.
# This is removed by https://github.com/matrix-org/matrix-doc/pull/2260
if room_version_obj.special_case_aliases_auth:
logger.debug("Allowing! %s", event)
return
logger.debug("Allowing! %s", event)
return

if logger.isEnabledFor(logging.DEBUG):
logger.debug("Auth events: %s", [a.event_id for a in auth_events.values()])
Expand Down
12 changes: 5 additions & 7 deletions synapse/events/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from twisted.internet import defer

from synapse.api.constants import EventTypes, RelationTypes
from synapse.api.room_versions import RoomVersion
from synapse.util.async_helpers import yieldable_gather_results

from . import EventBase
Expand All @@ -43,7 +44,7 @@ def prune_event(event: EventBase) -> EventBase:
the user has specified, but we do want to keep necessary information like
type, state_key etc.
"""
pruned_event_dict = prune_event_dict(event.get_dict())
pruned_event_dict = prune_event_dict(event.room_version, event.get_dict())

from . import make_event_from_dict

Expand All @@ -57,15 +58,12 @@ def prune_event(event: EventBase) -> EventBase:
return pruned_event


def prune_event_dict(event_dict):
def prune_event_dict(room_version: RoomVersion, event_dict: dict) -> dict:
"""Redacts the event_dict in the same way as `prune_event`, except it
operates on dicts rather than event objects

Args:
event_dict (dict)

Returns:
dict: A copy of the pruned event dict
A copy of the pruned event dict
"""

allowed_keys = [
Expand Down Expand Up @@ -112,7 +110,7 @@ def add_fields(*fields):
"kick",
"redact",
)
elif event_type == EventTypes.Aliases:
elif event_type == EventTypes.Aliases and room_version.special_case_aliases_auth:
add_fields("aliases")
elif event_type == EventTypes.RoomHistoryVisibility:
add_fields("history_visibility")
Expand Down
10 changes: 8 additions & 2 deletions synapse/storage/data_stores/main/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,11 @@ async def _censor_redactions(self):
and original_event.internal_metadata.is_redacted()
):
# Redaction was allowed
pruned_json = encode_json(prune_event_dict(original_event.get_dict()))
pruned_json = encode_json(
prune_event_dict(
original_event.room_version, original_event.get_dict()
)
)
else:
# Redaction wasn't allowed
pruned_json = None
Expand Down Expand Up @@ -1929,7 +1933,9 @@ def delete_expired_event_txn(txn):
return

# Prune the event's dict then convert it to JSON.
pruned_json = encode_json(prune_event_dict(event.get_dict()))
pruned_json = encode_json(
prune_event_dict(event.room_version, event.get_dict())
)

# Update the event_json table to replace the event's JSON with the pruned
# JSON.
Expand Down
35 changes: 33 additions & 2 deletions tests/events/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from synapse.api.room_versions import RoomVersions
from synapse.events import make_event_from_dict
from synapse.events.utils import (
copy_power_levels_contents,
Expand All @@ -36,9 +37,9 @@ class PruneEventTestCase(unittest.TestCase):
""" Asserts that a new event constructed with `evdict` will look like
`matchdict` when it is redacted. """

def run_test(self, evdict, matchdict):
def run_test(self, evdict, matchdict, **kwargs):
self.assertEquals(
prune_event(make_event_from_dict(evdict)).get_dict(), matchdict
prune_event(make_event_from_dict(evdict, **kwargs)).get_dict(), matchdict
)

def test_minimal(self):
Expand Down Expand Up @@ -128,6 +129,36 @@ def test_content(self):
},
)

def test_alias_event(self):
"""Alias events have special behavior up through room version 6."""
self.run_test(
{
"type": "m.room.aliases",
"event_id": "$test:domain",
"content": {"aliases": ["test"]},
},
{
"type": "m.room.aliases",
"event_id": "$test:domain",
"content": {"aliases": ["test"]},
"signatures": {},
"unsigned": {},
},
)

def test_msc2432_alias_event(self):
"""After MSC2432, alias events have no special behavior."""
self.run_test(
{"type": "m.room.aliases", "content": {"aliases": ["test"]}},
{
"type": "m.room.aliases",
"content": {},
"signatures": {},
"unsigned": {},
},
room_version=RoomVersions.MSC2432_DEV,
)


class SerializeEventTestCase(unittest.TestCase):
def serialize(self, ev, fields):
Expand Down
93 changes: 92 additions & 1 deletion tests/test_event_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from synapse.api.errors import AuthError
from synapse.api.room_versions import RoomVersions
from synapse.events import make_event_from_dict
from synapse.types import get_domain_from_id


class EventAuthTestCase(unittest.TestCase):
Expand Down Expand Up @@ -51,7 +52,7 @@ def test_random_users_cannot_send_state_before_first_pl(self):
_random_state_event(joiner),
auth_events,
do_sig_check=False,
),
)

def test_state_default_level(self):
"""
Expand Down Expand Up @@ -87,6 +88,83 @@ def test_state_default_level(self):
RoomVersions.V1, _random_state_event(king), auth_events, do_sig_check=False,
)

def test_alias_event(self):
"""Alias events have special behavior up through room version 6."""
creator = "@creator:example.com"
other = "@other:example.com"
auth_events = {
("m.room.create", ""): _create_event(creator),
("m.room.member", creator): _join_event(creator),
}

# creator should be able to send aliases
event_auth.check(
RoomVersions.V1, _alias_event(creator), auth_events, do_sig_check=False,
)

# Reject an event with no state key.
with self.assertRaises(AuthError):
event_auth.check(
RoomVersions.V1,
_alias_event(creator, state_key=""),
auth_events,
do_sig_check=False,
)

# If the domain of the sender does not match the state key, reject.
with self.assertRaises(AuthError):
event_auth.check(
RoomVersions.V1,
_alias_event(creator, state_key="test.com"),
auth_events,
do_sig_check=False,
)

# Note that the member does *not* need to be in the room.
event_auth.check(
RoomVersions.V1, _alias_event(other), auth_events, do_sig_check=False,
)

def test_msc2432_alias_event(self):
"""After MSC2432, alias events have no special behavior."""
creator = "@creator:example.com"
other = "@other:example.com"
auth_events = {
("m.room.create", ""): _create_event(creator),
("m.room.member", creator): _join_event(creator),
}

# creator should be able to send aliases
event_auth.check(
RoomVersions.MSC2432_DEV,
_alias_event(creator),
auth_events,
do_sig_check=False,
)

# No particular checks are done on the state key.
event_auth.check(
RoomVersions.MSC2432_DEV,
_alias_event(creator, state_key=""),
auth_events,
do_sig_check=False,
)
event_auth.check(
RoomVersions.MSC2432_DEV,
_alias_event(creator, state_key="test.com"),
auth_events,
do_sig_check=False,
)

# Per standard auth rules, the member must be in the room.
with self.assertRaises(AuthError):
event_auth.check(
RoomVersions.MSC2432_DEV,
_alias_event(other),
auth_events,
do_sig_check=False,
)


# helpers for making events

Expand Down Expand Up @@ -131,6 +209,19 @@ def _power_levels_event(sender, content):
)


def _alias_event(sender, **kwargs):
data = {
"room_id": TEST_ROOM_ID,
"event_id": _get_event_id(),
"type": "m.room.aliases",
"sender": sender,
"state_key": get_domain_from_id(sender),
"content": {"aliases": []},
}
data.update(**kwargs)
return make_event_from_dict(data)


def _random_state_event(sender):
return make_event_from_dict(
{
Expand Down