Skip to content
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
19 changes: 18 additions & 1 deletion federation/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,35 @@ func (s *Server) MustMakeRoom(t ct.TestLike, roomVer gomatrixserverlib.RoomVersi
if !s.listening {
ct.Fatalf(s.t, "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!")
}

// Generate a unique room ID, prefixed with an incrementing counter.
// This ensures that room IDs are not re-used across tests, even if a Complement server happens
// to re-use the same port as a previous one, which
// * reduces noise when searching through logs and
// * prevents homeservers from getting confused when multiple test cases re-use the same homeserver deployment.
// This value is temporary for domainless room IDs and will be replaced with the create event ID.
roomID := fmt.Sprintf("!%d-%s:%s", len(s.rooms), util.RandomString(18), s.serverName)
t.Logf("Creating room %s with version %s", roomID, roomVer)
room := NewServerRoom(roomVer, roomID)
for _, opt := range opts {
// let the caller replace the room impl before we try to create events
opt(room)
}

iRoomVer := gomatrixserverlib.MustGetRoomVersion(roomVer)
if iRoomVer.DomainlessRoomIDs() {
if len(events) == 0 || events[0].Type != spec.MRoomCreate {
ct.Fatalf(s.t, "MustMakeRoom: room version %s requires the create event as an initial event but it wasn't found", roomVer)
}
room.RoomID = ""
// build and sign the create event to work out the room ID
createEvent := s.MustCreateEvent(t, room, events[0])
events = events[1:]
room.RoomID = "!" + createEvent.EventID()[1:]
room.AddEvent(createEvent)
}

t.Logf("Creating room %s with version %s", room.RoomID, roomVer)

// sign all these events
for _, ev := range events {
signedEvent := s.MustCreateEvent(t, room, ev)
Expand Down
26 changes: 21 additions & 5 deletions federation/server_room.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util"

"github.com/matrix-org/complement/b"
"github.com/matrix-org/complement/ct"
Expand Down Expand Up @@ -302,7 +303,7 @@ func (r *ServerRoom) GetEventInTimeline(eventID string) (gomatrixserverlib.PDU,
return nil, false
}

func initialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
func initialPowerLevelsContent(ver gomatrixserverlib.IRoomVersion, roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
c.Defaults()
c.Events = map[string]int64{
"m.room.name": 50,
Expand All @@ -312,14 +313,18 @@ func initialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLev
"m.room.avatar": 50,
"m.room.aliases": 0, // anyone can publish aliases by default. Has to be 0 else state_default is used.
}
c.Users = map[string]int64{roomCreator: 100}
if ver.PrivilegedCreators() {
c.Users = map[string]int64{}
} else {
c.Users = map[string]int64{roomCreator: 100}
}
return c
}

// InitialRoomEvents returns the initial set of events that get created when making a room.
func InitialRoomEvents(roomVer gomatrixserverlib.RoomVersion, creator string) []Event {
// need to serialise/deserialise to get map[string]interface{} annoyingly
plContent := initialPowerLevelsContent(creator)
plContent := initialPowerLevelsContent(gomatrixserverlib.MustGetRoomVersion(roomVer), creator)
plBytes, _ := json.Marshal(plContent)
var plContentMap map[string]interface{}
json.Unmarshal(plBytes, &plContentMap)
Expand All @@ -331,6 +336,11 @@ func InitialRoomEvents(roomVer gomatrixserverlib.RoomVersion, creator string) []
Content: map[string]interface{}{
"creator": creator,
"room_version": roomVer,
// We have to add randomness to the create event, else if you create 2x v12+ rooms in the same millisecond
// they will get the same room ID, clobbering internal data structures and causing extremely confusing
// behaviour. By adding this entropy, we ensure that even if rooms are created in the same millisecond, their
// hashes will not be the same.
"complement_entropy": util.RandomString(18),
},
},
{
Expand Down Expand Up @@ -441,19 +451,25 @@ func (i *ServerRoomImplDefault) ProtoEventCreator(room *ServerRoom, ev Event) (*
PrevEvents: prevEvents,
AuthEvents: ev.AuthEvents,
Redacts: ev.Redacts,
Version: gomatrixserverlib.MustGetRoomVersion(room.Version),
}
if err := proto.SetContent(ev.Content); err != nil {
return nil, fmt.Errorf("EventCreator: failed to marshal event content: %s - %+v", err, ev.Content)
}
if err := proto.SetUnsigned(ev.Content); err != nil {
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
if len(ev.Unsigned) > 0 {
if err := proto.SetUnsigned(ev.Unsigned); err != nil {
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
}
}
if proto.AuthEvents == nil {
var stateNeeded gomatrixserverlib.StateNeeded
stateNeeded, err := gomatrixserverlib.StateNeededForProtoEvent(&proto)
if err != nil {
return nil, fmt.Errorf("EventCreator: failed to work out auth_events : %s", err)
}
if proto.Version.DomainlessRoomIDs() {
stateNeeded.Create = false
}
proto.AuthEvents = room.AuthEvents(stateNeeded)
}
return &proto, nil
Expand Down
87 changes: 34 additions & 53 deletions tests/v12_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,55 +29,6 @@ var maxCanonicalJSONInt = math.Pow(2, 53) - 1

const roomVersion12 = "12"

var V12ServerRoom = federation.ServerRoomImplCustom{
ProtoEventCreatorFn: Protov12EventCreator,
}

// Override how Complement makes proto events so we can conditionally disable/enable the inclusion of the create event
// depending on whether we're running in combined mode or not.
// Complement also doesn't set the room version correctly on the ProtoEvent as this was a new addition to GMSL.
func Protov12EventCreator(def federation.ServerRoomImpl, room *federation.ServerRoom, ev federation.Event) (*gomatrixserverlib.ProtoEvent, error) {
var prevEvents interface{}
if ev.PrevEvents != nil {
// We deliberately want to set the prev events.
prevEvents = ev.PrevEvents
} else {
// No other prev events were supplied so we'll just
// use the forward extremities of the room, which is
// the usual behaviour.
prevEvents = room.ForwardExtremities
}
proto := gomatrixserverlib.ProtoEvent{
SenderID: ev.Sender,
Depth: int64(room.Depth + 1), // depth starts at 1
Type: ev.Type,
StateKey: ev.StateKey,
RoomID: room.RoomID,
PrevEvents: prevEvents,
AuthEvents: ev.AuthEvents,
Redacts: ev.Redacts,
Version: gomatrixserverlib.MustGetRoomVersion(room.Version),
}
if err := proto.SetContent(ev.Content); err != nil {
return nil, fmt.Errorf("EventCreator: failed to marshal event content: %s - %+v", err, ev.Content)
}
if err := proto.SetUnsigned(ev.Content); err != nil {
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
}
if proto.AuthEvents == nil {
var stateNeeded gomatrixserverlib.StateNeeded
// this does the right thing for v12
stateNeeded, err := gomatrixserverlib.StateNeededForProtoEvent(&proto)
if err != nil {
return nil, fmt.Errorf("EventCreator: failed to work out auth_events : %s", err)
}
// we never include the create event if the HS supports MSC4291
stateNeeded.Create = false
proto.AuthEvents = room.AuthEvents(stateNeeded)
}
return &proto, nil
}

// Test that the creator can kick an admin created both via
// trusted_private_chat and by explicit promotion, including beyond PL100.
// Also checks the creator isn't in the PL event.
Expand Down Expand Up @@ -246,7 +197,7 @@ func TestMSC4289PrivilegedRoomCreators(t *testing.T) {
"room_version": roomVersion12,
"preset": "public_chat",
})
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob)
plEventID := alice.SendEventSynced(t, roomID, b.Event{
Type: spec.MRoomPowerLevels,
StateKey: b.Ptr(""),
Expand Down Expand Up @@ -663,6 +614,36 @@ func TestMSC4291RoomIDAsHashOfCreateEvent(t *testing.T) {
assertCreateEventIsRoomID(t, alice, roomID)
}

func TestComplementCanCreateValidV12Rooms(t *testing.T) {
deployment := complement.Deploy(t, 1)
defer deployment.Destroy(t)
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
srv := federation.NewServer(t, deployment,
federation.HandleKeyRequests(),
federation.HandleMakeSendJoinRequests(),
federation.HandleTransactionRequests(nil, nil),
federation.HandleEventRequests(),
)
srv.UnexpectedRequestsAreErrors = false
cancel := srv.Listen()
defer cancel()
bob := srv.UserID("bob")
srvRoom := srv.MustMakeRoom(t, roomVersion12, federation.InitialRoomEvents(roomVersion12, bob))
alice.MustJoinRoom(t, srvRoom.RoomID, []spec.ServerName{srv.ServerName()})

msg := srv.MustCreateEvent(t, srvRoom, federation.Event{
Type: "m.room.message",
Sender: bob,
Content: map[string]interface{}{
"msgtype": "m.text",
"body": "Hello world",
},
})
srvRoom.AddEvent(msg)
srv.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{msg.JSON()}, nil)
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncTimelineHasEventID(srvRoom.RoomID, msg.EventID()))
}

func TestMSC4291RoomIDAsHashOfCreateEvent_AuthEventsOmitsCreateEvent(t *testing.T) {
deployment := complement.Deploy(t, 1)
defer deployment.Destroy(t)
Expand All @@ -683,7 +664,7 @@ func TestMSC4291RoomIDAsHashOfCreateEvent_AuthEventsOmitsCreateEvent(t *testing.
defer cancel()
bob := srv.UserID("bob")

room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob)

createEvent := room.CurrentState(spec.MRoomCreate, "")
if createEvent == nil {
Expand Down Expand Up @@ -954,7 +935,7 @@ func TestMSC4297StateResolutionV2_1_starts_from_empty_set(t *testing.T) {
"preset": "public_chat",
})
bob.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie)
joinRulePublic := room.CurrentState(spec.MRoomJoinRules, "")
aliceJoin := room.CurrentState(spec.MRoomMember, alice.UserID)
synchronisationEventID := bob.SendEventSynced(t, room.RoomID, b.Event{
Expand Down Expand Up @@ -1150,7 +1131,7 @@ func TestMSC4297StateResolutionV2_1_includes_conflicted_subgraph(t *testing.T) {
})
alice.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
bob.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie)
firstPowerLevelEvent := room.CurrentState(spec.MRoomPowerLevels, "")
alice.SendEventSynced(t, roomID, b.Event{
Type: spec.MRoomPowerLevels,
Expand Down
Loading