From 7b81908a380569fe666d392a5137fd00806e32ac Mon Sep 17 00:00:00 2001 From: FrenchGithubUser Date: Tue, 14 Apr 2026 16:51:44 +0200 Subject: [PATCH] feat: update tests to pass with room v11 --- .github/workflows/pipeline.yml | 2 +- lib/Protocol/Matrix.pm | 42 ++++++++++++++++++++++++----- lib/SyTest/Federation/AuthChecks.pm | 29 +++++++++++++++++--- lib/SyTest/Federation/Datastore.pm | 13 ++++----- lib/SyTest/Federation/Protocol.pm | 7 ++--- lib/SyTest/Federation/Room.pm | 11 +++++--- tests/30rooms/01state.pl | 9 ++++--- 7 files changed, 87 insertions(+), 26 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index ca8a4d3c9..946446096 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -83,7 +83,7 @@ jobs: if [[ -z "$BRANCH_NAME" || $BRANCH_NAME =~ ^refs/pull/.* ]]; then continue fi - (wget -O - "https://github.com/element-hq/synapse/archive/$BRANCH_NAME.tar.gz" \ + (wget -O - "https://github.com/famedly/synapse/archive/$BRANCH_NAME.tar.gz" \ | tar -xz --strip-components=1 -C /src/) \ && echo "Successfully downloaded and extracted $BRANCH_NAME.tar.gz" \ && break diff --git a/lib/Protocol/Matrix.pm b/lib/Protocol/Matrix.pm index b301e50ba..1629c4756 100644 --- a/lib/Protocol/Matrix.pm +++ b/lib/Protocol/Matrix.pm @@ -266,11 +266,13 @@ my %ALLOWED_CONTENT_BY_TYPE = ( sub redact_event { - my ( $event ) = @_; + my ( $event, $room_version ) = @_; + $room_version //= 1; defined( my $type = $event->{type} ) or croak "Event requires a 'type'"; + delete $event->{redacts}; my $old_content = delete $event->{content}; my $old_unsigned = delete $event->{unsigned}; @@ -278,6 +280,32 @@ sub redact_event my $new_content = $event->{content} = {}; + # Room version 11+ uses updated redaction rules: + # - m.room.create: entire content property is preserved + # - m.room.power_levels: 'invite' is also preserved + # - m.room.member: 'third_party_invite.signed' is also preserved + # - m.room.redaction: 'redacts' is also preserved + if( defined $room_version and $room_version =~ /\A[0-9]+\z/ and $room_version >= 11 ) { + if( $type eq 'm.room.create' ) { + %$new_content = %{ $old_content // {} }; + $event->{unsigned}{age_ts} = $old_unsigned->{age_ts} if exists $old_unsigned->{age_ts}; + return; + } + if( $type eq 'm.room.power_levels' ) { + exists $old_content->{invite} and $new_content->{invite} = $old_content->{invite}; + } + if( $type eq 'm.room.member' ) { + my $tpi = $old_content->{third_party_invite}; + if( ref $tpi eq 'HASH' && exists $tpi->{signed} ) { + $new_content->{third_party_invite} = { signed => $tpi->{signed} }; + } + } + if( $type eq 'm.room.redaction' ) { + # In v11, 'redacts' moved into content; preserve content.redacts + exists $old_content->{redacts} and $new_content->{redacts} = $old_content->{redacts}; + } + } + if( my $allowed_content_keys = $ALLOWED_CONTENT_BY_TYPE{$type} ) { exists $old_content->{$_} and $new_content->{$_} = $old_content->{$_} for @$allowed_content_keys; @@ -288,8 +316,8 @@ sub redact_event sub redacted_event { - my ( $event ) = @_; - redact_event( $event = { %$event } ); + my ( $event, $room_version ) = @_; + redact_event( $event = { %$event }, $room_version ); return $event; } @@ -309,6 +337,7 @@ sub sign_event_json my $origin = $args{origin} or croak "Require an 'origin'"; my $key_id = $args{key_id} or croak "Require a 'key_id'"; + my $room_version = delete $args{room_version}; # 'hashes' records the original unredacted version { @@ -319,7 +348,7 @@ sub sign_event_json } # Signature is of redacted version - sign_json( my $signed = redacted_event( $event ), %args ); + sign_json( my $signed = redacted_event( $event, $room_version ), %args ); $event->{signatures} = $signed->{signatures}; } @@ -349,9 +378,10 @@ sub signed_event_json sub verify_event_json_signature { - my ( $event, @args ) = @_; + my ( $event, %args ) = @_; - verify_json_signature( redacted_event( $event ), @args ); + my $room_version = delete $args{room_version}; + verify_json_signature( redacted_event( $event, $room_version ), %args ); } =head1 AUTHOR diff --git a/lib/SyTest/Federation/AuthChecks.pm b/lib/SyTest/Federation/AuthChecks.pm index 52c9cad78..d436e2c73 100644 --- a/lib/SyTest/Federation/AuthChecks.pm +++ b/lib/SyTest/Federation/AuthChecks.pm @@ -48,9 +48,12 @@ sub auth_check_event $accepted_events, $event->{auth_events}, "m.room.create" ); + my $room_creator = _creator_for_create_event( $create_event ); + return 0 unless defined $room_creator; + { users => { - $create_event->{content}{creator} => 100, + $room_creator => 100, }, users_default => 0, @@ -86,6 +89,24 @@ sub auth_check_event return 1; } +sub _creator_for_create_event +{ + my ( $create_event ) = @_; + + $create_event or + return undef; + + my $room_version = $create_event->{content}{room_version}; + + # For room version 11+, 'creator' is absent from content, we use sender. + if( defined $room_version and $room_version =~ /\A[0-9]+\z/ and $room_version >= 11 ) { + return $create_event->{sender}; + } + + # For older room versions, 'creator' must be present explicitly. + return $create_event->{content}{creator}; +} + sub auth_check_event_m_room_create { my $self = shift; @@ -96,7 +117,8 @@ sub auth_check_event_m_room_create return 0; # Any m.room.create event is acceptable, provided that the creator matches - return $event->{sender} eq $event->{content}{creator}; + my $creator = _creator_for_create_event( $event ); + return defined( $creator ) and $event->{sender} eq $creator; } sub auth_check_event_m_room_member @@ -117,7 +139,8 @@ sub auth_check_event_m_room_member $accepted_events, $event->{auth_events}, "m.room.create" ); - if( $create_event and $event->{state_key} eq $create_event->{content}{creator} ) { + my $creator = _creator_for_create_event( $create_event ); + if( $creator and $event->{state_key} eq $creator ) { return 1; } diff --git a/lib/SyTest/Federation/Datastore.pm b/lib/SyTest/Federation/Datastore.pm index 6f6d4e0bd..0cab891dc 100644 --- a/lib/SyTest/Federation/Datastore.pm +++ b/lib/SyTest/Federation/Datastore.pm @@ -77,12 +77,13 @@ the C key. sub sign_event { my $self = shift; - my ( $event ) = @_; + my ( $event, %args ) = @_; sign_event_json( $event, - secret_key => $self->secret_key, - origin => $self->server_name, - key_id => $self->key_id, + secret_key => $self->secret_key, + origin => $self->server_name, + key_id => $self->key_id, + room_version => $args{room_version}, ); } @@ -204,12 +205,12 @@ sub create_event $event_id = $self->next_event_id( $event_id_suffix ); $event->{event_id} = $event_id; } - $self->sign_event( $event ); + $self->sign_event( $event, room_version => $room_version ); } else { die "event with explicit event_id in room v$room_version" if defined $event_id; - $self->sign_event( $event ); + $self->sign_event( $event, room_version => $room_version ); $event_id = id_for_event( $event, $room_version ); } diff --git a/lib/SyTest/Federation/Protocol.pm b/lib/SyTest/Federation/Protocol.pm index 1f8daf2af..36c5d852f 100644 --- a/lib/SyTest/Federation/Protocol.pm +++ b/lib/SyTest/Federation/Protocol.pm @@ -44,9 +44,10 @@ Calculates the reference hash of an event. sub hash_event { - my ( $event ) = @_; + my ( $event, $room_version ) = @_; + $room_version //= 1; croak "Require an event" unless ref $event eq 'HASH'; - my $redacted = redacted_event( $event ); + my $redacted = redacted_event( $event, $room_version ); delete $redacted->{signatures}; delete $redacted->{age_ts}; delete $redacted->{unsigned}; @@ -76,7 +77,7 @@ sub id_for_event return $event_id; } - my $event_hash = hash_event( $event ); + my $event_hash = hash_event( $event, $room_version ); # room v3 uses the unpadded-base64-encoded hash if( $room_version eq '3' ) { diff --git a/lib/SyTest/Federation/Room.pm b/lib/SyTest/Federation/Room.pm index f13aae5b1..ef8b97008 100644 --- a/lib/SyTest/Federation/Room.pm +++ b/lib/SyTest/Federation/Room.pm @@ -180,13 +180,16 @@ sub create_initial_events $self->room_version eq "1" ? undef : $self->room_version ); + # For room version 11+, 'creator' is absent from content, we use sender. + my $create_content = { + defined( $room_version ) ? ( room_version => $room_version ) : (), + }; + $create_content->{creator} = $creator if !defined( $room_version ) || $room_version < 11; + $self->create_and_insert_event( type => "m.room.create", - content => { - creator => $creator, - defined( $room_version ) ? ( room_version => $room_version ) : (), - }, + content => $create_content, sender => $creator, state_key => "", ); diff --git a/tests/30rooms/01state.pl b/tests/30rooms/01state.pl index 01bfc274a..be7662b48 100644 --- a/tests/30rooms/01state.pl +++ b/tests/30rooms/01state.pl @@ -32,9 +32,12 @@ $event->{sender} eq $user->user_id or die "Expected user_id to be ${\$user->user_id}"; - assert_json_keys( my $content = $event->{content}, qw( creator )); - $content->{creator} eq $user->user_id or - die "Expected creator to be ${\$user->user_id}"; + my $content = $event->{content}; + # For room version 11+, 'creator' is absent from content, we use sender. + if( exists $content->{creator} ) { + $content->{creator} eq $user->user_id or + die "Expected creator to be ${\$user->user_id}"; + } return 1; });