aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--federationsender/api/api.go1
-rw-r--r--federationsender/internal/api.go14
-rw-r--r--federationsender/inthttp/client.go32
-rw-r--r--federationsender/inthttp/server.go22
-rw-r--r--go.mod11
-rw-r--r--go.sum29
-rw-r--r--setup/mscs/msc2946/msc2946.go317
-rw-r--r--setup/mscs/msc2946/msc2946_test.go22
-rw-r--r--setup/mscs/mscs.go2
9 files changed, 379 insertions, 71 deletions
diff --git a/federationsender/api/api.go b/federationsender/api/api.go
index e4d176b1..dfc2dd8a 100644
--- a/federationsender/api/api.go
+++ b/federationsender/api/api.go
@@ -22,6 +22,7 @@ type FederationClient interface {
GetEvent(ctx context.Context, s gomatrixserverlib.ServerName, eventID string) (res gomatrixserverlib.Transaction, err error)
GetServerKeys(ctx context.Context, matrixServer gomatrixserverlib.ServerName) (gomatrixserverlib.ServerKeys, error)
MSC2836EventRelationships(ctx context.Context, dst gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error)
+ MSC2946Spaces(ctx context.Context, dst gomatrixserverlib.ServerName, roomID string, r gomatrixserverlib.MSC2946SpacesRequest) (res gomatrixserverlib.MSC2946SpacesResponse, err error)
LookupServerKeys(ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp) ([]gomatrixserverlib.ServerKeys, error)
}
diff --git a/federationsender/internal/api.go b/federationsender/internal/api.go
index 407e7ffe..1de774ef 100644
--- a/federationsender/internal/api.go
+++ b/federationsender/internal/api.go
@@ -244,3 +244,17 @@ func (a *FederationSenderInternalAPI) MSC2836EventRelationships(
}
return ires.(gomatrixserverlib.MSC2836EventRelationshipsResponse), nil
}
+
+func (a *FederationSenderInternalAPI) MSC2946Spaces(
+ ctx context.Context, s gomatrixserverlib.ServerName, roomID string, r gomatrixserverlib.MSC2946SpacesRequest,
+) (res gomatrixserverlib.MSC2946SpacesResponse, err error) {
+ ctx, cancel := context.WithTimeout(ctx, time.Minute)
+ defer cancel()
+ ires, err := a.doRequest(s, func() (interface{}, error) {
+ return a.federation.MSC2946Spaces(ctx, s, roomID, r)
+ })
+ if err != nil {
+ return res, err
+ }
+ return ires.(gomatrixserverlib.MSC2946SpacesResponse), nil
+}
diff --git a/federationsender/inthttp/client.go b/federationsender/inthttp/client.go
index fe98ff33..81d3fc51 100644
--- a/federationsender/inthttp/client.go
+++ b/federationsender/inthttp/client.go
@@ -33,6 +33,7 @@ const (
FederationSenderGetServerKeysPath = "/federationsender/client/getServerKeys"
FederationSenderLookupServerKeysPath = "/federationsender/client/lookupServerKeys"
FederationSenderEventRelationshipsPath = "/federationsender/client/msc2836eventRelationships"
+ FederationSenderSpacesSummaryPath = "/federationsender/client/msc2946spacesSummary"
)
// NewFederationSenderClient creates a FederationSenderInternalAPI implemented by talking to a HTTP POST API.
@@ -449,3 +450,34 @@ func (h *httpFederationSenderInternalAPI) MSC2836EventRelationships(
}
return response.Res, nil
}
+
+type spacesReq struct {
+ S gomatrixserverlib.ServerName
+ Req gomatrixserverlib.MSC2946SpacesRequest
+ RoomID string
+ Res gomatrixserverlib.MSC2946SpacesResponse
+ Err *api.FederationClientError
+}
+
+func (h *httpFederationSenderInternalAPI) MSC2946Spaces(
+ ctx context.Context, dst gomatrixserverlib.ServerName, roomID string, r gomatrixserverlib.MSC2946SpacesRequest,
+) (res gomatrixserverlib.MSC2946SpacesResponse, err error) {
+ span, ctx := opentracing.StartSpanFromContext(ctx, "MSC2946Spaces")
+ defer span.Finish()
+
+ request := spacesReq{
+ S: dst,
+ Req: r,
+ RoomID: roomID,
+ }
+ var response spacesReq
+ apiURL := h.federationSenderURL + FederationSenderSpacesSummaryPath
+ err = httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
+ if err != nil {
+ return res, err
+ }
+ if response.Err != nil {
+ return res, response.Err
+ }
+ return response.Res, nil
+}
diff --git a/federationsender/inthttp/server.go b/federationsender/inthttp/server.go
index 293fb420..be995111 100644
--- a/federationsender/inthttp/server.go
+++ b/federationsender/inthttp/server.go
@@ -329,4 +329,26 @@ func AddRoutes(intAPI api.FederationSenderInternalAPI, internalAPIMux *mux.Route
return util.JSONResponse{Code: http.StatusOK, JSON: request}
}),
)
+ internalAPIMux.Handle(
+ FederationSenderSpacesSummaryPath,
+ httputil.MakeInternalAPI("MSC2946SpacesSummary", func(req *http.Request) util.JSONResponse {
+ var request spacesReq
+ if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
+ return util.MessageResponse(http.StatusBadRequest, err.Error())
+ }
+ res, err := intAPI.MSC2946Spaces(req.Context(), request.S, request.RoomID, request.Req)
+ if err != nil {
+ ferr, ok := err.(*api.FederationClientError)
+ if ok {
+ request.Err = ferr
+ } else {
+ request.Err = &api.FederationClientError{
+ Err: err.Error(),
+ }
+ }
+ }
+ request.Res = res
+ return util.JSONResponse{Code: http.StatusOK, JSON: request}
+ }),
+ )
}
diff --git a/go.mod b/go.mod
index c9438841..58871dfb 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,7 @@ require (
github.com/matrix-org/go-http-js-libp2p v0.0.0-20200518170932-783164aeeda4
github.com/matrix-org/go-sqlite3-js v0.0.0-20200522092705-bc8506ccbcf3
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd
- github.com/matrix-org/gomatrixserverlib v0.0.0-20210113173004-b1c67ac867cc
+ github.com/matrix-org/gomatrixserverlib v0.0.0-20210119115951-bd57c7cff614
github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
github.com/mattn/go-sqlite3 v1.14.2
@@ -33,16 +33,15 @@ require (
github.com/pressly/goose v2.7.0-rc5+incompatible
github.com/prometheus/client_golang v1.7.1
github.com/sirupsen/logrus v1.7.0
- github.com/tidwall/gjson v1.6.3
- github.com/tidwall/match v1.0.2 // indirect
- github.com/tidwall/sjson v1.1.2
+ github.com/tidwall/gjson v1.6.7
+ github.com/tidwall/sjson v1.1.4
github.com/uber/jaeger-client-go v2.25.0+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/yggdrasil-network/yggdrasil-go v0.3.15-0.20201006093556-760d9a7fd5ee
go.uber.org/atomic v1.6.0
- golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9
+ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b
- golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
+ golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect
gopkg.in/h2non/bimg.v1 v1.1.4
gopkg.in/yaml.v2 v2.3.0
)
diff --git a/go.sum b/go.sum
index 7accb06e..b79337fa 100644
--- a/go.sum
+++ b/go.sum
@@ -567,8 +567,12 @@ github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26 h1:Hr3zjRsq2bh
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0=
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd h1:xVrqJK3xHREMNjwjljkAUaadalWc0rRbmVuQatzmgwg=
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
-github.com/matrix-org/gomatrixserverlib v0.0.0-20210113173004-b1c67ac867cc h1:n2Hnbg8RZ4102Qmxie1riLkIyrqeqShJUILg1miSmDI=
-github.com/matrix-org/gomatrixserverlib v0.0.0-20210113173004-b1c67ac867cc/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20210115150839-9ba5f3e11086 h1:nfGXVXx+cg1iBAWatukPsBe5OKsW+TdmF/qydnt04eg=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20210115150839-9ba5f3e11086/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20210115152401-7c4619994337 h1:HJ9iH00PwMDaXsH7vWpO7nRucz+d92QLoH0PNW7hs58=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20210115152401-7c4619994337/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20210119115951-bd57c7cff614 h1:X5FP1YOiGmPfpK4IAc8KyX8lOW4nC81/YZPTbOWAyKs=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20210119115951-bd57c7cff614/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91 h1:HJ6U3S3ljJqNffYMcIeAncp5qT/i+ZMiJ2JC2F0aXP4=
github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91/go.mod h1:sjyPyRxKM5uw1nD2cJ6O2OxI6GOqyVBfNXqKjBZTBZE=
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7 h1:ntrLa/8xVzeSs8vHFHK25k0C+NV74sYMJnNSg5NoSRo=
@@ -810,13 +814,12 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
-github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
-github.com/tidwall/gjson v1.6.3 h1:aHoiiem0dr7GHkW001T1SMTJ7X5PvyekH5WX0whWGnI=
-github.com/tidwall/gjson v1.6.3/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
+github.com/tidwall/gjson v1.6.7 h1:Mb1M9HZCRWEcXQ8ieJo7auYyyiSux6w9XN3AdTpxJrE=
+github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
-github.com/tidwall/match v1.0.2 h1:uuqvHuBGSedK7awZ2YoAtpnimfwBGFjHuWLuLqQj+bU=
-github.com/tidwall/match v1.0.2/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
+github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
+github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8=
github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -824,8 +827,8 @@ github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/sjson v1.0.3 h1:DeF+0LZqvIt4fKYw41aPB29ZGlvwVkHKktoXJ1YW9Y8=
github.com/tidwall/sjson v1.0.3/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y=
-github.com/tidwall/sjson v1.1.2 h1:NC5okI+tQ8OG/oyzchvwXXxRxCV/FVdhODbPKkQ25jQ=
-github.com/tidwall/sjson v1.1.2/go.mod h1:SEzaDwxiPzKzNfUEO4HbYF/m4UCSJDsGgNqsS1LvdoY=
+github.com/tidwall/sjson v1.1.4 h1:bTSsPLdAYF5QNLSwYsKfBKKTnlGbIuhqL3CpRsjzGhg=
+github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg=
github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=
github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
@@ -906,8 +909,8 @@ golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzht
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o=
-golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -996,8 +999,8 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
+golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/setup/mscs/msc2946/msc2946.go b/setup/mscs/msc2946/msc2946.go
index 2b547737..c3a68632 100644
--- a/setup/mscs/msc2946/msc2946.go
+++ b/setup/mscs/msc2946/msc2946.go
@@ -17,13 +17,17 @@ package msc2946
import (
"context"
+ "encoding/json"
"fmt"
"net/http"
"strings"
"sync"
+ "time"
"github.com/gorilla/mux"
chttputil "github.com/matrix-org/dendrite/clientapi/httputil"
+ "github.com/matrix-org/dendrite/clientapi/jsonerror"
+ fs "github.com/matrix-org/dendrite/federationsender/api"
"github.com/matrix-org/dendrite/internal/hooks"
"github.com/matrix-org/dendrite/internal/httputil"
roomserver "github.com/matrix-org/dendrite/roomserver/api"
@@ -40,38 +44,16 @@ const (
ConstSpaceParentEventType = "org.matrix.msc1772.space.parent"
)
-// SpacesRequest is the request body to POST /_matrix/client/r0/rooms/{roomID}/spaces
-type SpacesRequest struct {
- MaxRoomsPerSpace int `json:"max_rooms_per_space"`
- Limit int `json:"limit"`
- Batch string `json:"batch"`
-}
-
// Defaults sets the request defaults
-func (r *SpacesRequest) Defaults() {
+func Defaults(r *gomatrixserverlib.MSC2946SpacesRequest) {
r.Limit = 100
r.MaxRoomsPerSpace = -1
}
-// SpacesResponse is the response body to POST /_matrix/client/r0/rooms/{roomID}/spaces
-type SpacesResponse struct {
- NextBatch string `json:"next_batch"`
- // Rooms are nodes on the space graph.
- Rooms []Room `json:"rooms"`
- // Events are edges on the space graph, exclusively m.space.child or m.space.parent events
- Events []gomatrixserverlib.ClientEvent `json:"events"`
-}
-
-// Room is a node on the space graph
-type Room struct {
- gomatrixserverlib.PublicRoom
- NumRefs int `json:"num_refs"`
- RoomType string `json:"room_type"`
-}
-
// Enable this MSC
func Enable(
base *setup.BaseDendrite, rsAPI roomserver.RoomserverInternalAPI, userAPI userapi.UserInternalAPI,
+ fsAPI fs.FederationSenderInternalAPI, keyRing gomatrixserverlib.JSONVerifier,
) error {
db, err := NewDatabase(&base.Cfg.MSCs.Database)
if err != nil {
@@ -89,12 +71,69 @@ func Enable(
})
base.PublicClientAPIMux.Handle("/unstable/rooms/{roomID}/spaces",
- httputil.MakeAuthAPI("spaces", userAPI, spacesHandler(db, rsAPI)),
+ httputil.MakeAuthAPI("spaces", userAPI, spacesHandler(db, rsAPI, fsAPI, base.Cfg.Global.ServerName)),
).Methods(http.MethodPost, http.MethodOptions)
+
+ base.PublicFederationAPIMux.Handle("/unstable/spaces/{roomID}", httputil.MakeExternalAPI(
+ "msc2946_fed_spaces", func(req *http.Request) util.JSONResponse {
+ fedReq, errResp := gomatrixserverlib.VerifyHTTPRequest(
+ req, time.Now(), base.Cfg.Global.ServerName, keyRing,
+ )
+ if fedReq == nil {
+ return errResp
+ }
+ // Extract the room ID from the request. Sanity check request data.
+ params, err := httputil.URLDecodeMapValues(mux.Vars(req))
+ if err != nil {
+ return util.ErrorResponse(err)
+ }
+ roomID := params["roomID"]
+ return federatedSpacesHandler(req.Context(), fedReq, roomID, db, rsAPI, fsAPI, base.Cfg.Global.ServerName)
+ },
+ )).Methods(http.MethodPost, http.MethodOptions)
return nil
}
-func spacesHandler(db Database, rsAPI roomserver.RoomserverInternalAPI) func(*http.Request, *userapi.Device) util.JSONResponse {
+func federatedSpacesHandler(
+ ctx context.Context, fedReq *gomatrixserverlib.FederationRequest, roomID string, db Database,
+ rsAPI roomserver.RoomserverInternalAPI, fsAPI fs.FederationSenderInternalAPI,
+ thisServer gomatrixserverlib.ServerName,
+) util.JSONResponse {
+ inMemoryBatchCache := make(map[string]set)
+ var r gomatrixserverlib.MSC2946SpacesRequest
+ Defaults(&r)
+ if err := json.Unmarshal(fedReq.Content(), &r); err != nil {
+ return util.JSONResponse{
+ Code: http.StatusBadRequest,
+ JSON: jsonerror.BadJSON("The request body could not be decoded into valid JSON. " + err.Error()),
+ }
+ }
+ if r.Limit > 100 {
+ r.Limit = 100
+ }
+ w := walker{
+ req: &r,
+ rootRoomID: roomID,
+ serverName: fedReq.Origin(),
+ thisServer: thisServer,
+ ctx: ctx,
+
+ db: db,
+ rsAPI: rsAPI,
+ fsAPI: fsAPI,
+ inMemoryBatchCache: inMemoryBatchCache,
+ }
+ res := w.walk()
+ return util.JSONResponse{
+ Code: 200,
+ JSON: res,
+ }
+}
+
+func spacesHandler(
+ db Database, rsAPI roomserver.RoomserverInternalAPI, fsAPI fs.FederationSenderInternalAPI,
+ thisServer gomatrixserverlib.ServerName,
+) func(*http.Request, *userapi.Device) util.JSONResponse {
return func(req *http.Request, device *userapi.Device) util.JSONResponse {
inMemoryBatchCache := make(map[string]set)
// Extract the room ID from the request. Sanity check request data.
@@ -103,8 +142,8 @@ func spacesHandler(db Database, rsAPI roomserver.RoomserverInternalAPI) func(*ht
return util.ErrorResponse(err)
}
roomID := params["roomID"]
- var r SpacesRequest
- r.Defaults()
+ var r gomatrixserverlib.MSC2946SpacesRequest
+ Defaults(&r)
if resErr := chttputil.UnmarshalJSONRequest(req, &r); resErr != nil {
return *resErr
}
@@ -115,10 +154,12 @@ func spacesHandler(db Database, rsAPI roomserver.RoomserverInternalAPI) func(*ht
req: &r,
rootRoomID: roomID,
caller: device,
+ thisServer: thisServer,
ctx: req.Context(),
db: db,
rsAPI: rsAPI,
+ fsAPI: fsAPI,
inMemoryBatchCache: inMemoryBatchCache,
}
res := w.walk()
@@ -130,11 +171,14 @@ func spacesHandler(db Database, rsAPI roomserver.RoomserverInternalAPI) func(*ht
}
type walker struct {
- req *SpacesRequest
+ req *gomatrixserverlib.MSC2946SpacesRequest
rootRoomID string
caller *userapi.Device
+ serverName gomatrixserverlib.ServerName
+ thisServer gomatrixserverlib.ServerName
db Database
rsAPI roomserver.RoomserverInternalAPI
+ fsAPI fs.FederationSenderInternalAPI
ctx context.Context
// user ID|device ID|batch_num => event/room IDs sent to client
@@ -142,10 +186,26 @@ type walker struct {
mu sync.Mutex
}
+func (w *walker) roomIsExcluded(roomID string) bool {
+ for _, exclRoom := range w.req.ExcludeRooms {
+ if exclRoom == roomID {
+ return true
+ }
+ }
+ return false
+}
+
+func (w *walker) callerID() string {
+ if w.caller != nil {
+ return w.caller.UserID + "|" + w.caller.ID
+ }
+ return string(w.serverName)
+}
+
func (w *walker) alreadySent(id string) bool {
w.mu.Lock()
defer w.mu.Unlock()
- m, ok := w.inMemoryBatchCache[w.caller.UserID+"|"+w.caller.ID]
+ m, ok := w.inMemoryBatchCache[w.callerID()]
if !ok {
return false
}
@@ -155,17 +215,17 @@ func (w *walker) alreadySent(id string) bool {
func (w *walker) markSent(id string) {
w.mu.Lock()
defer w.mu.Unlock()
- m := w.inMemoryBatchCache[w.caller.UserID+"|"+w.caller.ID]
+ m := w.inMemoryBatchCache[w.callerID()]
if m == nil {
m = make(set)
}
m[id] = true
- w.inMemoryBatchCache[w.caller.UserID+"|"+w.caller.ID] = m
+ w.inMemoryBatchCache[w.callerID()] = m
}
// nolint:gocyclo
-func (w *walker) walk() *SpacesResponse {
- var res SpacesResponse
+func (w *walker) walk() *gomatrixserverlib.MSC2946SpacesResponse {
+ var res gomatrixserverlib.MSC2946SpacesResponse
// Begin walking the graph starting with the room ID in the request in a queue of unvisited rooms
unvisited := []string{w.rootRoomID}
processed := make(set)
@@ -178,9 +238,20 @@ func (w *walker) walk() *SpacesResponse {
}
// Mark this room as processed.
processed[roomID] = true
+
// Is the caller currently joined to the room or is the room `world_readable`
// If no, skip this room. If yes, continue.
- if !w.authorised(roomID) {
+ if !w.roomExists(roomID) || !w.authorised(roomID) {
+ // attempt to query this room over federation, as either we've never heard of it before
+ // or we've left it and hence are not authorised (but info may be exposed regardless)
+ fedRes, err := w.federatedRoomInfo(roomID)
+ if err != nil {
+ util.GetLogger(w.ctx).WithError(err).WithField("room_id", roomID).Errorf("failed to query federated spaces")
+ continue
+ }
+ if fedRes != nil {
+ res = combineResponses(res, *fedRes)
+ }
continue
}
// Get all `m.space.child` and `m.space.parent` state events for the room. *In addition*, get
@@ -194,7 +265,7 @@ func (w *walker) walk() *SpacesResponse {
// If this room has not ever been in `rooms` (across multiple requests), extract the
// `PublicRoomsChunk` for this room.
- if !w.alreadySent(roomID) {
+ if !w.alreadySent(roomID) && !w.roomIsExcluded(roomID) {
pubRoom := w.publicRoomsChunk(roomID)
roomType := ""
create := w.stateEvent(roomID, gomatrixserverlib.MRoomCreate, "")
@@ -204,11 +275,12 @@ func (w *walker) walk() *SpacesResponse {
}
// Add the total number of events to `PublicRoomsChunk` under `num_refs`. Add `PublicRoomsChunk` to `rooms`.
- res.Rooms = append(res.Rooms, Room{
+ res.Rooms = append(res.Rooms, gomatrixserverlib.MSC2946Room{
PublicRoom: *pubRoom,
NumRefs: refs.len(),
RoomType: roomType,
})
+ w.markSent(roomID)
}
uniqueRooms := make(set)
@@ -218,9 +290,11 @@ func (w *walker) walk() *SpacesResponse {
if w.rootRoomID == roomID {
for _, ev := range refs.events() {
if !w.alreadySent(ev.EventID()) {
- res.Events = append(res.Events, gomatrixserverlib.HeaderedToClientEvent(
- ev, gomatrixserverlib.FormatAll,
- ))
+ strip := stripped(ev.Event)
+ if strip == nil {
+ continue
+ }
+ res.Events = append(res.Events, *strip)
uniqueRooms[ev.RoomID()] = true
uniqueRooms[SpaceTarget(ev)] = true
w.markSent(ev.EventID())
@@ -240,9 +314,16 @@ func (w *walker) walk() *SpacesResponse {
if w.alreadySent(ev.EventID()) {
continue
}
- res.Events = append(res.Events, gomatrixserverlib.HeaderedToClientEvent(
- ev, gomatrixserverlib.FormatAll,
- ))
+ // Skip the room if it's part of exclude_rooms but ONLY IF the source matches, as we still
+ // want to catch arrows which point to excluded rooms.
+ if w.roomIsExcluded(ev.RoomID()) {
+ continue
+ }
+ strip := stripped(ev.Event)
+ if strip == nil {
+ continue
+ }
+ res.Events = append(res.Events, *strip)
uniqueRooms[ev.RoomID()] = true
uniqueRooms[SpaceTarget(ev)] = true
w.markSent(ev.EventID())
@@ -289,8 +370,120 @@ func (w *walker) publicRoomsChunk(roomID string) *gomatrixserverlib.PublicRoom {
return &pubRooms[0]
}
+// federatedRoomInfo returns more of the spaces graph from another server. Returns nil if this was
+// unsuccessful.
+func (w *walker) federatedRoomInfo(roomID string) (*gomatrixserverlib.MSC2946SpacesResponse, error) {
+ // only do federated requests for client requests
+ if w.caller == nil {
+ return nil, nil
+ }
+ // extract events which point to this room ID and extract their vias
+ events, err := w.db.References(w.ctx, roomID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get References events: %w", err)
+ }
+ vias := make(set)
+ for _, ev := range events {
+ if ev.StateKeyEquals(roomID) {
+ // event points at this room, extract vias
+ content := struct {
+ Vias []string `json:"via"`
+ }{}
+ if err = json.Unmarshal(ev.Content(), &content); err != nil {
+ continue // silently ignore corrupted state events
+ }
+ for _, v := range content.Vias {
+ vias[v] = true
+ }
+ }
+ }
+ util.GetLogger(w.ctx).Infof("Querying federatedRoomInfo via %+v", vias)
+ ctx := context.Background()
+ // query more of the spaces graph using these servers
+ for serverName := range vias {
+ if serverName == string(w.thisServer) {
+ continue
+ }
+ res, err := w.fsAPI.MSC2946Spaces(ctx, gomatrixserverlib.ServerName(serverName), roomID, gomatrixserverlib.MSC2946SpacesRequest{
+ Limit: w.req.Limit,
+ MaxRoomsPerSpace: w.req.MaxRoomsPerSpace,
+ })
+ if err != nil {
+ util.GetLogger(w.ctx).WithError(err).Warnf("failed to call MSC2946Spaces on server %s", serverName)
+ continue
+ }
+ return &res, nil
+ }
+ return nil, nil
+}
+
+func (w *walker) roomExists(roomID string) bool {
+ var queryRes roomserver.QueryServerJoinedToRoomResponse
+ err := w.rsAPI.QueryServerJoinedToRoom(w.ctx, &roomserver.QueryServerJoinedToRoomRequest{
+ RoomID: roomID,
+ ServerName: w.thisServer,
+ }, &queryRes)
+ if err != nil {
+ util.GetLogger(w.ctx).WithError(err).Error("failed to QueryServerJoinedToRoom")
+ return false
+ }
+ // if the room exists but we aren't in the room then we might have stale data so we want to fetch
+ // it fresh via federation
+ return queryRes.RoomExists && queryRes.IsInRoom
+}
+
// authorised returns true iff the user is joined this room or the room is world_readable
func (w *walker) authorised(roomID string) bool {
+ if w.caller != nil {
+ return w.authorisedUser(roomID)
+ }
+ return w.authorisedServer(roomID)
+}
+
+// authorisedServer returns true iff the server is joined this room or the room is world_readable
+func (w *walker) authorisedServer(roomID string) bool {
+ // Check history visibility first
+ hisVisTuple := gomatrixserverlib.StateKeyTuple{
+ EventType: gomatrixserverlib.MRoomHistoryVisibility,
+ StateKey: "",
+ }
+ var queryRoomRes roomserver.QueryCurrentStateResponse
+ err := w.rsAPI.QueryCurrentState(w.ctx, &roomserver.QueryCurrentStateRequest{
+ RoomID: roomID,
+ StateTuples: []gomatrixserverlib.StateKeyTuple{
+ hisVisTuple,
+ },
+ }, &queryRoomRes)
+ if err != nil {
+ util.GetLogger(w.ctx).WithError(err).Error("failed to QueryCurrentState")
+ return false
+ }
+ hisVisEv := queryRoomRes.StateEvents[hisVisTuple]
+ if hisVisEv != nil {
+ hisVis, _ := hisVisEv.HistoryVisibility()
+ if hisVis == "world_readable" {
+ return true
+ }
+ }
+ // check if server is joined to the room
+ var queryRes fs.QueryJoinedHostServerNamesInRoomResponse
+ err = w.fsAPI.QueryJoinedHostServerNamesInRoom(w.ctx, &fs.QueryJoinedHostServerNamesInRoomRequest{
+ RoomID: roomID,
+ }, &queryRes)
+ if err != nil {
+ util.GetLogger(w.ctx).WithError(err).Error("failed to QueryJoinedHostServerNamesInRoom")
+ return false
+ }
+ for _, srv := range queryRes.ServerNames {
+ if srv == w.serverName {
+ return true
+ }
+ }
+ return false
+}
+
+// authorisedUser returns true iff the user is joined this room or the room is world_readable
+func (w *walker) authorisedUser(roomID string) bool {
hisVisTuple := gomatrixserverlib.StateKeyTuple{
EventType: gomatrixserverlib.MRoomHistoryVisibility,
StateKey: "",
@@ -374,3 +567,41 @@ func (el eventLookup) events() (events []*gomatrixserverlib.HeaderedEvent) {
}
type set map[string]bool
+
+func stripped(ev *gomatrixserverlib.Event) *gomatrixserverlib.MSC2946StrippedEvent {
+ if ev.StateKey() == nil {
+ return nil
+ }
+ return &gomatrixserverlib.MSC2946StrippedEvent{
+ Type: ev.Type(),
+ StateKey: *ev.StateKey(),
+ Content: ev.Content(),
+ Sender: ev.Sender(),
+ RoomID: ev.RoomID(),
+ }
+}
+
+func combineResponses(local, remote gomatrixserverlib.MSC2946SpacesResponse) gomatrixserverlib.MSC2946SpacesResponse {
+ knownRooms := make(set)
+ for _, room := range local.Rooms {
+ knownRooms[room.RoomID] = true
+ }
+ knownEvents := make(set)
+ for _, event := range local.Events {
+ knownEvents[event.RoomID+event.Type+event.StateKey] = true
+ }
+ // mux in remote entries if and only if they aren't present already
+ for _, room := range remote.Rooms {
+ if knownRooms[room.RoomID] {
+ continue
+ }
+ local.Rooms = append(local.Rooms, room)
+ }
+ for _, event := range remote.Events {
+ if knownEvents[event.RoomID+event.Type+event.StateKey] {
+ continue
+ }
+ local.Events = append(local.Events, event)
+ }
+ return local
+}
diff --git a/setup/mscs/msc2946/msc2946_test.go b/setup/mscs/msc2946/msc2946_test.go
index d2d935e8..4f180a98 100644
--- a/setup/mscs/msc2946/msc2946_test.go
+++ b/setup/mscs/msc2946/msc2946_test.go
@@ -41,6 +41,7 @@ var (
client = &http.Client{
Timeout: 10 * time.Second,
}
+ roomVer = gomatrixserverlib.RoomVersionV6
)
// Basic sanity check of MSC2946 logic. Tests a single room with a few state events
@@ -269,13 +270,13 @@ func TestMSC2946(t *testing.T) {
})
}
-func newReq(t *testing.T, jsonBody map[string]interface{}) *msc2946.SpacesRequest {
+func newReq(t *testing.T, jsonBody map[string]interface{}) *gomatrixserverlib.MSC2946SpacesRequest {
t.Helper()
b, err := json.Marshal(jsonBody)
if err != nil {
t.Fatalf("Failed to marshal request: %s", err)
}
- var r msc2946.SpacesRequest
+ var r gomatrixserverlib.MSC2946SpacesRequest
if err := json.Unmarshal(b, &r); err != nil {
t.Fatalf("Failed to unmarshal request: %s", err)
}
@@ -299,10 +300,10 @@ func runServer(t *testing.T, router *mux.Router) func() {
}
}
-func postSpaces(t *testing.T, expectCode int, accessToken, roomID string, req *msc2946.SpacesRequest) *msc2946.SpacesResponse {
+func postSpaces(t *testing.T, expectCode int, accessToken, roomID string, req *gomatrixserverlib.MSC2946SpacesRequest) *gomatrixserverlib.MSC2946SpacesResponse {
t.Helper()
- var r msc2946.SpacesRequest
- r.Defaults()
+ var r gomatrixserverlib.MSC2946SpacesRequest
+ msc2946.Defaults(&r)
data, err := json.Marshal(req)
if err != nil {
t.Fatalf("failed to marshal request: %s", err)
@@ -324,7 +325,7 @@ func postSpaces(t *testing.T, expectCode int, accessToken, roomID string, req *m
t.Fatalf("wrong response code, got %d want %d - body: %s", res.StatusCode, expectCode, string(body))
}
if res.StatusCode == 200 {
- var result msc2946.SpacesResponse
+ var result gomatrixserverlib.MSC2946SpacesResponse
body, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatalf("response 200 OK but failed to read response body: %s", err)
@@ -400,6 +401,12 @@ type testRoomserverAPI struct {
pubRoomState map[string]map[gomatrixserverlib.StateKeyTuple]string
}
+func (r *testRoomserverAPI) QueryServerJoinedToRoom(ctx context.Context, req *roomserver.QueryServerJoinedToRoomRequest, res *roomserver.QueryServerJoinedToRoomResponse) error {
+ res.IsInRoom = true
+ res.RoomExists = true
+ return nil
+}
+
func (r *testRoomserverAPI) QueryBulkStateContent(ctx context.Context, req *roomserver.QueryBulkStateContentRequest, res *roomserver.QueryBulkStateContentResponse) error {
res.Rooms = make(map[string]map[gomatrixserverlib.StateKeyTuple]string)
for _, roomID := range req.RoomIDs {
@@ -452,7 +459,7 @@ func injectEvents(t *testing.T, userAPI userapi.UserInternalAPI, rsAPI roomserve
PublicFederationAPIMux: mux.NewRouter().PathPrefix(httputil.PublicFederationPathPrefix).Subrouter(),
}
- err := msc2946.Enable(base, rsAPI, userAPI)
+ err := msc2946.Enable(base, rsAPI, userAPI, nil, nil)
if err != nil {
t.Fatalf("failed to enable MSC2946: %s", err)
}
@@ -472,7 +479,6 @@ type fledglingEvent struct {
func mustCreateEvent(t *testing.T, ev fledglingEvent) (result *gomatrixserverlib.HeaderedEvent) {
t.Helper()
- roomVer := gomatrixserverlib.RoomVersionV6
seed := make([]byte, ed25519.SeedSize) // zero seed
key := ed25519.NewKeyFromSeed(seed)
eb := gomatrixserverlib.EventBuilder{
diff --git a/setup/mscs/mscs.go b/setup/mscs/mscs.go
index bf210362..027885c8 100644
--- a/setup/mscs/mscs.go
+++ b/setup/mscs/mscs.go
@@ -41,7 +41,7 @@ func EnableMSC(base *setup.BaseDendrite, monolith *setup.Monolith, msc string) e
case "msc2836":
return msc2836.Enable(base, monolith.RoomserverAPI, monolith.FederationSenderAPI, monolith.UserAPI, monolith.KeyRing)
case "msc2946":
- return msc2946.Enable(base, monolith.RoomserverAPI, monolith.UserAPI)
+ return msc2946.Enable(base, monolith.RoomserverAPI, monolith.UserAPI, monolith.FederationSenderAPI, monolith.KeyRing)
default:
return fmt.Errorf("EnableMSC: unknown msc '%s'", msc)
}