Skip to content

Commit 7465729

Browse files
authored
Merge pull request #84 from globekeeper/release/upstream-v0.13.6-17
Release/upstream v0.13.6 17
2 parents 0aaf181 + 6a3772d commit 7465729

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1322
-230
lines changed

.github/codecov.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ coverage:
77
project:
88
default:
99
target: auto
10-
threshold: 0%
10+
threshold: 0.1%
1111
base: auto
1212
flags:
1313
- unittests

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,4 @@ go.work*
8383
__debug_bin*
8484

8585
cmd/dendrite-monolith-server/dendrite-monolith-server
86-
build
86+
build

CHANGES.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Changelog
22

3+
## Dendrite 0.13.6 (2024-01-26)
4+
5+
Upgrading to this version is **highly** recommended, as it contains several QoL improvements.
6+
7+
### Fixes
8+
9+
- Use `AckExplicitPolicy` for JetStream consumers, so messages don't pile up in NATS
10+
- A rare panic when assigning a state key NID has been fixed
11+
- A rare panic when checking powerlevels has been fixed
12+
- Notary keys requests for all keys now work correctly
13+
- Spec compliance:
14+
- Return `M_INVALID_PARAM` when querying room aliases
15+
- Handle empty `from` parameter when requesting `/messages`
16+
- Add CORP headers on media endpoints
17+
- Remove `aliases` from `/publicRooms` responses
18+
- Allow `+` in MXIDs (Contributed by [RosstheRoss](https://github.com/RosstheRoss))
19+
- Fixes membership transitions from `knock` to `join` in `knock_restricted` rooms
20+
- Incremental syncs now batch querying events (Contributed by [recht](https://github.com/recht))
21+
- Move `/joined_members` back to the clientAPI/roomserver, which should make bridges happier again
22+
- Backfilling from other servers now only uses at max 100 events instead of potentially thousands
23+
324
## Dendrite 0.13.5 (2023-12-12)
425

526
Upgrading to this version is **highly** recommended, as it fixes several long-standing bugs in

build/docker/Dockerfile.demo-pinecone

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
FROM docker.io/golang:1.21-alpine AS base
1+
# Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved
2+
FROM docker.io/golang:1.21-alpine3.18 AS base
23

34
#
45
# Needs to be separate from the main Dockerfile for OpenShift,

build/docker/Dockerfile.demo-yggdrasil

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
FROM docker.io/golang:1.21-alpine AS base
1+
# Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved
2+
FROM docker.io/golang:1.21-alpine3.18 AS base
23

34
#
45
# Needs to be separate from the main Dockerfile for OpenShift,

clientapi/clientapi_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,3 +2148,130 @@ func TestKeyBackup(t *testing.T) {
21482148
}
21492149
})
21502150
}
2151+
2152+
func TestGetMembership(t *testing.T) {
2153+
alice := test.NewUser(t)
2154+
bob := test.NewUser(t)
2155+
2156+
testCases := []struct {
2157+
name string
2158+
roomID string
2159+
user *test.User
2160+
additionalEvents func(t *testing.T, room *test.Room)
2161+
request func(t *testing.T, room *test.Room, accessToken string) *http.Request
2162+
wantOK bool
2163+
wantMemberCount int
2164+
}{
2165+
2166+
{
2167+
name: "/joined_members - Bob never joined",
2168+
user: bob,
2169+
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
2170+
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
2171+
"access_token": accessToken,
2172+
}))
2173+
},
2174+
wantOK: false,
2175+
},
2176+
{
2177+
name: "/joined_members - Alice joined",
2178+
user: alice,
2179+
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
2180+
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
2181+
"access_token": accessToken,
2182+
}))
2183+
},
2184+
wantOK: true,
2185+
wantMemberCount: 1,
2186+
},
2187+
{
2188+
name: "/joined_members - Alice leaves, shouldn't be able to see members ",
2189+
user: alice,
2190+
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
2191+
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
2192+
"access_token": accessToken,
2193+
}))
2194+
},
2195+
additionalEvents: func(t *testing.T, room *test.Room) {
2196+
room.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
2197+
"membership": "leave",
2198+
}, test.WithStateKey(alice.ID))
2199+
},
2200+
wantOK: false,
2201+
},
2202+
{
2203+
name: "/joined_members - Bob joins, Alice sees two members",
2204+
user: alice,
2205+
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
2206+
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
2207+
"access_token": accessToken,
2208+
}))
2209+
},
2210+
additionalEvents: func(t *testing.T, room *test.Room) {
2211+
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
2212+
"membership": "join",
2213+
}, test.WithStateKey(bob.ID))
2214+
},
2215+
wantOK: true,
2216+
wantMemberCount: 2,
2217+
},
2218+
}
2219+
2220+
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
2221+
2222+
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
2223+
routers := httputil.NewRouters()
2224+
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
2225+
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
2226+
defer close()
2227+
natsInstance := jetstream.NATSInstance{}
2228+
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
2229+
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
2230+
2231+
// Use an actual roomserver for this
2232+
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
2233+
rsAPI.SetFederationAPI(nil, nil)
2234+
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
2235+
2236+
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
2237+
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
2238+
2239+
accessTokens := map[*test.User]userDevice{
2240+
alice: {},
2241+
bob: {},
2242+
}
2243+
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
2244+
2245+
for _, tc := range testCases {
2246+
t.Run(tc.name, func(t *testing.T) {
2247+
room := test.NewRoom(t, alice)
2248+
t.Cleanup(func() {
2249+
t.Logf("running cleanup for %s", tc.name)
2250+
})
2251+
// inject additional events
2252+
if tc.additionalEvents != nil {
2253+
tc.additionalEvents(t, room)
2254+
}
2255+
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
2256+
t.Fatalf("failed to send events: %v", err)
2257+
}
2258+
2259+
w := httptest.NewRecorder()
2260+
routers.Client.ServeHTTP(w, tc.request(t, room, accessTokens[tc.user].accessToken))
2261+
if w.Code != 200 && tc.wantOK {
2262+
t.Logf("%s", w.Body.String())
2263+
t.Fatalf("got HTTP %d want %d", w.Code, 200)
2264+
}
2265+
t.Logf("[%s] Resp: %s", tc.name, w.Body.String())
2266+
2267+
// check we got the expected events
2268+
if tc.wantOK {
2269+
memberCount := len(gjson.GetBytes(w.Body.Bytes(), "joined").Map())
2270+
if memberCount != tc.wantMemberCount {
2271+
t.Fatalf("expected %d members, got %d", tc.wantMemberCount, memberCount)
2272+
}
2273+
}
2274+
})
2275+
}
2276+
})
2277+
}

