From d68b04419191c9330b4d0fa36892a78a1826c04c Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 3 Oct 2023 14:56:20 +0100 Subject: [PATCH] Add dedicated federation.Event Fixes #381 --- client/client.go | 3 +++ internal/b/blueprints.go | 18 --------------- internal/federation/server.go | 7 +++--- internal/federation/server_room.go | 22 +++++++++++++++++-- tests/csapi/sync_test.go | 9 ++++---- tests/direct_messaging_test.go | 2 +- tests/federation_redaction_test.go | 19 +++++++++------- tests/federation_room_event_auth_test.go | 10 ++++----- ...federation_room_get_missing_events_test.go | 8 +++---- tests/federation_room_join_test.go | 18 +++++++-------- tests/federation_unreject_rejected_test.go | 10 ++++----- 11 files changed, 65 insertions(+), 61 deletions(-) diff --git a/client/client.go b/client/client.go index 0066d6c6..f624aa6f 100644 --- a/client/client.go +++ b/client/client.go @@ -249,6 +249,9 @@ func (c *CSAPI) Unsafe_SendEventUnsyncedWithTxnID(t TestLike, roomID string, e b if e.StateKey != nil { paths = []string{"_matrix", "client", "v3", "rooms", roomID, "state", e.Type, *e.StateKey} } + if e.Sender != "" { + t.Fatalf("Event.Sender must not be set, as this is set by the client in use (%s)", c.UserID) + } res := c.MustDo(t, "PUT", paths, WithJSONBody(t, e.Content)) body := ParseJSON(t, res) eventID := GetJSONFieldStr(t, body, "event_id") diff --git a/internal/b/blueprints.go b/internal/b/blueprints.go index 70976776..11aea5c2 100644 --- a/internal/b/blueprints.go +++ b/internal/b/blueprints.go @@ -97,24 +97,6 @@ type Event struct { Sender string StateKey *string Content map[string]interface{} - - /* The following fields are ignored in blueprints as clients are unable to set them. - * They are used with federation.Server. - */ - - Unsigned map[string]interface{} - - // The events needed to authenticate this event. - // This can be either []EventReference for room v1/v2, or []string for room v3 onwards. - // If it is left at nil, MustCreateEvent will populate it automatically based on the room state. - AuthEvents interface{} - - // The prev events of the event if we want to override or falsify them. - // If it is left at nil, MustCreateEvent will populate it automatically based on the forward extremities. - PrevEvents interface{} - - // If this is a redaction, the event that it redacts - Redacts string } func MustValidate(bp Blueprint) Blueprint { diff --git a/internal/federation/server.go b/internal/federation/server.go index c82827a7..448db2c8 100644 --- a/internal/federation/server.go +++ b/internal/federation/server.go @@ -29,7 +29,6 @@ import ( "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" - "github.com/matrix-org/complement/internal/b" "github.com/matrix-org/complement/internal/config" "github.com/matrix-org/complement/internal/docker" ) @@ -162,7 +161,7 @@ func (s *Server) MakeAliasMapping(aliasLocalpart, roomID string) string { // MustMakeRoom will add a room to this server so it is accessible to other servers when prompted via federation. // The `events` will be added to this room. Returns the created room. -func (s *Server) MustMakeRoom(t *testing.T, roomVer gomatrixserverlib.RoomVersion, events []b.Event) *ServerRoom { +func (s *Server) MustMakeRoom(t *testing.T, roomVer gomatrixserverlib.RoomVersion, events []Event) *ServerRoom { if !s.listening { s.t.Fatalf("MustMakeRoom() called before Listen() - this is not supported because Listen() chooses a high-numbered port and thus changes the server name and thus changes the room ID. Ensure you Listen() first!") } @@ -291,7 +290,7 @@ func (s *Server) DoFederationRequest( // MustCreateEvent will create and sign a new latest event for the given room. // It does not insert this event into the room however. See ServerRoom.AddEvent for that. -func (s *Server) MustCreateEvent(t *testing.T, room *ServerRoom, ev b.Event) gomatrixserverlib.PDU { +func (s *Server) MustCreateEvent(t *testing.T, room *ServerRoom, ev Event) gomatrixserverlib.PDU { t.Helper() content, err := json.Marshal(ev.Content) if err != nil { @@ -451,7 +450,7 @@ func (s *Server) MustLeaveRoom(t *testing.T, deployment *docker.Deployment, remo } } else { // make the leave event - leaveEvent = s.MustCreateEvent(t, room, b.Event{ + leaveEvent = s.MustCreateEvent(t, room, Event{ Type: "m.room.member", StateKey: &userID, Sender: userID, diff --git a/internal/federation/server_room.go b/internal/federation/server_room.go index 82e1ef7f..a0b46c55 100644 --- a/internal/federation/server_room.go +++ b/internal/federation/server_room.go @@ -11,6 +11,24 @@ import ( "github.com/matrix-org/complement/internal/b" ) +type Event struct { + Type string + Sender string + StateKey *string + Content map[string]interface{} + + Unsigned map[string]interface{} + // The events needed to authenticate this event. + // This can be either []EventReference for room v1/v2, or []string for room v3 onwards. + // If it is left at nil, MustCreateEvent will populate it automatically based on the room state. + AuthEvents interface{} + // The prev events of the event if we want to override or falsify them. + // If it is left at nil, MustCreateEvent will populate it automatically based on the forward extremities. + PrevEvents interface{} + // If this is a redaction, the event that it redacts + Redacts string +} + // ServerRoom represents a room on this test federation server type ServerRoom struct { Version gomatrixserverlib.RoomVersion @@ -212,13 +230,13 @@ func initialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLev } // InitialRoomEvents returns the initial set of events that get created when making a room. -func InitialRoomEvents(roomVer gomatrixserverlib.RoomVersion, creator string) []b.Event { +func InitialRoomEvents(roomVer gomatrixserverlib.RoomVersion, creator string) []Event { // need to serialise/deserialise to get map[string]interface{} annoyingly plContent := initialPowerLevelsContent(creator) plBytes, _ := json.Marshal(plContent) var plContentMap map[string]interface{} json.Unmarshal(plBytes, &plContentMap) - return []b.Event{ + return []Event{ { Type: "m.room.create", StateKey: b.Ptr(""), diff --git a/tests/csapi/sync_test.go b/tests/csapi/sync_test.go index 98ee05af..918e9637 100644 --- a/tests/csapi/sync_test.go +++ b/tests/csapi/sync_test.go @@ -291,17 +291,16 @@ func TestSync(t *testing.T) { // a good event - in another room - to act as a sentinel. It's not // guaranteed, but hopefully if the sentinel is received, so was the // redaction. - redactionEvent := srv.MustCreateEvent(t, redactionRoom, b.Event{ + redactionEvent := srv.MustCreateEvent(t, redactionRoom, federation.Event{ Type: "m.room.redaction", Sender: charlie, Content: map[string]interface{}{}, - Redacts: "$12345", - }) + Redacts: "$12345"}) redactionRoom.AddEvent(redactionEvent) t.Logf("Created redaction event %s", redactionEvent.EventID()) srv.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{redactionEvent.JSON()}, nil) - sentinelEvent := srv.MustCreateEvent(t, sentinelRoom, b.Event{ + sentinelEvent := srv.MustCreateEvent(t, sentinelRoom, federation.Event{ Type: "m.room.test", Sender: charlie, Content: map[string]interface{}{"body": "1234"}, @@ -318,7 +317,7 @@ func TestSync(t *testing.T) { pdus := make([]json.RawMessage, 11) var lastSentEventId string for i := range pdus { - ev := srv.MustCreateEvent(t, redactionRoom, b.Event{ + ev := srv.MustCreateEvent(t, redactionRoom, federation.Event{ Type: "m.room.message", Sender: charlie, Content: map[string]interface{}{}, diff --git a/tests/direct_messaging_test.go b/tests/direct_messaging_test.go index 7ed86598..93854111 100644 --- a/tests/direct_messaging_test.go +++ b/tests/direct_messaging_test.go @@ -126,7 +126,7 @@ func TestIsDirectFlagFederation(t *testing.T) { bob := srv.UserID("bob") room := srv.MustMakeRoom(t, roomVer, federation.InitialRoomEvents(roomVer, bob)) - dmInviteEvent := srv.MustCreateEvent(t, room, b.Event{ + dmInviteEvent := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.member", StateKey: &alice.UserID, Sender: bob, diff --git a/tests/federation_redaction_test.go b/tests/federation_redaction_test.go index ef9a89a6..29c2c00b 100644 --- a/tests/federation_redaction_test.go +++ b/tests/federation_redaction_test.go @@ -1,12 +1,13 @@ package tests import ( + "testing" + "time" + "github.com/matrix-org/complement/internal/b" "github.com/matrix-org/complement/internal/federation" "github.com/matrix-org/complement/runtime" "github.com/matrix-org/gomatrixserverlib" - "testing" - "time" ) // test that a redaction is sent out over federation even if we don't have the original event @@ -56,13 +57,12 @@ func TestFederationRedactSendsWithoutEvent(t *testing.T) { alice.JoinRoom(t, roomAlias, []string{srv.ServerName()}) // inject event to redact in the room - badEvent := srv.MustCreateEvent(t, serverRoom, b.Event{ + badEvent := srv.MustCreateEvent(t, serverRoom, federation.Event{ Type: "m.room.message", Sender: charlie, Content: map[string]interface{}{ "body": "666", - }, - }) + }}) serverRoom.AddEvent(badEvent) eventID := badEvent.EventID() @@ -70,9 +70,12 @@ func TestFederationRedactSendsWithoutEvent(t *testing.T) { eventToRedact := eventID + ":" + fullServerName // the client sends a request to the local homeserver to send the redaction - res := alice.SendRedaction(t, serverRoom.RoomID, b.Event{Type: wantEventType, Content: map[string]interface{}{ - "msgtype": "m.room.redaction"}, - Redacts: eventToRedact}, eventToRedact) + res := alice.SendRedaction(t, serverRoom.RoomID, b.Event{ + Type: wantEventType, + Content: map[string]interface{}{ + "reason": "reasons...", + }, + }, eventToRedact) // wait for redaction to arrive at remote homeserver waiter.Wait(t, 1*time.Second) diff --git a/tests/federation_room_event_auth_test.go b/tests/federation_room_event_auth_test.go index 58d3f08e..3a317ba4 100644 --- a/tests/federation_room_event_auth_test.go +++ b/tests/federation_room_event_auth_test.go @@ -118,7 +118,7 @@ func TestInboundFederationRejectsEventsWithRejectedAuthEvents(t *testing.T) { charlieMembershipEvent := room.CurrentState("m.room.member", charlie) // have Charlie send a PL event which will be rejected - rejectedEvent := srv.MustCreateEvent(t, room, b.Event{ + rejectedEvent := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.power_levels", StateKey: b.Ptr(""), Sender: charlie, @@ -140,7 +140,7 @@ func TestInboundFederationRejectsEventsWithRejectedAuthEvents(t *testing.T) { // create an event to be pulled in as an outlier, which is valid according to its prev events, // but uses the rejected event among its auth events. - outlierEvent := srv.MustCreateEvent(t, room, b.Event{ + outlierEvent := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.member", StateKey: &charlie, Sender: charlie, @@ -166,7 +166,7 @@ func TestInboundFederationRejectsEventsWithRejectedAuthEvents(t *testing.T) { charlieMembershipEvent, outlierEvent, } - sentEvent1 := srv.MustCreateEvent(t, room, b.Event{ + sentEvent1 := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.message", Sender: charlie, Content: map[string]interface{}{"body": "sentEvent1"}, @@ -178,7 +178,7 @@ func TestInboundFederationRejectsEventsWithRejectedAuthEvents(t *testing.T) { // another a regular event which refers to the outlier event, but // this time we will give a different answer to /event_auth - sentEvent2 := srv.MustCreateEvent(t, room, b.Event{ + sentEvent2 := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.message", Sender: charlie, Content: map[string]interface{}{"body": "sentEvent1"}, @@ -190,7 +190,7 @@ func TestInboundFederationRejectsEventsWithRejectedAuthEvents(t *testing.T) { t.Logf("Created sent event 2 %s", sentEvent2.EventID()) // finally, a genuine regular event. - sentinelEvent := srv.MustCreateEvent(t, room, b.Event{ + sentinelEvent := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.message", Sender: charlie, Content: map[string]interface{}{"body": "sentinelEvent"}, diff --git a/tests/federation_room_get_missing_events_test.go b/tests/federation_room_get_missing_events_test.go index 48c84e01..a568af79 100644 --- a/tests/federation_room_get_missing_events_test.go +++ b/tests/federation_room_get_missing_events_test.go @@ -73,7 +73,7 @@ func TestGetMissingEventsGapFilling(t *testing.T) { var missingEventIDs []string numMissingEvents := 5 for i := 0; i < numMissingEvents; i++ { - missingEvent := srv.MustCreateEvent(t, srvRoom, b.Event{ + missingEvent := srv.MustCreateEvent(t, srvRoom, federation.Event{ Sender: bob, Type: "m.room.message", Content: map[string]interface{}{ @@ -86,7 +86,7 @@ func TestGetMissingEventsGapFilling(t *testing.T) { } // 3) Inject a final event into Complement - mostRecentEvent := srv.MustCreateEvent(t, srvRoom, b.Event{ + mostRecentEvent := srv.MustCreateEvent(t, srvRoom, federation.Event{ Sender: bob, Type: "m.room.message", Content: map[string]interface{}{ @@ -237,7 +237,7 @@ func TestOutboundFederationIgnoresMissingEventWithBadJSONForRoomVersion6(t *test room.AddEvent(signedBadEvent) // send the first "good" event, referencing the broken event as a prev_event - sentEvent := srv.MustCreateEvent(t, room, b.Event{ + sentEvent := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.message", Sender: charlie, Content: map[string]interface{}{ @@ -291,7 +291,7 @@ func TestOutboundFederationIgnoresMissingEventWithBadJSONForRoomVersion6(t *test // it just ignores it, so we need to send another event referring to the // first one and check that we get a /get_missing_events request. - message3 := srv.MustCreateEvent(t, room, b.Event{ + message3 := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.message", Sender: charlie, Content: map[string]interface{}{ diff --git a/tests/federation_room_join_test.go b/tests/federation_room_join_test.go index 0d51963f..7ee127f9 100644 --- a/tests/federation_room_join_test.go +++ b/tests/federation_room_join_test.go @@ -158,7 +158,7 @@ func TestJoinFederatedRoomWithUnverifiableEvents(t *testing.T) { room := srv.MustMakeRoom(t, ver, federation.InitialRoomEvents(ver, charlie)) roomAlias := srv.MakeAliasMapping("MissingSignatures", room.RoomID) // create a normal event then remove the signatures key - signedEvent := srv.MustCreateEvent(t, room, b.Event{ + signedEvent := srv.MustCreateEvent(t, room, federation.Event{ Sender: charlie, StateKey: b.Ptr(""), Type: "m.room.name", @@ -181,7 +181,7 @@ func TestJoinFederatedRoomWithUnverifiableEvents(t *testing.T) { room := srv.MustMakeRoom(t, ver, federation.InitialRoomEvents(ver, charlie)) roomAlias := srv.MakeAliasMapping("BadSignatures", room.RoomID) // create a normal event then modify the signatures - signedEvent := srv.MustCreateEvent(t, room, b.Event{ + signedEvent := srv.MustCreateEvent(t, room, federation.Event{ Sender: charlie, StateKey: b.Ptr(""), Type: "m.room.name", @@ -212,7 +212,7 @@ func TestJoinFederatedRoomWithUnverifiableEvents(t *testing.T) { roomAlias := srv.MakeAliasMapping("UnobtainableKeys", room.RoomID) // create a normal event then modify the signatures to have a bogus key ID which Complement does // not have the keys for - signedEvent := srv.MustCreateEvent(t, room, b.Event{ + signedEvent := srv.MustCreateEvent(t, room, federation.Event{ Sender: charlie, StateKey: b.Ptr(""), Type: "m.room.name", @@ -246,7 +246,7 @@ func TestJoinFederatedRoomWithUnverifiableEvents(t *testing.T) { roomAlias := srv.MakeAliasMapping("UnverifiableAuthEvents", room.RoomID) // create a normal event then modify the signatures - rawEvent := srv.MustCreateEvent(t, room, b.Event{ + rawEvent := srv.MustCreateEvent(t, room, federation.Event{ Sender: charlie, StateKey: &charlie, Type: "m.room.member", @@ -271,7 +271,7 @@ func TestJoinFederatedRoomWithUnverifiableEvents(t *testing.T) { t.Logf("Created badly signed auth event %s", badlySignedEvent.EventID()) // and now add another event which will use it as an auth event. - goodEvent := srv.MustCreateEvent(t, room, b.Event{ + goodEvent := srv.MustCreateEvent(t, room, federation.Event{ Sender: charlie, StateKey: &charlie, Type: "m.room.member", @@ -453,7 +453,7 @@ func testValidationForSendMembershipEndpoint(t *testing.T, baseApiPath, expected } t.Run("regular event", func(t *testing.T) { - event := srv.MustCreateEvent(t, room, b.Event{ + event := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.message", Sender: charlie, Content: map[string]interface{}{"body": "bzz"}, @@ -461,7 +461,7 @@ func testValidationForSendMembershipEndpoint(t *testing.T, baseApiPath, expected assertRequestFails(t, event) }) t.Run("non-state membership event", func(t *testing.T) { - event := srv.MustCreateEvent(t, room, b.Event{ + event := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.member", Sender: charlie, Content: map[string]interface{}{"body": "bzz"}, @@ -475,7 +475,7 @@ func testValidationForSendMembershipEndpoint(t *testing.T, baseApiPath, expected if membershipType == expectedMembership { continue } - event := srv.MustCreateEvent(t, room, b.Event{ + event := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.member", Sender: charlie, StateKey: &charlie, @@ -488,7 +488,7 @@ func testValidationForSendMembershipEndpoint(t *testing.T, baseApiPath, expected // right sort of membership, but mismatched state_key t.Run("event with mismatched state key", func(t *testing.T) { - event := srv.MustCreateEvent(t, room, b.Event{ + event := srv.MustCreateEvent(t, room, federation.Event{ Type: "m.room.member", Sender: charlie, StateKey: b.Ptr(srv.UserID("doris")), diff --git a/tests/federation_unreject_rejected_test.go b/tests/federation_unreject_rejected_test.go index f7f38079..e255dfa9 100644 --- a/tests/federation_unreject_rejected_test.go +++ b/tests/federation_unreject_rejected_test.go @@ -44,20 +44,20 @@ func TestUnrejectRejectedEvents(t *testing.T) { // Create the events. Event A will have whatever the current forward // extremities are as prev events. Event B will refer to event A only // to guarantee the test will work. - eventA := srv.MustCreateEvent(t, serverRoom, b.Event{ + eventA := srv.MustCreateEvent(t, serverRoom, federation.Event{ Type: "m.event.a", Sender: bob, Content: map[string]interface{}{ "event": "A", }, }) - eventB := srv.MustCreateEvent(t, serverRoom, b.Event{ - Type: "m.event.b", - Sender: bob, - PrevEvents: []string{eventA.EventID()}, + eventB := srv.MustCreateEvent(t, serverRoom, federation.Event{ + Type: "m.event.b", + Sender: bob, Content: map[string]interface{}{ "event": "B", }, + PrevEvents: []string{eventA.EventID()}, }) // Send event B into the room. Event A at this point is unknown