Skip to content

Commit 3f92454

Browse files
Merge pull request #11 from famedly/famedly-release/v1.147.1
Famedly release/v1.147.1
2 parents 5a43cf3 + 31825b0 commit 3f92454

4 files changed

Lines changed: 121 additions & 74 deletions

File tree

federation/server.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,18 +180,35 @@ func (s *Server) MustMakeRoom(t ct.TestLike, roomVer gomatrixserverlib.RoomVersi
180180
if !s.listening {
181181
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!")
182182
}
183+
183184
// Generate a unique room ID, prefixed with an incrementing counter.
184185
// This ensures that room IDs are not re-used across tests, even if a Complement server happens
185186
// to re-use the same port as a previous one, which
186187
// * reduces noise when searching through logs and
187188
// * prevents homeservers from getting confused when multiple test cases re-use the same homeserver deployment.
189+
// This value is temporary for domainless room IDs and will be replaced with the create event ID.
188190
roomID := fmt.Sprintf("!%d-%s:%s", len(s.rooms), util.RandomString(18), s.serverName)
189-
t.Logf("Creating room %s with version %s", roomID, roomVer)
190191
room := NewServerRoom(roomVer, roomID)
191192
for _, opt := range opts {
193+
// let the caller replace the room impl before we try to create events
192194
opt(room)
193195
}
194196

197+
iRoomVer := gomatrixserverlib.MustGetRoomVersion(roomVer)
198+
if iRoomVer.DomainlessRoomIDs() {
199+
if len(events) == 0 || events[0].Type != spec.MRoomCreate {
200+
ct.Fatalf(s.t, "MustMakeRoom: room version %s requires the create event as an initial event but it wasn't found", roomVer)
201+
}
202+
room.RoomID = ""
203+
// build and sign the create event to work out the room ID
204+
createEvent := s.MustCreateEvent(t, room, events[0])
205+
events = events[1:]
206+
room.RoomID = "!" + createEvent.EventID()[1:]
207+
room.AddEvent(createEvent)
208+
}
209+
210+
t.Logf("Creating room %s with version %s", room.RoomID, roomVer)
211+
195212
// sign all these events
196213
for _, ev := range events {
197214
signedEvent := s.MustCreateEvent(t, room, ev)

federation/server_room.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/matrix-org/gomatrixserverlib"
1111
"github.com/matrix-org/gomatrixserverlib/fclient"
1212
"github.com/matrix-org/gomatrixserverlib/spec"
13+
"github.com/matrix-org/util"
1314

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

305-
func initialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
306+
func initialPowerLevelsContent(ver gomatrixserverlib.IRoomVersion, roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
306307
c.Defaults()
307308
c.Events = map[string]int64{
308309
"m.room.name": 50,
@@ -312,14 +313,18 @@ func initialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLev
312313
"m.room.avatar": 50,
313314
"m.room.aliases": 0, // anyone can publish aliases by default. Has to be 0 else state_default is used.
314315
}
315-
c.Users = map[string]int64{roomCreator: 100}
316+
if ver.PrivilegedCreators() {
317+
c.Users = map[string]int64{}
318+
} else {
319+
c.Users = map[string]int64{roomCreator: 100}
320+
}
316321
return c
317322
}
318323

319324
// InitialRoomEvents returns the initial set of events that get created when making a room.
320325
func InitialRoomEvents(roomVer gomatrixserverlib.RoomVersion, creator string) []Event {
321326
// need to serialise/deserialise to get map[string]interface{} annoyingly
322-
plContent := initialPowerLevelsContent(creator)
327+
plContent := initialPowerLevelsContent(gomatrixserverlib.MustGetRoomVersion(roomVer), creator)
323328
plBytes, _ := json.Marshal(plContent)
324329
var plContentMap map[string]interface{}
325330
json.Unmarshal(plBytes, &plContentMap)
@@ -331,6 +336,11 @@ func InitialRoomEvents(roomVer gomatrixserverlib.RoomVersion, creator string) []
331336
Content: map[string]interface{}{
332337
"creator": creator,
333338
"room_version": roomVer,
339+
// We have to add randomness to the create event, else if you create 2x v12+ rooms in the same millisecond
340+
// they will get the same room ID, clobbering internal data structures and causing extremely confusing
341+
// behaviour. By adding this entropy, we ensure that even if rooms are created in the same millisecond, their
342+
// hashes will not be the same.
343+
"complement_entropy": util.RandomString(18),
334344
},
335345
},
336346
{
@@ -441,19 +451,25 @@ func (i *ServerRoomImplDefault) ProtoEventCreator(room *ServerRoom, ev Event) (*
441451
PrevEvents: prevEvents,
442452
AuthEvents: ev.AuthEvents,
443453
Redacts: ev.Redacts,
454+
Version: gomatrixserverlib.MustGetRoomVersion(room.Version),
444455
}
445456
if err := proto.SetContent(ev.Content); err != nil {
446457
return nil, fmt.Errorf("EventCreator: failed to marshal event content: %s - %+v", err, ev.Content)
447458
}
448-
if err := proto.SetUnsigned(ev.Content); err != nil {
449-
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
459+
if len(ev.Unsigned) > 0 {
460+
if err := proto.SetUnsigned(ev.Unsigned); err != nil {
461+
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
462+
}
450463
}
451464
if proto.AuthEvents == nil {
452465
var stateNeeded gomatrixserverlib.StateNeeded
453466
stateNeeded, err := gomatrixserverlib.StateNeededForProtoEvent(&proto)
454467
if err != nil {
455468
return nil, fmt.Errorf("EventCreator: failed to work out auth_events : %s", err)
456469
}
470+
if proto.Version.DomainlessRoomIDs() {
471+
stateNeeded.Create = false
472+
}
457473
proto.AuthEvents = room.AuthEvents(stateNeeded)
458474
}
459475
return &proto, nil

tests/csapi/media_async_uploads_test.go

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import (
88

99
"github.com/matrix-org/complement"
1010
"github.com/matrix-org/complement/client"
11+
"github.com/matrix-org/complement/ct"
1112
"github.com/matrix-org/complement/helpers"
1213
"github.com/matrix-org/complement/internal/data"
1314
"github.com/matrix-org/complement/match"
1415
"github.com/matrix-org/complement/must"
1516
"github.com/matrix-org/complement/runtime"
1617
)
1718

19+
const pngContentType = "image/png"
20+
1821
func TestAsyncUpload(t *testing.T) {
1922
runtime.SkipIf(t, runtime.Dendrite) // Dendrite doesn't support async uploads
2023

@@ -23,61 +26,91 @@ func TestAsyncUpload(t *testing.T) {
2326

2427
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
2528

26-
var mxcURI, mediaID string
2729
t.Run("Create media", func(t *testing.T) {
28-
mxcURI = alice.CreateMedia(t)
29-
parts := strings.Split(mxcURI, "/")
30-
mediaID = parts[len(parts)-1]
30+
alice.CreateMedia(t)
3131
})
3232

33-
origin, mediaID := client.SplitMxc(mxcURI)
34-
3533
t.Run("Not yet uploaded", func(t *testing.T) {
34+
mxcURI := alice.CreateMedia(t)
35+
parts := strings.Split(mxcURI, "/")
36+
mediaID := parts[len(parts)-1]
37+
origin, mediaID := client.SplitMxc(mxcURI)
38+
3639
// Check that the media is not yet uploaded
3740
res := alice.Do(t, "GET", []string{"_matrix", "media", "v3", "download", origin, mediaID})
3841
must.MatchResponse(t, res, match.HTTPResponse{
3942
StatusCode: http.StatusGatewayTimeout,
4043
JSON: []match.JSON{
4144
match.JSONKeyEqual("errcode", "M_NOT_YET_UPLOADED"),
42-
match.JSONKeyEqual("error", "Media has not been uploaded yet"),
45+
match.JSONKeyPresent("error"),
4346
},
4447
})
4548
})
4649

47-
wantContentType := "image/png"
48-
4950
t.Run("Upload media", func(t *testing.T) {
50-
alice.UploadMediaAsync(t, origin, mediaID, data.MatrixPng, "test.png", wantContentType)
51+
_ = asyncUploadMedia(t, alice)
5152
})
5253

5354
t.Run("Cannot upload to a media ID that has already been uploaded to", func(t *testing.T) {
55+
// First upload some media that we can conflict with
56+
mxcURI := asyncUploadMedia(t, alice)
57+
parts := strings.Split(mxcURI, "/")
58+
mediaID := parts[len(parts)-1]
59+
origin, mediaID := client.SplitMxc(mxcURI)
60+
61+
// Then try upload again using the same `mediaID`
5462
res := alice.Do(t, "PUT", []string{"_matrix", "media", "v3", "upload", origin, mediaID})
5563
must.MatchResponse(t, res, match.HTTPResponse{
5664
StatusCode: http.StatusConflict,
5765
JSON: []match.JSON{
5866
match.JSONKeyEqual("errcode", "M_CANNOT_OVERWRITE_MEDIA"),
59-
match.JSONKeyEqual("error", "Media ID already has content"),
67+
match.JSONKeyPresent("error"),
6068
},
6169
})
6270
})
6371

72+
// TODO: This is the same as the test below (both use authenticated media). Previously
73+
// this was testing unauthenticated vs authenticated media. We should resolve this by
74+
// removing one of these tests or ideally, keeping both authenticated and
75+
// unauthenticated tests and just gate it behind some homeserver check for
76+
// unauthenticated media support (see
77+
// https://github.com/matrix-org/complement/pull/746#discussion_r2718904066)
6478
t.Run("Download media", func(t *testing.T) {
79+
mxcURI := asyncUploadMedia(t, alice)
80+
6581
content, contentType := alice.DownloadContentAuthenticated(t, mxcURI)
6682
if !bytes.Equal(data.MatrixPng, content) {
6783
t.Fatalf("uploaded and downloaded content doesn't match: want %v\ngot\n%v", data.MatrixPng, content)
6884
}
69-
if contentType != wantContentType {
70-
t.Fatalf("expected contentType to be %s, got %s", wantContentType, contentType)
85+
if contentType != pngContentType {
86+
t.Fatalf("expected contentType to be %s, got %s", pngContentType, contentType)
7187
}
7288
})
7389

7490
t.Run("Download media over _matrix/client/v1/media/download", func(t *testing.T) {
91+
mxcURI := asyncUploadMedia(t, alice)
92+
7593
content, contentType := alice.DownloadContentAuthenticated(t, mxcURI)
7694
if !bytes.Equal(data.MatrixPng, content) {
7795
t.Fatalf("uploaded and downloaded content doesn't match: want %v\ngot\n%v", data.MatrixPng, content)
7896
}
79-
if contentType != wantContentType {
80-
t.Fatalf("expected contentType to be %s, got %s", wantContentType, contentType)
97+
if contentType != pngContentType {
98+
t.Fatalf("expected contentType to be %s, got %s", pngContentType, contentType)
8199
}
82100
})
83101
}
102+
103+
func asyncUploadMedia(
104+
t ct.TestLike,
105+
matrixClient *client.CSAPI,
106+
) string {
107+
t.Helper()
108+
109+
mxcURI := matrixClient.CreateMedia(t)
110+
parts := strings.Split(mxcURI, "/")
111+
mediaID := parts[len(parts)-1]
112+
origin, mediaID := client.SplitMxc(mxcURI)
113+
matrixClient.UploadMediaAsync(t, origin, mediaID, data.MatrixPng, "test.png", pngContentType)
114+
115+
return mxcURI
116+
}

tests/v12_test.go

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,55 +29,6 @@ var maxCanonicalJSONInt = math.Pow(2, 53) - 1
2929

3030
const roomVersion12 = "12"
3131

32-
var V12ServerRoom = federation.ServerRoomImplCustom{
33-
ProtoEventCreatorFn: Protov12EventCreator,
34-
}
35-
36-
// Override how Complement makes proto events so we can conditionally disable/enable the inclusion of the create event
37-
// depending on whether we're running in combined mode or not.
38-
// Complement also doesn't set the room version correctly on the ProtoEvent as this was a new addition to GMSL.
39-
func Protov12EventCreator(def federation.ServerRoomImpl, room *federation.ServerRoom, ev federation.Event) (*gomatrixserverlib.ProtoEvent, error) {
40-
var prevEvents interface{}
41-
if ev.PrevEvents != nil {
42-
// We deliberately want to set the prev events.
43-
prevEvents = ev.PrevEvents
44-
} else {
45-
// No other prev events were supplied so we'll just
46-
// use the forward extremities of the room, which is
47-
// the usual behaviour.
48-
prevEvents = room.ForwardExtremities
49-
}
50-
proto := gomatrixserverlib.ProtoEvent{
51-
SenderID: ev.Sender,
52-
Depth: int64(room.Depth + 1), // depth starts at 1
53-
Type: ev.Type,
54-
StateKey: ev.StateKey,
55-
RoomID: room.RoomID,
56-
PrevEvents: prevEvents,
57-
AuthEvents: ev.AuthEvents,
58-
Redacts: ev.Redacts,
59-
Version: gomatrixserverlib.MustGetRoomVersion(room.Version),
60-
}
61-
if err := proto.SetContent(ev.Content); err != nil {
62-
return nil, fmt.Errorf("EventCreator: failed to marshal event content: %s - %+v", err, ev.Content)
63-
}
64-
if err := proto.SetUnsigned(ev.Content); err != nil {
65-
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
66-
}
67-
if proto.AuthEvents == nil {
68-
var stateNeeded gomatrixserverlib.StateNeeded
69-
// this does the right thing for v12
70-
stateNeeded, err := gomatrixserverlib.StateNeededForProtoEvent(&proto)
71-
if err != nil {
72-
return nil, fmt.Errorf("EventCreator: failed to work out auth_events : %s", err)
73-
}
74-
// we never include the create event if the HS supports MSC4291
75-
stateNeeded.Create = false
76-
proto.AuthEvents = room.AuthEvents(stateNeeded)
77-
}
78-
return &proto, nil
79-
}
80-
8132
// Test that the creator can kick an admin created both via
8233
// trusted_private_chat and by explicit promotion, including beyond PL100.
8334
// Also checks the creator isn't in the PL event.
@@ -246,7 +197,7 @@ func TestMSC4289PrivilegedRoomCreators(t *testing.T) {
246197
"room_version": roomVersion12,
247198
"preset": "public_chat",
248199
})
249-
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
200+
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob)
250201
plEventID := alice.SendEventSynced(t, roomID, b.Event{
251202
Type: spec.MRoomPowerLevels,
252203
StateKey: b.Ptr(""),
@@ -663,6 +614,36 @@ func TestMSC4291RoomIDAsHashOfCreateEvent(t *testing.T) {
663614
assertCreateEventIsRoomID(t, alice, roomID)
664615
}
665616

617+
func TestComplementCanCreateValidV12Rooms(t *testing.T) {
618+
deployment := complement.Deploy(t, 1)
619+
defer deployment.Destroy(t)
620+
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
621+
srv := federation.NewServer(t, deployment,
622+
federation.HandleKeyRequests(),
623+
federation.HandleMakeSendJoinRequests(),
624+
federation.HandleTransactionRequests(nil, nil),
625+
federation.HandleEventRequests(),
626+
)
627+
srv.UnexpectedRequestsAreErrors = false
628+
cancel := srv.Listen()
629+
defer cancel()
630+
bob := srv.UserID("bob")
631+
srvRoom := srv.MustMakeRoom(t, roomVersion12, federation.InitialRoomEvents(roomVersion12, bob))
632+
alice.MustJoinRoom(t, srvRoom.RoomID, []spec.ServerName{srv.ServerName()})
633+
634+
msg := srv.MustCreateEvent(t, srvRoom, federation.Event{
635+
Type: "m.room.message",
636+
Sender: bob,
637+
Content: map[string]interface{}{
638+
"msgtype": "m.text",
639+
"body": "Hello world",
640+
},
641+
})
642+
srvRoom.AddEvent(msg)
643+
srv.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{msg.JSON()}, nil)
644+
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncTimelineHasEventID(srvRoom.RoomID, msg.EventID()))
645+
}
646+
666647
func TestMSC4291RoomIDAsHashOfCreateEvent_AuthEventsOmitsCreateEvent(t *testing.T) {
667648
deployment := complement.Deploy(t, 1)
668649
defer deployment.Destroy(t)
@@ -683,7 +664,7 @@ func TestMSC4291RoomIDAsHashOfCreateEvent_AuthEventsOmitsCreateEvent(t *testing.
683664
defer cancel()
684665
bob := srv.UserID("bob")
685666

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

688669
createEvent := room.CurrentState(spec.MRoomCreate, "")
689670
if createEvent == nil {
@@ -954,7 +935,7 @@ func TestMSC4297StateResolutionV2_1_starts_from_empty_set(t *testing.T) {
954935
"preset": "public_chat",
955936
})
956937
bob.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
957-
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
938+
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie)
958939
joinRulePublic := room.CurrentState(spec.MRoomJoinRules, "")
959940
aliceJoin := room.CurrentState(spec.MRoomMember, alice.UserID)
960941
synchronisationEventID := bob.SendEventSynced(t, room.RoomID, b.Event{
@@ -1150,7 +1131,7 @@ func TestMSC4297StateResolutionV2_1_includes_conflicted_subgraph(t *testing.T) {
11501131
})
11511132
alice.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
11521133
bob.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
1153-
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
1134+
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie)
11541135
firstPowerLevelEvent := room.CurrentState(spec.MRoomPowerLevels, "")
11551136
alice.SendEventSynced(t, roomID, b.Event{
11561137
Type: spec.MRoomPowerLevels,

0 commit comments

Comments
 (0)