clientapi/routing/directory.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func DirectoryRoom(
5555
if err != nil {
5656
return util.JSONResponse{
5757
Code: http.StatusBadRequest,
58-
JSON: spec.BadJSON("Room alias must be in the form '#localpart:domain'"),
58+
JSON: spec.InvalidParam("Room alias must be in the form '#localpart:domain'"),
5959
}
6060
}
6161

@@ -134,7 +134,7 @@ func SetLocalAlias(
134134
if err != nil {
135135
return util.JSONResponse{
136136
Code: http.StatusBadRequest,
137-
JSON: spec.BadJSON("Room alias must be in the form '#localpart:domain'"),
137+
JSON: spec.InvalidParam("Room alias must be in the form '#localpart:domain'"),
138138
}
139139
}
140140

clientapi/routing/memberships.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2024 The Matrix.org Foundation C.I.C.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package routing
16+
17+
import (
18+
"encoding/json"
19+
"net/http"
20+
21+
"github.com/matrix-org/dendrite/roomserver/api"
22+
userapi "github.com/matrix-org/dendrite/userapi/api"
23+
"github.com/matrix-org/gomatrixserverlib/spec"
24+
"github.com/matrix-org/util"
25+
)
26+
27+
// https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-joined-members
28+
type getJoinedMembersResponse struct {
29+
Joined map[string]joinedMember `json:"joined"`
30+
}
31+
32+
type joinedMember struct {
33+
DisplayName string `json:"display_name"`
34+
AvatarURL string `json:"avatar_url"`
35+
}
36+
37+
// The database stores 'displayname' without an underscore.
38+
// Deserialize into this and then change to the actual API response
39+
type databaseJoinedMember struct {
40+
DisplayName string `json:"displayname"`
41+
AvatarURL string `json:"avatar_url"`
42+
}
43+
44+
// GetJoinedMembers implements
45+
//
46+
// GET /rooms/{roomId}/joined_members
47+
func GetJoinedMembers(
48+
req *http.Request, device *userapi.Device, roomID string,
49+
rsAPI api.ClientRoomserverAPI,
50+
) util.JSONResponse {
51+
// Validate the userID
52+
userID, err := spec.NewUserID(device.UserID, true)
53+
if err != nil {
54+
return util.JSONResponse{
55+
Code: http.StatusBadRequest,
56+
JSON: spec.InvalidParam("Device UserID is invalid"),
57+
}
58+
}
59+
60+
// Validate the roomID
61+
validRoomID, err := spec.NewRoomID(roomID)
62+
if err != nil {
63+
return util.JSONResponse{
64+
Code: http.StatusBadRequest,
65+
JSON: spec.InvalidParam("RoomID is invalid"),
66+
}
67+
}
68+
69+
// Get the current memberships for the requesting user to determine
70+
// if they are allowed to query this endpoint.
71+
queryReq := api.QueryMembershipForUserRequest{
72+
RoomID: validRoomID.String(),
73+
UserID: *userID,
74+
}
75+
76+
var queryRes api.QueryMembershipForUserResponse
77+
if queryErr := rsAPI.QueryMembershipForUser(req.Context(), &queryReq, &queryRes); queryErr != nil {
78+
util.GetLogger(req.Context()).WithError(queryErr).Error("rsAPI.QueryMembershipsForRoom failed")
79+
return util.JSONResponse{
80+
Code: http.StatusInternalServerError,
81+
JSON: spec.InternalServerError{},
82+
}
83+
}
84+
85+
if !queryRes.HasBeenInRoom {
86+
return util.JSONResponse{
87+
Code: http.StatusForbidden,
88+
JSON: spec.Forbidden("You aren't a member of the room and weren't previously a member of the room."),
89+
}
90+
}
91+
92+
if !queryRes.IsInRoom {
93+
return util.JSONResponse{
94+
Code: http.StatusForbidden,
95+
JSON: spec.Forbidden("You aren't a member of the room and weren't previously a member of the room."),
96+
}
97+
}
98+
99+
// Get the current membership events
100+
var membershipsForRoomResp api.QueryMembershipsForRoomResponse
101+
if err = rsAPI.QueryMembershipsForRoom(req.Context(), &api.QueryMembershipsForRoomRequest{
102+
JoinedOnly: true,
103+
RoomID: validRoomID.String(),
104+
}, &membershipsForRoomResp); err != nil {
105+
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryEventsByID failed")
106+
return util.JSONResponse{
107+
Code: http.StatusInternalServerError,
108+
JSON: spec.InternalServerError{},
109+
}
110+
}
111+
112+
var res getJoinedMembersResponse
113+
res.Joined = make(map[string]joinedMember)
114+
for _, ev := range membershipsForRoomResp.JoinEvents {
115+
var content databaseJoinedMember
116+
if err := json.Unmarshal(ev.Content, &content); err != nil {
117+
util.GetLogger(req.Context()).WithError(err).Error("failed to unmarshal event content")
118+
return util.JSONResponse{
119+
Code: http.StatusInternalServerError,
120+
JSON: spec.InternalServerError{},
121+
}
122+
}
123+
124+
userID, err := rsAPI.QueryUserIDForSender(req.Context(), *validRoomID, spec.SenderID(ev.Sender))
125+
if err != nil || userID == nil {
126+
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryUserIDForSender failed")
127+
return util.JSONResponse{
128+
Code: http.StatusInternalServerError,
129+
JSON: spec.InternalServerError{},
130+
}
131+
}
132+
133+
res.Joined[userID.String()] = joinedMember(content)
134+
}
135+
return util.JSONResponse{
136+
Code: http.StatusOK,
137+
JSON: res,
138+
}
139+
}

clientapi/routing/register.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,7 @@ func handleGuestRegistration(
635635
AccessToken: token,
636636
IPAddr: req.RemoteAddr,
637637
UserAgent: req.UserAgent(),
638+
FromRegistration: true,
638639
}, &devRes)
639640
if err != nil {
640641
return util.JSONResponse{
@@ -982,6 +983,7 @@ func completeRegistration(
982983
DeviceID: deviceID,
983984
IPAddr: ipAddr,
984985
UserAgent: userAgent,
986+
FromRegistration: true,
985987
}, &devRes)
986988
if err != nil {
987989
return util.JSONResponse{

clientapi/routing/routing.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,4 +1526,14 @@ func Setup(
15261526
return GetPresence(req, device, natsClient, cfg.Matrix.JetStream.Prefixed(jetstream.RequestPresence), vars["userId"])
15271527
}),
15281528
).Methods(http.MethodGet, http.MethodOptions)
1529+
1530+
v3mux.Handle("/rooms/{roomID}/joined_members",
1531+
httputil.MakeAuthAPI("rooms_members", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
1532+
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
1533+
if err != nil {
1534+
return util.ErrorResponse(err)
1535+
}
1536+
return GetJoinedMembers(req, device, vars["roomID"], rsAPI)
1537+
}),
1538+
).Methods(http.MethodGet, http.MethodOptions)
15291539
}

0 commit comments

Comments
 (0)