diff --git a/.github/workflows/dendrite.yml b/.github/workflows/dendrite.yml index 2101c7a50..17f02b09e 100644 --- a/.github/workflows/dendrite.yml +++ b/.github/workflows/dendrite.yml @@ -393,7 +393,7 @@ jobs: # See https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md specifically GOROOT_1_17_X64 run: | sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev - go get -v github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest + go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest - name: Run actions/checkout@v3 for dendrite uses: actions/checkout@v3 with: diff --git a/.github/workflows/helm.yml b/.github/workflows/helm.yml index 7cdc369ba..a9c1718a0 100644 --- a/.github/workflows/helm.yml +++ b/.github/workflows/helm.yml @@ -6,6 +6,7 @@ on: - main paths: - 'helm/**' # only execute if we have helm chart changes + workflow_dispatch: jobs: release: diff --git a/.gitignore b/.gitignore index fe5e82797..dcfbf8007 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,4 @@ complement/ docs/_site media_store/ +build \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index a327370e1..bb8d38a8b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -179,7 +179,6 @@ linters-settings: linters: enable: - - deadcode - errcheck - goconst - gocyclo @@ -191,10 +190,8 @@ linters: - misspell # Check code comments, whereas misspell in CI checks *.md files - nakedret - staticcheck - - structcheck - unparam - unused - - varcheck enable-all: false disable: - bodyclose diff --git a/appservice/api/query.go b/appservice/api/query.go index eb567b2ee..472266d9e 100644 --- a/appservice/api/query.go +++ b/appservice/api/query.go @@ -22,8 +22,6 @@ import ( "encoding/json" "errors" - "github.com/matrix-org/gomatrixserverlib" - "github.com/matrix-org/dendrite/clientapi/auth/authtypes" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -150,6 +148,10 @@ type ASLocationResponse struct { Fields json.RawMessage `json:"fields"` } +// ErrProfileNotExists is returned when trying to lookup a user's profile that +// doesn't exist locally. +var ErrProfileNotExists = errors.New("no known profile for given user ID") + // RetrieveUserProfile is a wrapper that queries both the local database and // application services for a given user's profile // TODO: Remove this, it's called from federationapi and clientapi but is a pure function @@ -157,25 +159,11 @@ func RetrieveUserProfile( ctx context.Context, userID string, asAPI AppServiceInternalAPI, - profileAPI userapi.ClientUserAPI, + profileAPI userapi.ProfileAPI, ) (*authtypes.Profile, error) { - localpart, _, err := gomatrixserverlib.SplitID('@', userID) - if err != nil { - return nil, err - } - // Try to query the user from the local database - res := &userapi.QueryProfileResponse{} - err = profileAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: userID}, res) - if err != nil { - return nil, err - } - profile := &authtypes.Profile{ - Localpart: localpart, - DisplayName: res.DisplayName, - AvatarURL: res.AvatarURL, - } - if res.UserExists { + profile, err := profileAPI.QueryProfile(ctx, userID) + if err == nil { return profile, nil } @@ -188,19 +176,15 @@ func RetrieveUserProfile( // If no user exists, return if !userResp.UserIDExists { - return nil, errors.New("no known profile for given user ID") + return nil, ErrProfileNotExists } // Try to query the user from the local database again - err = profileAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: userID}, res) + profile, err = profileAPI.QueryProfile(ctx, userID) if err != nil { return nil, err } // profile should not be nil at this point - return &authtypes.Profile{ - Localpart: localpart, - DisplayName: res.DisplayName, - AvatarURL: res.AvatarURL, - }, nil + return profile, nil } diff --git a/appservice/appservice.go b/appservice/appservice.go index d13d9eb10..1f6037ee2 100644 --- a/appservice/appservice.go +++ b/appservice/appservice.go @@ -16,10 +16,7 @@ package appservice import ( "context" - "crypto/tls" - "net/http" "sync" - "time" "github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/process" @@ -44,20 +41,10 @@ func NewInternalAPI( userAPI userapi.AppserviceUserAPI, rsAPI roomserverAPI.RoomserverInternalAPI, ) appserviceAPI.AppServiceInternalAPI { - client := &http.Client{ - Timeout: time.Second * 30, - Transport: &http.Transport{ - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: cfg.AppServiceAPI.DisableTLSValidation, - }, - Proxy: http.ProxyFromEnvironment, - }, - } + // Create appserivce query API with an HTTP client that will be used for all // outbound and inbound requests (inbound only for the internal API) appserviceQueryAPI := &query.AppServiceQueryAPI{ - HTTPClient: client, Cfg: &cfg.AppServiceAPI, ProtocolCache: map[string]appserviceAPI.ASProtocolResponse{}, CacheMu: sync.Mutex{}, @@ -84,7 +71,7 @@ func NewInternalAPI( js, _ := natsInstance.Prepare(processContext, &cfg.Global.JetStream) consumer := consumers.NewOutputRoomEventConsumer( processContext, &cfg.AppServiceAPI, - client, js, rsAPI, + js, rsAPI, ) if err := consumer.Start(); err != nil { logrus.WithError(err).Panicf("failed to start appservice roomserver consumer") diff --git a/appservice/appservice_test.go b/appservice/appservice_test.go index 752901a9c..282c63128 100644 --- a/appservice/appservice_test.go +++ b/appservice/appservice_test.go @@ -3,16 +3,22 @@ package appservice_test import ( "context" "encoding/json" + "fmt" + "net" "net/http" "net/http/httptest" + "path" "reflect" "regexp" "strings" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/matrix-org/dendrite/appservice" "github.com/matrix-org/dendrite/appservice/api" + "github.com/matrix-org/dendrite/appservice/consumers" "github.com/matrix-org/dendrite/internal/caching" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/roomserver" @@ -114,20 +120,20 @@ func TestAppserviceInternalAPI(t *testing.T) { defer close() // Create a dummy application service - cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{ - { - ID: "someID", - URL: srv.URL, - ASToken: "", - HSToken: "", - SenderLocalpart: "senderLocalPart", - NamespaceMap: map[string][]config.ApplicationServiceNamespace{ - "users": {{RegexpObject: regexp.MustCompile("as-.*")}}, - "aliases": {{RegexpObject: regexp.MustCompile("asroom-.*")}}, - }, - Protocols: []string{existingProtocol}, + as := &config.ApplicationService{ + ID: "someID", + URL: srv.URL, + ASToken: "", + HSToken: "", + SenderLocalpart: "senderLocalPart", + NamespaceMap: map[string][]config.ApplicationServiceNamespace{ + "users": {{RegexpObject: regexp.MustCompile("as-.*")}}, + "aliases": {{RegexpObject: regexp.MustCompile("asroom-.*")}}, }, + Protocols: []string{existingProtocol}, } + as.CreateHTTPClient(cfg.AppServiceAPI.DisableTLSValidation) + cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as} t.Cleanup(func() { ctx.ShutdownDendrite() @@ -145,6 +151,103 @@ func TestAppserviceInternalAPI(t *testing.T) { }) } +func TestAppserviceInternalAPI_UnixSocket_Simple(t *testing.T) { + + // Set expected results + existingProtocol := "irc" + wantLocationResponse := []api.ASLocationResponse{{Protocol: existingProtocol, Fields: []byte("{}")}} + wantUserResponse := []api.ASUserResponse{{Protocol: existingProtocol, Fields: []byte("{}")}} + wantProtocolResponse := api.ASProtocolResponse{Instances: []api.ProtocolInstance{{Fields: []byte("{}")}}} + + // create a dummy AS url, handling some cases + srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case strings.Contains(r.URL.Path, "location"): + // Check if we've got an existing protocol, if so, return a proper response. + if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol { + if err := json.NewEncoder(w).Encode(wantLocationResponse); err != nil { + t.Fatalf("failed to encode response: %s", err) + } + return + } + if err := json.NewEncoder(w).Encode([]api.ASLocationResponse{}); err != nil { + t.Fatalf("failed to encode response: %s", err) + } + return + case strings.Contains(r.URL.Path, "user"): + if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol { + if err := json.NewEncoder(w).Encode(wantUserResponse); err != nil { + t.Fatalf("failed to encode response: %s", err) + } + return + } + if err := json.NewEncoder(w).Encode([]api.UserResponse{}); err != nil { + t.Fatalf("failed to encode response: %s", err) + } + return + case strings.Contains(r.URL.Path, "protocol"): + if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol { + if err := json.NewEncoder(w).Encode(wantProtocolResponse); err != nil { + t.Fatalf("failed to encode response: %s", err) + } + return + } + if err := json.NewEncoder(w).Encode(nil); err != nil { + t.Fatalf("failed to encode response: %s", err) + } + return + default: + t.Logf("hit location: %s", r.URL.Path) + } + })) + + tmpDir := t.TempDir() + socket := path.Join(tmpDir, "socket") + l, err := net.Listen("unix", socket) + assert.NoError(t, err) + _ = srv.Listener.Close() + srv.Listener = l + srv.Start() + defer srv.Close() + + cfg, ctx, tearDown := testrig.CreateConfig(t, test.DBTypeSQLite) + defer tearDown() + + // Create a dummy application service + as := &config.ApplicationService{ + ID: "someID", + URL: fmt.Sprintf("unix://%s", socket), + ASToken: "", + HSToken: "", + SenderLocalpart: "senderLocalPart", + NamespaceMap: map[string][]config.ApplicationServiceNamespace{ + "users": {{RegexpObject: regexp.MustCompile("as-.*")}}, + "aliases": {{RegexpObject: regexp.MustCompile("asroom-.*")}}, + }, + Protocols: []string{existingProtocol}, + } + as.CreateHTTPClient(cfg.AppServiceAPI.DisableTLSValidation) + cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as} + + t.Cleanup(func() { + ctx.ShutdownDendrite() + ctx.WaitForShutdown() + }) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + // Create required internal APIs + natsInstance := jetstream.NATSInstance{} + cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions) + rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil) + asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI) + + t.Run("UserIDExists", func(t *testing.T) { + testUserIDExists(t, asAPI, "@as-testing:test", true) + testUserIDExists(t, asAPI, "@as1-testing:test", false) + }) + +} + func testUserIDExists(t *testing.T, asAPI api.AppServiceInternalAPI, userID string, wantExists bool) { ctx := context.Background() userResp := &api.UserIDExistsResponse{} @@ -236,7 +339,7 @@ func TestRoomserverConsumerOneInvite(t *testing.T) { evChan := make(chan struct{}) // create a dummy AS url, handling the events srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var txn gomatrixserverlib.ApplicationServiceTransaction + var txn consumers.ApplicationServiceTransaction err := json.NewDecoder(r.Body).Decode(&txn) if err != nil { t.Fatal(err) @@ -254,20 +357,21 @@ func TestRoomserverConsumerOneInvite(t *testing.T) { })) defer srv.Close() - // Create a dummy application service - cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{ - { - ID: "someID", - URL: srv.URL, - ASToken: "", - HSToken: "", - SenderLocalpart: "senderLocalPart", - NamespaceMap: map[string][]config.ApplicationServiceNamespace{ - "users": {{RegexpObject: regexp.MustCompile(bob.ID)}}, - "aliases": {{RegexpObject: regexp.MustCompile(room.ID)}}, - }, + as := &config.ApplicationService{ + ID: "someID", + URL: srv.URL, + ASToken: "", + HSToken: "", + SenderLocalpart: "senderLocalPart", + NamespaceMap: map[string][]config.ApplicationServiceNamespace{ + "users": {{RegexpObject: regexp.MustCompile(bob.ID)}}, + "aliases": {{RegexpObject: regexp.MustCompile(room.ID)}}, }, } + as.CreateHTTPClient(cfg.AppServiceAPI.DisableTLSValidation) + + // Create a dummy application service + cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as} caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) // Create required internal APIs diff --git a/appservice/consumers/roomserver.go b/appservice/consumers/roomserver.go index 16b3b8231..586ca33a8 100644 --- a/appservice/consumers/roomserver.go +++ b/appservice/consumers/roomserver.go @@ -32,15 +32,21 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/process" + "github.com/matrix-org/dendrite/syncapi/synctypes" log "github.com/sirupsen/logrus" ) +// ApplicationServiceTransaction is the transaction that is sent off to an +// application service. +type ApplicationServiceTransaction struct { + Events []synctypes.ClientEvent `json:"events"` +} + // OutputRoomEventConsumer consumes events that originated in the room server. type OutputRoomEventConsumer struct { ctx context.Context cfg *config.AppServiceAPI - client *http.Client jetstream nats.JetStreamContext topic string rsAPI api.AppserviceRoomserverAPI @@ -56,14 +62,12 @@ type appserviceState struct { func NewOutputRoomEventConsumer( process *process.ProcessContext, cfg *config.AppServiceAPI, - client *http.Client, js nats.JetStreamContext, rsAPI api.AppserviceRoomserverAPI, ) *OutputRoomEventConsumer { return &OutputRoomEventConsumer{ ctx: process.Context(), cfg: cfg, - client: client, jetstream: js, topic: cfg.Matrix.JetStream.Prefixed(jetstream.OutputRoomEvent), rsAPI: rsAPI, @@ -174,8 +178,8 @@ func (s *OutputRoomEventConsumer) sendEvents( ) error { // Create the transaction body. transaction, err := json.Marshal( - gomatrixserverlib.ApplicationServiceTransaction{ - Events: gomatrixserverlib.HeaderedToClientEvents(events, gomatrixserverlib.FormatAll), + ApplicationServiceTransaction{ + Events: synctypes.HeaderedToClientEvents(events, synctypes.FormatAll), }, ) if err != nil { @@ -189,13 +193,13 @@ func (s *OutputRoomEventConsumer) sendEvents( // Send the transaction to the appservice. // https://matrix.org/docs/spec/application_service/r0.1.2#put-matrix-app-v1-transactions-txnid - address := fmt.Sprintf("%s/transactions/%s?access_token=%s", state.URL, txnID, url.QueryEscape(state.HSToken)) + address := fmt.Sprintf("%s/transactions/%s?access_token=%s", state.RequestUrl(), txnID, url.QueryEscape(state.HSToken)) req, err := http.NewRequestWithContext(ctx, "PUT", address, bytes.NewBuffer(transaction)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") - resp, err := s.client.Do(req) + resp, err := state.HTTPClient.Do(req) if err != nil { return state.backoffAndPause(err) } @@ -206,7 +210,7 @@ func (s *OutputRoomEventConsumer) sendEvents( case http.StatusOK: state.backoff = 0 default: - return state.backoffAndPause(fmt.Errorf("received HTTP status code %d from appservice", resp.StatusCode)) + return state.backoffAndPause(fmt.Errorf("received HTTP status code %d from appservice url %s", resp.StatusCode, address)) } return nil } diff --git a/appservice/query/query.go b/appservice/query/query.go index 0466f81d0..ca8d7b3a3 100644 --- a/appservice/query/query.go +++ b/appservice/query/query.go @@ -37,7 +37,6 @@ const userIDExistsPath = "/users/" // AppServiceQueryAPI is an implementation of api.AppServiceQueryAPI type AppServiceQueryAPI struct { - HTTPClient *http.Client Cfg *config.AppServiceAPI ProtocolCache map[string]api.ASProtocolResponse CacheMu sync.Mutex @@ -57,7 +56,7 @@ func (a *AppServiceQueryAPI) RoomAliasExists( for _, appservice := range a.Cfg.Derived.ApplicationServices { if appservice.URL != "" && appservice.IsInterestedInRoomAlias(request.Alias) { // The full path to the rooms API, includes hs token - URL, err := url.Parse(appservice.URL + roomAliasExistsPath) + URL, err := url.Parse(appservice.RequestUrl() + roomAliasExistsPath) if err != nil { return err } @@ -73,7 +72,7 @@ func (a *AppServiceQueryAPI) RoomAliasExists( } req = req.WithContext(ctx) - resp, err := a.HTTPClient.Do(req) + resp, err := appservice.HTTPClient.Do(req) if resp != nil { defer func() { err = resp.Body.Close() @@ -124,7 +123,7 @@ func (a *AppServiceQueryAPI) UserIDExists( for _, appservice := range a.Cfg.Derived.ApplicationServices { if appservice.URL != "" && appservice.IsInterestedInUserID(request.UserID) { // The full path to the rooms API, includes hs token - URL, err := url.Parse(appservice.URL + userIDExistsPath) + URL, err := url.Parse(appservice.RequestUrl() + userIDExistsPath) if err != nil { return err } @@ -137,7 +136,7 @@ func (a *AppServiceQueryAPI) UserIDExists( if err != nil { return err } - resp, err := a.HTTPClient.Do(req.WithContext(ctx)) + resp, err := appservice.HTTPClient.Do(req.WithContext(ctx)) if resp != nil { defer func() { err = resp.Body.Close() @@ -212,12 +211,12 @@ func (a *AppServiceQueryAPI) Locations( var asLocations []api.ASLocationResponse params.Set("access_token", as.HSToken) - url := as.URL + api.ASLocationPath + url := as.RequestUrl() + api.ASLocationPath if req.Protocol != "" { url += "/" + req.Protocol } - if err := requestDo[[]api.ASLocationResponse](a.HTTPClient, url+"?"+params.Encode(), &asLocations); err != nil { + if err := requestDo[[]api.ASLocationResponse](as.HTTPClient, url+"?"+params.Encode(), &asLocations); err != nil { log.WithError(err).Error("unable to get 'locations' from application service") continue } @@ -247,12 +246,12 @@ func (a *AppServiceQueryAPI) User( var asUsers []api.ASUserResponse params.Set("access_token", as.HSToken) - url := as.URL + api.ASUserPath + url := as.RequestUrl() + api.ASUserPath if req.Protocol != "" { url += "/" + req.Protocol } - if err := requestDo[[]api.ASUserResponse](a.HTTPClient, url+"?"+params.Encode(), &asUsers); err != nil { + if err := requestDo[[]api.ASUserResponse](as.HTTPClient, url+"?"+params.Encode(), &asUsers); err != nil { log.WithError(err).Error("unable to get 'user' from application service") continue } @@ -290,7 +289,7 @@ func (a *AppServiceQueryAPI) Protocols( response := api.ASProtocolResponse{} for _, as := range a.Cfg.Derived.ApplicationServices { var proto api.ASProtocolResponse - if err := requestDo[api.ASProtocolResponse](a.HTTPClient, as.URL+api.ASProtocolPath+req.Protocol, &proto); err != nil { + if err := requestDo[api.ASProtocolResponse](as.HTTPClient, as.RequestUrl()+api.ASProtocolPath+req.Protocol, &proto); err != nil { log.WithError(err).Error("unable to get 'protocol' from application service") continue } @@ -320,7 +319,7 @@ func (a *AppServiceQueryAPI) Protocols( for _, as := range a.Cfg.Derived.ApplicationServices { for _, p := range as.Protocols { var proto api.ASProtocolResponse - if err := requestDo[api.ASProtocolResponse](a.HTTPClient, as.URL+api.ASProtocolPath+p, &proto); err != nil { + if err := requestDo[api.ASProtocolResponse](as.HTTPClient, as.RequestUrl()+api.ASProtocolPath+p, &proto); err != nil { log.WithError(err).Error("unable to get 'protocol' from application service") continue } diff --git a/clientapi/admin_test.go b/clientapi/admin_test.go index 3e7cb875c..da1ac70b9 100644 --- a/clientapi/admin_test.go +++ b/clientapi/admin_test.go @@ -8,7 +8,6 @@ import ( "testing" "time" - "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/federationapi" "github.com/matrix-org/dendrite/internal/caching" "github.com/matrix-org/dendrite/internal/httputil" @@ -20,6 +19,7 @@ import ( "github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/syncapi" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/tidwall/gjson" @@ -41,7 +41,7 @@ func TestAdminResetPassword(t *testing.T) { natsInstance := jetstream.NATSInstance{} // add a vhost cfg.Global.VirtualHosts = append(cfg.Global.VirtualHosts, &config.VirtualHost{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ServerName: "vh1"}, + SigningIdentity: fclient.SigningIdentity{ServerName: "vh1"}, }) routers := httputil.NewRouters() @@ -54,10 +54,10 @@ func TestAdminResetPassword(t *testing.T) { AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) // Create the users in the userapi and login - accessTokens := map[*test.User]string{ - aliceAdmin: "", - bob: "", - vhUser: "", + accessTokens := map[*test.User]userDevice{ + aliceAdmin: {}, + bob: {}, + vhUser: {}, } createAccessTokens(t, accessTokens, userAPI, ctx, routers) @@ -103,7 +103,7 @@ func TestAdminResetPassword(t *testing.T) { } if tc.withHeader { - req.Header.Set("Authorization", "Bearer "+accessTokens[tc.requestingUser]) + req.Header.Set("Authorization", "Bearer "+accessTokens[tc.requestingUser].accessToken) } rec := httptest.NewRecorder() @@ -154,8 +154,8 @@ func TestPurgeRoom(t *testing.T) { AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) // Create the users in the userapi and login - accessTokens := map[*test.User]string{ - aliceAdmin: "", + accessTokens := map[*test.User]userDevice{ + aliceAdmin: {}, } createAccessTokens(t, accessTokens, userAPI, ctx, routers) @@ -174,7 +174,7 @@ func TestPurgeRoom(t *testing.T) { t.Run(tc.name, func(t *testing.T) { req := test.NewRequest(t, http.MethodPost, "/_dendrite/admin/purgeRoom/"+tc.roomID) - req.Header.Set("Authorization", "Bearer "+accessTokens[aliceAdmin]) + req.Header.Set("Authorization", "Bearer "+accessTokens[aliceAdmin].accessToken) rec := httptest.NewRecorder() routers.DendriteAdmin.ServeHTTP(rec, req) @@ -224,8 +224,8 @@ func TestAdminEvacuateRoom(t *testing.T) { AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) // Create the users in the userapi and login - accessTokens := map[*test.User]string{ - aliceAdmin: "", + accessTokens := map[*test.User]userDevice{ + aliceAdmin: {}, } createAccessTokens(t, accessTokens, userAPI, ctx, routers) @@ -243,7 +243,7 @@ func TestAdminEvacuateRoom(t *testing.T) { t.Run(tc.name, func(t *testing.T) { req := test.NewRequest(t, http.MethodPost, "/_dendrite/admin/evacuateRoom/"+tc.roomID) - req.Header.Set("Authorization", "Bearer "+accessTokens[aliceAdmin]) + req.Header.Set("Authorization", "Bearer "+accessTokens[aliceAdmin].accessToken) rec := httptest.NewRecorder() routers.DendriteAdmin.ServeHTTP(rec, req) @@ -262,6 +262,25 @@ func TestAdminEvacuateRoom(t *testing.T) { } }) } + + // Wait for the FS API to have consumed every message + js, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream) + timeout := time.After(time.Second) + for { + select { + case <-timeout: + t.Fatalf("FS API didn't process all events in time") + default: + } + info, err := js.ConsumerInfo(cfg.Global.JetStream.Prefixed(jetstream.OutputRoomEvent), cfg.Global.JetStream.Durable("FederationAPIRoomServerConsumer")+"Pull") + if err != nil { + time.Sleep(time.Millisecond * 10) + continue + } + if info.NumPending == 0 && info.NumAckPending == 0 { + break + } + } }) } @@ -308,8 +327,8 @@ func TestAdminEvacuateUser(t *testing.T) { AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) // Create the users in the userapi and login - accessTokens := map[*test.User]string{ - aliceAdmin: "", + accessTokens := map[*test.User]userDevice{ + aliceAdmin: {}, } createAccessTokens(t, accessTokens, userAPI, ctx, routers) @@ -329,7 +348,7 @@ func TestAdminEvacuateUser(t *testing.T) { t.Run(tc.name, func(t *testing.T) { req := test.NewRequest(t, http.MethodPost, "/_dendrite/admin/evacuateUser/"+tc.userID) - req.Header.Set("Authorization", "Bearer "+accessTokens[aliceAdmin]) + req.Header.Set("Authorization", "Bearer "+accessTokens[aliceAdmin].accessToken) rec := httptest.NewRecorder() routers.DendriteAdmin.ServeHTTP(rec, req) @@ -390,8 +409,8 @@ func TestAdminMarkAsStale(t *testing.T) { AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) // Create the users in the userapi and login - accessTokens := map[*test.User]string{ - aliceAdmin: "", + accessTokens := map[*test.User]userDevice{ + aliceAdmin: {}, } createAccessTokens(t, accessTokens, userAPI, ctx, routers) @@ -409,7 +428,7 @@ func TestAdminMarkAsStale(t *testing.T) { t.Run(tc.name, func(t *testing.T) { req := test.NewRequest(t, http.MethodPost, "/_dendrite/admin/refreshDevices/"+tc.userID) - req.Header.Set("Authorization", "Bearer "+accessTokens[aliceAdmin]) + req.Header.Set("Authorization", "Bearer "+accessTokens[aliceAdmin].accessToken) rec := httptest.NewRecorder() routers.DendriteAdmin.ServeHTTP(rec, req) @@ -421,35 +440,3 @@ func TestAdminMarkAsStale(t *testing.T) { } }) } - -func createAccessTokens(t *testing.T, accessTokens map[*test.User]string, userAPI uapi.UserInternalAPI, ctx context.Context, routers httputil.Routers) { - t.Helper() - for u := range accessTokens { - localpart, serverName, _ := gomatrixserverlib.SplitID('@', u.ID) - userRes := &uapi.PerformAccountCreationResponse{} - password := util.RandomString(8) - if err := userAPI.PerformAccountCreation(ctx, &uapi.PerformAccountCreationRequest{ - AccountType: u.AccountType, - Localpart: localpart, - ServerName: serverName, - Password: password, - }, userRes); err != nil { - t.Errorf("failed to create account: %s", err) - } - - req := test.NewRequest(t, http.MethodPost, "/_matrix/client/v3/login", test.WithJSONBody(t, map[string]interface{}{ - "type": authtypes.LoginTypePassword, - "identifier": map[string]interface{}{ - "type": "m.id.user", - "user": u.ID, - }, - "password": password, - })) - rec := httptest.NewRecorder() - routers.Client.ServeHTTP(rec, req) - if rec.Code != http.StatusOK { - t.Fatalf("failed to login: %s", rec.Body.String()) - } - accessTokens[u] = gjson.GetBytes(rec.Body.Bytes(), "access_token").String() - } -} diff --git a/clientapi/api/api.go b/clientapi/api/api.go index d96b032f0..23974c865 100644 --- a/clientapi/api/api.go +++ b/clientapi/api/api.go @@ -14,10 +14,10 @@ package api -import "github.com/matrix-org/gomatrixserverlib" +import "github.com/matrix-org/gomatrixserverlib/fclient" // ExtraPublicRoomsProvider provides a way to inject extra published rooms into /publicRooms requests. type ExtraPublicRoomsProvider interface { // Rooms returns the extra rooms. This is called on-demand by clients, so cache appropriately. - Rooms() []gomatrixserverlib.PublicRoom + Rooms() []fclient.PublicRoom } diff --git a/clientapi/auth/authtypes/threepid.go b/clientapi/auth/authtypes/threepid.go index 60d77dc6f..ebafbe6aa 100644 --- a/clientapi/auth/authtypes/threepid.go +++ b/clientapi/auth/authtypes/threepid.go @@ -16,6 +16,8 @@ package authtypes // ThreePID represents a third-party identifier type ThreePID struct { - Address string `json:"address"` - Medium string `json:"medium"` + Address string `json:"address"` + Medium string `json:"medium"` + AddedAt int64 `json:"added_at"` + ValidatedAt int64 `json:"validated_at"` } diff --git a/clientapi/auth/login_test.go b/clientapi/auth/login_test.go index 044062c42..c91cba241 100644 --- a/clientapi/auth/login_test.go +++ b/clientapi/auth/login_test.go @@ -25,7 +25,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/userutil" "github.com/matrix-org/dendrite/setup/config" uapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -68,7 +68,7 @@ func TestLoginFromJSONReader(t *testing.T) { var userAPI fakeUserInternalAPI cfg := &config.ClientAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: serverName, }, }, @@ -148,7 +148,7 @@ func TestBadLoginFromJSONReader(t *testing.T) { var userAPI fakeUserInternalAPI cfg := &config.ClientAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: serverName, }, }, diff --git a/clientapi/auth/user_interactive_test.go b/clientapi/auth/user_interactive_test.go index 5d97b31ce..383a53399 100644 --- a/clientapi/auth/user_interactive_test.go +++ b/clientapi/auth/user_interactive_test.go @@ -9,6 +9,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -47,7 +48,7 @@ func (d *fakeAccountDatabase) QueryAccountByPassword(ctx context.Context, req *a func setup() *UserInteractive { cfg := &config.ClientAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: serverName, }, }, diff --git a/clientapi/clientapi.go b/clientapi/clientapi.go index d35f1d4d1..b57c8061e 100644 --- a/clientapi/clientapi.go +++ b/clientapi/clientapi.go @@ -19,7 +19,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/process" userapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" appserviceAPI "github.com/matrix-org/dendrite/appservice/api" "github.com/matrix-org/dendrite/clientapi/api" @@ -37,7 +37,7 @@ func AddPublicRoutes( routers httputil.Routers, cfg *config.Dendrite, natsInstance *jetstream.NATSInstance, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, transactionsCache *transactions.Cache, diff --git a/clientapi/clientapi_test.go b/clientapi/clientapi_test.go new file mode 100644 index 000000000..0be262735 --- /dev/null +++ b/clientapi/clientapi_test.go @@ -0,0 +1,1632 @@ +package clientapi + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" + + "github.com/matrix-org/dendrite/internal/pushrules" + "github.com/matrix-org/gomatrix" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" + + "github.com/matrix-org/dendrite/appservice" + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" + "github.com/matrix-org/dendrite/clientapi/routing" + "github.com/matrix-org/dendrite/clientapi/threepid" + "github.com/matrix-org/dendrite/internal/caching" + "github.com/matrix-org/dendrite/internal/httputil" + "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/roomserver" + "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/roomserver/version" + "github.com/matrix-org/dendrite/setup/base" + "github.com/matrix-org/dendrite/setup/config" + "github.com/matrix-org/dendrite/setup/jetstream" + "github.com/matrix-org/dendrite/test" + "github.com/matrix-org/dendrite/test/testrig" + "github.com/matrix-org/dendrite/userapi" + uapi "github.com/matrix-org/dendrite/userapi/api" +) + +type userDevice struct { + accessToken string + deviceID string + password string +} + +func TestGetPutDevices(t *testing.T) { + alice := test.NewUser(t) + bob := test.NewUser(t) + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + testCases := []struct { + name string + requestUser *test.User + deviceUser *test.User + request *http.Request + wantStatusCode int + validateFunc func(t *testing.T, device userDevice, routers httputil.Routers) + }{ + { + name: "can get all devices", + requestUser: alice, + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/devices", strings.NewReader("")), + wantStatusCode: http.StatusOK, + }, + { + name: "can get specific own device", + requestUser: alice, + deviceUser: alice, + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/devices/", strings.NewReader("")), + wantStatusCode: http.StatusOK, + }, + { + name: "can not get device for different user", + requestUser: alice, + deviceUser: bob, + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/devices/", strings.NewReader("")), + wantStatusCode: http.StatusNotFound, + }, + { + name: "can update own device", + requestUser: alice, + deviceUser: alice, + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/devices/", strings.NewReader(`{"display_name":"my new displayname"}`)), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, device userDevice, routers httputil.Routers) { + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/devices/"+device.deviceID, strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+device.accessToken) + rec := httptest.NewRecorder() + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + gotDisplayName := gjson.GetBytes(rec.Body.Bytes(), "display_name").Str + if gotDisplayName != "my new displayname" { + t.Fatalf("expected displayname '%s', got '%s'", "my new displayname", gotDisplayName) + } + }, + }, + { + // this should return "device does not exist" + name: "can not update device for different user", + requestUser: alice, + deviceUser: bob, + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/devices/", strings.NewReader(`{"display_name":"my new displayname"}`)), + wantStatusCode: http.StatusNotFound, + }, + } + + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + natsInstance := jetstream.NATSInstance{} + defer close() + + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + + // We mostly need the rsAPI for this test, so nil for other APIs/caches etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + accessTokens := map[*test.User]userDevice{ + alice: {}, + bob: {}, + } + createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + dev := accessTokens[tc.requestUser] + if tc.deviceUser != nil { + tc.request = httptest.NewRequest(tc.request.Method, tc.request.RequestURI+accessTokens[tc.deviceUser].deviceID, tc.request.Body) + } + tc.request.Header.Set("Authorization", "Bearer "+dev.accessToken) + rec := httptest.NewRecorder() + routers.Client.ServeHTTP(rec, tc.request) + if rec.Code != tc.wantStatusCode { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + if tc.wantStatusCode != http.StatusOK && rec.Code != http.StatusOK { + return + } + if tc.validateFunc != nil { + tc.validateFunc(t, dev, routers) + } + }) + } + }) +} + +// Deleting devices requires the UIA dance, so do this in a different test +func TestDeleteDevice(t *testing.T) { + alice := test.NewUser(t) + localpart, serverName, _ := gomatrixserverlib.SplitID('@', alice.ID) + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType) + defer closeDB() + + natsInstance := jetstream.NATSInstance{} + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + + // We mostly need the rsAPI/ for this test, so nil for other APIs/caches etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + accessTokens := map[*test.User]userDevice{ + alice: {}, + } + + // create the account and an initial device + createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers) + + // create some more devices + accessToken := util.RandomString(8) + devRes := &uapi.PerformDeviceCreationResponse{} + if err := userAPI.PerformDeviceCreation(processCtx.Context(), &uapi.PerformDeviceCreationRequest{ + Localpart: localpart, + ServerName: serverName, + AccessToken: accessToken, + NoDeviceListUpdate: true, + }, devRes); err != nil { + t.Fatal(err) + } + if !devRes.DeviceCreated { + t.Fatalf("failed to create device") + } + secondDeviceID := devRes.Device.ID + + // initiate UIA for the second device + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodDelete, "/_matrix/client/v3/devices/"+secondDeviceID, strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusUnauthorized { + t.Fatalf("expected HTTP 401, got %d: %s", rec.Code, rec.Body.String()) + } + // get the session ID + sessionID := gjson.GetBytes(rec.Body.Bytes(), "session").Str + + // prepare UIA request body + reqBody := bytes.Buffer{} + if err := json.NewEncoder(&reqBody).Encode(map[string]interface{}{ + "auth": map[string]string{ + "session": sessionID, + "type": authtypes.LoginTypePassword, + "user": alice.ID, + "password": accessTokens[alice].password, + }, + }); err != nil { + t.Fatal(err) + } + + // copy the request body, so we can use it again for the successful delete + reqBody2 := reqBody + + // do the same request again, this time with our UIA, but for a different device ID, this should fail + rec = httptest.NewRecorder() + + req = httptest.NewRequest(http.MethodDelete, "/_matrix/client/v3/devices/"+accessTokens[alice].deviceID, &reqBody) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusForbidden { + t.Fatalf("expected HTTP 403, got %d: %s", rec.Code, rec.Body.String()) + } + + // do the same request again, this time with our UIA, but for the correct device ID, this should be fine + rec = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodDelete, "/_matrix/client/v3/devices/"+secondDeviceID, &reqBody2) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + + // verify devices are deleted + rec = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/devices", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + for _, device := range gjson.GetBytes(rec.Body.Bytes(), "devices.#.device_id").Array() { + if device.Str == secondDeviceID { + t.Fatalf("expected device %s to be deleted, but wasn't", secondDeviceID) + } + } + }) +} + +// Deleting devices requires the UIA dance, so do this in a different test +func TestDeleteDevices(t *testing.T) { + alice := test.NewUser(t) + localpart, serverName, _ := gomatrixserverlib.SplitID('@', alice.ID) + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType) + defer closeDB() + + natsInstance := jetstream.NATSInstance{} + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + + // We mostly need the rsAPI/ for this test, so nil for other APIs/caches etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + accessTokens := map[*test.User]userDevice{ + alice: {}, + } + + // create the account and an initial device + createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers) + + // create some more devices + var devices []string + for i := 0; i < 10; i++ { + accessToken := util.RandomString(8) + devRes := &uapi.PerformDeviceCreationResponse{} + if err := userAPI.PerformDeviceCreation(processCtx.Context(), &uapi.PerformDeviceCreationRequest{ + Localpart: localpart, + ServerName: serverName, + AccessToken: accessToken, + NoDeviceListUpdate: true, + }, devRes); err != nil { + t.Fatal(err) + } + if !devRes.DeviceCreated { + t.Fatalf("failed to create device") + } + devices = append(devices, devRes.Device.ID) + } + + // initiate UIA + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/delete_devices", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusUnauthorized { + t.Fatalf("expected HTTP 401, got %d: %s", rec.Code, rec.Body.String()) + } + // get the session ID + sessionID := gjson.GetBytes(rec.Body.Bytes(), "session").Str + + // prepare UIA request body + reqBody := bytes.Buffer{} + if err := json.NewEncoder(&reqBody).Encode(map[string]interface{}{ + "auth": map[string]string{ + "session": sessionID, + "type": authtypes.LoginTypePassword, + "user": alice.ID, + "password": accessTokens[alice].password, + }, + "devices": devices[5:], + }); err != nil { + t.Fatal(err) + } + + // do the same request again, this time with our UIA, + rec = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/delete_devices", &reqBody) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + + // verify devices are deleted + rec = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/devices", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + for _, device := range gjson.GetBytes(rec.Body.Bytes(), "devices.#.device_id").Array() { + for _, deletedDevice := range devices[5:] { + if device.Str == deletedDevice { + t.Fatalf("expected device %s to be deleted, but wasn't", deletedDevice) + } + } + } + }) +} + +func createAccessTokens(t *testing.T, accessTokens map[*test.User]userDevice, userAPI uapi.UserInternalAPI, ctx context.Context, routers httputil.Routers) { + t.Helper() + for u := range accessTokens { + localpart, serverName, _ := gomatrixserverlib.SplitID('@', u.ID) + userRes := &uapi.PerformAccountCreationResponse{} + password := util.RandomString(8) + if err := userAPI.PerformAccountCreation(ctx, &uapi.PerformAccountCreationRequest{ + AccountType: u.AccountType, + Localpart: localpart, + ServerName: serverName, + Password: password, + }, userRes); err != nil { + t.Errorf("failed to create account: %s", err) + } + req := test.NewRequest(t, http.MethodPost, "/_matrix/client/v3/login", test.WithJSONBody(t, map[string]interface{}{ + "type": authtypes.LoginTypePassword, + "identifier": map[string]interface{}{ + "type": "m.id.user", + "user": u.ID, + }, + "password": password, + })) + rec := httptest.NewRecorder() + routers.Client.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("failed to login: %s", rec.Body.String()) + } + accessTokens[u] = userDevice{ + accessToken: gjson.GetBytes(rec.Body.Bytes(), "access_token").String(), + deviceID: gjson.GetBytes(rec.Body.Bytes(), "device_id").String(), + password: password, + } + } +} + +func TestSetDisplayname(t *testing.T) { + alice := test.NewUser(t) + bob := test.NewUser(t) + notLocalUser := &test.User{ID: "@charlie:localhost", Localpart: "charlie"} + changeDisplayName := "my new display name" + + testCases := []struct { + name string + user *test.User + wantOK bool + changeReq io.Reader + wantDisplayName string + }{ + { + name: "invalid user", + user: &test.User{ID: "!notauser"}, + }, + { + name: "non-existent user", + user: &test.User{ID: "@doesnotexist:test"}, + }, + { + name: "non-local user is not allowed", + user: notLocalUser, + }, + { + name: "existing user is allowed to change own name", + user: alice, + wantOK: true, + wantDisplayName: changeDisplayName, + }, + { + name: "existing user is not allowed to change own name if name is empty", + user: bob, + wantOK: false, + wantDisplayName: "", + }, + } + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType) + defer closeDB() + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + natsInstance := &jetstream.NATSInstance{} + + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil) + asPI := appservice.NewInternalAPI(processCtx, cfg, natsInstance, userAPI, rsAPI) + + AddPublicRoutes(processCtx, routers, cfg, natsInstance, base.CreateFederationClient(cfg, nil), rsAPI, asPI, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + accessTokens := map[*test.User]userDevice{ + alice: {}, + bob: {}, + } + + createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + wantDisplayName := tc.user.Localpart + if tc.changeReq == nil { + tc.changeReq = strings.NewReader("") + } + + // check profile after initial account creation + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/profile/"+tc.user.ID, strings.NewReader("")) + t.Logf("%s", req.URL.String()) + routers.Client.ServeHTTP(rec, req) + + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d", rec.Code) + } + + if gotDisplayName := gjson.GetBytes(rec.Body.Bytes(), "displayname").Str; tc.wantOK && gotDisplayName != wantDisplayName { + t.Fatalf("expected displayname to be '%s', but got '%s'", wantDisplayName, gotDisplayName) + } + + // now set the new display name + wantDisplayName = tc.wantDisplayName + tc.changeReq = strings.NewReader(fmt.Sprintf(`{"displayname":"%s"}`, tc.wantDisplayName)) + + rec = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/profile/"+tc.user.ID+"/displayname", tc.changeReq) + req.Header.Set("Authorization", "Bearer "+accessTokens[tc.user].accessToken) + + routers.Client.ServeHTTP(rec, req) + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + + // now only get the display name + rec = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/profile/"+tc.user.ID+"/displayname", strings.NewReader("")) + + routers.Client.ServeHTTP(rec, req) + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + + if gotDisplayName := gjson.GetBytes(rec.Body.Bytes(), "displayname").Str; tc.wantOK && gotDisplayName != wantDisplayName { + t.Fatalf("expected displayname to be '%s', but got '%s'", wantDisplayName, gotDisplayName) + } + }) + } + }) +} + +func TestSetAvatarURL(t *testing.T) { + alice := test.NewUser(t) + bob := test.NewUser(t) + notLocalUser := &test.User{ID: "@charlie:localhost", Localpart: "charlie"} + changeDisplayName := "mxc://newMXID" + + testCases := []struct { + name string + user *test.User + wantOK bool + changeReq io.Reader + avatar_url string + }{ + { + name: "invalid user", + user: &test.User{ID: "!notauser"}, + }, + { + name: "non-existent user", + user: &test.User{ID: "@doesnotexist:test"}, + }, + { + name: "non-local user is not allowed", + user: notLocalUser, + }, + { + name: "existing user is allowed to change own avatar", + user: alice, + wantOK: true, + avatar_url: changeDisplayName, + }, + { + name: "existing user is not allowed to change own avatar if avatar is empty", + user: bob, + wantOK: false, + avatar_url: "", + }, + } + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType) + defer closeDB() + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + natsInstance := &jetstream.NATSInstance{} + + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil) + asPI := appservice.NewInternalAPI(processCtx, cfg, natsInstance, userAPI, rsAPI) + + AddPublicRoutes(processCtx, routers, cfg, natsInstance, base.CreateFederationClient(cfg, nil), rsAPI, asPI, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + accessTokens := map[*test.User]userDevice{ + alice: {}, + bob: {}, + } + + createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + wantAvatarURL := "" + if tc.changeReq == nil { + tc.changeReq = strings.NewReader("") + } + + // check profile after initial account creation + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/profile/"+tc.user.ID, strings.NewReader("")) + t.Logf("%s", req.URL.String()) + routers.Client.ServeHTTP(rec, req) + + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d", rec.Code) + } + + if gotDisplayName := gjson.GetBytes(rec.Body.Bytes(), "avatar_url").Str; tc.wantOK && gotDisplayName != wantAvatarURL { + t.Fatalf("expected displayname to be '%s', but got '%s'", wantAvatarURL, gotDisplayName) + } + + // now set the new display name + wantAvatarURL = tc.avatar_url + tc.changeReq = strings.NewReader(fmt.Sprintf(`{"avatar_url":"%s"}`, tc.avatar_url)) + + rec = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/profile/"+tc.user.ID+"/avatar_url", tc.changeReq) + req.Header.Set("Authorization", "Bearer "+accessTokens[tc.user].accessToken) + + routers.Client.ServeHTTP(rec, req) + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + + // now only get the display name + rec = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/profile/"+tc.user.ID+"/avatar_url", strings.NewReader("")) + + routers.Client.ServeHTTP(rec, req) + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + + if gotDisplayName := gjson.GetBytes(rec.Body.Bytes(), "avatar_url").Str; tc.wantOK && gotDisplayName != wantAvatarURL { + t.Fatalf("expected displayname to be '%s', but got '%s'", wantAvatarURL, gotDisplayName) + } + }) + } + }) +} + +func TestTyping(t *testing.T) { + alice := test.NewUser(t) + room := test.NewRoom(t, alice) + ctx := context.Background() + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + defer close() + natsInstance := jetstream.NATSInstance{} + + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + // Needed to create accounts + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + // We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + // Create the users in the userapi and login + accessTokens := map[*test.User]userDevice{ + alice: {}, + } + createAccessTokens(t, accessTokens, userAPI, ctx, routers) + + // Create the room + if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil { + t.Fatal(err) + } + + testCases := []struct { + name string + typingForUser string + roomID string + requestBody io.Reader + wantOK bool + }{ + { + name: "can not set typing for different user", + typingForUser: "@notourself:test", + roomID: room.ID, + requestBody: strings.NewReader(""), + }, + { + name: "invalid request body", + typingForUser: alice.ID, + roomID: room.ID, + requestBody: strings.NewReader(""), + }, + { + name: "non-existent room", + typingForUser: alice.ID, + roomID: "!doesnotexist:test", + }, + { + name: "invalid room ID", + typingForUser: alice.ID, + roomID: "@notaroomid:test", + }, + { + name: "allowed to set own typing status", + typingForUser: alice.ID, + roomID: room.ID, + requestBody: strings.NewReader(`{"typing":true}`), + wantOK: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/rooms/"+tc.roomID+"/typing/"+tc.typingForUser, tc.requestBody) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + }) + } + }) +} + +func TestMembership(t *testing.T) { + alice := test.NewUser(t) + bob := test.NewUser(t) + room := test.NewRoom(t, alice) + ctx := context.Background() + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + cfg.ClientAPI.RateLimiting.Enabled = false + defer close() + natsInstance := jetstream.NATSInstance{} + + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + // Needed to create accounts + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + rsAPI.SetUserAPI(userAPI) + // We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + // Create the users in the userapi and login + accessTokens := map[*test.User]userDevice{ + alice: {}, + bob: {}, + } + createAccessTokens(t, accessTokens, userAPI, ctx, routers) + + // Create the room + if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil { + t.Fatal(err) + } + + invalidBodyRequest := func(roomID, membershipType string) *http.Request { + return httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", roomID, membershipType), strings.NewReader("")) + } + + missingUserIDRequest := func(roomID, membershipType string) *http.Request { + return httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", roomID, membershipType), strings.NewReader("{}")) + } + + testCases := []struct { + name string + roomID string + request *http.Request + wantOK bool + asUser *test.User + }{ + { + name: "ban - invalid request body", + request: invalidBodyRequest(room.ID, "ban"), + }, + { + name: "kick - invalid request body", + request: invalidBodyRequest(room.ID, "kick"), + }, + { + name: "unban - invalid request body", + request: invalidBodyRequest(room.ID, "unban"), + }, + { + name: "invite - invalid request body", + request: invalidBodyRequest(room.ID, "invite"), + }, + { + name: "ban - missing user_id body", + request: missingUserIDRequest(room.ID, "ban"), + }, + { + name: "kick - missing user_id body", + request: missingUserIDRequest(room.ID, "kick"), + }, + { + name: "unban - missing user_id body", + request: missingUserIDRequest(room.ID, "unban"), + }, + { + name: "invite - missing user_id body", + request: missingUserIDRequest(room.ID, "invite"), + }, + { + name: "Bob forgets invalid room", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", "!doesnotexist", "forget"), strings.NewReader("")), + asUser: bob, + }, + { + name: "Alice can not ban Bob in non-existent room", // fails because "not joined" + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", "!doesnotexist:test", "ban"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + }, + { + name: "Alice can not kick Bob in non-existent room", // fails because "not joined" + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", "!doesnotexist:test", "kick"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + }, + // the following must run in sequence, as they build up on each other + { + name: "Alice invites Bob", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "invite"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + wantOK: true, + }, + { + name: "Bob accepts invite", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "join"), strings.NewReader("")), + wantOK: true, + asUser: bob, + }, + { + name: "Alice verifies that Bob is joined", // returns an error if no membership event can be found + request: httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s/m.room.member/%s", room.ID, "state", bob.ID), strings.NewReader("")), + wantOK: true, + }, + { + name: "Bob forgets the room but is still a member", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "forget"), strings.NewReader("")), + wantOK: false, // user is still in the room + asUser: bob, + }, + { + name: "Bob can not kick Alice", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "kick"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, alice.ID))), + wantOK: false, // powerlevel too low + asUser: bob, + }, + { + name: "Bob can not ban Alice", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "ban"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, alice.ID))), + wantOK: false, // powerlevel too low + asUser: bob, + }, + { + name: "Alice can kick Bob", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "kick"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + wantOK: true, + }, + { + name: "Alice can ban Bob", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "ban"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + wantOK: true, + }, + { + name: "Alice can not kick Bob again", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "kick"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + wantOK: false, // can not kick banned/left user + }, + { + name: "Bob can not unban himself", // mostly because of not being a member of the room + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "unban"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + asUser: bob, + }, + { + name: "Alice can not invite Bob again", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "invite"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + wantOK: false, // user still banned + }, + { + name: "Alice can unban Bob", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "unban"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + wantOK: true, + }, + { + name: "Alice can not unban Bob again", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "unban"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + wantOK: false, + }, + { + name: "Alice can invite Bob again", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "invite"), strings.NewReader(fmt.Sprintf(`{"user_id":"%s"}`, bob.ID))), + wantOK: true, + }, + { + name: "Bob can reject the invite by leaving", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "leave"), strings.NewReader("")), + wantOK: true, + asUser: bob, + }, + { + name: "Bob can forget the room", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "forget"), strings.NewReader("")), + wantOK: true, + asUser: bob, + }, + { + name: "Bob can forget the room again", + request: httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/%s", room.ID, "forget"), strings.NewReader("")), + wantOK: true, + asUser: bob, + }, + // END must run in sequence + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if tc.asUser == nil { + tc.asUser = alice + } + rec := httptest.NewRecorder() + tc.request.Header.Set("Authorization", "Bearer "+accessTokens[tc.asUser].accessToken) + routers.Client.ServeHTTP(rec, tc.request) + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + if !tc.wantOK && rec.Code == http.StatusOK { + t.Fatalf("expected request to fail, but didn't: %s", rec.Body.String()) + } + t.Logf("%s", rec.Body.String()) + }) + } + }) +} + +func TestCapabilities(t *testing.T) { + alice := test.NewUser(t) + ctx := context.Background() + + // construct the expected result + versionsMap := map[gomatrixserverlib.RoomVersion]string{} + for v, desc := range version.SupportedRoomVersions() { + if desc.Stable { + versionsMap[v] = "stable" + } else { + versionsMap[v] = "unstable" + } + } + + expectedMap := map[string]interface{}{ + "capabilities": map[string]interface{}{ + "m.change_password": map[string]bool{ + "enabled": true, + }, + "m.room_versions": map[string]interface{}{ + "default": version.DefaultRoomVersion(), + "available": versionsMap, + }, + }, + } + + expectedBuf := &bytes.Buffer{} + err := json.NewEncoder(expectedBuf).Encode(expectedMap) + assert.NoError(t, err) + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + cfg.ClientAPI.RateLimiting.Enabled = false + defer close() + natsInstance := jetstream.NATSInstance{} + + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + + // Needed to create accounts + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + // We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + // Create the users in the userapi and login + accessTokens := map[*test.User]userDevice{ + alice: {}, + } + createAccessTokens(t, accessTokens, userAPI, ctx, routers) + + testCases := []struct { + name string + request *http.Request + }{ + { + name: "can get capabilities", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/capabilities", strings.NewReader("")), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rec := httptest.NewRecorder() + tc.request.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, tc.request) + assert.Equal(t, http.StatusOK, rec.Code) + assert.ObjectsAreEqual(expectedBuf.Bytes(), rec.Body.Bytes()) + }) + } + }) +} + +func TestTurnserver(t *testing.T) { + alice := test.NewUser(t) + ctx := context.Background() + + cfg, processCtx, close := testrig.CreateConfig(t, test.DBTypeSQLite) + cfg.ClientAPI.RateLimiting.Enabled = false + defer close() + natsInstance := jetstream.NATSInstance{} + + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + + // Needed to create accounts + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + //rsAPI.SetUserAPI(userAPI) + // We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + // Create the users in the userapi and login + accessTokens := map[*test.User]userDevice{ + alice: {}, + } + createAccessTokens(t, accessTokens, userAPI, ctx, routers) + + testCases := []struct { + name string + turnConfig config.TURN + wantEmptyResponse bool + }{ + { + name: "no turn server configured", + wantEmptyResponse: true, + }, + { + name: "servers configured but not userLifeTime", + wantEmptyResponse: true, + turnConfig: config.TURN{URIs: []string{""}}, + }, + { + name: "missing sharedSecret/username/password", + wantEmptyResponse: true, + turnConfig: config.TURN{URIs: []string{""}, UserLifetime: "1m"}, + }, + { + name: "with shared secret", + turnConfig: config.TURN{URIs: []string{""}, UserLifetime: "1m", SharedSecret: "iAmSecret"}, + }, + { + name: "with username/password secret", + turnConfig: config.TURN{URIs: []string{""}, UserLifetime: "1m", Username: "username", Password: "iAmSecret"}, + }, + { + name: "only username set", + turnConfig: config.TURN{URIs: []string{""}, UserLifetime: "1m", Username: "username"}, + wantEmptyResponse: true, + }, + { + name: "only password set", + turnConfig: config.TURN{URIs: []string{""}, UserLifetime: "1m", Username: "username"}, + wantEmptyResponse: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/voip/turnServer", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + cfg.ClientAPI.TURN = tc.turnConfig + routers.Client.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code) + + if tc.wantEmptyResponse && rec.Body.String() != "{}" { + t.Fatalf("expected an empty response, but got %s", rec.Body.String()) + } + if !tc.wantEmptyResponse { + assert.NotEqual(t, "{}", rec.Body.String()) + + resp := gomatrix.RespTurnServer{} + err := json.NewDecoder(rec.Body).Decode(&resp) + assert.NoError(t, err) + + duration, _ := time.ParseDuration(tc.turnConfig.UserLifetime) + assert.Equal(t, tc.turnConfig.URIs, resp.URIs) + assert.Equal(t, int(duration.Seconds()), resp.TTL) + if tc.turnConfig.Username != "" && tc.turnConfig.Password != "" { + assert.Equal(t, tc.turnConfig.Username, resp.Username) + assert.Equal(t, tc.turnConfig.Password, resp.Password) + } + } + }) + } +} + +func Test3PID(t *testing.T) { + alice := test.NewUser(t) + ctx := context.Background() + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + cfg.ClientAPI.RateLimiting.Enabled = false + cfg.FederationAPI.DisableTLSValidation = true // needed to be able to connect to our identityServer below + defer close() + natsInstance := jetstream.NATSInstance{} + + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + + // Needed to create accounts + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + // We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + // Create the users in the userapi and login + accessTokens := map[*test.User]userDevice{ + alice: {}, + } + createAccessTokens(t, accessTokens, userAPI, ctx, routers) + + identityServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case strings.Contains(r.URL.String(), "getValidated3pid"): + resp := threepid.GetValidatedResponse{} + switch r.URL.Query().Get("client_secret") { + case "fail": + resp.ErrCode = "M_SESSION_NOT_VALIDATED" + case "fail2": + resp.ErrCode = "some other error" + case "fail3": + _, _ = w.Write([]byte("{invalidJson")) + return + case "success": + resp.Medium = "email" + case "success2": + resp.Medium = "email" + resp.Address = "somerandom@address.com" + } + _ = json.NewEncoder(w).Encode(resp) + case strings.Contains(r.URL.String(), "requestToken"): + resp := threepid.SID{SID: "randomSID"} + _ = json.NewEncoder(w).Encode(resp) + } + })) + defer identityServer.Close() + + identityServerBase := strings.TrimPrefix(identityServer.URL, "https://") + + testCases := []struct { + name string + request *http.Request + wantOK bool + setTrustedServer bool + wantLen3PIDs int + }{ + { + name: "can get associated threepid info", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/account/3pid", strings.NewReader("")), + wantOK: true, + }, + { + name: "can not set threepid info with invalid JSON", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid", strings.NewReader("")), + }, + { + name: "can not set threepid info with untrusted server", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid", strings.NewReader("{}")), + }, + { + name: "can check threepid info with trusted server, but unverified", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid", strings.NewReader(fmt.Sprintf(`{"three_pid_creds":{"id_server":"%s","client_secret":"fail"}}`, identityServerBase))), + setTrustedServer: true, + wantOK: false, + }, + { + name: "can check threepid info with trusted server, but fails for some other reason", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid", strings.NewReader(fmt.Sprintf(`{"three_pid_creds":{"id_server":"%s","client_secret":"fail2"}}`, identityServerBase))), + setTrustedServer: true, + wantOK: false, + }, + { + name: "can check threepid info with trusted server, but fails because of invalid json", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid", strings.NewReader(fmt.Sprintf(`{"three_pid_creds":{"id_server":"%s","client_secret":"fail3"}}`, identityServerBase))), + setTrustedServer: true, + wantOK: false, + }, + { + name: "can save threepid info with trusted server", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid", strings.NewReader(fmt.Sprintf(`{"three_pid_creds":{"id_server":"%s","client_secret":"success"}}`, identityServerBase))), + setTrustedServer: true, + wantOK: true, + }, + { + name: "can save threepid info with trusted server using bind=true", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid", strings.NewReader(fmt.Sprintf(`{"three_pid_creds":{"id_server":"%s","client_secret":"success2"},"bind":true}`, identityServerBase))), + setTrustedServer: true, + wantOK: true, + }, + { + name: "can get associated threepid info again", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/account/3pid", strings.NewReader("")), + wantOK: true, + wantLen3PIDs: 2, + }, + { + name: "can delete associated threepid info", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid/delete", strings.NewReader(`{"medium":"email","address":"somerandom@address.com"}`)), + wantOK: true, + }, + { + name: "can get associated threepid after deleting association", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/account/3pid", strings.NewReader("")), + wantOK: true, + wantLen3PIDs: 1, + }, + { + name: "can not request emailToken with invalid request body", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid/email/requestToken", strings.NewReader("")), + }, + { + name: "can not request emailToken for in use address", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid/email/requestToken", strings.NewReader(fmt.Sprintf(`{"client_secret":"somesecret","email":"","send_attempt":1,"id_server":"%s"}`, identityServerBase))), + }, + { + name: "can request emailToken", + request: httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/account/3pid/email/requestToken", strings.NewReader(fmt.Sprintf(`{"client_secret":"somesecret","email":"somerandom@address.com","send_attempt":1,"id_server":"%s"}`, identityServerBase))), + wantOK: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + if tc.setTrustedServer { + cfg.Global.TrustedIDServers = []string{identityServerBase} + } + + rec := httptest.NewRecorder() + tc.request.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.Client.ServeHTTP(rec, tc.request) + t.Logf("Response: %s", rec.Body.String()) + if tc.wantOK && rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String()) + } + if !tc.wantOK && rec.Code == http.StatusOK { + t.Fatalf("expected request to fail, but didn't: %s", rec.Body.String()) + } + if tc.wantLen3PIDs > 0 { + var resp routing.ThreePIDsResponse + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatal(err) + } + if len(resp.ThreePIDs) != tc.wantLen3PIDs { + t.Fatalf("expected %d threepids, got %d", tc.wantLen3PIDs, len(resp.ThreePIDs)) + } + } + }) + } + }) +} + +func TestPushRules(t *testing.T) { + alice := test.NewUser(t) + + // create the default push rules, used when validating responses + localpart, serverName, _ := gomatrixserverlib.SplitID('@', alice.ID) + pushRuleSets := pushrules.DefaultAccountRuleSets(localpart, serverName) + defaultRules, err := json.Marshal(pushRuleSets) + assert.NoError(t, err) + + ruleID1 := "myrule" + ruleID2 := "myrule2" + ruleID3 := "myrule3" + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + cfg.ClientAPI.RateLimiting.Enabled = false + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + natsInstance := jetstream.NATSInstance{} + defer close() + + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil) + + // We mostly need the rsAPI for this test, so nil for other APIs/caches etc. + AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics) + + accessTokens := map[*test.User]userDevice{ + alice: {}, + } + createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers) + + testCases := []struct { + name string + request *http.Request + wantStatusCode int + validateFunc func(t *testing.T, respBody *bytes.Buffer) // used when updating rules, otherwise wantStatusCode should be enough + queryAttr map[string]string + }{ + { + name: "can not get rules without trailing slash", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can get default rules", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/", strings.NewReader("")), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + assert.Equal(t, defaultRules, respBody.Bytes()) + }, + }, + { + name: "can get rules by scope", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/", strings.NewReader("")), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + assert.Equal(t, gjson.GetBytes(defaultRules, "global").Raw, respBody.String()) + }, + }, + { + name: "can not get invalid rules by scope", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/doesnotexist/", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not get rules for invalid scope and kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/doesnotexist/invalid/", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not get rules for invalid kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/invalid/", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can get rules by scope and kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/", strings.NewReader("")), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + assert.Equal(t, gjson.GetBytes(defaultRules, "global.override").Raw, respBody.String()) + }, + }, + { + name: "can get rules by scope and content kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/content/", strings.NewReader("")), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + assert.Equal(t, gjson.GetBytes(defaultRules, "global.content").Raw, respBody.String()) + }, + }, + { + name: "can not get rules by scope and room kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/room/", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not get rules by scope and sender kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/sender/", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can get rules by scope and underride kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/underride/", strings.NewReader("")), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + assert.Equal(t, gjson.GetBytes(defaultRules, "global.underride").Raw, respBody.String()) + }, + }, + { + name: "can not get rules by scope, kind and ID for invalid scope", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/doesnotexist/doesnotexist/.m.rule.master", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not get rules by scope, kind and ID for invalid kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/doesnotexist/.m.rule.master", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can get rules by scope, kind and ID", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/.m.rule.master", strings.NewReader("")), + wantStatusCode: http.StatusOK, + }, + { + name: "can not get rules by scope, kind and ID for invalid ID", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/.m.rule.doesnotexist", strings.NewReader("")), + wantStatusCode: http.StatusNotFound, + }, + { + name: "can not get status for invalid attribute", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/invalid", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not get status for invalid kind", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/invalid/.m.rule.master/enabled", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not get enabled status for invalid scope", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/invalid/override/.m.rule.master/enabled", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not get enabled status for invalid rule", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/doesnotexist/enabled", strings.NewReader("")), + wantStatusCode: http.StatusNotFound, + }, + { + name: "can get enabled rules by scope, kind and ID", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/enabled", strings.NewReader("")), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + assert.False(t, gjson.GetBytes(respBody.Bytes(), "enabled").Bool(), "expected master rule to be disabled") + }, + }, + { + name: "can get actions scope, kind and ID", + request: httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/actions", strings.NewReader("")), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + actions := gjson.GetBytes(respBody.Bytes(), "actions").Array() + // only a basic check + assert.Equal(t, 1, len(actions)) + }, + }, + { + name: "can not set enabled status with invalid JSON", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/enabled", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not set attribute for invalid attribute", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/doesnotexist", strings.NewReader("{}")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not set attribute for invalid scope", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/invalid/override/.m.rule.master/enabled", strings.NewReader("{}")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not set attribute for invalid kind", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/invalid/.m.rule.master/enabled", strings.NewReader("{}")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not set attribute for invalid rule", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/override/invalid/enabled", strings.NewReader("{}")), + wantStatusCode: http.StatusNotFound, + }, + { + name: "can set enabled status with valid JSON", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/enabled", strings.NewReader(`{"enabled":true}`)), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/enabled", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code, rec.Body.String()) + assert.True(t, gjson.GetBytes(rec.Body.Bytes(), "enabled").Bool(), "expected master rule to be enabled: %s", rec.Body.String()) + }, + }, + { + name: "can set actions with valid JSON", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/actions", strings.NewReader(`{"actions":["dont_notify","notify"]}`)), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/override/.m.rule.master/actions", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code, rec.Body.String()) + assert.Equal(t, 2, len(gjson.GetBytes(rec.Body.Bytes(), "actions").Array()), "expected 2 actions %s", rec.Body.String()) + }, + }, + { + name: "can not create new push rule with invalid JSON", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/content/myrule", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not create new push rule with invalid rule content", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/content/myrule", strings.NewReader("{}")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not create new push rule with invalid scope", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/invalid/content/myrule", strings.NewReader(`{"actions":["notify"],"pattern":"world"}`)), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can create new push rule with valid rule content", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/content/myrule", strings.NewReader(`{"actions":["notify"],"pattern":"world"}`)), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/content/myrule/actions", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code, rec.Body.String()) + assert.Equal(t, 1, len(gjson.GetBytes(rec.Body.Bytes(), "actions").Array()), "expected 1 action %s", rec.Body.String()) + }, + }, + { + name: "can not create new push starting with a dot", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/content/.myrule", strings.NewReader(`{"actions":["notify"],"pattern":"world"}`)), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can create new push rule after existing", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/content/myrule2", strings.NewReader(`{"actions":["notify"],"pattern":"world"}`)), + queryAttr: map[string]string{ + "after": ruleID1, + }, + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/content/", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code, rec.Body.String()) + rules := gjson.ParseBytes(rec.Body.Bytes()) + for i, rule := range rules.Array() { + if rule.Get("rule_id").Str == ruleID1 && i != 0 { + t.Fatalf("expected '%s' to be the first, but wasn't", ruleID1) + } + if rule.Get("rule_id").Str == ruleID2 && i != 1 { + t.Fatalf("expected '%s' to be the second, but wasn't", ruleID2) + } + } + }, + }, + { + name: "can create new push rule before existing", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/content/myrule3", strings.NewReader(`{"actions":["notify"],"pattern":"world"}`)), + queryAttr: map[string]string{ + "before": ruleID1, + }, + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/content/", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code, rec.Body.String()) + rules := gjson.ParseBytes(rec.Body.Bytes()) + for i, rule := range rules.Array() { + if rule.Get("rule_id").Str == ruleID3 && i != 0 { + t.Fatalf("expected '%s' to be the first, but wasn't", ruleID3) + } + if rule.Get("rule_id").Str == ruleID1 && i != 1 { + t.Fatalf("expected '%s' to be the second, but wasn't", ruleID1) + } + if rule.Get("rule_id").Str == ruleID2 && i != 2 { + t.Fatalf("expected '%s' to be the third, but wasn't", ruleID1) + } + } + }, + }, + { + name: "can modify existing push rule", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/content/myrule2", strings.NewReader(`{"actions":["dont_notify"],"pattern":"world"}`)), + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/content/myrule2/actions", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code, rec.Body.String()) + actions := gjson.GetBytes(rec.Body.Bytes(), "actions").Array() + // there should only be one action + assert.Equal(t, "dont_notify", actions[0].Str) + }, + }, + { + name: "can move existing push rule to the front", + request: httptest.NewRequest(http.MethodPut, "/_matrix/client/v3/pushrules/global/content/myrule2", strings.NewReader(`{"actions":["dont_notify"],"pattern":"world"}`)), + queryAttr: map[string]string{ + "before": ruleID3, + }, + wantStatusCode: http.StatusOK, + validateFunc: func(t *testing.T, respBody *bytes.Buffer) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/pushrules/global/content/", strings.NewReader("")) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + routers.Client.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code, rec.Body.String()) + rules := gjson.ParseBytes(rec.Body.Bytes()) + for i, rule := range rules.Array() { + if rule.Get("rule_id").Str == ruleID2 && i != 0 { + t.Fatalf("expected '%s' to be the first, but wasn't", ruleID2) + } + if rule.Get("rule_id").Str == ruleID3 && i != 1 { + t.Fatalf("expected '%s' to be the second, but wasn't", ruleID3) + } + if rule.Get("rule_id").Str == ruleID1 && i != 2 { + t.Fatalf("expected '%s' to be the third, but wasn't", ruleID1) + } + } + }, + }, + { + name: "can not delete push rule with invalid scope", + request: httptest.NewRequest(http.MethodDelete, "/_matrix/client/v3/pushrules/invalid/content/myrule2", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not delete push rule with invalid kind", + request: httptest.NewRequest(http.MethodDelete, "/_matrix/client/v3/pushrules/global/invalid/myrule2", strings.NewReader("")), + wantStatusCode: http.StatusBadRequest, + }, + { + name: "can not delete push rule with non-existent rule", + request: httptest.NewRequest(http.MethodDelete, "/_matrix/client/v3/pushrules/global/content/doesnotexist", strings.NewReader("")), + wantStatusCode: http.StatusNotFound, + }, + { + name: "can delete existing push rule", + request: httptest.NewRequest(http.MethodDelete, "/_matrix/client/v3/pushrules/global/content/myrule2", strings.NewReader("")), + wantStatusCode: http.StatusOK, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rec := httptest.NewRecorder() + + if tc.queryAttr != nil { + params := url.Values{} + for k, v := range tc.queryAttr { + params.Set(k, v) + } + + tc.request = httptest.NewRequest(tc.request.Method, tc.request.URL.String()+"?"+params.Encode(), tc.request.Body) + } + + tc.request.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.Client.ServeHTTP(rec, tc.request) + assert.Equal(t, tc.wantStatusCode, rec.Code, rec.Body.String()) + if tc.validateFunc != nil { + tc.validateFunc(t, rec.Body) + } + t.Logf("%s", rec.Body.String()) + }) + } + }) +} diff --git a/clientapi/routing/capabilities.go b/clientapi/routing/capabilities.go index b7d47e916..e6c1a9b8c 100644 --- a/clientapi/routing/capabilities.go +++ b/clientapi/routing/capabilities.go @@ -17,26 +17,21 @@ package routing import ( "net/http" - "github.com/matrix-org/dendrite/clientapi/jsonerror" - roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" - + "github.com/matrix-org/dendrite/roomserver/version" + "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" ) // GetCapabilities returns information about the server's supported feature set // and other relevant capabilities to an authenticated user. -func GetCapabilities( - req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, -) util.JSONResponse { - roomVersionsQueryReq := roomserverAPI.QueryRoomVersionCapabilitiesRequest{} - roomVersionsQueryRes := roomserverAPI.QueryRoomVersionCapabilitiesResponse{} - if err := rsAPI.QueryRoomVersionCapabilities( - req.Context(), - &roomVersionsQueryReq, - &roomVersionsQueryRes, - ); err != nil { - util.GetLogger(req.Context()).WithError(err).Error("queryAPI.QueryRoomVersionCapabilities failed") - return jsonerror.InternalServerError() +func GetCapabilities() util.JSONResponse { + versionsMap := map[gomatrixserverlib.RoomVersion]string{} + for v, desc := range version.SupportedRoomVersions() { + if desc.Stable { + versionsMap[v] = "stable" + } else { + versionsMap[v] = "unstable" + } } response := map[string]interface{}{ @@ -44,7 +39,10 @@ func GetCapabilities( "m.change_password": map[string]bool{ "enabled": true, }, - "m.room_versions": roomVersionsQueryRes, + "m.room_versions": map[string]interface{}{ + "default": version.DefaultRoomVersion(), + "available": versionsMap, + }, }, } diff --git a/clientapi/routing/createroom.go b/clientapi/routing/createroom.go index a0d80903d..c2a432137 100644 --- a/clientapi/routing/createroom.go +++ b/clientapi/routing/createroom.go @@ -448,7 +448,7 @@ func createRoom( builder.PrevEvents = []gomatrixserverlib.EventReference{builtEvents[i-1].EventReference()} } var ev *gomatrixserverlib.Event - ev, err = buildEvent(&builder, userDomain, &authEvents, cfg, evTime, roomVersion) + ev, err = builder.AddAuthEventsAndBuild(userDomain, &authEvents, evTime, roomVersion, cfg.Matrix.KeyID, cfg.Matrix.PrivateKey) if err != nil { util.GetLogger(ctx).WithError(err).Error("buildEvent failed") return jsonerror.InternalServerError() @@ -599,31 +599,3 @@ func createRoom( JSON: response, } } - -// buildEvent fills out auth_events for the builder then builds the event -func buildEvent( - builder *gomatrixserverlib.EventBuilder, - serverName gomatrixserverlib.ServerName, - provider gomatrixserverlib.AuthEventProvider, - cfg *config.ClientAPI, - evTime time.Time, - roomVersion gomatrixserverlib.RoomVersion, -) (*gomatrixserverlib.Event, error) { - eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder) - if err != nil { - return nil, err - } - refs, err := eventsNeeded.AuthEventReferences(provider) - if err != nil { - return nil, err - } - builder.AuthEvents = refs - event, err := builder.Build( - evTime, serverName, cfg.Matrix.KeyID, - cfg.Matrix.PrivateKey, roomVersion, - ) - if err != nil { - return nil, fmt.Errorf("cannot build event %s : Builder failed to build. %w", builder.Type, err) - } - return event, nil -} diff --git a/clientapi/routing/deactivate.go b/clientapi/routing/deactivate.go index f213db7f3..3f4f539f6 100644 --- a/clientapi/routing/deactivate.go +++ b/clientapi/routing/deactivate.go @@ -33,7 +33,7 @@ func Deactivate( return *errRes } - localpart, _, err := gomatrixserverlib.SplitID('@', login.Username()) + localpart, serverName, err := gomatrixserverlib.SplitID('@', login.Username()) if err != nil { util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed") return jsonerror.InternalServerError() @@ -41,7 +41,8 @@ func Deactivate( var res api.PerformAccountDeactivationResponse err = accountAPI.PerformAccountDeactivation(ctx, &api.PerformAccountDeactivationRequest{ - Localpart: localpart, + Localpart: localpart, + ServerName: serverName, }, &res) if err != nil { util.GetLogger(ctx).WithError(err).Error("userAPI.PerformAccountDeactivation failed") diff --git a/clientapi/routing/device.go b/clientapi/routing/device.go index e3a02661c..331bacc3c 100644 --- a/clientapi/routing/device.go +++ b/clientapi/routing/device.go @@ -15,6 +15,7 @@ package routing import ( + "encoding/json" "io" "net" "net/http" @@ -146,12 +147,6 @@ func UpdateDeviceByID( JSON: jsonerror.Forbidden("device does not exist"), } } - if performRes.Forbidden { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: jsonerror.Forbidden("device not owned by current user"), - } - } return util.JSONResponse{ Code: http.StatusOK, @@ -189,7 +184,7 @@ func DeleteDeviceById( if dev != deviceID { return util.JSONResponse{ Code: http.StatusForbidden, - JSON: jsonerror.Forbidden("session & device mismatch"), + JSON: jsonerror.Forbidden("session and device mismatch"), } } } @@ -242,16 +237,37 @@ func DeleteDeviceById( // DeleteDevices handles POST requests to /delete_devices func DeleteDevices( - req *http.Request, userAPI api.ClientUserAPI, device *api.Device, + req *http.Request, userInteractiveAuth *auth.UserInteractive, userAPI api.ClientUserAPI, device *api.Device, ) util.JSONResponse { ctx := req.Context() - payload := devicesDeleteJSON{} - if resErr := httputil.UnmarshalJSONRequest(req, &payload); resErr != nil { - return *resErr + bodyBytes, err := io.ReadAll(req.Body) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()), + } + } + defer req.Body.Close() // nolint:errcheck + + // initiate UIA + login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes, device) + if errRes != nil { + return *errRes } - defer req.Body.Close() // nolint: errcheck + if login.Username() != device.UserID { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("unable to delete devices for other user"), + } + } + + payload := devicesDeleteJSON{} + if err = json.Unmarshal(bodyBytes, &payload); err != nil { + util.GetLogger(ctx).WithError(err).Error("unable to unmarshal device deletion request") + return jsonerror.InternalServerError() + } var res api.PerformDeviceDeletionResponse if err := userAPI.PerformDeviceDeletion(ctx, &api.PerformDeviceDeletionRequest{ diff --git a/clientapi/routing/directory.go b/clientapi/routing/directory.go index b3c5aae45..696f0c1ef 100644 --- a/clientapi/routing/directory.go +++ b/clientapi/routing/directory.go @@ -19,6 +19,7 @@ import ( "net/http" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/matrix-org/dendrite/clientapi/httputil" @@ -45,7 +46,7 @@ func (r *roomDirectoryResponse) fillServers(servers []gomatrixserverlib.ServerNa func DirectoryRoom( req *http.Request, roomAlias string, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, cfg *config.ClientAPI, rsAPI roomserverAPI.ClientRoomserverAPI, fedSenderAPI federationAPI.ClientFederationAPI, diff --git a/clientapi/routing/directory_public.go b/clientapi/routing/directory_public.go index 606744767..8e1e05a53 100644 --- a/clientapi/routing/directory_public.go +++ b/clientapi/routing/directory_public.go @@ -24,6 +24,7 @@ import ( "sync" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/matrix-org/dendrite/clientapi/api" @@ -35,7 +36,7 @@ import ( var ( cacheMu sync.Mutex - publicRoomsCache []gomatrixserverlib.PublicRoom + publicRoomsCache []fclient.PublicRoom ) type PublicRoomReq struct { @@ -56,7 +57,7 @@ type filter struct { func GetPostPublicRooms( req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, cfg *config.ClientAPI, ) util.JSONResponse { var request PublicRoomReq @@ -102,10 +103,10 @@ func GetPostPublicRooms( func publicRooms( ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider, -) (*gomatrixserverlib.RespPublicRooms, error) { +) (*fclient.RespPublicRooms, error) { - response := gomatrixserverlib.RespPublicRooms{ - Chunk: []gomatrixserverlib.PublicRoom{}, + response := fclient.RespPublicRooms{ + Chunk: []fclient.PublicRoom{}, } var limit int64 var offset int64 @@ -122,7 +123,7 @@ func publicRooms( } err = nil - var rooms []gomatrixserverlib.PublicRoom + var rooms []fclient.PublicRoom if request.Since == "" { rooms = refreshPublicRoomCache(ctx, rsAPI, extRoomsProvider, request) } else { @@ -146,14 +147,14 @@ func publicRooms( return &response, err } -func filterRooms(rooms []gomatrixserverlib.PublicRoom, searchTerm string) []gomatrixserverlib.PublicRoom { +func filterRooms(rooms []fclient.PublicRoom, searchTerm string) []fclient.PublicRoom { if searchTerm == "" { return rooms } normalizedTerm := strings.ToLower(searchTerm) - result := make([]gomatrixserverlib.PublicRoom, 0) + result := make([]fclient.PublicRoom, 0) for _, room := range rooms { if strings.Contains(strings.ToLower(room.Name), normalizedTerm) || strings.Contains(strings.ToLower(room.Topic), normalizedTerm) || @@ -214,7 +215,7 @@ func fillPublicRoomsReq(httpReq *http.Request, request *PublicRoomReq) *util.JSO // limit=3&since=6 => G (prev='3', next='') // // A value of '-1' for prev/next indicates no position. -func sliceInto(slice []gomatrixserverlib.PublicRoom, since int64, limit int64) (subset []gomatrixserverlib.PublicRoom, prev, next int) { +func sliceInto(slice []fclient.PublicRoom, since int64, limit int64) (subset []fclient.PublicRoom, prev, next int) { prev = -1 next = -1 @@ -241,10 +242,10 @@ func sliceInto(slice []gomatrixserverlib.PublicRoom, since int64, limit int64) ( func refreshPublicRoomCache( ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider, request PublicRoomReq, -) []gomatrixserverlib.PublicRoom { +) []fclient.PublicRoom { cacheMu.Lock() defer cacheMu.Unlock() - var extraRooms []gomatrixserverlib.PublicRoom + var extraRooms []fclient.PublicRoom if extRoomsProvider != nil { extraRooms = extRoomsProvider.Rooms() } @@ -269,7 +270,7 @@ func refreshPublicRoomCache( util.GetLogger(ctx).WithError(err).Error("PopulatePublicRooms failed") return publicRoomsCache } - publicRoomsCache = []gomatrixserverlib.PublicRoom{} + publicRoomsCache = []fclient.PublicRoom{} publicRoomsCache = append(publicRoomsCache, pubRooms...) publicRoomsCache = append(publicRoomsCache, extraRooms...) publicRoomsCache = dedupeAndShuffle(publicRoomsCache) @@ -281,16 +282,16 @@ func refreshPublicRoomCache( return publicRoomsCache } -func getPublicRoomsFromCache() []gomatrixserverlib.PublicRoom { +func getPublicRoomsFromCache() []fclient.PublicRoom { cacheMu.Lock() defer cacheMu.Unlock() return publicRoomsCache } -func dedupeAndShuffle(in []gomatrixserverlib.PublicRoom) []gomatrixserverlib.PublicRoom { +func dedupeAndShuffle(in []fclient.PublicRoom) []fclient.PublicRoom { // de-duplicate rooms with the same room ID. We can join the room via any of these aliases as we know these servers // are alive and well, so we arbitrarily pick one (purposefully shuffling them to spread the load a bit) - var publicRooms []gomatrixserverlib.PublicRoom + var publicRooms []fclient.PublicRoom haveRoomIDs := make(map[string]bool) rand.Shuffle(len(in), func(i, j int) { in[i], in[j] = in[j], in[i] diff --git a/clientapi/routing/directory_public_test.go b/clientapi/routing/directory_public_test.go index 65ad392c2..de2f01b19 100644 --- a/clientapi/routing/directory_public_test.go +++ b/clientapi/routing/directory_public_test.go @@ -4,17 +4,17 @@ import ( "reflect" "testing" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) -func pubRoom(name string) gomatrixserverlib.PublicRoom { - return gomatrixserverlib.PublicRoom{ +func pubRoom(name string) fclient.PublicRoom { + return fclient.PublicRoom{ Name: name, } } func TestSliceInto(t *testing.T) { - slice := []gomatrixserverlib.PublicRoom{ + slice := []fclient.PublicRoom{ pubRoom("a"), pubRoom("b"), pubRoom("c"), pubRoom("d"), pubRoom("e"), pubRoom("f"), pubRoom("g"), } limit := int64(3) @@ -22,7 +22,7 @@ func TestSliceInto(t *testing.T) { since int64 wantPrev int wantNext int - wantSubset []gomatrixserverlib.PublicRoom + wantSubset []fclient.PublicRoom }{ { since: 0, diff --git a/clientapi/routing/joinroom.go b/clientapi/routing/joinroom.go index e371d9214..3493dd6d8 100644 --- a/clientapi/routing/joinroom.go +++ b/clientapi/routing/joinroom.go @@ -18,6 +18,7 @@ import ( "net/http" "time" + appserviceAPI "github.com/matrix-org/dendrite/appservice/api" "github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/jsonerror" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" @@ -61,21 +62,19 @@ func JoinRoomByIDOrAlias( // Work out our localpart for the client profile request. // Request our profile content to populate the request content with. - res := &api.QueryProfileResponse{} - err := profileAPI.QueryProfile(req.Context(), &api.QueryProfileRequest{UserID: device.UserID}, res) - if err != nil || !res.UserExists { - if !res.UserExists { - util.GetLogger(req.Context()).Error("Unable to query user profile, no profile found.") - return util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: jsonerror.Unknown("Unable to query user profile, no profile found."), - } - } + profile, err := profileAPI.QueryProfile(req.Context(), device.UserID) - util.GetLogger(req.Context()).WithError(err).Error("UserProfileAPI.QueryProfile failed") - } else { - joinReq.Content["displayname"] = res.DisplayName - joinReq.Content["avatar_url"] = res.AvatarURL + switch err { + case nil: + joinReq.Content["displayname"] = profile.DisplayName + joinReq.Content["avatar_url"] = profile.AvatarURL + case appserviceAPI.ErrProfileNotExists: + util.GetLogger(req.Context()).Error("Unable to query user profile, no profile found.") + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: jsonerror.Unknown("Unable to query user profile, no profile found."), + } + default: } // Ask the roomserver to perform the join. diff --git a/clientapi/routing/login_test.go b/clientapi/routing/login_test.go index b27730767..bff676826 100644 --- a/clientapi/routing/login_test.go +++ b/clientapi/routing/login_test.go @@ -17,6 +17,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/matrix-org/dendrite/test" @@ -39,7 +40,7 @@ func TestLogin(t *testing.T) { natsInstance := jetstream.NATSInstance{} // add a vhost cfg.Global.VirtualHosts = append(cfg.Global.VirtualHosts, &config.VirtualHost{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ServerName: "vh1"}, + SigningIdentity: fclient.SigningIdentity{ServerName: "vh1"}, }) cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) diff --git a/clientapi/routing/membership.go b/clientapi/routing/membership.go index 482c1f5f7..1a96d4b1d 100644 --- a/clientapi/routing/membership.go +++ b/clientapi/routing/membership.go @@ -16,11 +16,12 @@ package routing import ( "context" - "errors" "fmt" "net/http" "time" + "github.com/matrix-org/gomatrixserverlib" + appserviceAPI "github.com/matrix-org/dendrite/appservice/api" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/httputil" @@ -31,76 +32,56 @@ import ( roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" userapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" ) -var errMissingUserID = errors.New("'user_id' must be supplied") - func SendBan( req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID string, cfg *config.ClientAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, ) util.JSONResponse { - body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) + body, evTime, reqErr := extractRequestData(req) if reqErr != nil { return *reqErr } + if body.UserID == "" { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON("missing user_id"), + } + } + errRes := checkMemberInRoom(req.Context(), rsAPI, device.UserID, roomID) if errRes != nil { return *errRes } - plEvent := roomserverAPI.GetStateEvent(req.Context(), rsAPI, roomID, gomatrixserverlib.StateKeyTuple{ - EventType: gomatrixserverlib.MRoomPowerLevels, - StateKey: "", - }) - if plEvent == nil { - return util.JSONResponse{ - Code: 403, - JSON: jsonerror.Forbidden("You don't have permission to ban this user, no power_levels event in this room."), - } - } - pl, err := plEvent.PowerLevels() - if err != nil { - return util.JSONResponse{ - Code: 403, - JSON: jsonerror.Forbidden("You don't have permission to ban this user, the power_levels event for this room is malformed so auth checks cannot be performed."), - } + pl, errRes := getPowerlevels(req, rsAPI, roomID) + if errRes != nil { + return *errRes } allowedToBan := pl.UserLevel(device.UserID) >= pl.Ban if !allowedToBan { return util.JSONResponse{ - Code: 403, + Code: http.StatusForbidden, JSON: jsonerror.Forbidden("You don't have permission to ban this user, power level too low."), } } - return sendMembership(req.Context(), profileAPI, device, roomID, "ban", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI) + return sendMembership(req.Context(), profileAPI, device, roomID, gomatrixserverlib.Ban, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI) } func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID, membership, reason string, cfg *config.ClientAPI, targetUserID string, evTime time.Time, - roomVer gomatrixserverlib.RoomVersion, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI) util.JSONResponse { event, err := buildMembershipEvent( ctx, targetUserID, reason, profileAPI, device, membership, roomID, false, cfg, evTime, rsAPI, asAPI, ) - if err == errMissingUserID { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: jsonerror.BadJSON(err.Error()), - } - } else if err == eventutil.ErrRoomNoExists { - return util.JSONResponse{ - Code: http.StatusNotFound, - JSON: jsonerror.NotFound(err.Error()), - } - } else if err != nil { + if err != nil { util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed") return jsonerror.InternalServerError() } @@ -109,7 +90,7 @@ func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, devic if err = roomserverAPI.SendEvents( ctx, rsAPI, roomserverAPI.KindNew, - []*gomatrixserverlib.HeaderedEvent{event.Event.Headered(roomVer)}, + []*gomatrixserverlib.HeaderedEvent{event}, device.UserDomain(), serverName, serverName, @@ -131,13 +112,65 @@ func SendKick( roomID string, cfg *config.ClientAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, ) util.JSONResponse { - body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) + body, evTime, reqErr := extractRequestData(req) if reqErr != nil { return *reqErr } if body.UserID == "" { return util.JSONResponse{ - Code: 400, + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON("missing user_id"), + } + } + + errRes := checkMemberInRoom(req.Context(), rsAPI, device.UserID, roomID) + if errRes != nil { + return *errRes + } + + pl, errRes := getPowerlevels(req, rsAPI, roomID) + if errRes != nil { + return *errRes + } + allowedToKick := pl.UserLevel(device.UserID) >= pl.Kick + if !allowedToKick { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("You don't have permission to kick this user, power level too low."), + } + } + + var queryRes roomserverAPI.QueryMembershipForUserResponse + err := rsAPI.QueryMembershipForUser(req.Context(), &roomserverAPI.QueryMembershipForUserRequest{ + RoomID: roomID, + UserID: body.UserID, + }, &queryRes) + if err != nil { + return util.ErrorResponse(err) + } + // kick is only valid if the user is not currently banned or left (that is, they are joined or invited) + if queryRes.Membership != gomatrixserverlib.Join && queryRes.Membership != gomatrixserverlib.Invite { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Unknown("cannot /kick banned or left users"), + } + } + // TODO: should we be using SendLeave instead? + return sendMembership(req.Context(), profileAPI, device, roomID, gomatrixserverlib.Leave, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI) +} + +func SendUnban( + req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, + roomID string, cfg *config.ClientAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, +) util.JSONResponse { + body, evTime, reqErr := extractRequestData(req) + if reqErr != nil { + return *reqErr + } + if body.UserID == "" { + return util.JSONResponse{ + Code: http.StatusBadRequest, JSON: jsonerror.BadJSON("missing user_id"), } } @@ -155,56 +188,16 @@ func SendKick( if err != nil { return util.ErrorResponse(err) } - // kick is only valid if the user is not currently banned or left (that is, they are joined or invited) - if queryRes.Membership != "join" && queryRes.Membership != "invite" { - return util.JSONResponse{ - Code: 403, - JSON: jsonerror.Unknown("cannot /kick banned or left users"), - } - } - // TODO: should we be using SendLeave instead? - return sendMembership(req.Context(), profileAPI, device, roomID, "leave", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI) -} -func SendUnban( - req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, - roomID string, cfg *config.ClientAPI, - rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, -) util.JSONResponse { - body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) - if reqErr != nil { - return *reqErr - } - if body.UserID == "" { - return util.JSONResponse{ - Code: 400, - JSON: jsonerror.BadJSON("missing user_id"), - } - } - - var queryRes roomserverAPI.QueryMembershipForUserResponse - err := rsAPI.QueryMembershipForUser(req.Context(), &roomserverAPI.QueryMembershipForUserRequest{ - RoomID: roomID, - UserID: body.UserID, - }, &queryRes) - if err != nil { - return util.ErrorResponse(err) - } - if !queryRes.RoomExists { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: jsonerror.Forbidden("room does not exist"), - } - } // unban is only valid if the user is currently banned - if queryRes.Membership != "ban" { + if queryRes.Membership != gomatrixserverlib.Ban { return util.JSONResponse{ - Code: 400, + Code: http.StatusBadRequest, JSON: jsonerror.Unknown("can only /unban users that are banned"), } } // TODO: should we be using SendLeave instead? - return sendMembership(req.Context(), profileAPI, device, roomID, "leave", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI) + return sendMembership(req.Context(), profileAPI, device, roomID, gomatrixserverlib.Leave, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI) } func SendInvite( @@ -212,7 +205,7 @@ func SendInvite( roomID string, cfg *config.ClientAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, ) util.JSONResponse { - body, evTime, _, reqErr := extractRequestData(req, roomID, rsAPI) + body, evTime, reqErr := extractRequestData(req) if reqErr != nil { return *reqErr } @@ -234,6 +227,18 @@ func SendInvite( } } + if body.UserID == "" { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON("missing user_id"), + } + } + + errRes := checkMemberInRoom(req.Context(), rsAPI, device.UserID, roomID) + if errRes != nil { + return *errRes + } + // We already received the return value, so no need to check for an error here. response, _ := sendInvite(req.Context(), profileAPI, device, roomID, body.UserID, body.Reason, cfg, rsAPI, asAPI, evTime) return response @@ -250,20 +255,10 @@ func sendInvite( asAPI appserviceAPI.AppServiceInternalAPI, evTime time.Time, ) (util.JSONResponse, error) { event, err := buildMembershipEvent( - ctx, userID, reason, profileAPI, device, "invite", + ctx, userID, reason, profileAPI, device, gomatrixserverlib.Invite, roomID, false, cfg, evTime, rsAPI, asAPI, ) - if err == errMissingUserID { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: jsonerror.BadJSON(err.Error()), - }, err - } else if err == eventutil.ErrRoomNoExists { - return util.JSONResponse{ - Code: http.StatusNotFound, - JSON: jsonerror.NotFound(err.Error()), - }, err - } else if err != nil { + if err != nil { util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed") return jsonerror.InternalServerError(), err } @@ -357,19 +352,7 @@ func loadProfile( return profile, err } -func extractRequestData(req *http.Request, roomID string, rsAPI roomserverAPI.ClientRoomserverAPI) ( - body *threepid.MembershipRequest, evTime time.Time, roomVer gomatrixserverlib.RoomVersion, resErr *util.JSONResponse, -) { - verReq := roomserverAPI.QueryRoomVersionForRoomRequest{RoomID: roomID} - verRes := roomserverAPI.QueryRoomVersionForRoomResponse{} - if err := rsAPI.QueryRoomVersionForRoom(req.Context(), &verReq, &verRes); err != nil { - resErr = &util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: jsonerror.UnsupportedRoomVersion(err.Error()), - } - return - } - roomVer = verRes.RoomVersion +func extractRequestData(req *http.Request) (body *threepid.MembershipRequest, evTime time.Time, resErr *util.JSONResponse) { if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { resErr = reqErr @@ -432,34 +415,17 @@ func checkAndProcessThreepid( } func checkMemberInRoom(ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, userID, roomID string) *util.JSONResponse { - tuple := gomatrixserverlib.StateKeyTuple{ - EventType: gomatrixserverlib.MRoomMember, - StateKey: userID, - } - var membershipRes roomserverAPI.QueryCurrentStateResponse - err := rsAPI.QueryCurrentState(ctx, &roomserverAPI.QueryCurrentStateRequest{ - RoomID: roomID, - StateTuples: []gomatrixserverlib.StateKeyTuple{tuple}, + var membershipRes roomserverAPI.QueryMembershipForUserResponse + err := rsAPI.QueryMembershipForUser(ctx, &roomserverAPI.QueryMembershipForUserRequest{ + RoomID: roomID, + UserID: userID, }, &membershipRes) if err != nil { - util.GetLogger(ctx).WithError(err).Error("QueryCurrentState: could not query membership for user") + util.GetLogger(ctx).WithError(err).Error("QueryMembershipForUser: could not query membership for user") e := jsonerror.InternalServerError() return &e } - ev := membershipRes.StateEvents[tuple] - if ev == nil { - return &util.JSONResponse{ - Code: http.StatusForbidden, - JSON: jsonerror.Forbidden("user does not belong to room"), - } - } - membership, err := ev.Membership() - if err != nil { - util.GetLogger(ctx).WithError(err).Error("Member event isn't valid") - e := jsonerror.InternalServerError() - return &e - } - if membership != gomatrixserverlib.Join { + if !membershipRes.IsInRoom { return &util.JSONResponse{ Code: http.StatusForbidden, JSON: jsonerror.Forbidden("user does not belong to room"), @@ -511,3 +477,24 @@ func SendForget( JSON: struct{}{}, } } + +func getPowerlevels(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, roomID string) (*gomatrixserverlib.PowerLevelContent, *util.JSONResponse) { + plEvent := roomserverAPI.GetStateEvent(req.Context(), rsAPI, roomID, gomatrixserverlib.StateKeyTuple{ + EventType: gomatrixserverlib.MRoomPowerLevels, + StateKey: "", + }) + if plEvent == nil { + return nil, &util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("You don't have permission to perform this action, no power_levels event in this room."), + } + } + pl, err := plEvent.PowerLevels() + if err != nil { + return nil, &util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("You don't have permission to perform this action, the power_levels event for this room is malformed so auth checks cannot be performed."), + } + } + return pl, nil +} diff --git a/clientapi/routing/profile.go b/clientapi/routing/profile.go index 92a75fc78..38b37a25e 100644 --- a/clientapi/routing/profile.go +++ b/clientapi/routing/profile.go @@ -20,6 +20,7 @@ import ( "time" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" appserviceAPI "github.com/matrix-org/dendrite/appservice/api" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" @@ -36,14 +37,14 @@ import ( // GetProfile implements GET /profile/{userID} func GetProfile( - req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, + req *http.Request, profileAPI userapi.ProfileAPI, cfg *config.ClientAPI, userID string, asAPI appserviceAPI.AppServiceInternalAPI, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, ) util.JSONResponse { profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation) if err != nil { - if err == eventutil.ErrProfileNoExists { + if err == appserviceAPI.ErrProfileNotExists { return util.JSONResponse{ Code: http.StatusNotFound, JSON: jsonerror.NotFound("The user does not exist or does not have a profile"), @@ -56,7 +57,7 @@ func GetProfile( return util.JSONResponse{ Code: http.StatusOK, - JSON: eventutil.ProfileResponse{ + JSON: eventutil.UserProfile{ AvatarURL: profile.AvatarURL, DisplayName: profile.DisplayName, }, @@ -65,34 +66,28 @@ func GetProfile( // GetAvatarURL implements GET /profile/{userID}/avatar_url func GetAvatarURL( - req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, + req *http.Request, profileAPI userapi.ProfileAPI, cfg *config.ClientAPI, userID string, asAPI appserviceAPI.AppServiceInternalAPI, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, ) util.JSONResponse { - profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation) - if err != nil { - if err == eventutil.ErrProfileNoExists { - return util.JSONResponse{ - Code: http.StatusNotFound, - JSON: jsonerror.NotFound("The user does not exist or does not have a profile"), - } - } - - util.GetLogger(req.Context()).WithError(err).Error("getProfile failed") - return jsonerror.InternalServerError() + profile := GetProfile(req, profileAPI, cfg, userID, asAPI, federation) + p, ok := profile.JSON.(eventutil.UserProfile) + // not a profile response, so most likely an error, return that + if !ok { + return profile } return util.JSONResponse{ Code: http.StatusOK, - JSON: eventutil.AvatarURL{ - AvatarURL: profile.AvatarURL, + JSON: eventutil.UserProfile{ + AvatarURL: p.AvatarURL, }, } } // SetAvatarURL implements PUT /profile/{userID}/avatar_url func SetAvatarURL( - req *http.Request, profileAPI userapi.ClientUserAPI, + req *http.Request, profileAPI userapi.ProfileAPI, device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI, ) util.JSONResponse { if userID != device.UserID { @@ -102,7 +97,7 @@ func SetAvatarURL( } } - var r eventutil.AvatarURL + var r eventutil.UserProfile if resErr := httputil.UnmarshalJSONRequest(req, &r); resErr != nil { return *resErr } @@ -134,24 +129,20 @@ func SetAvatarURL( } } - setRes := &userapi.PerformSetAvatarURLResponse{} - if err = profileAPI.SetAvatarURL(req.Context(), &userapi.PerformSetAvatarURLRequest{ - Localpart: localpart, - ServerName: domain, - AvatarURL: r.AvatarURL, - }, setRes); err != nil { + profile, changed, err := profileAPI.SetAvatarURL(req.Context(), localpart, domain, r.AvatarURL) + if err != nil { util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetAvatarURL failed") return jsonerror.InternalServerError() } // No need to build new membership events, since nothing changed - if !setRes.Changed { + if !changed { return util.JSONResponse{ Code: http.StatusOK, JSON: struct{}{}, } } - response, err := updateProfile(req.Context(), rsAPI, device, setRes.Profile, userID, cfg, evTime) + response, err := updateProfile(req.Context(), rsAPI, device, profile, userID, cfg, evTime) if err != nil { return response } @@ -164,34 +155,28 @@ func SetAvatarURL( // GetDisplayName implements GET /profile/{userID}/displayname func GetDisplayName( - req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, + req *http.Request, profileAPI userapi.ProfileAPI, cfg *config.ClientAPI, userID string, asAPI appserviceAPI.AppServiceInternalAPI, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, ) util.JSONResponse { - profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation) - if err != nil { - if err == eventutil.ErrProfileNoExists { - return util.JSONResponse{ - Code: http.StatusNotFound, - JSON: jsonerror.NotFound("The user does not exist or does not have a profile"), - } - } - - util.GetLogger(req.Context()).WithError(err).Error("getProfile failed") - return jsonerror.InternalServerError() + profile := GetProfile(req, profileAPI, cfg, userID, asAPI, federation) + p, ok := profile.JSON.(eventutil.UserProfile) + // not a profile response, so most likely an error, return that + if !ok { + return profile } return util.JSONResponse{ Code: http.StatusOK, - JSON: eventutil.DisplayName{ - DisplayName: profile.DisplayName, + JSON: eventutil.UserProfile{ + DisplayName: p.DisplayName, }, } } // SetDisplayName implements PUT /profile/{userID}/displayname func SetDisplayName( - req *http.Request, profileAPI userapi.ClientUserAPI, + req *http.Request, profileAPI userapi.ProfileAPI, device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI, ) util.JSONResponse { if userID != device.UserID { @@ -201,7 +186,7 @@ func SetDisplayName( } } - var r eventutil.DisplayName + var r eventutil.UserProfile if resErr := httputil.UnmarshalJSONRequest(req, &r); resErr != nil { return *resErr } @@ -233,25 +218,20 @@ func SetDisplayName( } } - profileRes := &userapi.PerformUpdateDisplayNameResponse{} - err = profileAPI.SetDisplayName(req.Context(), &userapi.PerformUpdateDisplayNameRequest{ - Localpart: localpart, - ServerName: domain, - DisplayName: r.DisplayName, - }, profileRes) + profile, changed, err := profileAPI.SetDisplayName(req.Context(), localpart, domain, r.DisplayName) if err != nil { util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetDisplayName failed") return jsonerror.InternalServerError() } // No need to build new membership events, since nothing changed - if !profileRes.Changed { + if !changed { return util.JSONResponse{ Code: http.StatusOK, JSON: struct{}{}, } } - response, err := updateProfile(req.Context(), rsAPI, device, profileRes.Profile, userID, cfg, evTime) + response, err := updateProfile(req.Context(), rsAPI, device, profile, userID, cfg, evTime) if err != nil { return response } @@ -308,12 +288,12 @@ func updateProfile( // getProfile gets the full profile of a user by querying the database or a // remote homeserver. // Returns an error when something goes wrong or specifically -// eventutil.ErrProfileNoExists when the profile doesn't exist. +// eventutil.ErrProfileNotExists when the profile doesn't exist. func getProfile( - ctx context.Context, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, + ctx context.Context, profileAPI userapi.ProfileAPI, cfg *config.ClientAPI, userID string, asAPI appserviceAPI.AppServiceInternalAPI, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, ) (*authtypes.Profile, error) { localpart, domain, err := gomatrixserverlib.SplitID('@', userID) if err != nil { @@ -325,7 +305,7 @@ func getProfile( if fedErr != nil { if x, ok := fedErr.(gomatrix.HTTPError); ok { if x.Code == http.StatusNotFound { - return nil, eventutil.ErrProfileNoExists + return nil, appserviceAPI.ErrProfileNotExists } } diff --git a/clientapi/routing/pushrules.go b/clientapi/routing/pushrules.go index 856f52c75..f1a539adf 100644 --- a/clientapi/routing/pushrules.go +++ b/clientapi/routing/pushrules.go @@ -31,7 +31,7 @@ func errorResponse(ctx context.Context, err error, msg string, args ...interface } func GetAllPushRules(ctx context.Context, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { - ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) + ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID) if err != nil { return errorResponse(ctx, err, "queryPushRulesJSON failed") } @@ -42,7 +42,7 @@ func GetAllPushRules(ctx context.Context, device *userapi.Device, userAPI userap } func GetPushRulesByScope(ctx context.Context, scope string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { - ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) + ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID) if err != nil { return errorResponse(ctx, err, "queryPushRulesJSON failed") } @@ -57,7 +57,7 @@ func GetPushRulesByScope(ctx context.Context, scope string, device *userapi.Devi } func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { - ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) + ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") } @@ -66,7 +66,8 @@ func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rule set"), "pushRuleSetByScope failed") } rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) - if rulesPtr == nil { + // Even if rulesPtr is not nil, there may not be any rules for this kind + if rulesPtr == nil || (rulesPtr != nil && len(*rulesPtr) == 0) { return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rules kind"), "pushRuleSetKindPointer failed") } return util.JSONResponse{ @@ -76,7 +77,7 @@ func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi } func GetPushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { - ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) + ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") } @@ -101,7 +102,10 @@ func GetPushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, beforeRuleID string, body io.Reader, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { var newRule pushrules.Rule if err := json.NewDecoder(body).Decode(&newRule); err != nil { - return errorResponse(ctx, err, "JSON Decode failed") + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON(err.Error()), + } } newRule.RuleID = ruleID @@ -110,7 +114,7 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, return errorResponse(ctx, jsonerror.InvalidArgumentValue(errs[0].Error()), "rule sanity check failed: %v", errs) } - ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) + ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") } @@ -120,6 +124,7 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, } rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) if rulesPtr == nil { + // while this should be impossible (ValidateRule would already return an error), better keep it around return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rules kind"), "pushRuleSetKindPointer failed") } i := pushRuleIndexByID(*rulesPtr, ruleID) @@ -144,7 +149,7 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, } // Add new rule. - i, err := findPushRuleInsertionIndex(*rulesPtr, afterRuleID, beforeRuleID) + i, err = findPushRuleInsertionIndex(*rulesPtr, afterRuleID, beforeRuleID) if err != nil { return errorResponse(ctx, err, "findPushRuleInsertionIndex failed") } @@ -153,7 +158,7 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, util.GetLogger(ctx).WithField("after", afterRuleID).WithField("before", beforeRuleID).Infof("Added new push rule at %d", i) } - if err := putPushRules(ctx, device.UserID, ruleSets, userAPI); err != nil { + if err = userAPI.PerformPushRulesPut(ctx, device.UserID, ruleSets); err != nil { return errorResponse(ctx, err, "putPushRules failed") } @@ -161,7 +166,7 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, } func DeletePushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { - ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) + ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") } @@ -180,7 +185,7 @@ func DeletePushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, dev *rulesPtr = append((*rulesPtr)[:i], (*rulesPtr)[i+1:]...) - if err := putPushRules(ctx, device.UserID, ruleSets, userAPI); err != nil { + if err = userAPI.PerformPushRulesPut(ctx, device.UserID, ruleSets); err != nil { return errorResponse(ctx, err, "putPushRules failed") } @@ -192,7 +197,7 @@ func GetPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri if err != nil { return errorResponse(ctx, err, "pushRuleAttrGetter failed") } - ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) + ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") } @@ -238,7 +243,7 @@ func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri return errorResponse(ctx, err, "pushRuleAttrSetter failed") } - ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) + ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") } @@ -258,7 +263,7 @@ func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri if !reflect.DeepEqual(attrGet((*rulesPtr)[i]), attrGet(&newPartialRule)) { attrSet((*rulesPtr)[i], &newPartialRule) - if err := putPushRules(ctx, device.UserID, ruleSets, userAPI); err != nil { + if err = userAPI.PerformPushRulesPut(ctx, device.UserID, ruleSets); err != nil { return errorResponse(ctx, err, "putPushRules failed") } } @@ -266,28 +271,6 @@ func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri return util.JSONResponse{Code: http.StatusOK, JSON: struct{}{}} } -func queryPushRules(ctx context.Context, userID string, userAPI userapi.ClientUserAPI) (*pushrules.AccountRuleSets, error) { - var res userapi.QueryPushRulesResponse - if err := userAPI.QueryPushRules(ctx, &userapi.QueryPushRulesRequest{UserID: userID}, &res); err != nil { - util.GetLogger(ctx).WithError(err).Error("userAPI.QueryPushRules failed") - return nil, err - } - return res.RuleSets, nil -} - -func putPushRules(ctx context.Context, userID string, ruleSets *pushrules.AccountRuleSets, userAPI userapi.ClientUserAPI) error { - req := userapi.PerformPushRulesPutRequest{ - UserID: userID, - RuleSets: ruleSets, - } - var res struct{} - if err := userAPI.PerformPushRulesPut(ctx, &req, &res); err != nil { - util.GetLogger(ctx).WithError(err).Error("userAPI.PerformPushRulesPut failed") - return err - } - return nil -} - func pushRuleSetByScope(ruleSets *pushrules.AccountRuleSets, scope pushrules.Scope) *pushrules.RuleSet { switch scope { case pushrules.GlobalScope: diff --git a/clientapi/routing/register.go b/clientapi/routing/register.go index ff6a0900e..d880961f9 100644 --- a/clientapi/routing/register.go +++ b/clientapi/routing/register.go @@ -888,13 +888,7 @@ func completeRegistration( } if displayName != "" { - nameReq := userapi.PerformUpdateDisplayNameRequest{ - Localpart: username, - ServerName: serverName, - DisplayName: displayName, - } - var nameRes userapi.PerformUpdateDisplayNameResponse - err = userAPI.SetDisplayName(ctx, &nameReq, &nameRes) + _, _, err = userAPI.SetDisplayName(ctx, username, serverName, displayName) if err != nil { return util.JSONResponse{ Code: http.StatusInternalServerError, diff --git a/clientapi/routing/register_test.go b/clientapi/routing/register_test.go index 46cd8b2b9..b07f636dd 100644 --- a/clientapi/routing/register_test.go +++ b/clientapi/routing/register_test.go @@ -611,11 +611,9 @@ func TestRegisterUserWithDisplayName(t *testing.T) { assert.Equal(t, http.StatusOK, response.Code) - req := api.QueryProfileRequest{UserID: "@user:server"} - var res api.QueryProfileResponse - err := userAPI.QueryProfile(processCtx.Context(), &req, &res) + profile, err := userAPI.QueryProfile(processCtx.Context(), "@user:server") assert.NoError(t, err) - assert.Equal(t, expectedDisplayName, res.DisplayName) + assert.Equal(t, expectedDisplayName, profile.DisplayName) }) } @@ -662,10 +660,8 @@ func TestRegisterAdminUsingSharedSecret(t *testing.T) { ) assert.Equal(t, http.StatusOK, response.Code) - profilReq := api.QueryProfileRequest{UserID: "@alice:server"} - var profileRes api.QueryProfileResponse - err = userAPI.QueryProfile(processCtx.Context(), &profilReq, &profileRes) + profile, err := userAPI.QueryProfile(processCtx.Context(), "@alice:server") assert.NoError(t, err) - assert.Equal(t, expectedDisplayName, profileRes.DisplayName) + assert.Equal(t, expectedDisplayName, profile.DisplayName) }) } diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index f20326370..27174dd98 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -18,11 +18,12 @@ import ( "context" "net/http" "strings" - "sync" "github.com/gorilla/mux" + "github.com/matrix-org/dendrite/setup/base" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/nats-io/nats.go" "github.com/prometheus/client_golang/prometheus" @@ -56,7 +57,7 @@ func Setup( asAPI appserviceAPI.AppServiceInternalAPI, userAPI userapi.ClientUserAPI, userDirectoryProvider userapi.QuerySearchProfilesAPI, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, syncProducer *producers.SyncAPIProducer, transactionsCache *transactions.Cache, federationSender federationAPI.ClientFederationAPI, @@ -201,18 +202,13 @@ func Setup( // server notifications if cfg.Matrix.ServerNotices.Enabled { logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice") - var serverNotificationSender *userapi.Device - var err error - notificationSenderOnce := &sync.Once{} + serverNotificationSender, err := getSenderDevice(context.Background(), rsAPI, userAPI, cfg) + if err != nil { + logrus.WithError(err).Fatal("unable to get account for sending sending server notices") + } synapseAdminRouter.Handle("/admin/v1/send_server_notice/{txnID}", httputil.MakeAuthAPI("send_server_notice", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - notificationSenderOnce.Do(func() { - serverNotificationSender, err = getSenderDevice(context.Background(), rsAPI, userAPI, cfg) - if err != nil { - logrus.WithError(err).Fatal("unable to get account for sending sending server notices") - } - }) // not specced, but ensure we're rate limiting requests to this endpoint if r := rateLimits.Limit(req, device); r != nil { return *r @@ -234,12 +230,6 @@ func Setup( synapseAdminRouter.Handle("/admin/v1/send_server_notice", httputil.MakeAuthAPI("send_server_notice", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - notificationSenderOnce.Do(func() { - serverNotificationSender, err = getSenderDevice(context.Background(), rsAPI, userAPI, cfg) - if err != nil { - logrus.WithError(err).Fatal("unable to get account for sending sending server notices") - } - }) // not specced, but ensure we're rate limiting requests to this endpoint if r := rateLimits.Limit(req, device); r != nil { return *r @@ -872,6 +862,8 @@ func Setup( // Browsers use the OPTIONS HTTP method to check if the CORS policy allows // PUT requests, so we need to allow this method + threePIDClient := base.CreateClient(dendriteCfg, nil) // TODO: Move this somewhere else, e.g. pass in as parameter + v3mux.Handle("/account/3pid", httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { return GetAssociated3PIDs(req, userAPI, device) @@ -880,11 +872,11 @@ func Setup( v3mux.Handle("/account/3pid", httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - return CheckAndSave3PIDAssociation(req, userAPI, device, cfg) + return CheckAndSave3PIDAssociation(req, userAPI, device, cfg, threePIDClient) }), ).Methods(http.MethodPost, http.MethodOptions) - unstableMux.Handle("/account/3pid/delete", + v3mux.Handle("/account/3pid/delete", httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { return Forget3PID(req, userAPI) }), @@ -892,7 +884,7 @@ func Setup( v3mux.Handle("/{path:(?:account/3pid|register)}/email/requestToken", httputil.MakeExternalAPI("account_3pid_request_token", func(req *http.Request) util.JSONResponse { - return RequestEmailToken(req, userAPI, cfg) + return RequestEmailToken(req, userAPI, cfg, threePIDClient) }), ).Methods(http.MethodPost, http.MethodOptions) @@ -1126,7 +1118,7 @@ func Setup( v3mux.Handle("/delete_devices", httputil.MakeAuthAPI("delete_devices", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - return DeleteDevices(req, userAPI, device) + return DeleteDevices(req, userInteractiveAuth, userAPI, device) }), ).Methods(http.MethodPost, http.MethodOptions) @@ -1205,7 +1197,7 @@ func Setup( if r := rateLimits.Limit(req, device); r != nil { return *r } - return GetCapabilities(req, rsAPI) + return GetCapabilities() }, httputil.WithAllowGuests()), ).Methods(http.MethodGet, http.MethodOptions) diff --git a/clientapi/routing/sendtyping.go b/clientapi/routing/sendtyping.go index 3f92e4227..9dc884d62 100644 --- a/clientapi/routing/sendtyping.go +++ b/clientapi/routing/sendtyping.go @@ -15,12 +15,13 @@ package routing import ( "net/http" + "github.com/matrix-org/util" + "github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/producers" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" userapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/util" ) type typingContentJSON struct { diff --git a/clientapi/routing/server_notices.go b/clientapi/routing/server_notices.go index fb93d8783..d6191f3b4 100644 --- a/clientapi/routing/server_notices.go +++ b/clientapi/routing/server_notices.go @@ -295,30 +295,28 @@ func getSenderDevice( } // Set the avatarurl for the user - avatarRes := &userapi.PerformSetAvatarURLResponse{} - if err = userAPI.SetAvatarURL(ctx, &userapi.PerformSetAvatarURLRequest{ - Localpart: cfg.Matrix.ServerNotices.LocalPart, - ServerName: cfg.Matrix.ServerName, - AvatarURL: cfg.Matrix.ServerNotices.AvatarURL, - }, avatarRes); err != nil { + profile, avatarChanged, err := userAPI.SetAvatarURL(ctx, + cfg.Matrix.ServerNotices.LocalPart, + cfg.Matrix.ServerName, + cfg.Matrix.ServerNotices.AvatarURL, + ) + if err != nil { util.GetLogger(ctx).WithError(err).Error("userAPI.SetAvatarURL failed") return nil, err } - profile := avatarRes.Profile - // Set the displayname for the user - displayNameRes := &userapi.PerformUpdateDisplayNameResponse{} - if err = userAPI.SetDisplayName(ctx, &userapi.PerformUpdateDisplayNameRequest{ - Localpart: cfg.Matrix.ServerNotices.LocalPart, - ServerName: cfg.Matrix.ServerName, - DisplayName: cfg.Matrix.ServerNotices.DisplayName, - }, displayNameRes); err != nil { + _, displayNameChanged, err := userAPI.SetDisplayName(ctx, + cfg.Matrix.ServerNotices.LocalPart, + cfg.Matrix.ServerName, + cfg.Matrix.ServerNotices.DisplayName, + ) + if err != nil { util.GetLogger(ctx).WithError(err).Error("userAPI.SetDisplayName failed") return nil, err } - if displayNameRes.Changed { + if displayNameChanged { profile.DisplayName = cfg.Matrix.ServerNotices.DisplayName } @@ -334,7 +332,7 @@ func getSenderDevice( // We've got an existing account, return the first device of it if len(deviceRes.Devices) > 0 { // If there were changes to the profile, create a new membership event - if displayNameRes.Changed || avatarRes.Changed { + if displayNameChanged || avatarChanged { _, err = updateProfile(ctx, rsAPI, &deviceRes.Devices[0], profile, accRes.Account.UserID, cfg, time.Now()) if err != nil { return nil, err diff --git a/clientapi/routing/state.go b/clientapi/routing/state.go index 12984c39a..59ed9a5f6 100644 --- a/clientapi/routing/state.go +++ b/clientapi/routing/state.go @@ -22,6 +22,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/syncapi/synctypes" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -29,7 +30,7 @@ import ( ) type stateEventInStateResp struct { - gomatrixserverlib.ClientEvent + synctypes.ClientEvent PrevContent json.RawMessage `json:"prev_content,omitempty"` ReplacesState string `json:"replaces_state,omitempty"` } @@ -122,7 +123,7 @@ func OnIncomingStateRequest(ctx context.Context, device *userapi.Device, rsAPI a "state_at_event": !wantLatestState, }).Info("Fetching all state") - stateEvents := []gomatrixserverlib.ClientEvent{} + stateEvents := []synctypes.ClientEvent{} if wantLatestState { // If we are happy to use the latest state, either because the user is // still in the room, or because the room is world-readable, then just @@ -131,7 +132,7 @@ func OnIncomingStateRequest(ctx context.Context, device *userapi.Device, rsAPI a for _, ev := range stateRes.StateEvents { stateEvents = append( stateEvents, - gomatrixserverlib.HeaderedToClientEvent(ev, gomatrixserverlib.FormatAll), + synctypes.HeaderedToClientEvent(ev, synctypes.FormatAll), ) } } else { @@ -150,7 +151,7 @@ func OnIncomingStateRequest(ctx context.Context, device *userapi.Device, rsAPI a for _, ev := range stateAfterRes.StateEvents { stateEvents = append( stateEvents, - gomatrixserverlib.HeaderedToClientEvent(ev, gomatrixserverlib.FormatAll), + synctypes.HeaderedToClientEvent(ev, synctypes.FormatAll), ) } } @@ -309,7 +310,7 @@ func OnIncomingStateTypeRequest( } stateEvent := stateEventInStateResp{ - ClientEvent: gomatrixserverlib.HeaderedToClientEvent(event, gomatrixserverlib.FormatAll), + ClientEvent: synctypes.HeaderedToClientEvent(event, synctypes.FormatAll), } var res interface{} diff --git a/clientapi/routing/threepid.go b/clientapi/routing/threepid.go index 971bfcad3..102b1d1cb 100644 --- a/clientapi/routing/threepid.go +++ b/clientapi/routing/threepid.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/userapi/api" userdb "github.com/matrix-org/dendrite/userapi/storage" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -33,7 +34,7 @@ type reqTokenResponse struct { SID string `json:"sid"` } -type threePIDsResponse struct { +type ThreePIDsResponse struct { ThreePIDs []authtypes.ThreePID `json:"threepids"` } @@ -41,7 +42,7 @@ type threePIDsResponse struct { // // POST /account/3pid/email/requestToken // POST /register/email/requestToken -func RequestEmailToken(req *http.Request, threePIDAPI api.ClientUserAPI, cfg *config.ClientAPI) util.JSONResponse { +func RequestEmailToken(req *http.Request, threePIDAPI api.ClientUserAPI, cfg *config.ClientAPI, client *fclient.Client) util.JSONResponse { var body threepid.EmailAssociationRequest if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { return *reqErr @@ -72,7 +73,7 @@ func RequestEmailToken(req *http.Request, threePIDAPI api.ClientUserAPI, cfg *co } } - resp.SID, err = threepid.CreateSession(req.Context(), body, cfg) + resp.SID, err = threepid.CreateSession(req.Context(), body, cfg, client) if err == threepid.ErrNotTrusted { return util.JSONResponse{ Code: http.StatusBadRequest, @@ -92,7 +93,7 @@ func RequestEmailToken(req *http.Request, threePIDAPI api.ClientUserAPI, cfg *co // CheckAndSave3PIDAssociation implements POST /account/3pid func CheckAndSave3PIDAssociation( req *http.Request, threePIDAPI api.ClientUserAPI, device *api.Device, - cfg *config.ClientAPI, + cfg *config.ClientAPI, client *fclient.Client, ) util.JSONResponse { var body threepid.EmailAssociationCheckRequest if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { @@ -100,7 +101,7 @@ func CheckAndSave3PIDAssociation( } // Check if the association has been validated - verified, address, medium, err := threepid.CheckAssociation(req.Context(), body.Creds, cfg) + verified, address, medium, err := threepid.CheckAssociation(req.Context(), body.Creds, cfg, client) if err == threepid.ErrNotTrusted { return util.JSONResponse{ Code: http.StatusBadRequest, @@ -123,13 +124,8 @@ func CheckAndSave3PIDAssociation( if body.Bind { // Publish the association on the identity server if requested - err = threepid.PublishAssociation(body.Creds, device.UserID, cfg) - if err == threepid.ErrNotTrusted { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: jsonerror.NotTrusted(body.Creds.IDServer), - } - } else if err != nil { + err = threepid.PublishAssociation(req.Context(), body.Creds, device.UserID, cfg, client) + if err != nil { util.GetLogger(req.Context()).WithError(err).Error("threepid.PublishAssociation failed") return jsonerror.InternalServerError() } @@ -180,7 +176,7 @@ func GetAssociated3PIDs( return util.JSONResponse{ Code: http.StatusOK, - JSON: threePIDsResponse{res.ThreePIDs}, + JSON: ThreePIDsResponse{res.ThreePIDs}, } } @@ -191,7 +187,10 @@ func Forget3PID(req *http.Request, threepidAPI api.ClientUserAPI) util.JSONRespo return *reqErr } - if err := threepidAPI.PerformForgetThreePID(req.Context(), &api.PerformForgetThreePIDRequest{}, &struct{}{}); err != nil { + if err := threepidAPI.PerformForgetThreePID(req.Context(), &api.PerformForgetThreePIDRequest{ + ThreePID: body.Address, + Medium: body.Medium, + }, &struct{}{}); err != nil { util.GetLogger(req.Context()).WithError(err).Error("threepidAPI.PerformForgetThreePID failed") return jsonerror.InternalServerError() } diff --git a/clientapi/routing/userdirectory.go b/clientapi/routing/userdirectory.go index 62af9efa4..a4cf8e9c2 100644 --- a/clientapi/routing/userdirectory.go +++ b/clientapi/routing/userdirectory.go @@ -26,6 +26,7 @@ import ( userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -41,7 +42,7 @@ func SearchUserDirectory( provider userapi.QuerySearchProfilesAPI, searchString string, limit int, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, localServerName gomatrixserverlib.ServerName, ) util.JSONResponse { if limit < 10 { diff --git a/clientapi/threepid/invites.go b/clientapi/threepid/invites.go index 1f294a032..a9910b782 100644 --- a/clientapi/threepid/invites.go +++ b/clientapi/threepid/invites.go @@ -209,24 +209,17 @@ func queryIDServerStoreInvite( body *MembershipRequest, roomID string, ) (*idServerStoreInviteResponse, error) { // Retrieve the sender's profile to get their display name - localpart, serverName, err := gomatrixserverlib.SplitID('@', device.UserID) + _, serverName, err := gomatrixserverlib.SplitID('@', device.UserID) if err != nil { return nil, err } var profile *authtypes.Profile if cfg.Matrix.IsLocalServerName(serverName) { - res := &userapi.QueryProfileResponse{} - err = userAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: device.UserID}, res) + profile, err = userAPI.QueryProfile(ctx, device.UserID) if err != nil { return nil, err } - profile = &authtypes.Profile{ - Localpart: localpart, - DisplayName: res.DisplayName, - AvatarURL: res.AvatarURL, - } - } else { profile = &authtypes.Profile{} } diff --git a/clientapi/threepid/threepid.go b/clientapi/threepid/threepid.go index 1e64e3034..1fe573b1b 100644 --- a/clientapi/threepid/threepid.go +++ b/clientapi/threepid/threepid.go @@ -25,6 +25,7 @@ import ( "strings" "github.com/matrix-org/dendrite/setup/config" + "github.com/matrix-org/gomatrixserverlib/fclient" ) // EmailAssociationRequest represents the request defined at https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register-email-requesttoken @@ -37,7 +38,7 @@ type EmailAssociationRequest struct { // EmailAssociationCheckRequest represents the request defined at https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-account-3pid type EmailAssociationCheckRequest struct { - Creds Credentials `json:"threePidCreds"` + Creds Credentials `json:"three_pid_creds"` Bind bool `json:"bind"` } @@ -48,12 +49,16 @@ type Credentials struct { Secret string `json:"client_secret"` } +type SID struct { + SID string `json:"sid"` +} + // CreateSession creates a session on an identity server. // Returns the session's ID. // Returns an error if there was a problem sending the request or decoding the // response, or if the identity server responded with a non-OK status. func CreateSession( - ctx context.Context, req EmailAssociationRequest, cfg *config.ClientAPI, + ctx context.Context, req EmailAssociationRequest, cfg *config.ClientAPI, client *fclient.Client, ) (string, error) { if err := isTrusted(req.IDServer, cfg); err != nil { return "", err @@ -73,8 +78,7 @@ func CreateSession( } request.Header.Add("Content-Type", "application/x-www-form-urlencoded") - client := http.Client{} - resp, err := client.Do(request.WithContext(ctx)) + resp, err := client.DoHTTPRequest(ctx, request) if err != nil { return "", err } @@ -85,14 +89,20 @@ func CreateSession( } // Extract the SID from the response and return it - var sid struct { - SID string `json:"sid"` - } + var sid SID err = json.NewDecoder(resp.Body).Decode(&sid) return sid.SID, err } +type GetValidatedResponse struct { + Medium string `json:"medium"` + ValidatedAt int64 `json:"validated_at"` + Address string `json:"address"` + ErrCode string `json:"errcode"` + Error string `json:"error"` +} + // CheckAssociation checks the status of an ongoing association validation on an // identity server. // Returns a boolean set to true if the association has been validated, false if not. @@ -102,6 +112,7 @@ func CreateSession( // response, or if the identity server responded with a non-OK status. func CheckAssociation( ctx context.Context, creds Credentials, cfg *config.ClientAPI, + client *fclient.Client, ) (bool, string, string, error) { if err := isTrusted(creds.IDServer, cfg); err != nil { return false, "", "", err @@ -112,19 +123,12 @@ func CheckAssociation( if err != nil { return false, "", "", err } - resp, err := http.DefaultClient.Do(req.WithContext(ctx)) + resp, err := client.DoHTTPRequest(ctx, req) if err != nil { return false, "", "", err } - var respBody struct { - Medium string `json:"medium"` - ValidatedAt int64 `json:"validated_at"` - Address string `json:"address"` - ErrCode string `json:"errcode"` - Error string `json:"error"` - } - + var respBody GetValidatedResponse if err = json.NewDecoder(resp.Body).Decode(&respBody); err != nil { return false, "", "", err } @@ -142,7 +146,7 @@ func CheckAssociation( // identifier and a Matrix ID. // Returns an error if there was a problem sending the request or decoding the // response, or if the identity server responded with a non-OK status. -func PublishAssociation(creds Credentials, userID string, cfg *config.ClientAPI) error { +func PublishAssociation(ctx context.Context, creds Credentials, userID string, cfg *config.ClientAPI, client *fclient.Client) error { if err := isTrusted(creds.IDServer, cfg); err != nil { return err } @@ -160,8 +164,7 @@ func PublishAssociation(creds Credentials, userID string, cfg *config.ClientAPI) } request.Header.Add("Content-Type", "application/x-www-form-urlencoded") - client := http.Client{} - resp, err := client.Do(request) + resp, err := client.DoHTTPRequest(ctx, request) if err != nil { return err } diff --git a/clientapi/userutil/userutil_test.go b/clientapi/userutil/userutil_test.go index ee6bf8a01..8910983bc 100644 --- a/clientapi/userutil/userutil_test.go +++ b/clientapi/userutil/userutil_test.go @@ -17,6 +17,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) var ( @@ -30,7 +31,7 @@ var ( // TestGoodUserID checks that correct localpart is returned for a valid user ID. func TestGoodUserID(t *testing.T) { cfg := &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: serverName, }, } @@ -49,7 +50,7 @@ func TestGoodUserID(t *testing.T) { // TestWithLocalpartOnly checks that localpart is returned when usernameParam contains only localpart. func TestWithLocalpartOnly(t *testing.T) { cfg := &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: serverName, }, } @@ -68,7 +69,7 @@ func TestWithLocalpartOnly(t *testing.T) { // TestIncorrectDomain checks for error when there's server name mismatch. func TestIncorrectDomain(t *testing.T) { cfg := &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: invalidServerName, }, } @@ -83,7 +84,7 @@ func TestIncorrectDomain(t *testing.T) { // TestBadUserID checks that ParseUsernameParam fails for invalid user ID func TestBadUserID(t *testing.T) { cfg := &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: serverName, }, } diff --git a/cmd/dendrite-demo-pinecone/conn/client.go b/cmd/dendrite-demo-pinecone/conn/client.go index 885de0057..4571de157 100644 --- a/cmd/dendrite-demo-pinecone/conn/client.go +++ b/cmd/dendrite-demo-pinecone/conn/client.go @@ -22,7 +22,7 @@ import ( "strings" "github.com/matrix-org/dendrite/setup/config" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "nhooyr.io/websocket" pineconeRouter "github.com/matrix-org/pinecone/router" @@ -91,17 +91,17 @@ func createTransport(s *pineconeSessions.Sessions) *http.Transport { func CreateClient( s *pineconeSessions.Sessions, -) *gomatrixserverlib.Client { - return gomatrixserverlib.NewClient( - gomatrixserverlib.WithTransport(createTransport(s)), +) *fclient.Client { + return fclient.NewClient( + fclient.WithTransport(createTransport(s)), ) } func CreateFederationClient( cfg *config.Dendrite, s *pineconeSessions.Sessions, -) *gomatrixserverlib.FederationClient { - return gomatrixserverlib.NewFederationClient( +) *fclient.FederationClient { + return fclient.NewFederationClient( cfg.Global.SigningIdentities(), - gomatrixserverlib.WithTransport(createTransport(s)), + fclient.WithTransport(createTransport(s)), ) } diff --git a/cmd/dendrite-demo-pinecone/rooms/rooms.go b/cmd/dendrite-demo-pinecone/rooms/rooms.go index 0ac705cc1..808956c97 100644 --- a/cmd/dendrite-demo-pinecone/rooms/rooms.go +++ b/cmd/dendrite-demo-pinecone/rooms/rooms.go @@ -22,6 +22,7 @@ import ( "github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/defaults" "github.com/matrix-org/dendrite/federationapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" pineconeRouter "github.com/matrix-org/pinecone/router" @@ -32,14 +33,14 @@ type PineconeRoomProvider struct { r *pineconeRouter.Router s *pineconeSessions.Sessions fedSender api.FederationInternalAPI - fedClient *gomatrixserverlib.FederationClient + fedClient *fclient.FederationClient } func NewPineconeRoomProvider( r *pineconeRouter.Router, s *pineconeSessions.Sessions, fedSender api.FederationInternalAPI, - fedClient *gomatrixserverlib.FederationClient, + fedClient *fclient.FederationClient, ) *PineconeRoomProvider { p := &PineconeRoomProvider{ r: r, @@ -50,7 +51,7 @@ func NewPineconeRoomProvider( return p } -func (p *PineconeRoomProvider) Rooms() []gomatrixserverlib.PublicRoom { +func (p *PineconeRoomProvider) Rooms() []fclient.PublicRoom { list := map[gomatrixserverlib.ServerName]struct{}{} for k := range defaults.DefaultServerNames { list[k] = struct{}{} @@ -67,14 +68,14 @@ func (p *PineconeRoomProvider) Rooms() []gomatrixserverlib.PublicRoom { // bulkFetchPublicRoomsFromServers fetches public rooms from the list of homeservers. // Returns a list of public rooms. func bulkFetchPublicRoomsFromServers( - ctx context.Context, fedClient *gomatrixserverlib.FederationClient, + ctx context.Context, fedClient *fclient.FederationClient, origin gomatrixserverlib.ServerName, homeservers map[gomatrixserverlib.ServerName]struct{}, -) (publicRooms []gomatrixserverlib.PublicRoom) { +) (publicRooms []fclient.PublicRoom) { limit := 200 // follow pipeline semantics, see https://blog.golang.org/pipelines for more info. // goroutines send rooms to this channel - roomCh := make(chan gomatrixserverlib.PublicRoom, int(limit)) + roomCh := make(chan fclient.PublicRoom, int(limit)) // signalling channel to tell goroutines to stop sending rooms and quit done := make(chan bool) // signalling to say when we can close the room channel diff --git a/cmd/dendrite-demo-pinecone/users/users.go b/cmd/dendrite-demo-pinecone/users/users.go index fc66bf299..5bd056e18 100644 --- a/cmd/dendrite-demo-pinecone/users/users.go +++ b/cmd/dendrite-demo-pinecone/users/users.go @@ -28,6 +28,7 @@ import ( "github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/defaults" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" pineconeRouter "github.com/matrix-org/pinecone/router" @@ -38,7 +39,7 @@ type PineconeUserProvider struct { r *pineconeRouter.Router s *pineconeSessions.Sessions userAPI userapi.QuerySearchProfilesAPI - fedClient *gomatrixserverlib.FederationClient + fedClient *fclient.FederationClient } const PublicURL = "/_matrix/p2p/profiles" @@ -47,7 +48,7 @@ func NewPineconeUserProvider( r *pineconeRouter.Router, s *pineconeSessions.Sessions, userAPI userapi.QuerySearchProfilesAPI, - fedClient *gomatrixserverlib.FederationClient, + fedClient *fclient.FederationClient, ) *PineconeUserProvider { p := &PineconeUserProvider{ r: r, @@ -94,7 +95,7 @@ func (p *PineconeUserProvider) QuerySearchProfiles(ctx context.Context, req *use // Returns a list of user profiles. func bulkFetchUserDirectoriesFromServers( ctx context.Context, req *userapi.QuerySearchProfilesRequest, - fedClient *gomatrixserverlib.FederationClient, + fedClient *fclient.FederationClient, homeservers map[gomatrixserverlib.ServerName]struct{}, ) (profiles []authtypes.Profile) { jsonBody, err := json.Marshal(req) diff --git a/cmd/dendrite-demo-yggdrasil/yggconn/client.go b/cmd/dendrite-demo-yggdrasil/yggconn/client.go index 51365547b..c25acf2ec 100644 --- a/cmd/dendrite-demo-yggdrasil/yggconn/client.go +++ b/cmd/dendrite-demo-yggdrasil/yggconn/client.go @@ -5,7 +5,7 @@ import ( "time" "github.com/matrix-org/dendrite/setup/config" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) type yggroundtripper struct { @@ -17,7 +17,7 @@ func (y *yggroundtripper) RoundTrip(req *http.Request) (*http.Response, error) { return y.inner.RoundTrip(req) } -func (n *Node) CreateClient() *gomatrixserverlib.Client { +func (n *Node) CreateClient() *fclient.Client { tr := &http.Transport{} tr.RegisterProtocol( "matrix", &yggroundtripper{ @@ -31,14 +31,14 @@ func (n *Node) CreateClient() *gomatrixserverlib.Client { }, }, ) - return gomatrixserverlib.NewClient( - gomatrixserverlib.WithTransport(tr), + return fclient.NewClient( + fclient.WithTransport(tr), ) } func (n *Node) CreateFederationClient( cfg *config.Dendrite, -) *gomatrixserverlib.FederationClient { +) *fclient.FederationClient { tr := &http.Transport{} tr.RegisterProtocol( "matrix", &yggroundtripper{ @@ -52,8 +52,8 @@ func (n *Node) CreateFederationClient( }, }, ) - return gomatrixserverlib.NewFederationClient( + return fclient.NewFederationClient( cfg.Global.SigningIdentities(), - gomatrixserverlib.WithTransport(tr), + fclient.WithTransport(tr), ) } diff --git a/cmd/dendrite-demo-yggdrasil/yggrooms/yggrooms.go b/cmd/dendrite-demo-yggdrasil/yggrooms/yggrooms.go index 0de64755e..180990d54 100644 --- a/cmd/dendrite-demo-yggdrasil/yggrooms/yggrooms.go +++ b/cmd/dendrite-demo-yggdrasil/yggrooms/yggrooms.go @@ -22,17 +22,18 @@ import ( "github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn" "github.com/matrix-org/dendrite/federationapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) type YggdrasilRoomProvider struct { node *yggconn.Node fedSender api.FederationInternalAPI - fedClient *gomatrixserverlib.FederationClient + fedClient *fclient.FederationClient } func NewYggdrasilRoomProvider( - node *yggconn.Node, fedSender api.FederationInternalAPI, fedClient *gomatrixserverlib.FederationClient, + node *yggconn.Node, fedSender api.FederationInternalAPI, fedClient *fclient.FederationClient, ) *YggdrasilRoomProvider { p := &YggdrasilRoomProvider{ node: node, @@ -42,7 +43,7 @@ func NewYggdrasilRoomProvider( return p } -func (p *YggdrasilRoomProvider) Rooms() []gomatrixserverlib.PublicRoom { +func (p *YggdrasilRoomProvider) Rooms() []fclient.PublicRoom { return bulkFetchPublicRoomsFromServers( context.Background(), p.fedClient, gomatrixserverlib.ServerName(p.node.DerivedServerName()), @@ -53,14 +54,14 @@ func (p *YggdrasilRoomProvider) Rooms() []gomatrixserverlib.PublicRoom { // bulkFetchPublicRoomsFromServers fetches public rooms from the list of homeservers. // Returns a list of public rooms. func bulkFetchPublicRoomsFromServers( - ctx context.Context, fedClient *gomatrixserverlib.FederationClient, + ctx context.Context, fedClient *fclient.FederationClient, origin gomatrixserverlib.ServerName, homeservers []gomatrixserverlib.ServerName, -) (publicRooms []gomatrixserverlib.PublicRoom) { +) (publicRooms []fclient.PublicRoom) { limit := 200 // follow pipeline semantics, see https://blog.golang.org/pipelines for more info. // goroutines send rooms to this channel - roomCh := make(chan gomatrixserverlib.PublicRoom, int(limit)) + roomCh := make(chan fclient.PublicRoom, int(limit)) // signalling channel to tell goroutines to stop sending rooms and quit done := make(chan bool) // signalling to say when we can close the room channel diff --git a/cmd/dendrite/main.go b/cmd/dendrite/main.go index a22677ebe..66eb88f87 100644 --- a/cmd/dendrite/main.go +++ b/cmd/dendrite/main.go @@ -25,7 +25,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/process" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/sirupsen/logrus" "github.com/matrix-org/dendrite/appservice" @@ -96,9 +96,9 @@ func main() { } // create DNS cache - var dnsCache *gomatrixserverlib.DNSCache + var dnsCache *fclient.DNSCache if cfg.Global.DNSCache.Enabled { - dnsCache = gomatrixserverlib.NewDNSCache( + dnsCache = fclient.NewDNSCache( cfg.Global.DNSCache.CacheSize, cfg.Global.DNSCache.CacheLifetime, ) diff --git a/cmd/furl/main.go b/cmd/furl/main.go index b208ba868..32e997049 100644 --- a/cmd/furl/main.go +++ b/cmd/furl/main.go @@ -13,6 +13,7 @@ import ( "os" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) var requestFrom = flag.String("from", "", "the server name that the request should originate from") @@ -49,8 +50,8 @@ func main() { } serverName := gomatrixserverlib.ServerName(*requestFrom) - client := gomatrixserverlib.NewFederationClient( - []*gomatrixserverlib.SigningIdentity{ + client := fclient.NewFederationClient( + []*fclient.SigningIdentity{ { ServerName: serverName, KeyID: gomatrixserverlib.KeyID(keyBlock.Headers["Key-ID"]), diff --git a/docs/FAQ.md b/docs/FAQ.md index 2899aa982..200020726 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -18,7 +18,7 @@ Mostly, although there are still bugs and missing features. If you are a confide ## Is Dendrite feature-complete? -No, although a good portion of the Matrix specification has been implemented. Mostly missing are client features - see the [readme](../README.md) at the root of the repository for more information. +No, although a good portion of the Matrix specification has been implemented. Mostly missing are client features - see the [readme](https://github.com/matrix-org/dendrite/blob/main/README.md) at the root of the repository for more information. ## Why doesn't Dendrite have "x" yet? diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index a61786c1d..0901965c1 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -14,7 +14,7 @@ GEM execjs coffee-script-source (1.11.1) colorator (1.1.0) - commonmarker (0.23.7) + commonmarker (0.23.9) concurrent-ruby (1.2.0) dnsruby (1.61.9) simpleidn (~> 0.1) @@ -231,9 +231,9 @@ GEM jekyll-seo-tag (~> 2.1) minitest (5.17.0) multipart-post (2.1.1) - nokogiri (1.13.10-arm64-darwin) + nokogiri (1.14.3-arm64-darwin) racc (~> 1.4) - nokogiri (1.13.10-x86_64-linux) + nokogiri (1.14.3-x86_64-linux) racc (~> 1.4) octokit (4.22.0) faraday (>= 0.9) @@ -241,7 +241,7 @@ GEM pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (4.0.7) - racc (1.6.1) + racc (1.6.2) rb-fsevent (0.11.1) rb-inotify (0.10.1) ffi (~> 1.0) diff --git a/federationapi/api/api.go b/federationapi/api/api.go index e4c0b2714..e23bec271 100644 --- a/federationapi/api/api.go +++ b/federationapi/api/api.go @@ -7,6 +7,7 @@ import ( "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/dendrite/federationapi/types" ) @@ -22,8 +23,8 @@ type FederationInternalAPI interface { QueryServerKeys(ctx context.Context, request *QueryServerKeysRequest, response *QueryServerKeysResponse) error LookupServerKeys(ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp) ([]gomatrixserverlib.ServerKeys, error) - MSC2836EventRelationships(ctx context.Context, origin, dst gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error) - MSC2946Spaces(ctx context.Context, origin, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool) (res gomatrixserverlib.MSC2946SpacesResponse, err error) + MSC2836EventRelationships(ctx context.Context, origin, dst gomatrixserverlib.ServerName, r fclient.MSC2836EventRelationshipsRequest, roomVersion gomatrixserverlib.RoomVersion) (res fclient.MSC2836EventRelationshipsResponse, err error) + MSC2946Spaces(ctx context.Context, origin, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool) (res fclient.MSC2946SpacesResponse, err error) // Broadcasts an EDU to all servers in rooms we are joined to. Used in the yggdrasil demos. PerformBroadcastEDU( @@ -66,9 +67,9 @@ type RoomserverFederationAPI interface { // containing only the server names (without information for membership events). // The response will include this server if they are joined to the room. QueryJoinedHostServerNamesInRoom(ctx context.Context, request *QueryJoinedHostServerNamesInRoomRequest, response *QueryJoinedHostServerNamesInRoomResponse) error - GetEventAuth(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string) (res gomatrixserverlib.RespEventAuth, err error) + GetEventAuth(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string) (res fclient.RespEventAuth, err error) GetEvent(ctx context.Context, origin, s gomatrixserverlib.ServerName, eventID string) (res gomatrixserverlib.Transaction, err error) - LookupMissingEvents(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.RespMissingEvents, err error) + LookupMissingEvents(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, missing fclient.MissingEvents, roomVersion gomatrixserverlib.RoomVersion) (res fclient.RespMissingEvents, err error) } type P2PFederationAPI interface { @@ -98,45 +99,45 @@ type P2PFederationAPI interface { // implements as proxy calls, with built-in backoff/retries/etc. Errors returned from functions in // this interface are of type FederationClientError type KeyserverFederationAPI interface { - GetUserDevices(ctx context.Context, origin, s gomatrixserverlib.ServerName, userID string) (res gomatrixserverlib.RespUserDevices, err error) - ClaimKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string) (res gomatrixserverlib.RespClaimKeys, err error) - QueryKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, keys map[string][]string) (res gomatrixserverlib.RespQueryKeys, err error) + GetUserDevices(ctx context.Context, origin, s gomatrixserverlib.ServerName, userID string) (res fclient.RespUserDevices, err error) + ClaimKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string) (res fclient.RespClaimKeys, err error) + QueryKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, keys map[string][]string) (res fclient.RespQueryKeys, err error) } // an interface for gmsl.FederationClient - contains functions called by federationapi only. type FederationClient interface { P2PFederationClient gomatrixserverlib.KeyClient - SendTransaction(ctx context.Context, t gomatrixserverlib.Transaction) (res gomatrixserverlib.RespSend, err error) + SendTransaction(ctx context.Context, t gomatrixserverlib.Transaction) (res fclient.RespSend, err error) // Perform operations - LookupRoomAlias(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomAlias string) (res gomatrixserverlib.RespDirectory, err error) - Peek(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, peekID string, roomVersions []gomatrixserverlib.RoomVersion) (res gomatrixserverlib.RespPeek, err error) - MakeJoin(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, userID string, roomVersions []gomatrixserverlib.RoomVersion) (res gomatrixserverlib.RespMakeJoin, err error) - SendJoin(ctx context.Context, origin, s gomatrixserverlib.ServerName, event *gomatrixserverlib.Event) (res gomatrixserverlib.RespSendJoin, err error) - MakeLeave(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, userID string) (res gomatrixserverlib.RespMakeLeave, err error) + LookupRoomAlias(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomAlias string) (res fclient.RespDirectory, err error) + Peek(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, peekID string, roomVersions []gomatrixserverlib.RoomVersion) (res fclient.RespPeek, err error) + MakeJoin(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, userID string, roomVersions []gomatrixserverlib.RoomVersion) (res fclient.RespMakeJoin, err error) + SendJoin(ctx context.Context, origin, s gomatrixserverlib.ServerName, event *gomatrixserverlib.Event) (res fclient.RespSendJoin, err error) + MakeLeave(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, userID string) (res fclient.RespMakeLeave, err error) SendLeave(ctx context.Context, origin, s gomatrixserverlib.ServerName, event *gomatrixserverlib.Event) (err error) - SendInviteV2(ctx context.Context, origin, s gomatrixserverlib.ServerName, request gomatrixserverlib.InviteV2Request) (res gomatrixserverlib.RespInviteV2, err error) + SendInviteV2(ctx context.Context, origin, s gomatrixserverlib.ServerName, request gomatrixserverlib.InviteV2Request) (res fclient.RespInviteV2, err error) GetEvent(ctx context.Context, origin, s gomatrixserverlib.ServerName, eventID string) (res gomatrixserverlib.Transaction, err error) - GetEventAuth(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string) (res gomatrixserverlib.RespEventAuth, err error) - GetUserDevices(ctx context.Context, origin, s gomatrixserverlib.ServerName, userID string) (gomatrixserverlib.RespUserDevices, error) - ClaimKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string) (gomatrixserverlib.RespClaimKeys, error) - QueryKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, keys map[string][]string) (gomatrixserverlib.RespQueryKeys, error) + GetEventAuth(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string) (res fclient.RespEventAuth, err error) + GetUserDevices(ctx context.Context, origin, s gomatrixserverlib.ServerName, userID string) (fclient.RespUserDevices, error) + ClaimKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string) (fclient.RespClaimKeys, error) + QueryKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, keys map[string][]string) (fclient.RespQueryKeys, error) Backfill(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, limit int, eventIDs []string) (res gomatrixserverlib.Transaction, err error) - MSC2836EventRelationships(ctx context.Context, origin, dst gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error) - MSC2946Spaces(ctx context.Context, origin, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool) (res gomatrixserverlib.MSC2946SpacesResponse, err error) + MSC2836EventRelationships(ctx context.Context, origin, dst gomatrixserverlib.ServerName, r fclient.MSC2836EventRelationshipsRequest, roomVersion gomatrixserverlib.RoomVersion) (res fclient.MSC2836EventRelationshipsResponse, err error) + MSC2946Spaces(ctx context.Context, origin, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool) (res fclient.MSC2946SpacesResponse, err error) ExchangeThirdPartyInvite(ctx context.Context, origin, s gomatrixserverlib.ServerName, builder gomatrixserverlib.EventBuilder) (err error) - LookupState(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, eventID string, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.RespState, err error) - LookupStateIDs(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, eventID string) (res gomatrixserverlib.RespStateIDs, err error) - LookupMissingEvents(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.RespMissingEvents, err error) + LookupState(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, eventID string, roomVersion gomatrixserverlib.RoomVersion) (res fclient.RespState, err error) + LookupStateIDs(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, eventID string) (res fclient.RespStateIDs, err error) + LookupMissingEvents(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, missing fclient.MissingEvents, roomVersion gomatrixserverlib.RoomVersion) (res fclient.RespMissingEvents, err error) } type P2PFederationClient interface { - P2PSendTransactionToRelay(ctx context.Context, u gomatrixserverlib.UserID, t gomatrixserverlib.Transaction, forwardingServer gomatrixserverlib.ServerName) (res gomatrixserverlib.EmptyResp, err error) - P2PGetTransactionFromRelay(ctx context.Context, u gomatrixserverlib.UserID, prev gomatrixserverlib.RelayEntry, relayServer gomatrixserverlib.ServerName) (res gomatrixserverlib.RespGetRelayTransaction, err error) + P2PSendTransactionToRelay(ctx context.Context, u gomatrixserverlib.UserID, t gomatrixserverlib.Transaction, forwardingServer gomatrixserverlib.ServerName) (res fclient.EmptyResp, err error) + P2PGetTransactionFromRelay(ctx context.Context, u gomatrixserverlib.UserID, prev fclient.RelayEntry, relayServer gomatrixserverlib.ServerName) (res fclient.RespGetRelayTransaction, err error) } // FederationClientError is returned from FederationClient methods in the event of a problem. diff --git a/federationapi/federationapi.go b/federationapi/federationapi.go index 3b5394a13..c64fa550d 100644 --- a/federationapi/federationapi.go +++ b/federationapi/federationapi.go @@ -21,6 +21,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/process" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/sirupsen/logrus" "github.com/matrix-org/dendrite/federationapi/api" @@ -48,7 +49,7 @@ func AddPublicRoutes( dendriteConfig *config.Dendrite, natsInstance *jetstream.NATSInstance, userAPI userapi.FederationUserAPI, - federation *gomatrixserverlib.FederationClient, + federation *fclient.FederationClient, keyRing gomatrixserverlib.JSONVerifier, rsAPI roomserverAPI.FederationRoomserverAPI, fedAPI federationAPI.FederationInternalAPI, diff --git a/federationapi/federationapi_keys_test.go b/federationapi/federationapi_keys_test.go index 425cdbb97..2fa748bad 100644 --- a/federationapi/federationapi_keys_test.go +++ b/federationapi/federationapi_keys_test.go @@ -16,6 +16,7 @@ import ( "github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/process" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/dendrite/federationapi/api" "github.com/matrix-org/dendrite/federationapi/routing" @@ -24,12 +25,12 @@ import ( ) type server struct { - name gomatrixserverlib.ServerName // server name - validity time.Duration // key validity duration from now - config *config.FederationAPI // skeleton config, from TestMain - fedclient *gomatrixserverlib.FederationClient // uses MockRoundTripper - cache *caching.Caches // server-specific cache - api api.FederationInternalAPI // server-specific server key API + name gomatrixserverlib.ServerName // server name + validity time.Duration // key validity duration from now + config *config.FederationAPI // skeleton config, from TestMain + fedclient *fclient.FederationClient // uses MockRoundTripper + cache *caching.Caches // server-specific cache + api api.FederationInternalAPI // server-specific server key API } func (s *server) renew() { @@ -105,9 +106,9 @@ func TestMain(m *testing.M) { transport.RegisterProtocol("matrix", &MockRoundTripper{}) // Create the federation client. - s.fedclient = gomatrixserverlib.NewFederationClient( + s.fedclient = fclient.NewFederationClient( s.config.Matrix.SigningIdentities(), - gomatrixserverlib.WithTransport(transport), + fclient.WithTransport(transport), ) // Finally, build the server key APIs. diff --git a/federationapi/federationapi_test.go b/federationapi/federationapi_test.go index d39f40512..3c01a8259 100644 --- a/federationapi/federationapi_test.go +++ b/federationapi/federationapi_test.go @@ -15,6 +15,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/nats-io/nats.go" "github.com/matrix-org/dendrite/federationapi" @@ -104,7 +105,7 @@ func (f *fedClient) GetServerKeys(ctx context.Context, matrixServer gomatrixserv return keys, nil } -func (f *fedClient) MakeJoin(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, userID string, roomVersions []gomatrixserverlib.RoomVersion) (res gomatrixserverlib.RespMakeJoin, err error) { +func (f *fedClient) MakeJoin(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, userID string, roomVersions []gomatrixserverlib.RoomVersion) (res fclient.RespMakeJoin, err error) { for _, r := range f.allowJoins { if r.ID == roomID { res.RoomVersion = r.Version @@ -128,7 +129,7 @@ func (f *fedClient) MakeJoin(ctx context.Context, origin, s gomatrixserverlib.Se } return } -func (f *fedClient) SendJoin(ctx context.Context, origin, s gomatrixserverlib.ServerName, event *gomatrixserverlib.Event) (res gomatrixserverlib.RespSendJoin, err error) { +func (f *fedClient) SendJoin(ctx context.Context, origin, s gomatrixserverlib.ServerName, event *gomatrixserverlib.Event) (res fclient.RespSendJoin, err error) { f.fedClientMutex.Lock() defer f.fedClientMutex.Unlock() for _, r := range f.allowJoins { @@ -142,7 +143,7 @@ func (f *fedClient) SendJoin(ctx context.Context, origin, s gomatrixserverlib.Se return } -func (f *fedClient) SendTransaction(ctx context.Context, t gomatrixserverlib.Transaction) (res gomatrixserverlib.RespSend, err error) { +func (f *fedClient) SendTransaction(ctx context.Context, t gomatrixserverlib.Transaction) (res fclient.RespSend, err error) { f.fedClientMutex.Lock() defer f.fedClientMutex.Unlock() for _, edu := range t.EDUs { @@ -313,9 +314,9 @@ func TestRoomsV3URLEscapeDoNot404(t *testing.T) { defer cancel() serverName := gomatrixserverlib.ServerName(strings.TrimPrefix(baseURL, "https://")) - fedCli := gomatrixserverlib.NewFederationClient( + fedCli := fclient.NewFederationClient( cfg.Global.SigningIdentities(), - gomatrixserverlib.WithSkipVerify(true), + fclient.WithSkipVerify(true), ) for _, tc := range testCases { diff --git a/federationapi/internal/federationclient.go b/federationapi/internal/federationclient.go index db6348ec1..b0d5b1d1f 100644 --- a/federationapi/internal/federationclient.go +++ b/federationapi/internal/federationclient.go @@ -5,6 +5,7 @@ import ( "time" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) // Functions here are "proxying" calls to the gomatrixserverlib federation @@ -13,56 +14,56 @@ import ( func (a *FederationInternalAPI) GetEventAuth( ctx context.Context, origin, s gomatrixserverlib.ServerName, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string, -) (res gomatrixserverlib.RespEventAuth, err error) { +) (res fclient.RespEventAuth, err error) { ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) { return a.federation.GetEventAuth(ctx, origin, s, roomVersion, roomID, eventID) }) if err != nil { - return gomatrixserverlib.RespEventAuth{}, err + return fclient.RespEventAuth{}, err } - return ires.(gomatrixserverlib.RespEventAuth), nil + return ires.(fclient.RespEventAuth), nil } func (a *FederationInternalAPI) GetUserDevices( ctx context.Context, origin, s gomatrixserverlib.ServerName, userID string, -) (gomatrixserverlib.RespUserDevices, error) { +) (fclient.RespUserDevices, error) { ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) { return a.federation.GetUserDevices(ctx, origin, s, userID) }) if err != nil { - return gomatrixserverlib.RespUserDevices{}, err + return fclient.RespUserDevices{}, err } - return ires.(gomatrixserverlib.RespUserDevices), nil + return ires.(fclient.RespUserDevices), nil } func (a *FederationInternalAPI) ClaimKeys( ctx context.Context, origin, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string, -) (gomatrixserverlib.RespClaimKeys, error) { +) (fclient.RespClaimKeys, error) { ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) { return a.federation.ClaimKeys(ctx, origin, s, oneTimeKeys) }) if err != nil { - return gomatrixserverlib.RespClaimKeys{}, err + return fclient.RespClaimKeys{}, err } - return ires.(gomatrixserverlib.RespClaimKeys), nil + return ires.(fclient.RespClaimKeys), nil } func (a *FederationInternalAPI) QueryKeys( ctx context.Context, origin, s gomatrixserverlib.ServerName, keys map[string][]string, -) (gomatrixserverlib.RespQueryKeys, error) { +) (fclient.RespQueryKeys, error) { ires, err := a.doRequestIfNotBackingOffOrBlacklisted(s, func() (interface{}, error) { return a.federation.QueryKeys(ctx, origin, s, keys) }) if err != nil { - return gomatrixserverlib.RespQueryKeys{}, err + return fclient.RespQueryKeys{}, err } - return ires.(gomatrixserverlib.RespQueryKeys), nil + return ires.(fclient.RespQueryKeys), nil } func (a *FederationInternalAPI) Backfill( @@ -81,45 +82,46 @@ func (a *FederationInternalAPI) Backfill( func (a *FederationInternalAPI) LookupState( ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion, -) (res gomatrixserverlib.RespState, err error) { +) (res gomatrixserverlib.StateResponse, err error) { ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) { return a.federation.LookupState(ctx, origin, s, roomID, eventID, roomVersion) }) if err != nil { - return gomatrixserverlib.RespState{}, err + return &fclient.RespState{}, err } - return ires.(gomatrixserverlib.RespState), nil + r := ires.(fclient.RespState) + return &r, nil } func (a *FederationInternalAPI) LookupStateIDs( ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID, eventID string, -) (res gomatrixserverlib.RespStateIDs, err error) { +) (res gomatrixserverlib.StateIDResponse, err error) { ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) { return a.federation.LookupStateIDs(ctx, origin, s, roomID, eventID) }) if err != nil { - return gomatrixserverlib.RespStateIDs{}, err + return fclient.RespStateIDs{}, err } - return ires.(gomatrixserverlib.RespStateIDs), nil + return ires.(fclient.RespStateIDs), nil } func (a *FederationInternalAPI) LookupMissingEvents( ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, - missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion, -) (res gomatrixserverlib.RespMissingEvents, err error) { + missing fclient.MissingEvents, roomVersion gomatrixserverlib.RoomVersion, +) (res fclient.RespMissingEvents, err error) { ctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) { return a.federation.LookupMissingEvents(ctx, origin, s, roomID, missing, roomVersion) }) if err != nil { - return gomatrixserverlib.RespMissingEvents{}, err + return fclient.RespMissingEvents{}, err } - return ires.(gomatrixserverlib.RespMissingEvents), nil + return ires.(fclient.RespMissingEvents), nil } func (a *FederationInternalAPI) GetEvent( @@ -151,9 +153,9 @@ func (a *FederationInternalAPI) LookupServerKeys( } func (a *FederationInternalAPI) MSC2836EventRelationships( - ctx context.Context, origin, s gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest, + ctx context.Context, origin, s gomatrixserverlib.ServerName, r fclient.MSC2836EventRelationshipsRequest, roomVersion gomatrixserverlib.RoomVersion, -) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error) { +) (res fclient.MSC2836EventRelationshipsResponse, err error) { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) { @@ -162,12 +164,12 @@ func (a *FederationInternalAPI) MSC2836EventRelationships( if err != nil { return res, err } - return ires.(gomatrixserverlib.MSC2836EventRelationshipsResponse), nil + return ires.(fclient.MSC2836EventRelationshipsResponse), nil } func (a *FederationInternalAPI) MSC2946Spaces( ctx context.Context, origin, s gomatrixserverlib.ServerName, roomID string, suggestedOnly bool, -) (res gomatrixserverlib.MSC2946SpacesResponse, err error) { +) (res fclient.MSC2946SpacesResponse, err error) { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) { @@ -176,5 +178,5 @@ func (a *FederationInternalAPI) MSC2946Spaces( if err != nil { return res, err } - return ires.(gomatrixserverlib.MSC2946SpacesResponse), nil + return ires.(fclient.MSC2946SpacesResponse), nil } diff --git a/federationapi/internal/federationclient_test.go b/federationapi/internal/federationclient_test.go index 49137e2d8..948a96eec 100644 --- a/federationapi/internal/federationclient_test.go +++ b/federationapi/internal/federationclient_test.go @@ -25,6 +25,7 @@ import ( "github.com/matrix-org/dendrite/setup/process" "github.com/matrix-org/dendrite/test" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/stretchr/testify/assert" ) @@ -33,20 +34,20 @@ const ( FailuresUntilBlacklist = 8 ) -func (t *testFedClient) QueryKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, keys map[string][]string) (gomatrixserverlib.RespQueryKeys, error) { +func (t *testFedClient) QueryKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, keys map[string][]string) (fclient.RespQueryKeys, error) { t.queryKeysCalled = true if t.shouldFail { - return gomatrixserverlib.RespQueryKeys{}, fmt.Errorf("Failure") + return fclient.RespQueryKeys{}, fmt.Errorf("Failure") } - return gomatrixserverlib.RespQueryKeys{}, nil + return fclient.RespQueryKeys{}, nil } -func (t *testFedClient) ClaimKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string) (gomatrixserverlib.RespClaimKeys, error) { +func (t *testFedClient) ClaimKeys(ctx context.Context, origin, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string) (fclient.RespClaimKeys, error) { t.claimKeysCalled = true if t.shouldFail { - return gomatrixserverlib.RespClaimKeys{}, fmt.Errorf("Failure") + return fclient.RespClaimKeys{}, fmt.Errorf("Failure") } - return gomatrixserverlib.RespClaimKeys{}, nil + return fclient.RespClaimKeys{}, nil } func TestFederationClientQueryKeys(t *testing.T) { @@ -54,7 +55,7 @@ func TestFederationClientQueryKeys(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "server", }, }, @@ -85,7 +86,7 @@ func TestFederationClientQueryKeysBlacklisted(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "server", }, }, @@ -115,7 +116,7 @@ func TestFederationClientQueryKeysFailure(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "server", }, }, @@ -145,7 +146,7 @@ func TestFederationClientClaimKeys(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "server", }, }, @@ -176,7 +177,7 @@ func TestFederationClientClaimKeysBlacklisted(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "server", }, }, diff --git a/federationapi/internal/perform.go b/federationapi/internal/perform.go index dadb2b2b3..c580e5275 100644 --- a/federationapi/internal/perform.go +++ b/federationapi/internal/perform.go @@ -9,6 +9,7 @@ import ( "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/sirupsen/logrus" @@ -255,10 +256,10 @@ func (r *FederationInternalAPI) performJoinUsingServer( // waste the effort. // TODO: Can we expand Check here to return a list of missing auth // events rather than failing one at a time? - var respState *gomatrixserverlib.RespState - respState, err = respSendJoin.Check( + var respState gomatrixserverlib.StateResponse + respState, err = gomatrixserverlib.CheckSendJoinResponse( context.Background(), - respMakeJoin.RoomVersion, + respMakeJoin.RoomVersion, &respSendJoin, r.keyRing, event, federatedAuthProvider(ctx, r.federation, r.keyRing, origin, serverName), @@ -273,7 +274,7 @@ func (r *FederationInternalAPI) performJoinUsingServer( // joining a room, waiting for 200 OK then changing device keys and have those keys not be sent // to other servers (this was a cause of a flakey sytest "Local device key changes get to remote servers") // The events are trusted now as we performed auth checks above. - joinedHosts, err := consumers.JoinedHostsFromEvents(respState.StateEvents.TrustedEvents(respMakeJoin.RoomVersion, false)) + joinedHosts, err := consumers.JoinedHostsFromEvents(respState.GetStateEvents().TrustedEvents(respMakeJoin.RoomVersion, false)) if err != nil { return fmt.Errorf("JoinedHostsFromEvents: failed to get joined hosts: %s", err) } @@ -468,11 +469,12 @@ func (r *FederationInternalAPI) performOutboundPeekUsingServer( // we have the peek state now so let's process regardless of whether upstream gives up ctx = context.Background() - respState := respPeek.ToRespState() // authenticate the state returned (check its auth events etc) // the equivalent of CheckSendJoinResponse() - authEvents, _, err := respState.Check(ctx, respPeek.RoomVersion, r.keyRing, federatedAuthProvider(ctx, r.federation, r.keyRing, r.cfg.Matrix.ServerName, serverName)) + authEvents, stateEvents, err := gomatrixserverlib.CheckStateResponse( + ctx, &respPeek, respPeek.RoomVersion, r.keyRing, federatedAuthProvider(ctx, r.federation, r.keyRing, r.cfg.Matrix.ServerName, serverName), + ) if err != nil { return fmt.Errorf("error checking state returned from peeking: %w", err) } @@ -496,7 +498,11 @@ func (r *FederationInternalAPI) performOutboundPeekUsingServer( if err = roomserverAPI.SendEventWithState( ctx, r.rsAPI, r.cfg.Matrix.ServerName, roomserverAPI.KindNew, - &respState, + // use the authorized state from CheckStateResponse + &fclient.RespState{ + StateEvents: gomatrixserverlib.NewEventJSONsFromEvents(stateEvents), + AuthEvents: gomatrixserverlib.NewEventJSONsFromEvents(authEvents), + }, respPeek.LatestEvent.Headered(respPeek.RoomVersion), serverName, nil, diff --git a/federationapi/internal/perform_test.go b/federationapi/internal/perform_test.go index e6e366f99..90849dcf6 100644 --- a/federationapi/internal/perform_test.go +++ b/federationapi/internal/perform_test.go @@ -25,6 +25,7 @@ import ( "github.com/matrix-org/dendrite/setup/process" "github.com/matrix-org/dendrite/test" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/stretchr/testify/assert" ) @@ -35,8 +36,8 @@ type testFedClient struct { shouldFail bool } -func (t *testFedClient) LookupRoomAlias(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomAlias string) (res gomatrixserverlib.RespDirectory, err error) { - return gomatrixserverlib.RespDirectory{}, nil +func (t *testFedClient) LookupRoomAlias(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomAlias string) (res fclient.RespDirectory, err error) { + return fclient.RespDirectory{}, nil } func TestPerformWakeupServers(t *testing.T) { @@ -54,7 +55,7 @@ func TestPerformWakeupServers(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "relay", }, }, @@ -96,7 +97,7 @@ func TestQueryRelayServers(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "relay", }, }, @@ -133,7 +134,7 @@ func TestRemoveRelayServers(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "relay", }, }, @@ -169,7 +170,7 @@ func TestPerformDirectoryLookup(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "relay", }, }, @@ -204,7 +205,7 @@ func TestPerformDirectoryLookupRelaying(t *testing.T) { cfg := config.FederationAPI{ Matrix: &config.Global{ - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: server, }, }, diff --git a/federationapi/queue/destinationqueue.go b/federationapi/queue/destinationqueue.go index 12e6db9fa..a4542c498 100644 --- a/federationapi/queue/destinationqueue.go +++ b/federationapi/queue/destinationqueue.go @@ -23,6 +23,7 @@ import ( "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/sirupsen/logrus" "go.uber.org/atomic" @@ -50,7 +51,7 @@ type destinationQueue struct { queues *OutgoingQueues db storage.Database process *process.ProcessContext - signing map[gomatrixserverlib.ServerName]*gomatrixserverlib.SigningIdentity + signing map[gomatrixserverlib.ServerName]*fclient.SigningIdentity rsAPI api.FederationRoomserverAPI client fedapi.FederationClient // federation client origin gomatrixserverlib.ServerName // origin of requests diff --git a/federationapi/queue/queue.go b/federationapi/queue/queue.go index 5d6b8d44c..c0ecb2875 100644 --- a/federationapi/queue/queue.go +++ b/federationapi/queue/queue.go @@ -22,6 +22,7 @@ import ( "github.com/getsentry/sentry-go" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus" @@ -45,7 +46,7 @@ type OutgoingQueues struct { origin gomatrixserverlib.ServerName client fedapi.FederationClient statistics *statistics.Statistics - signing map[gomatrixserverlib.ServerName]*gomatrixserverlib.SigningIdentity + signing map[gomatrixserverlib.ServerName]*fclient.SigningIdentity queuesMutex sync.Mutex // protects the below queues map[gomatrixserverlib.ServerName]*destinationQueue } @@ -90,7 +91,7 @@ func NewOutgoingQueues( client fedapi.FederationClient, rsAPI api.FederationRoomserverAPI, statistics *statistics.Statistics, - signing []*gomatrixserverlib.SigningIdentity, + signing []*fclient.SigningIdentity, ) *OutgoingQueues { queues := &OutgoingQueues{ disabled: disabled, @@ -100,7 +101,7 @@ func NewOutgoingQueues( origin: origin, client: client, statistics: statistics, - signing: map[gomatrixserverlib.ServerName]*gomatrixserverlib.SigningIdentity{}, + signing: map[gomatrixserverlib.ServerName]*fclient.SigningIdentity{}, queues: map[gomatrixserverlib.ServerName]*destinationQueue{}, } for _, identity := range signing { diff --git a/federationapi/queue/queue_test.go b/federationapi/queue/queue_test.go index 55d1df4a2..65a925d34 100644 --- a/federationapi/queue/queue_test.go +++ b/federationapi/queue/queue_test.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/dendrite/internal/caching" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/test/testrig" + "github.com/matrix-org/gomatrixserverlib/fclient" "go.uber.org/atomic" "gotest.tools/v3/poll" @@ -80,24 +81,24 @@ type stubFederationClient struct { txRelayCount atomic.Uint32 } -func (f *stubFederationClient) SendTransaction(ctx context.Context, t gomatrixserverlib.Transaction) (res gomatrixserverlib.RespSend, err error) { +func (f *stubFederationClient) SendTransaction(ctx context.Context, t gomatrixserverlib.Transaction) (res fclient.RespSend, err error) { var result error if !f.shouldTxSucceed { result = fmt.Errorf("transaction failed") } f.txCount.Add(1) - return gomatrixserverlib.RespSend{}, result + return fclient.RespSend{}, result } -func (f *stubFederationClient) P2PSendTransactionToRelay(ctx context.Context, u gomatrixserverlib.UserID, t gomatrixserverlib.Transaction, forwardingServer gomatrixserverlib.ServerName) (res gomatrixserverlib.EmptyResp, err error) { +func (f *stubFederationClient) P2PSendTransactionToRelay(ctx context.Context, u gomatrixserverlib.UserID, t gomatrixserverlib.Transaction, forwardingServer gomatrixserverlib.ServerName) (res fclient.EmptyResp, err error) { var result error if !f.shouldTxRelaySucceed { result = fmt.Errorf("relay transaction failed") } f.txRelayCount.Add(1) - return gomatrixserverlib.EmptyResp{}, result + return fclient.EmptyResp{}, result } func mustCreatePDU(t *testing.T) *gomatrixserverlib.HeaderedEvent { @@ -127,7 +128,7 @@ func testSetup(failuresUntilBlacklist uint32, failuresUntilAssumedOffline uint32 rs := &stubFederationRoomServerAPI{} stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline) - signingInfo := []*gomatrixserverlib.SigningIdentity{ + signingInfo := []*fclient.SigningIdentity{ { KeyID: "ed21019:auto", PrivateKey: test.PrivateKeyA, diff --git a/federationapi/routing/devices.go b/federationapi/routing/devices.go index 871d26cd4..aae1299fe 100644 --- a/federationapi/routing/devices.go +++ b/federationapi/routing/devices.go @@ -19,6 +19,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/tidwall/gjson" ) @@ -53,10 +54,10 @@ func GetUserDevices( return jsonerror.InternalAPIError(req.Context(), err) } - response := gomatrixserverlib.RespUserDevices{ + response := fclient.RespUserDevices{ UserID: userID, StreamID: res.StreamID, - Devices: []gomatrixserverlib.RespUserDevice{}, + Devices: []fclient.RespUserDevice{}, } if masterKey, ok := sigRes.MasterKeys[userID]; ok { @@ -67,7 +68,7 @@ func GetUserDevices( } for _, dev := range res.Devices { - var key gomatrixserverlib.RespUserDeviceKeys + var key fclient.RespUserDeviceKeys err := json.Unmarshal(dev.DeviceKeys.KeyJSON, &key) if err != nil { util.GetLogger(req.Context()).WithError(err).Warnf("malformed device key: %s", string(dev.DeviceKeys.KeyJSON)) @@ -79,7 +80,7 @@ func GetUserDevices( displayName = gjson.GetBytes(dev.DeviceKeys.KeyJSON, "unsigned.device_display_name").Str } - device := gomatrixserverlib.RespUserDevice{ + device := fclient.RespUserDevice{ DeviceID: dev.DeviceID, DisplayName: displayName, Keys: key, diff --git a/federationapi/routing/eventauth.go b/federationapi/routing/eventauth.go index 2f1f3baf6..65a2a9bc8 100644 --- a/federationapi/routing/eventauth.go +++ b/federationapi/routing/eventauth.go @@ -19,6 +19,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -70,7 +71,7 @@ func GetEventAuth( return util.JSONResponse{ Code: http.StatusOK, - JSON: gomatrixserverlib.RespEventAuth{ + JSON: fclient.RespEventAuth{ AuthEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(response.AuthChainEvents), }, } diff --git a/federationapi/routing/invite.go b/federationapi/routing/invite.go index f424fcacd..c1fdf266b 100644 --- a/federationapi/routing/invite.go +++ b/federationapi/routing/invite.go @@ -25,6 +25,7 @@ import ( roomserverVersion "github.com/matrix-org/dendrite/roomserver/version" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -218,12 +219,12 @@ func processInvite( if isInviteV2 { return util.JSONResponse{ Code: http.StatusOK, - JSON: gomatrixserverlib.RespInviteV2{Event: signedEvent.JSON()}, + JSON: fclient.RespInviteV2{Event: signedEvent.JSON()}, } } else { return util.JSONResponse{ Code: http.StatusOK, - JSON: gomatrixserverlib.RespInvite{Event: signedEvent.JSON()}, + JSON: fclient.RespInvite{Event: signedEvent.JSON()}, } } } diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index 03809df75..1476f903f 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -22,6 +22,7 @@ import ( "time" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/sirupsen/logrus" @@ -433,7 +434,7 @@ func SendJoin( // https://matrix.org/docs/spec/server_server/latest#put-matrix-federation-v1-send-join-roomid-eventid return util.JSONResponse{ Code: http.StatusOK, - JSON: gomatrixserverlib.RespSendJoin{ + JSON: fclient.RespSendJoin{ StateEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(stateAndAuthChainResponse.StateEvents), AuthEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(stateAndAuthChainResponse.AuthChainEvents), Origin: cfg.Matrix.ServerName, diff --git a/federationapi/routing/keys.go b/federationapi/routing/keys.go index 2885cc916..db768591f 100644 --- a/federationapi/routing/keys.go +++ b/federationapi/routing/keys.go @@ -25,6 +25,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/sirupsen/logrus" "golang.org/x/crypto/ed25519" @@ -144,7 +145,7 @@ func LocalKeys(cfg *config.FederationAPI, serverName gomatrixserverlib.ServerNam func localKeys(cfg *config.FederationAPI, serverName gomatrixserverlib.ServerName) (*gomatrixserverlib.ServerKeys, error) { var keys gomatrixserverlib.ServerKeys - var identity *gomatrixserverlib.SigningIdentity + var identity *fclient.SigningIdentity var err error if virtualHost := cfg.Matrix.VirtualHostForHTTPHost(serverName); virtualHost == nil { if identity, err = cfg.Matrix.SigningIdentityFor(cfg.Matrix.ServerName); err != nil { diff --git a/federationapi/routing/missingevents.go b/federationapi/routing/missingevents.go index 531cb9e28..63a32b9c4 100644 --- a/federationapi/routing/missingevents.go +++ b/federationapi/routing/missingevents.go @@ -19,6 +19,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -67,7 +68,7 @@ func GetMissingEvents( eventsResponse.Events = filterEvents(eventsResponse.Events, roomID) - resp := gomatrixserverlib.RespMissingEvents{ + resp := fclient.RespMissingEvents{ Events: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(eventsResponse.Events), } diff --git a/federationapi/routing/peek.go b/federationapi/routing/peek.go index bc4dac90f..6c4d315c0 100644 --- a/federationapi/routing/peek.go +++ b/federationapi/routing/peek.go @@ -21,6 +21,7 @@ import ( "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -87,7 +88,7 @@ func Peek( return util.JSONResponse{Code: http.StatusNotFound, JSON: nil} } - respPeek := gomatrixserverlib.RespPeek{ + respPeek := fclient.RespPeek{ StateEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(response.StateEvents), AuthEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(response.AuthChainEvents), RoomVersion: response.RoomVersion, diff --git a/federationapi/routing/profile.go b/federationapi/routing/profile.go index e4d2230ad..55641b216 100644 --- a/federationapi/routing/profile.go +++ b/federationapi/routing/profile.go @@ -50,10 +50,7 @@ func GetProfile( } } - var profileRes userapi.QueryProfileResponse - err = userAPI.QueryProfile(httpReq.Context(), &userapi.QueryProfileRequest{ - UserID: userID, - }, &profileRes) + profile, err := userAPI.QueryProfile(httpReq.Context(), userID) if err != nil { util.GetLogger(httpReq.Context()).WithError(err).Error("userAPI.QueryProfile failed") return jsonerror.InternalServerError() @@ -65,21 +62,21 @@ func GetProfile( if field != "" { switch field { case "displayname": - res = eventutil.DisplayName{ - DisplayName: profileRes.DisplayName, + res = eventutil.UserProfile{ + DisplayName: profile.DisplayName, } case "avatar_url": - res = eventutil.AvatarURL{ - AvatarURL: profileRes.AvatarURL, + res = eventutil.UserProfile{ + AvatarURL: profile.AvatarURL, } default: code = http.StatusBadRequest res = jsonerror.InvalidArgumentValue("The request body did not contain an allowed value of argument 'field'. Allowed values are either: 'avatar_url', 'displayname'.") } } else { - res = eventutil.ProfileResponse{ - AvatarURL: profileRes.AvatarURL, - DisplayName: profileRes.DisplayName, + res = eventutil.UserProfile{ + AvatarURL: profile.AvatarURL, + DisplayName: profile.DisplayName, } } diff --git a/federationapi/routing/profile_test.go b/federationapi/routing/profile_test.go index d5e9997fa..d249fce14 100644 --- a/federationapi/routing/profile_test.go +++ b/federationapi/routing/profile_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/gorilla/mux" + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing" fedAPI "github.com/matrix-org/dendrite/federationapi" fedInternal "github.com/matrix-org/dendrite/federationapi/internal" @@ -43,8 +44,8 @@ type fakeUserAPI struct { userAPI.FederationUserAPI } -func (u *fakeUserAPI) QueryProfile(ctx context.Context, req *userAPI.QueryProfileRequest, res *userAPI.QueryProfileResponse) error { - return nil +func (u *fakeUserAPI) QueryProfile(ctx context.Context, userID string) (*authtypes.Profile, error) { + return &authtypes.Profile{}, nil } func TestHandleQueryProfile(t *testing.T) { diff --git a/federationapi/routing/publicrooms.go b/federationapi/routing/publicrooms.go index 34025932a..7c5d6a02e 100644 --- a/federationapi/routing/publicrooms.go +++ b/federationapi/routing/publicrooms.go @@ -7,6 +7,7 @@ import ( "strconv" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/matrix-org/dendrite/clientapi/httputil" @@ -48,9 +49,9 @@ func GetPostPublicRooms(req *http.Request, rsAPI roomserverAPI.FederationRoomser func publicRooms( ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.FederationRoomserverAPI, -) (*gomatrixserverlib.RespPublicRooms, error) { +) (*fclient.RespPublicRooms, error) { - var response gomatrixserverlib.RespPublicRooms + var response fclient.RespPublicRooms var limit int16 var offset int64 limit = request.Limit @@ -122,7 +123,7 @@ func fillPublicRoomsReq(httpReq *http.Request, request *PublicRoomReq) *util.JSO } // due to lots of switches -func fillInRooms(ctx context.Context, roomIDs []string, rsAPI roomserverAPI.FederationRoomserverAPI) ([]gomatrixserverlib.PublicRoom, error) { +func fillInRooms(ctx context.Context, roomIDs []string, rsAPI roomserverAPI.FederationRoomserverAPI) ([]fclient.PublicRoom, error) { avatarTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.avatar", StateKey: ""} nameTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.name", StateKey: ""} canonicalTuple := gomatrixserverlib.StateKeyTuple{EventType: gomatrixserverlib.MRoomCanonicalAlias, StateKey: ""} @@ -144,10 +145,10 @@ func fillInRooms(ctx context.Context, roomIDs []string, rsAPI roomserverAPI.Fede util.GetLogger(ctx).WithError(err).Error("QueryBulkStateContent failed") return nil, err } - chunk := make([]gomatrixserverlib.PublicRoom, len(roomIDs)) + chunk := make([]fclient.PublicRoom, len(roomIDs)) i := 0 for roomID, data := range stateRes.Rooms { - pub := gomatrixserverlib.PublicRoom{ + pub := fclient.PublicRoom{ RoomID: roomID, } joinCount := 0 diff --git a/federationapi/routing/query.go b/federationapi/routing/query.go index e6dc52601..6b1c371ec 100644 --- a/federationapi/routing/query.go +++ b/federationapi/routing/query.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -50,7 +51,7 @@ func RoomAliasToID( } } - var resp gomatrixserverlib.RespDirectory + var resp fclient.RespDirectory if domain == cfg.Matrix.ServerName { queryReq := &roomserverAPI.GetRoomIDForAliasRequest{ @@ -71,7 +72,7 @@ func RoomAliasToID( return jsonerror.InternalServerError() } - resp = gomatrixserverlib.RespDirectory{ + resp = fclient.RespDirectory{ RoomID: queryRes.RoomID, Servers: serverQueryRes.ServerNames, } diff --git a/federationapi/routing/query_test.go b/federationapi/routing/query_test.go index 6e702963f..807e7b2f2 100644 --- a/federationapi/routing/query_test.go +++ b/federationapi/routing/query_test.go @@ -35,6 +35,7 @@ import ( "github.com/matrix-org/dendrite/test" "github.com/matrix-org/dendrite/test/testrig" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/stretchr/testify/assert" "golang.org/x/crypto/ed25519" ) @@ -43,7 +44,7 @@ type fakeFedClient struct { fedclient.FederationClient } -func (f *fakeFedClient) LookupRoomAlias(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomAlias string) (res gomatrixserverlib.RespDirectory, err error) { +func (f *fakeFedClient) LookupRoomAlias(ctx context.Context, origin, s gomatrixserverlib.ServerName, roomAlias string) (res fclient.RespDirectory, err error) { return } diff --git a/federationapi/routing/state.go b/federationapi/routing/state.go index 1120cf260..1152c0932 100644 --- a/federationapi/routing/state.go +++ b/federationapi/routing/state.go @@ -20,6 +20,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -40,7 +41,7 @@ func GetState( return *err } - return util.JSONResponse{Code: http.StatusOK, JSON: &gomatrixserverlib.RespState{ + return util.JSONResponse{Code: http.StatusOK, JSON: &fclient.RespState{ AuthEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(authChain), StateEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(stateEvents), }} @@ -66,7 +67,7 @@ func GetStateIDs( stateEventIDs := getIDsFromEvent(stateEvents) authEventIDs := getIDsFromEvent(authEvents) - return util.JSONResponse{Code: http.StatusOK, JSON: gomatrixserverlib.RespStateIDs{ + return util.JSONResponse{Code: http.StatusOK, JSON: fclient.RespStateIDs{ StateEventIDs: stateEventIDs, AuthEventIDs: authEventIDs, }, diff --git a/federationapi/routing/threepid.go b/federationapi/routing/threepid.go index d07faef39..048183ad1 100644 --- a/federationapi/routing/threepid.go +++ b/federationapi/routing/threepid.go @@ -256,17 +256,14 @@ func createInviteFrom3PIDInvite( StateKey: &inv.MXID, } - var res userapi.QueryProfileResponse - err = userAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{ - UserID: inv.MXID, - }, &res) + profile, err := userAPI.QueryProfile(ctx, inv.MXID) if err != nil { return nil, err } content := gomatrixserverlib.MemberContent{ - AvatarURL: res.AvatarURL, - DisplayName: res.DisplayName, + AvatarURL: profile.AvatarURL, + DisplayName: profile.DisplayName, Membership: gomatrixserverlib.Invite, ThirdPartyInvite: &gomatrixserverlib.MemberThirdPartyInvite{ Signed: inv.Signed, diff --git a/go.mod b/go.mod index ba231b414..0fac0b999 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/blevesearch/bleve/v2 v2.3.6 github.com/codeclysm/extract v2.2.0+incompatible github.com/dgraph-io/ristretto v0.1.1 - github.com/docker/docker v20.10.19+incompatible + github.com/docker/docker v20.10.24+incompatible github.com/docker/go-connections v0.4.0 github.com/getsentry/sentry-go v0.14.0 github.com/gologme/log v1.3.0 @@ -22,7 +22,7 @@ require ( github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 - github.com/matrix-org/gomatrixserverlib v0.0.0-20230320105331-4dd7ff2f0e3a + github.com/matrix-org/gomatrixserverlib v0.0.0-20230414140439-3cf4cd94d75f github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 github.com/mattn/go-sqlite3 v1.14.16 @@ -42,10 +42,10 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible github.com/yggdrasil-network/yggdrasil-go v0.4.6 go.uber.org/atomic v1.10.0 - golang.org/x/crypto v0.6.0 + golang.org/x/crypto v0.8.0 golang.org/x/image v0.5.0 golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e - golang.org/x/term v0.5.0 + golang.org/x/term v0.7.0 gopkg.in/h2non/bimg.v1 v1.1.9 gopkg.in/yaml.v2 v2.4.0 gotest.tools/v3 v3.4.0 @@ -121,13 +121,13 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect go.etcd.io/bbolt v1.3.6 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect - golang.org/x/mod v0.6.0 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.7.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.2.0 // indirect + golang.org/x/tools v0.6.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/macaroon.v2 v2.1.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index f6dfa129f..ad3017f2f 100644 --- a/go.sum +++ b/go.sum @@ -134,8 +134,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.19+incompatible h1:lzEmjivyNHFHMNAFLXORMBXyGIhw/UP4DvJwvyKYq64= -github.com/docker/docker v20.10.19+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= +github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -321,8 +321,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= -github.com/matrix-org/gomatrixserverlib v0.0.0-20230320105331-4dd7ff2f0e3a h1:F6K1i61KcJ8cX/y0Q8/44Dh1w+fpESQd92gq885FDrI= -github.com/matrix-org/gomatrixserverlib v0.0.0-20230320105331-4dd7ff2f0e3a/go.mod h1:7HTbSZe+CIdmeqVyFMekwD5dFU8khWQyngKATvd12FU= +github.com/matrix-org/gomatrixserverlib v0.0.0-20230414140439-3cf4cd94d75f h1:sULN+zkwjt9bBy3dy5W98B6J/Pd4xOiF6yjWOBRRKmw= +github.com/matrix-org/gomatrixserverlib v0.0.0-20230414140439-3cf4cd94d75f/go.mod h1:7HTbSZe+CIdmeqVyFMekwD5dFU8khWQyngKATvd12FU= github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a h1:awrPDf9LEFySxTLKYBMCiObelNx/cBuv/wzllvCCH3A= github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a/go.mod h1:HchJX9oKMXaT2xYFs0Ha/6Zs06mxLU8k6F1ODnrGkeQ= github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y= @@ -502,8 +502,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -546,8 +546,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -583,8 +583,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -605,7 +605,6 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -658,12 +657,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 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= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -671,8 +670,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -726,8 +726,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/helm/dendrite/.helm-docs/monitoring.gotmpl b/helm/dendrite/.helm-docs/monitoring.gotmpl index 3618a1c1a..eaa336ebd 100644 --- a/helm/dendrite/.helm-docs/monitoring.gotmpl +++ b/helm/dendrite/.helm-docs/monitoring.gotmpl @@ -1,10 +1,15 @@ {{ define "chart.monitoringSection" }} ## Monitoring -[![Grafana Dashboard](https://grafana.com/api/dashboards/13916/images/9894/image)](https://grafana.com/grafana/dashboards/13916-dendrite/) +![Grafana Dashboard](grafana_dashboards/dendrite-rev2.png) * Works well with [Prometheus Operator](https://prometheus-operator.dev/) ([Helmchart](https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack)) and their setup of [Grafana](https://grafana.com/grafana/), by enabling the following values: ```yaml +dendrite_config: + global: + metrics: + enabled: true + prometheus: servicemonitor: enabled: true @@ -19,4 +24,4 @@ grafana: enabled: true # will deploy default dashboards ``` PS: The label `release=kube-prometheus-stack` is setup with the helmchart of the Prometheus Operator. For Grafana Dashboards it may be necessary to enable scanning in the correct namespaces (or ALL), enabled by `sidecar.dashboards.searchNamespace` in [Helmchart of grafana](https://artifacthub.io/packages/helm/grafana/grafana) (which is part of PrometheusOperator, so `grafana.sidecar.dashboards.searchNamespace`) -{{ end }} \ No newline at end of file +{{ end }} diff --git a/helm/dendrite/Chart.yaml b/helm/dendrite/Chart.yaml index 3ef45a6df..6a428e00f 100644 --- a/helm/dendrite/Chart.yaml +++ b/helm/dendrite/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: dendrite -version: "0.12.1" +version: "0.12.2" appVersion: "0.12.0" description: Dendrite Matrix Homeserver type: application diff --git a/helm/dendrite/README.md b/helm/dendrite/README.md index c3833edfb..ca5705c03 100644 --- a/helm/dendrite/README.md +++ b/helm/dendrite/README.md @@ -1,6 +1,7 @@ + # dendrite -![Version: 0.12.0](https://img.shields.io/badge/Version-0.12.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.12.0](https://img.shields.io/badge/AppVersion-0.12.0-informational?style=flat-square) +![Version: 0.12.2](https://img.shields.io/badge/Version-0.12.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.12.0](https://img.shields.io/badge/AppVersion-0.12.0-informational?style=flat-square) Dendrite Matrix Homeserver Status: **NOT PRODUCTION READY** @@ -54,6 +55,11 @@ Create a folder `appservices` and place your configurations in there. The confi | persistence.media.capacity | string | `"1Gi"` | PVC Storage Request for the media volume | | persistence.search.existingClaim | string | `""` | Use an existing volume claim for the fulltext search index | | persistence.search.capacity | string | `"1Gi"` | PVC Storage Request for the search volume | +| extraVolumes | list | `[]` | Add additional volumes to the Dendrite Pod | +| extraVolumeMounts | list | `[]` | Configure additional mount points volumes in the Dendrite Pod | +| strategy.type | string | `"RollingUpdate"` | Strategy to use for rolling updates (e.g. Recreate, RollingUpdate) If you are using ReadWriteOnce volumes, you should probably use Recreate | +| strategy.rollingUpdate.maxUnavailable | string | `"25%"` | Maximum number of pods that can be unavailable during the update process | +| strategy.rollingUpdate.maxSurge | string | `"25%"` | Maximum number of pods that can be scheduled above the desired number of pods | | dendrite_config.version | int | `2` | | | dendrite_config.global.server_name | string | `""` | **REQUIRED** Servername for this Dendrite deployment. | | dendrite_config.global.private_key | string | `"/etc/dendrite/secrets/signing.key"` | The private key to use. (**NOTE**: This is overriden in Helm) | @@ -157,10 +163,15 @@ Create a folder `appservices` and place your configurations in there. The confi ## Monitoring -[![Grafana Dashboard](https://grafana.com/api/dashboards/13916/images/9894/image)](https://grafana.com/grafana/dashboards/13916-dendrite/) +![Grafana Dashboard](grafana_dashboards/dendrite-rev2.png) * Works well with [Prometheus Operator](https://prometheus-operator.dev/) ([Helmchart](https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack)) and their setup of [Grafana](https://grafana.com/grafana/), by enabling the following values: ```yaml +dendrite_config: + global: + metrics: + enabled: true + prometheus: servicemonitor: enabled: true diff --git a/helm/dendrite/grafana_dashboards/dendrite-rev1.json b/helm/dendrite/grafana_dashboards/dendrite-rev1.json deleted file mode 100644 index 206e8af87..000000000 --- a/helm/dendrite/grafana_dashboards/dendrite-rev1.json +++ /dev/null @@ -1,1119 +0,0 @@ -{ - "__inputs": [ - { - "name": "DS_INFLUXDB_DOMOTICA", - "label": "", - "description": "", - "type": "datasource", - "pluginId": "influxdb", - "pluginName": "InfluxDB" - }, - { - "name": "DS_PROMETHEUS", - "label": "Prometheus", - "description": "", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "7.4.2" - }, - { - "type": "panel", - "id": "graph", - "name": "Graph", - "version": "" - }, - { - "type": "panel", - "id": "heatmap", - "name": "Heatmap", - "version": "" - }, - { - "type": "datasource", - "id": "influxdb", - "name": "InfluxDB", - "version": "1.0.0" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "stat", - "name": "Stat", - "version": "" - } - ], - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "description": "Dendrite dashboard from https://github.com/matrix-org/dendrite/", - "editable": true, - "gnetId": 13916, - "graphTooltip": 0, - "id": null, - "iteration": 1613683251329, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": "${DS_INFLUXDB_DOMOTICA}", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 4, - "panels": [], - "title": "Overview", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "$datasource", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 5, - "w": 10, - "x": 0, - "y": 1 - }, - "hiddenSeries": false, - "id": 2, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "rate(process_cpu_seconds_total{instance=\"$instance\",job=~\"$job\",index=~\"$index\"}[$bucket_size])", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{job}}-{{index}} ", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CPU usage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "percentunit", - "label": null, - "logBase": 1, - "max": "1", - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "datasource": "${DS_PROMETHEUS}", - "description": "Total number of registered users", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": {}, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 10, - "y": 1 - }, - "id": 20, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "7.4.2", - "targets": [ - { - "exemplar": false, - "expr": "dendrite_clientapi_reg_users_total", - "instant": false, - "interval": "", - "legendFormat": "Users", - "refId": "A" - } - ], - "title": "Registerd Users", - "type": "stat" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "description": "The number of sync requests that are active right now and are waiting to be woken by a notifier", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 5, - "w": 10, - "x": 14, - "y": 1 - }, - "hiddenSeries": false, - "id": 6, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 2, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(dendrite_syncapi_active_sync_requests{instance=\"$instance\"}[$bucket_size]))without (job,index)", - "hide": false, - "interval": "", - "legendFormat": "active", - "refId": "A" - }, - { - "expr": "sum(rate(dendrite_syncapi_waiting_sync_requests{instance=\"$instance\"}[$bucket_size]))without (job,index)", - "hide": false, - "interval": "", - "legendFormat": "waiting", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Sync API", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:232", - "format": "hertz", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "$$hashKey": "object:233", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cards": { - "cardPadding": null, - "cardRound": null - }, - "color": { - "cardColor": "#b4ff00", - "colorScale": "sqrt", - "colorScheme": "interpolateOranges", - "exponent": 0.5, - "mode": "spectrum" - }, - "dataFormat": "tsbuckets", - "datasource": "${DS_PROMETHEUS}", - "description": "How long it takes to build and submit a new event from the client API to the roomserver", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 24, - "x": 0, - "y": 6 - }, - "heatmap": {}, - "hideZeroBuckets": false, - "highlightCards": true, - "id": 24, - "legend": { - "show": false - }, - "pluginVersion": "7.4.2", - "reverseYBuckets": false, - "targets": [ - { - "expr": "dendrite_clientapi_sendevent_duration_millis_bucket{action=\"build\",instance=\"$instance\"}", - "interval": "", - "legendFormat": "{{le}}", - "refId": "A" - } - ], - "title": "Sendevent Duration", - "tooltip": { - "show": true, - "showHistogram": false - }, - "type": "heatmap", - "xAxis": { - "show": true - }, - "xBucketNumber": null, - "xBucketSize": null, - "yAxis": { - "decimals": null, - "format": "s", - "logBase": 1, - "max": null, - "min": "0", - "show": true, - "splitFactor": null - }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null - }, - { - "collapsed": false, - "datasource": "${DS_INFLUXDB_DOMOTICA}", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 11 - }, - "id": 8, - "panels": [], - "title": "Federation", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "description": "Collection of queues for sending transactions to other matrix servers", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 12 - }, - "hiddenSeries": false, - "id": 10, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dendrite_federationsender_destination_queues_running", - "interval": "", - "legendFormat": "Queue Running", - "refId": "A" - }, - { - "expr": "dendrite_federationsender_destination_queues_total", - "hide": false, - "interval": "", - "legendFormat": "Queue Total", - "refId": "B" - }, - { - "expr": "dendrite_federationsender_destination_queues_backing_off", - "hide": false, - "interval": "", - "legendFormat": "Backing Off", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Federation Sender Destination", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:443", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:444", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": "${DS_INFLUXDB_DOMOTICA}", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 18 - }, - "id": 26, - "panels": [], - "title": "Rooms", - "type": "row" - }, - { - "cards": { - "cardPadding": null, - "cardRound": null - }, - "color": { - "cardColor": "#b4ff00", - "colorScale": "sqrt", - "colorScheme": "interpolateOranges", - "exponent": 0.5, - "mode": "spectrum" - }, - "dataFormat": "timeseries", - "datasource": "${DS_PROMETHEUS}", - "description": "How long it takes the roomserver to process an event", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 19 - }, - "heatmap": {}, - "hideZeroBuckets": false, - "highlightCards": true, - "id": 28, - "legend": { - "show": false - }, - "pluginVersion": "7.4.2", - "reverseYBuckets": false, - "targets": [ - { - "expr": "sum(rate(dendrite_roomserver_processroomevent_duration_millis_bucket{instance=\"$instance\"}[$bucket_size])) by (le)", - "interval": "", - "legendFormat": "{{le}}", - "refId": "A" - } - ], - "title": "Room Event Processing", - "tooltip": { - "show": true, - "showHistogram": false - }, - "type": "heatmap", - "xAxis": { - "show": true - }, - "xBucketNumber": null, - "xBucketSize": null, - "yAxis": { - "decimals": null, - "format": "s", - "logBase": 1, - "max": null, - "min": null, - "show": true, - "splitFactor": null - }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null - }, - { - "collapsed": false, - "datasource": "${DS_INFLUXDB_DOMOTICA}", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 26 - }, - "id": 12, - "panels": [], - "title": "Caches", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 8, - "x": 0, - "y": 27 - }, - "hiddenSeries": false, - "id": 14, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dendrite_caching_in_memory_lru_server_key", - "interval": "", - "legendFormat": "Server keys", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Server Keys", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:667", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:668", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 8, - "x": 8, - "y": 27 - }, - "hiddenSeries": false, - "id": 16, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dendrite_caching_in_memory_lru_federation_event", - "interval": "", - "legendFormat": "Federation Event", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Federation Events", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:784", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:785", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 8, - "x": 16, - "y": 27 - }, - "hiddenSeries": false, - "id": 18, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dendrite_caching_in_memory_lru_roomserver_room_ids", - "interval": "", - "legendFormat": "Room IDs", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Room IDs", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:898", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:899", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "10s", - "schemaVersion": 27, - "style": "dark", - "tags": [ - "matrix", - "dendrite" - ], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "Prometheus", - "value": "Prometheus" - }, - "description": null, - "error": null, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "datasource", - "options": [], - "query": "prometheus", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - }, - { - "auto": true, - "auto_count": 100, - "auto_min": "30s", - "current": { - "selected": false, - "text": "auto", - "value": "$__auto_interval_bucket_size" - }, - "description": null, - "error": null, - "hide": 0, - "label": "Bucket Size", - "name": "bucket_size", - "options": [ - { - "selected": true, - "text": "auto", - "value": "$__auto_interval_bucket_size" - }, - { - "selected": false, - "text": "30s", - "value": "30s" - }, - { - "selected": false, - "text": "1m", - "value": "1m" - }, - { - "selected": false, - "text": "2m", - "value": "2m" - }, - { - "selected": false, - "text": "5m", - "value": "5m" - }, - { - "selected": false, - "text": "10m", - "value": "10m" - }, - { - "selected": false, - "text": "15m", - "value": "15m" - } - ], - "query": "30s,1m,2m,5m,10m,15m", - "queryValue": "", - "refresh": 2, - "skipUrlSync": false, - "type": "interval" - }, - { - "allValue": null, - "current": {}, - "datasource": "${DS_PROMETHEUS}", - "definition": "label_values(dendrite_caching_in_memory_lru_roominfo, instance)", - "description": null, - "error": null, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "instance", - "options": [], - "query": { - "query": "label_values(dendrite_caching_in_memory_lru_roominfo, instance)", - "refId": "StandardVariableQuery" - }, - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "${DS_PROMETHEUS}", - "definition": "label_values(dendrite_caching_in_memory_lru_roominfo, job)", - "description": null, - "error": null, - "hide": 0, - "includeAll": true, - "label": "Job", - "multi": true, - "name": "job", - "options": [], - "query": { - "query": "label_values(dendrite_caching_in_memory_lru_roominfo, job)", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": ".*", - "current": {}, - "datasource": "${DS_PROMETHEUS}", - "definition": "label_values(dendrite_caching_in_memory_lru_roominfo, index)", - "description": null, - "error": null, - "hide": 0, - "includeAll": true, - "label": null, - "multi": true, - "name": "index", - "options": [], - "query": { - "query": "label_values(dendrite_caching_in_memory_lru_roominfo, index)", - "refId": "StandardVariableQuery" - }, - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 3, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-3h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Dendrite", - "uid": "RoRt1jEGz", - "version": 8 -} \ No newline at end of file diff --git a/helm/dendrite/grafana_dashboards/dendrite-rev2.json b/helm/dendrite/grafana_dashboards/dendrite-rev2.json new file mode 100644 index 000000000..817f950b3 --- /dev/null +++ b/helm/dendrite/grafana_dashboards/dendrite-rev2.json @@ -0,0 +1,479 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "Dendrite dashboard from https://github.com/matrix-org/dendrite/", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 13916, + "graphTooltip": 0, + "id": 60, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 4, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Overview", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of registered users", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 7, + "x": 0, + "y": 1 + }, + "id": 20, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(dendrite_clientapi_reg_users_total{namespace=~\"$namespace\",service=~\"$service\"}) by (namespace,service)", + "instant": false, + "interval": "", + "legendFormat": "{{namespace}}: {{service}}", + "refId": "A" + } + ], + "title": "Registerd Users", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "The number of sync requests that are active right now and are waiting to be woken by a notifier", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 17, + "x": 7, + "y": 1 + }, + "id": 6, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(dendrite_syncapi_active_sync_requests{namespace=~\"$namespace\",service=~\"$service\"}[$__rate_interval]))by (namspace,service)", + "hide": false, + "interval": "", + "legendFormat": "active: {{namspace}} - {{service}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(dendrite_syncapi_waiting_sync_requests{namespace=~\"$namespace\",service=~\"$service\"}[$__rate_interval]))by (namespace,service)", + "hide": false, + "interval": "", + "legendFormat": "waiting: {{namspace}} - {{service}}", + "range": true, + "refId": "B" + } + ], + "title": "Sync API", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 8, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Federation", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Collection of queues for sending transactions to other matrix servers", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "dendrite_federationapi_destination_queues_running{namespace=~\"$namespace\",service=~\"$service\"}", + "interval": "", + "legendFormat": "Queue Running: {{namespace}}-{{service}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "dendrite_federationapi_destination_queues_total{namespace=~\"$namespace\",service=~\"$service\"}", + "hide": false, + "interval": "", + "legendFormat": "Queue Total: {{namespace}}-{{service}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "dendrite_federationapi_destination_queues_backing_off{namespace=~\"$namespace\",service=~\"$service\"}", + "hide": false, + "interval": "", + "legendFormat": "Backing Off: {{namespace}}-{{service}}", + "range": true, + "refId": "C" + } + ], + "title": "Federation Sender Destination", + "type": "timeseries" + } + ], + "refresh": "10s", + "schemaVersion": 37, + "style": "dark", + "tags": [ + "matrix", + "dendrite" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 0, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(dendrite_syncapi_active_sync_requests, namespace)", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "namespace", + "options": [], + "query": { + "query": "label_values(dendrite_syncapi_active_sync_requests, namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(dendrite_syncapi_active_sync_requests{namespace=~\"$namespace\"}, service)", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "service", + "options": [], + "query": { + "query": "label_values(dendrite_syncapi_active_sync_requests{namespace=~\"$namespace\"}, service)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Dendrite", + "uid": "RoRt1jEGz", + "version": 1, + "weekStart": "" +} diff --git a/helm/dendrite/grafana_dashboards/dendrite-rev2.png b/helm/dendrite/grafana_dashboards/dendrite-rev2.png new file mode 100644 index 000000000..179f4b5a1 Binary files /dev/null and b/helm/dendrite/grafana_dashboards/dendrite-rev2.png differ diff --git a/helm/dendrite/templates/deployment.yaml b/helm/dendrite/templates/deployment.yaml index 2a0f3a9e9..df7dbbdc3 100644 --- a/helm/dendrite/templates/deployment.yaml +++ b/helm/dendrite/templates/deployment.yaml @@ -12,6 +12,13 @@ spec: matchLabels: {{- include "dendrite.selectorLabels" . | nindent 6 }} replicas: 1 + strategy: + type: {{ $.Values.strategy.type }} + {{- if eq $.Values.strategy.type "RollingUpdate" }} + rollingUpdate: + maxSurge: {{ $.Values.strategy.rollingUpdate.maxSurge }} + maxUnavailable: {{ $.Values.strategy.rollingUpdate.maxUnavailable }} + {{- end }} template: metadata: labels: @@ -40,6 +47,9 @@ spec: - name: {{ include "dendrite.fullname" . }}-search persistentVolumeClaim: claimName: {{ default (print ( include "dendrite.fullname" . ) "-search-pvc") $.Values.persistence.search.existingClaim | quote }} + {{- with .Values.extraVolumes }} + {{ . | toYaml | nindent 6 }} + {{- end }} containers: - name: {{ .Chart.Name }} {{- include "image.name" . | nindent 8 }} @@ -73,6 +83,9 @@ spec: name: {{ include "dendrite.fullname" . }}-jetstream - mountPath: {{ .Values.dendrite_config.sync_api.search.index_path }} name: {{ include "dendrite.fullname" . }}-search + {{- with .Values.extraVolumeMounts }} + {{ . | toYaml | nindent 8 }} + {{- end }} livenessProbe: initialDelaySeconds: 10 periodSeconds: 10 diff --git a/helm/dendrite/values.yaml b/helm/dendrite/values.yaml index c219d27f8..41ec1c390 100644 --- a/helm/dendrite/values.yaml +++ b/helm/dendrite/values.yaml @@ -43,6 +43,30 @@ persistence: # -- PVC Storage Request for the search volume capacity: "1Gi" +# -- Add additional volumes to the Dendrite Pod +extraVolumes: [] +# ex. +# - name: extra-config +# secret: +# secretName: extra-config + + +# -- Configure additional mount points volumes in the Dendrite Pod +extraVolumeMounts: [] +# ex. +# - mountPath: /etc/dendrite/extra-config +# name: extra-config + +strategy: + # -- Strategy to use for rolling updates (e.g. Recreate, RollingUpdate) + # If you are using ReadWriteOnce volumes, you should probably use Recreate + type: RollingUpdate + rollingUpdate: + # -- Maximum number of pods that can be unavailable during the update process + maxUnavailable: 25% + # -- Maximum number of pods that can be scheduled above the desired number of pods + maxSurge: 25% + dendrite_config: version: 2 global: diff --git a/internal/caching/cache_space_rooms.go b/internal/caching/cache_space_rooms.go index 697f99269..100ab9023 100644 --- a/internal/caching/cache_space_rooms.go +++ b/internal/caching/cache_space_rooms.go @@ -1,18 +1,16 @@ package caching -import ( - "github.com/matrix-org/gomatrixserverlib" -) +import "github.com/matrix-org/gomatrixserverlib/fclient" type SpaceSummaryRoomsCache interface { - GetSpaceSummary(roomID string) (r gomatrixserverlib.MSC2946SpacesResponse, ok bool) - StoreSpaceSummary(roomID string, r gomatrixserverlib.MSC2946SpacesResponse) + GetSpaceSummary(roomID string) (r fclient.MSC2946SpacesResponse, ok bool) + StoreSpaceSummary(roomID string, r fclient.MSC2946SpacesResponse) } -func (c Caches) GetSpaceSummary(roomID string) (r gomatrixserverlib.MSC2946SpacesResponse, ok bool) { +func (c Caches) GetSpaceSummary(roomID string) (r fclient.MSC2946SpacesResponse, ok bool) { return c.SpaceSummaryRooms.Get(roomID) } -func (c Caches) StoreSpaceSummary(roomID string, r gomatrixserverlib.MSC2946SpacesResponse) { +func (c Caches) StoreSpaceSummary(roomID string, r fclient.MSC2946SpacesResponse) { c.SpaceSummaryRooms.Set(roomID, r) } diff --git a/internal/caching/caches.go b/internal/caching/caches.go index 479920466..a678632eb 100644 --- a/internal/caching/caches.go +++ b/internal/caching/caches.go @@ -17,6 +17,7 @@ package caching import ( "github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) // Caches contains a set of references to caches. They may be @@ -34,7 +35,7 @@ type Caches struct { RoomServerEventTypes Cache[types.EventTypeNID, string] // eventType NID -> eventType FederationPDUs Cache[int64, *gomatrixserverlib.HeaderedEvent] // queue NID -> PDU FederationEDUs Cache[int64, *gomatrixserverlib.EDU] // queue NID -> EDU - SpaceSummaryRooms Cache[string, gomatrixserverlib.MSC2946SpacesResponse] // room ID -> space response + SpaceSummaryRooms Cache[string, fclient.MSC2946SpacesResponse] // room ID -> space response LazyLoading Cache[lazyLoadingCacheKey, string] // composite key -> event ID } diff --git a/internal/caching/impl_ristretto.go b/internal/caching/impl_ristretto.go index 7663ddcb9..4656b6b7e 100644 --- a/internal/caching/impl_ristretto.go +++ b/internal/caching/impl_ristretto.go @@ -23,6 +23,7 @@ import ( "github.com/dgraph-io/ristretto" "github.com/dgraph-io/ristretto/z" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -146,7 +147,7 @@ func NewRistrettoCache(maxCost config.DataUnit, maxAge time.Duration, enableProm MaxAge: lesserOf(time.Hour/2, maxAge), }, }, - SpaceSummaryRooms: &RistrettoCachePartition[string, gomatrixserverlib.MSC2946SpacesResponse]{ // room ID -> space response + SpaceSummaryRooms: &RistrettoCachePartition[string, fclient.MSC2946SpacesResponse]{ // room ID -> space response cache: cache, Prefix: spaceSummaryRoomsCache, Mutable: true, diff --git a/internal/eventutil/events.go b/internal/eventutil/events.go index c572d8830..984a3f539 100644 --- a/internal/eventutil/events.go +++ b/internal/eventutil/events.go @@ -22,6 +22,7 @@ import ( "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib" ) @@ -39,7 +40,7 @@ var ErrRoomNoExists = errors.New("room does not exist") func QueryAndBuildEvent( ctx context.Context, builder *gomatrixserverlib.EventBuilder, cfg *config.Global, - identity *gomatrixserverlib.SigningIdentity, evTime time.Time, + identity *fclient.SigningIdentity, evTime time.Time, rsAPI api.QueryLatestEventsAndStateAPI, queryRes *api.QueryLatestEventsAndStateResponse, ) (*gomatrixserverlib.HeaderedEvent, error) { if queryRes == nil { @@ -59,7 +60,7 @@ func QueryAndBuildEvent( func BuildEvent( ctx context.Context, builder *gomatrixserverlib.EventBuilder, cfg *config.Global, - identity *gomatrixserverlib.SigningIdentity, evTime time.Time, + identity *fclient.SigningIdentity, evTime time.Time, eventsNeeded *gomatrixserverlib.StateNeeded, queryRes *api.QueryLatestEventsAndStateResponse, ) (*gomatrixserverlib.HeaderedEvent, error) { if err := addPrevEventsToEvent(builder, eventsNeeded, queryRes); err != nil { diff --git a/internal/eventutil/types.go b/internal/eventutil/types.go index 18175d6a0..e43c0412e 100644 --- a/internal/eventutil/types.go +++ b/internal/eventutil/types.go @@ -15,16 +15,11 @@ package eventutil import ( - "errors" "strconv" "github.com/matrix-org/dendrite/syncapi/types" ) -// ErrProfileNoExists is returned when trying to lookup a user's profile that -// doesn't exist locally. -var ErrProfileNoExists = errors.New("no known profile for given user ID") - // AccountData represents account data sent from the client API server to the // sync API server type AccountData struct { @@ -56,20 +51,10 @@ type NotificationData struct { UnreadNotificationCount int `json:"unread_notification_count"` } -// ProfileResponse is a struct containing all known user profile data -type ProfileResponse struct { - AvatarURL string `json:"avatar_url"` - DisplayName string `json:"displayname"` -} - -// AvatarURL is a struct containing only the URL to a user's avatar -type AvatarURL struct { - AvatarURL string `json:"avatar_url"` -} - -// DisplayName is a struct containing only a user's display name -type DisplayName struct { - DisplayName string `json:"displayname"` +// UserProfile is a struct containing all known user profile data +type UserProfile struct { + AvatarURL string `json:"avatar_url,omitempty"` + DisplayName string `json:"displayname,omitempty"` } // WeakBoolean is a type that will Unmarshal to true or false even if the encoded diff --git a/internal/pushrules/validate.go b/internal/pushrules/validate.go index f50c51bd7..b54ec3fb0 100644 --- a/internal/pushrules/validate.go +++ b/internal/pushrules/validate.go @@ -10,6 +10,10 @@ import ( func ValidateRule(kind Kind, rule *Rule) []error { var errs []error + if len(rule.RuleID) > 0 && rule.RuleID[:1] == "." { + errs = append(errs, fmt.Errorf("invalid rule ID: rule can not start with a dot")) + } + if !validRuleIDRE.MatchString(rule.RuleID) { errs = append(errs, fmt.Errorf("invalid rule ID: %s", rule.RuleID)) } diff --git a/internal/transactionrequest.go b/internal/transactionrequest.go index 13b00af50..60c02b129 100644 --- a/internal/transactionrequest.go +++ b/internal/transactionrequest.go @@ -28,6 +28,7 @@ import ( syncTypes "github.com/matrix-org/dendrite/syncapi/types" userAPI "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -97,7 +98,7 @@ func NewTxnReq( return t } -func (t *TxnReq) ProcessTransaction(ctx context.Context) (*gomatrixserverlib.RespSend, *util.JSONResponse) { +func (t *TxnReq) ProcessTransaction(ctx context.Context) (*fclient.RespSend, *util.JSONResponse) { var wg sync.WaitGroup wg.Add(1) go func() { @@ -107,7 +108,7 @@ func (t *TxnReq) ProcessTransaction(ctx context.Context) (*gomatrixserverlib.Res } }() - results := make(map[string]gomatrixserverlib.PDUResult) + results := make(map[string]fclient.PDUResult) roomVersions := make(map[string]gomatrixserverlib.RoomVersion) getRoomVersion := func(roomID string) gomatrixserverlib.RoomVersion { if v, ok := roomVersions[roomID]; ok { @@ -157,14 +158,14 @@ func (t *TxnReq) ProcessTransaction(ctx context.Context) (*gomatrixserverlib.Res continue } if api.IsServerBannedFromRoom(ctx, t.rsAPI, event.RoomID(), t.Origin) { - results[event.EventID()] = gomatrixserverlib.PDUResult{ + results[event.EventID()] = fclient.PDUResult{ Error: "Forbidden by server ACLs", } continue } if err = event.VerifyEventSignatures(ctx, t.keys); err != nil { util.GetLogger(ctx).WithError(err).Debugf("Transaction: Couldn't validate signature of event %q", event.EventID()) - results[event.EventID()] = gomatrixserverlib.PDUResult{ + results[event.EventID()] = fclient.PDUResult{ Error: err.Error(), } continue @@ -187,18 +188,18 @@ func (t *TxnReq) ProcessTransaction(ctx context.Context) (*gomatrixserverlib.Res true, ); err != nil { util.GetLogger(ctx).WithError(err).Errorf("Transaction: Couldn't submit event %q to input queue: %s", event.EventID(), err) - results[event.EventID()] = gomatrixserverlib.PDUResult{ + results[event.EventID()] = fclient.PDUResult{ Error: err.Error(), } continue } - results[event.EventID()] = gomatrixserverlib.PDUResult{} + results[event.EventID()] = fclient.PDUResult{} PDUCountTotal.WithLabelValues("success").Inc() } wg.Wait() - return &gomatrixserverlib.RespSend{PDUs: results}, nil + return &fclient.RespSend{PDUs: results}, nil } // nolint:gocyclo diff --git a/mediaapi/mediaapi.go b/mediaapi/mediaapi.go index 5d517ef22..284071a53 100644 --- a/mediaapi/mediaapi.go +++ b/mediaapi/mediaapi.go @@ -21,7 +21,7 @@ import ( "github.com/matrix-org/dendrite/mediaapi/storage" "github.com/matrix-org/dendrite/setup/config" userapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/sirupsen/logrus" ) @@ -31,7 +31,7 @@ func AddPublicRoutes( cm sqlutil.Connections, cfg *config.Dendrite, userAPI userapi.MediaUserAPI, - client *gomatrixserverlib.Client, + client *fclient.Client, ) { mediaDB, err := storage.NewMediaAPIDatasource(cm, &cfg.MediaAPI.Database) if err != nil { diff --git a/mediaapi/routing/download.go b/mediaapi/routing/download.go index c9299b1fc..412faceb3 100644 --- a/mediaapi/routing/download.go +++ b/mediaapi/routing/download.go @@ -37,6 +37,7 @@ import ( "github.com/matrix-org/dendrite/mediaapi/types" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -75,7 +76,7 @@ func Download( mediaID types.MediaID, cfg *config.MediaAPI, db storage.Database, - client *gomatrixserverlib.Client, + client *fclient.Client, activeRemoteRequests *types.ActiveRemoteRequests, activeThumbnailGeneration *types.ActiveThumbnailGeneration, isThumbnailRequest bool, @@ -205,7 +206,7 @@ func (r *downloadRequest) doDownload( w http.ResponseWriter, cfg *config.MediaAPI, db storage.Database, - client *gomatrixserverlib.Client, + client *fclient.Client, activeRemoteRequests *types.ActiveRemoteRequests, activeThumbnailGeneration *types.ActiveThumbnailGeneration, ) (*types.MediaMetadata, error) { @@ -513,7 +514,7 @@ func (r *downloadRequest) generateThumbnail( // Note: The named errorResponse return variable is used in a deferred broadcast of the metadata and error response to waiting goroutines. func (r *downloadRequest) getRemoteFile( ctx context.Context, - client *gomatrixserverlib.Client, + client *fclient.Client, cfg *config.MediaAPI, db storage.Database, activeRemoteRequests *types.ActiveRemoteRequests, @@ -615,7 +616,7 @@ func (r *downloadRequest) broadcastMediaMetadata(activeRemoteRequests *types.Act // fetchRemoteFileAndStoreMetadata fetches the file from the remote server and stores its metadata in the database func (r *downloadRequest) fetchRemoteFileAndStoreMetadata( ctx context.Context, - client *gomatrixserverlib.Client, + client *fclient.Client, absBasePath config.Path, maxFileSizeBytes config.FileSizeBytes, db storage.Database, @@ -713,7 +714,7 @@ func (r *downloadRequest) GetContentLengthAndReader(contentLengthHeader string, func (r *downloadRequest) fetchRemoteFile( ctx context.Context, - client *gomatrixserverlib.Client, + client *fclient.Client, absBasePath config.Path, maxFileSizeBytes config.FileSizeBytes, ) (types.Path, bool, error) { diff --git a/mediaapi/routing/routing.go b/mediaapi/routing/routing.go index 98e6a82ed..79e8308ae 100644 --- a/mediaapi/routing/routing.go +++ b/mediaapi/routing/routing.go @@ -26,6 +26,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -48,7 +49,7 @@ func Setup( cfg *config.Dendrite, db storage.Database, userAPI userapi.MediaUserAPI, - client *gomatrixserverlib.Client, + client *fclient.Client, ) { rateLimits := httputil.NewRateLimits(&cfg.ClientAPI.RateLimiting) @@ -103,7 +104,7 @@ func makeDownloadAPI( cfg *config.MediaAPI, rateLimits *httputil.RateLimits, db storage.Database, - client *gomatrixserverlib.Client, + client *fclient.Client, activeRemoteRequests *types.ActiveRemoteRequests, activeThumbnailGeneration *types.ActiveThumbnailGeneration, ) http.HandlerFunc { diff --git a/relayapi/api/api.go b/relayapi/api/api.go index 9b4b62e58..f0ed83262 100644 --- a/relayapi/api/api.go +++ b/relayapi/api/api.go @@ -18,6 +18,7 @@ import ( "context" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) // RelayInternalAPI is used to query information from the relay server. @@ -51,7 +52,7 @@ type RelayServerAPI interface { QueryTransactions( ctx context.Context, userID gomatrixserverlib.UserID, - previousEntry gomatrixserverlib.RelayEntry, + previousEntry fclient.RelayEntry, ) (QueryRelayTransactionsResponse, error) } diff --git a/relayapi/internal/perform.go b/relayapi/internal/perform.go index 62c7d446e..66d421190 100644 --- a/relayapi/internal/perform.go +++ b/relayapi/internal/perform.go @@ -21,6 +21,7 @@ import ( "github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/relayapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/sirupsen/logrus" ) @@ -46,7 +47,7 @@ func (r *RelayInternalAPI) PerformRelayServerSync( ) error { // Providing a default RelayEntry (EntryID = 0) is done to ask the relay if there are any // transactions available for this node. - prevEntry := gomatrixserverlib.RelayEntry{} + prevEntry := fclient.RelayEntry{} asyncResponse, err := r.fedClient.P2PGetTransactionFromRelay(ctx, userID, prevEntry, relayServer) if err != nil { logrus.Errorf("P2PGetTransactionFromRelay: %s", err.Error()) @@ -54,12 +55,12 @@ func (r *RelayInternalAPI) PerformRelayServerSync( } r.processTransaction(&asyncResponse.Transaction) - prevEntry = gomatrixserverlib.RelayEntry{EntryID: asyncResponse.EntryID} + prevEntry = fclient.RelayEntry{EntryID: asyncResponse.EntryID} for asyncResponse.EntriesQueued { // There are still more entries available for this node from the relay. logrus.Infof("Retrieving next entry from relay, previous: %v", prevEntry) asyncResponse, err = r.fedClient.P2PGetTransactionFromRelay(ctx, userID, prevEntry, relayServer) - prevEntry = gomatrixserverlib.RelayEntry{EntryID: asyncResponse.EntryID} + prevEntry = fclient.RelayEntry{EntryID: asyncResponse.EntryID} if err != nil { logrus.Errorf("P2PGetTransactionFromRelay: %s", err.Error()) return err @@ -97,7 +98,7 @@ func (r *RelayInternalAPI) PerformStoreTransaction( func (r *RelayInternalAPI) QueryTransactions( ctx context.Context, userID gomatrixserverlib.UserID, - previousEntry gomatrixserverlib.RelayEntry, + previousEntry fclient.RelayEntry, ) (api.QueryRelayTransactionsResponse, error) { logrus.Infof("QueryTransactions for %s", userID.Raw()) if previousEntry.EntryID > 0 { diff --git a/relayapi/internal/perform_test.go b/relayapi/internal/perform_test.go index 278706a3e..2c5e1f1f0 100644 --- a/relayapi/internal/perform_test.go +++ b/relayapi/internal/perform_test.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/dendrite/relayapi/storage/shared" "github.com/matrix-org/dendrite/test" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/stretchr/testify/assert" ) @@ -37,15 +38,15 @@ type testFedClient struct { func (f *testFedClient) P2PGetTransactionFromRelay( ctx context.Context, u gomatrixserverlib.UserID, - prev gomatrixserverlib.RelayEntry, + prev fclient.RelayEntry, relayServer gomatrixserverlib.ServerName, -) (res gomatrixserverlib.RespGetRelayTransaction, err error) { +) (res fclient.RespGetRelayTransaction, err error) { f.queryCount++ if f.shouldFail { return res, fmt.Errorf("Error") } - res = gomatrixserverlib.RespGetRelayTransaction{ + res = fclient.RespGetRelayTransaction{ Transaction: gomatrixserverlib.Transaction{}, EntryID: 0, } diff --git a/relayapi/relayapi.go b/relayapi/relayapi.go index fdc366e4c..bae6b0cf5 100644 --- a/relayapi/relayapi.go +++ b/relayapi/relayapi.go @@ -26,6 +26,7 @@ import ( rsAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/sirupsen/logrus" ) @@ -53,7 +54,7 @@ func AddPublicRoutes( func NewRelayInternalAPI( dendriteCfg *config.Dendrite, cm sqlutil.Connections, - fedClient *gomatrixserverlib.FederationClient, + fedClient *fclient.FederationClient, rsAPI rsAPI.RoomserverInternalAPI, keyRing *gomatrixserverlib.KeyRing, producer *producers.SyncAPIProducer, diff --git a/relayapi/relayapi_test.go b/relayapi/relayapi_test.go index 8973c8cc9..27426221c 100644 --- a/relayapi/relayapi_test.go +++ b/relayapi/relayapi_test.go @@ -32,6 +32,7 @@ import ( "github.com/matrix-org/dendrite/test" "github.com/matrix-org/dendrite/test/testrig" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/stretchr/testify/assert" ) @@ -79,7 +80,7 @@ func createGetRelayTxnHTTPRequest(serverName gomatrixserverlib.ServerName, userI pk := sk.Public().(ed25519.PublicKey) origin := gomatrixserverlib.ServerName(hex.EncodeToString(pk)) req := gomatrixserverlib.NewFederationRequest("GET", origin, serverName, "/_matrix/federation/v1/relay_txn/"+userID) - content := gomatrixserverlib.RelayEntry{EntryID: 0} + content := fclient.RelayEntry{EntryID: 0} req.SetContent(content) req.Sign(origin, gomatrixserverlib.KeyID(keyID), sk) httpreq, _ := req.HTTPRequest() diff --git a/relayapi/routing/relaytxn.go b/relayapi/routing/relaytxn.go index 63b42ec7d..06a2a07f2 100644 --- a/relayapi/routing/relaytxn.go +++ b/relayapi/routing/relaytxn.go @@ -21,6 +21,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/relayapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/sirupsen/logrus" ) @@ -35,7 +36,7 @@ func GetTransactionFromRelay( ) util.JSONResponse { logrus.Infof("Processing relay_txn for %s", userID.Raw()) - var previousEntry gomatrixserverlib.RelayEntry + var previousEntry fclient.RelayEntry if err := json.Unmarshal(fedReq.Content(), &previousEntry); err != nil { return util.JSONResponse{ Code: http.StatusInternalServerError, @@ -59,7 +60,7 @@ func GetTransactionFromRelay( return util.JSONResponse{ Code: http.StatusOK, - JSON: gomatrixserverlib.RespGetRelayTransaction{ + JSON: fclient.RespGetRelayTransaction{ Transaction: response.Transaction, EntryID: response.EntryID, EntriesQueued: response.EntriesQueued, diff --git a/relayapi/routing/relaytxn_test.go b/relayapi/routing/relaytxn_test.go index 4c099a642..bc76ddf2c 100644 --- a/relayapi/routing/relaytxn_test.go +++ b/relayapi/routing/relaytxn_test.go @@ -25,12 +25,13 @@ import ( "github.com/matrix-org/dendrite/relayapi/storage/shared" "github.com/matrix-org/dendrite/test" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/stretchr/testify/assert" ) func createQuery( userID gomatrixserverlib.UserID, - prevEntry gomatrixserverlib.RelayEntry, + prevEntry fclient.RelayEntry, ) gomatrixserverlib.FederationRequest { var federationPathPrefixV1 = "/_matrix/federation/v1" path := federationPathPrefixV1 + "/relay_txn/" + userID.Raw() @@ -60,11 +61,11 @@ func TestGetEmptyDatabaseReturnsNothing(t *testing.T) { &db, nil, nil, nil, nil, false, "", true, ) - request := createQuery(*userID, gomatrixserverlib.RelayEntry{}) + request := createQuery(*userID, fclient.RelayEntry{}) response := routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID) assert.Equal(t, http.StatusOK, response.Code) - jsonResponse := response.JSON.(gomatrixserverlib.RespGetRelayTransaction) + jsonResponse := response.JSON.(fclient.RespGetRelayTransaction) assert.Equal(t, false, jsonResponse.EntriesQueued) assert.Equal(t, gomatrixserverlib.Transaction{}, jsonResponse.Transaction) @@ -93,7 +94,7 @@ func TestGetInvalidPrevEntryFails(t *testing.T) { &db, nil, nil, nil, nil, false, "", true, ) - request := createQuery(*userID, gomatrixserverlib.RelayEntry{EntryID: -1}) + request := createQuery(*userID, fclient.RelayEntry{EntryID: -1}) response := routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID) assert.Equal(t, http.StatusInternalServerError, response.Code) } @@ -126,20 +127,20 @@ func TestGetReturnsSavedTransaction(t *testing.T) { &db, nil, nil, nil, nil, false, "", true, ) - request := createQuery(*userID, gomatrixserverlib.RelayEntry{}) + request := createQuery(*userID, fclient.RelayEntry{}) response := routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID) assert.Equal(t, http.StatusOK, response.Code) - jsonResponse := response.JSON.(gomatrixserverlib.RespGetRelayTransaction) + jsonResponse := response.JSON.(fclient.RespGetRelayTransaction) assert.True(t, jsonResponse.EntriesQueued) assert.Equal(t, transaction, jsonResponse.Transaction) // And once more to clear the queue - request = createQuery(*userID, gomatrixserverlib.RelayEntry{EntryID: jsonResponse.EntryID}) + request = createQuery(*userID, fclient.RelayEntry{EntryID: jsonResponse.EntryID}) response = routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID) assert.Equal(t, http.StatusOK, response.Code) - jsonResponse = response.JSON.(gomatrixserverlib.RespGetRelayTransaction) + jsonResponse = response.JSON.(fclient.RespGetRelayTransaction) assert.False(t, jsonResponse.EntriesQueued) assert.Equal(t, gomatrixserverlib.Transaction{}, jsonResponse.Transaction) @@ -189,28 +190,28 @@ func TestGetReturnsMultipleSavedTransactions(t *testing.T) { &db, nil, nil, nil, nil, false, "", true, ) - request := createQuery(*userID, gomatrixserverlib.RelayEntry{}) + request := createQuery(*userID, fclient.RelayEntry{}) response := routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID) assert.Equal(t, http.StatusOK, response.Code) - jsonResponse := response.JSON.(gomatrixserverlib.RespGetRelayTransaction) + jsonResponse := response.JSON.(fclient.RespGetRelayTransaction) assert.True(t, jsonResponse.EntriesQueued) assert.Equal(t, transaction, jsonResponse.Transaction) - request = createQuery(*userID, gomatrixserverlib.RelayEntry{EntryID: jsonResponse.EntryID}) + request = createQuery(*userID, fclient.RelayEntry{EntryID: jsonResponse.EntryID}) response = routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID) assert.Equal(t, http.StatusOK, response.Code) - jsonResponse = response.JSON.(gomatrixserverlib.RespGetRelayTransaction) + jsonResponse = response.JSON.(fclient.RespGetRelayTransaction) assert.True(t, jsonResponse.EntriesQueued) assert.Equal(t, transaction2, jsonResponse.Transaction) // And once more to clear the queue - request = createQuery(*userID, gomatrixserverlib.RelayEntry{EntryID: jsonResponse.EntryID}) + request = createQuery(*userID, fclient.RelayEntry{EntryID: jsonResponse.EntryID}) response = routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID) assert.Equal(t, http.StatusOK, response.Code) - jsonResponse = response.JSON.(gomatrixserverlib.RespGetRelayTransaction) + jsonResponse = response.JSON.(fclient.RespGetRelayTransaction) assert.False(t, jsonResponse.EntriesQueued) assert.Equal(t, gomatrixserverlib.Transaction{}, jsonResponse.Transaction) diff --git a/relayapi/routing/sendrelay.go b/relayapi/routing/sendrelay.go index ce744cb49..84c241032 100644 --- a/relayapi/routing/sendrelay.go +++ b/relayapi/routing/sendrelay.go @@ -21,6 +21,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/relayapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/sirupsen/logrus" ) @@ -36,7 +37,7 @@ func SendTransactionToRelay( ) util.JSONResponse { logrus.Infof("Processing send_relay for %s", userID.Raw()) - var txnEvents gomatrixserverlib.RelayEvents + var txnEvents fclient.RelayEvents if err := json.Unmarshal(fedReq.Content(), &txnEvents); err != nil { logrus.Info("The request body could not be decoded into valid JSON." + err.Error()) return util.JSONResponse{ diff --git a/roomserver/api/api.go b/roomserver/api/api.go index f6d003a44..dda5bb5a4 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -144,7 +144,6 @@ type ClientRoomserverAPI interface { QueryKnownUsers(ctx context.Context, req *QueryKnownUsersRequest, res *QueryKnownUsersResponse) error QueryRoomVersionForRoom(ctx context.Context, req *QueryRoomVersionForRoomRequest, res *QueryRoomVersionForRoomResponse) error QueryPublishedRooms(ctx context.Context, req *QueryPublishedRoomsRequest, res *QueryPublishedRoomsResponse) error - QueryRoomVersionCapabilities(ctx context.Context, req *QueryRoomVersionCapabilitiesRequest, res *QueryRoomVersionCapabilitiesResponse) error GetRoomIDForAlias(ctx context.Context, req *GetRoomIDForAliasRequest, res *GetRoomIDForAliasResponse) error GetAliasesForRoomID(ctx context.Context, req *GetAliasesForRoomIDRequest, res *GetAliasesForRoomIDResponse) error diff --git a/roomserver/api/query.go b/roomserver/api/query.go index 24722db0b..612c33156 100644 --- a/roomserver/api/query.go +++ b/roomserver/api/query.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" + "github.com/matrix-org/dendrite/syncapi/synctypes" ) // QueryLatestEventsAndStateRequest is a request to QueryLatestEventsAndState @@ -146,7 +147,7 @@ type QueryMembershipsForRoomRequest struct { // QueryMembershipsForRoomResponse is a response to QueryMembershipsForRoom type QueryMembershipsForRoomResponse struct { // The "m.room.member" events (of "join" membership) in the client format - JoinEvents []gomatrixserverlib.ClientEvent `json:"join_events"` + JoinEvents []synctypes.ClientEvent `json:"join_events"` // True if the user has been in room before and has either stayed in it or // left it. HasBeenInRoom bool `json:"has_been_in_room"` @@ -240,15 +241,6 @@ type QueryStateAndAuthChainResponse struct { IsRejected bool `json:"is_rejected"` } -// QueryRoomVersionCapabilitiesRequest asks for the default room version -type QueryRoomVersionCapabilitiesRequest struct{} - -// QueryRoomVersionCapabilitiesResponse is a response to QueryRoomVersionCapabilitiesRequest -type QueryRoomVersionCapabilitiesResponse struct { - DefaultRoomVersion gomatrixserverlib.RoomVersion `json:"default"` - AvailableRoomVersions map[gomatrixserverlib.RoomVersion]string `json:"available"` -} - // QueryRoomVersionForRoomRequest asks for the room version for a given room. type QueryRoomVersionForRoomRequest struct { RoomID string `json:"room_id"` diff --git a/roomserver/api/wrapper.go b/roomserver/api/wrapper.go index f220560ed..831ffe25c 100644 --- a/roomserver/api/wrapper.go +++ b/roomserver/api/wrapper.go @@ -18,6 +18,7 @@ import ( "context" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/sirupsen/logrus" ) @@ -49,10 +50,10 @@ func SendEvents( func SendEventWithState( ctx context.Context, rsAPI InputRoomEventsAPI, virtualHost gomatrixserverlib.ServerName, kind Kind, - state *gomatrixserverlib.RespState, event *gomatrixserverlib.HeaderedEvent, + state gomatrixserverlib.StateResponse, event *gomatrixserverlib.HeaderedEvent, origin gomatrixserverlib.ServerName, haveEventIDs map[string]bool, async bool, ) error { - outliers := state.Events(event.RoomVersion) + outliers := gomatrixserverlib.LineariseStateResponse(event.RoomVersion, state) ires := make([]InputRoomEvent, 0, len(outliers)) for _, outlier := range outliers { if haveEventIDs[outlier.EventID()] { @@ -65,7 +66,7 @@ func SendEventWithState( }) } - stateEvents := state.StateEvents.UntrustedEvents(event.RoomVersion) + stateEvents := state.GetStateEvents().UntrustedEvents(event.RoomVersion) stateEventIDs := make([]string, len(stateEvents)) for i := range stateEvents { stateEventIDs[i] = stateEvents[i].EventID() @@ -159,7 +160,7 @@ func IsServerBannedFromRoom(ctx context.Context, rsAPI FederationRoomserverAPI, // PopulatePublicRooms extracts PublicRoom information for all the provided room IDs. The IDs are not checked to see if they are visible in the // published room directory. // due to lots of switches -func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkStateContentAPI) ([]gomatrixserverlib.PublicRoom, error) { +func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkStateContentAPI) ([]fclient.PublicRoom, error) { avatarTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.avatar", StateKey: ""} nameTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.name", StateKey: ""} canonicalTuple := gomatrixserverlib.StateKeyTuple{EventType: gomatrixserverlib.MRoomCanonicalAlias, StateKey: ""} @@ -181,10 +182,10 @@ func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkS util.GetLogger(ctx).WithError(err).Error("QueryBulkStateContent failed") return nil, err } - chunk := make([]gomatrixserverlib.PublicRoom, len(roomIDs)) + chunk := make([]fclient.PublicRoom, len(roomIDs)) i := 0 for roomID, data := range stateRes.Rooms { - pub := gomatrixserverlib.PublicRoom{ + pub := fclient.PublicRoom{ RoomID: roomID, } joinCount := 0 diff --git a/roomserver/internal/api.go b/roomserver/internal/api.go index 7ca3675da..47e1bf2ff 100644 --- a/roomserver/internal/api.go +++ b/roomserver/internal/api.go @@ -160,6 +160,7 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederatio Cfg: &r.Cfg.RoomServer, DB: r.DB, FSAPI: r.fsAPI, + RSAPI: r, Inputer: r.Inputer, } r.Publisher = &perform.Publisher{ diff --git a/roomserver/internal/input/input.go b/roomserver/internal/input/input.go index cc0c673d0..83aa9e90f 100644 --- a/roomserver/internal/input/input.go +++ b/roomserver/internal/input/input.go @@ -24,6 +24,7 @@ import ( "time" userapi "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/Arceliar/phony" "github.com/getsentry/sentry-go" @@ -79,7 +80,7 @@ type Inputer struct { JetStream nats.JetStreamContext Durable nats.SubOpt ServerName gomatrixserverlib.ServerName - SigningIdentity *gomatrixserverlib.SigningIdentity + SigningIdentity *fclient.SigningIdentity FSAPI fedapi.RoomserverFederationAPI KeyRing gomatrixserverlib.JSONVerifier ACLs *acls.ServerACLs diff --git a/roomserver/internal/input/input_events.go b/roomserver/internal/input/input_events.go index 7c7a902f5..971befa07 100644 --- a/roomserver/internal/input/input_events.go +++ b/roomserver/internal/input/input_events.go @@ -27,6 +27,7 @@ import ( "github.com/tidwall/gjson" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -646,7 +647,7 @@ func (r *Inputer) fetchAuthEvents( } var err error - var res gomatrixserverlib.RespEventAuth + var res fclient.RespEventAuth var found bool for _, serverName := range servers { // Request the entire auth chain for the event in question. This should diff --git a/roomserver/internal/input/input_missing.go b/roomserver/internal/input/input_missing.go index daef957f1..16bc18d8b 100644 --- a/roomserver/internal/input/input_missing.go +++ b/roomserver/internal/input/input_missing.go @@ -8,6 +8,7 @@ import ( "time" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/sirupsen/logrus" @@ -517,10 +518,10 @@ func (t *missingStateReq) getMissingEvents(ctx context.Context, e *gomatrixserve t.hadEvent(ev.EventID) } - var missingResp *gomatrixserverlib.RespMissingEvents + var missingResp *fclient.RespMissingEvents for _, server := range t.servers { - var m gomatrixserverlib.RespMissingEvents - if m, err = t.federation.LookupMissingEvents(ctx, t.virtualHost, server, e.RoomID(), gomatrixserverlib.MissingEvents{ + var m fclient.RespMissingEvents + if m, err = t.federation.LookupMissingEvents(ctx, t.virtualHost, server, e.RoomID(), fclient.MissingEvents{ Limit: 20, // The latest event IDs that the sender already has. These are skipped when retrieving the previous events of latest_events. EarliestEvents: latestEvents, @@ -640,27 +641,19 @@ func (t *missingStateReq) lookupMissingStateViaState( if err != nil { return nil, err } + // Check that the returned state is valid. - authEvents, stateEvents, err := state.Check(ctx, roomVersion, t.keys, nil) + authEvents, stateEvents, err := gomatrixserverlib.CheckStateResponse(ctx, &fclient.RespState{ + StateEvents: state.GetStateEvents(), + AuthEvents: state.GetAuthEvents(), + }, roomVersion, t.keys, nil) if err != nil { return nil, err } - parsedState := &parsedRespState{ + return &parsedRespState{ AuthEvents: authEvents, StateEvents: stateEvents, - } - // Cache the results of this state lookup and deduplicate anything we already - // have in the cache, freeing up memory. - // We load these as trusted as we called state.Check before which loaded them as untrusted. - for i, evJSON := range state.AuthEvents { - ev, _ := gomatrixserverlib.NewEventFromTrustedJSON(evJSON, false, roomVersion) - parsedState.AuthEvents[i] = t.cacheAndReturn(ev) - } - for i, evJSON := range state.StateEvents { - ev, _ := gomatrixserverlib.NewEventFromTrustedJSON(evJSON, false, roomVersion) - parsedState.StateEvents[i] = t.cacheAndReturn(ev) - } - return parsedState, nil + }, nil } func (t *missingStateReq) lookupMissingStateViaStateIDs(ctx context.Context, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion) ( @@ -670,7 +663,7 @@ func (t *missingStateReq) lookupMissingStateViaStateIDs(ctx context.Context, roo t.log.Infof("lookupMissingStateViaStateIDs %s", eventID) // fetch the state event IDs at the time of the event - var stateIDs gomatrixserverlib.RespStateIDs + var stateIDs gomatrixserverlib.StateIDResponse var err error count := 0 totalctx, totalcancel := context.WithTimeout(ctx, time.Minute*5) @@ -688,7 +681,7 @@ func (t *missingStateReq) lookupMissingStateViaStateIDs(ctx context.Context, roo return nil, fmt.Errorf("t.federation.LookupStateIDs tried %d server(s), last error: %w", count, err) } // work out which auth/state IDs are missing - wantIDs := append(stateIDs.StateEventIDs, stateIDs.AuthEventIDs...) + wantIDs := append(stateIDs.GetStateEventIDs(), stateIDs.GetAuthEventIDs()...) missing := make(map[string]bool) var missingEventList []string t.haveEventsMutex.Lock() @@ -730,8 +723,8 @@ func (t *missingStateReq) lookupMissingStateViaStateIDs(ctx context.Context, roo t.log.WithFields(logrus.Fields{ "missing": missingCount, "event_id": eventID, - "total_state": len(stateIDs.StateEventIDs), - "total_auth_events": len(stateIDs.AuthEventIDs), + "total_state": len(stateIDs.GetStateEventIDs()), + "total_auth_events": len(stateIDs.GetAuthEventIDs()), }).Debug("Fetching all state at event") return t.lookupMissingStateViaState(ctx, roomID, eventID, roomVersion) } @@ -740,8 +733,8 @@ func (t *missingStateReq) lookupMissingStateViaStateIDs(ctx context.Context, roo t.log.WithFields(logrus.Fields{ "missing": missingCount, "event_id": eventID, - "total_state": len(stateIDs.StateEventIDs), - "total_auth_events": len(stateIDs.AuthEventIDs), + "total_state": len(stateIDs.GetStateEventIDs()), + "total_auth_events": len(stateIDs.GetAuthEventIDs()), "concurrent_requests": concurrentRequests, }).Debug("Fetching missing state at event") @@ -808,7 +801,7 @@ func (t *missingStateReq) lookupMissingStateViaStateIDs(ctx context.Context, roo } func (t *missingStateReq) createRespStateFromStateIDs( - stateIDs gomatrixserverlib.RespStateIDs, + stateIDs gomatrixserverlib.StateIDResponse, ) (*parsedRespState, error) { // nolint:unparam t.haveEventsMutex.Lock() defer t.haveEventsMutex.Unlock() @@ -816,18 +809,20 @@ func (t *missingStateReq) createRespStateFromStateIDs( // create a RespState response using the response to /state_ids as a guide respState := parsedRespState{} - for i := range stateIDs.StateEventIDs { - ev, ok := t.haveEvents[stateIDs.StateEventIDs[i]] + stateEventIDs := stateIDs.GetStateEventIDs() + authEventIDs := stateIDs.GetAuthEventIDs() + for i := range stateEventIDs { + ev, ok := t.haveEvents[stateEventIDs[i]] if !ok { - logrus.Tracef("Missing state event in createRespStateFromStateIDs: %s", stateIDs.StateEventIDs[i]) + logrus.Tracef("Missing state event in createRespStateFromStateIDs: %s", stateEventIDs[i]) continue } respState.StateEvents = append(respState.StateEvents, ev) } - for i := range stateIDs.AuthEventIDs { - ev, ok := t.haveEvents[stateIDs.AuthEventIDs[i]] + for i := range authEventIDs { + ev, ok := t.haveEvents[authEventIDs[i]] if !ok { - logrus.Tracef("Missing auth event in createRespStateFromStateIDs: %s", stateIDs.AuthEventIDs[i]) + logrus.Tracef("Missing auth event in createRespStateFromStateIDs: %s", authEventIDs[i]) continue } respState.AuthEvents = append(respState.AuthEvents, ev) diff --git a/roomserver/internal/perform/perform_admin.go b/roomserver/internal/perform/perform_admin.go index 0f1249114..f35e40bc9 100644 --- a/roomserver/internal/perform/perform_admin.go +++ b/roomserver/internal/perform/perform_admin.go @@ -322,7 +322,7 @@ func (r *Admin) PerformAdminDownloadState( stateEventMap := map[string]*gomatrixserverlib.Event{} for _, fwdExtremity := range fwdExtremities { - var state gomatrixserverlib.RespState + var state gomatrixserverlib.StateResponse state, err = r.Inputer.FSAPI.LookupState(ctx, r.Inputer.ServerName, req.ServerName, req.RoomID, fwdExtremity.EventID, roomInfo.RoomVersion) if err != nil { res.Error = &api.PerformError{ @@ -331,13 +331,13 @@ func (r *Admin) PerformAdminDownloadState( } return nil } - for _, authEvent := range state.AuthEvents.UntrustedEvents(roomInfo.RoomVersion) { + for _, authEvent := range state.GetAuthEvents().UntrustedEvents(roomInfo.RoomVersion) { if err = authEvent.VerifyEventSignatures(ctx, r.Inputer.KeyRing); err != nil { continue } authEventMap[authEvent.EventID()] = authEvent } - for _, stateEvent := range state.StateEvents.UntrustedEvents(roomInfo.RoomVersion) { + for _, stateEvent := range state.GetStateEvents().UntrustedEvents(roomInfo.RoomVersion) { if err = stateEvent.VerifyEventSignatures(ctx, r.Inputer.KeyRing); err != nil { continue } diff --git a/roomserver/internal/perform/perform_join.go b/roomserver/internal/perform/perform_join.go index fc7ba940c..02383a452 100644 --- a/roomserver/internal/perform/perform_join.go +++ b/roomserver/internal/perform/perform_join.go @@ -35,7 +35,6 @@ import ( "github.com/matrix-org/dendrite/roomserver/internal/input" "github.com/matrix-org/dendrite/roomserver/internal/query" "github.com/matrix-org/dendrite/roomserver/storage" - "github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/dendrite/setup/config" ) @@ -305,7 +304,13 @@ func (r *Joiner) performJoinRoomByID( // locally on the homeserver. // TODO: Check what happens if the room exists on the server // but everyone has since left. I suspect it does the wrong thing. - event, buildRes, err := buildEvent(ctx, r.DB, r.Cfg.Matrix, userDomain, &eb) + + var buildRes rsAPI.QueryLatestEventsAndStateResponse + identity, err := r.Cfg.Matrix.SigningIdentityFor(userDomain) + if err != nil { + return "", "", fmt.Errorf("error joining local room: %q", err) + } + event, err := eventutil.QueryAndBuildEvent(ctx, &eb, r.Cfg.Matrix, identity, time.Now(), r.RSAPI, &buildRes) switch err { case nil: @@ -430,46 +435,3 @@ func (r *Joiner) populateAuthorisedViaUserForRestrictedJoin( } return res.AuthorisedVia, nil } - -func buildEvent( - ctx context.Context, db storage.Database, cfg *config.Global, - senderDomain gomatrixserverlib.ServerName, - builder *gomatrixserverlib.EventBuilder, -) (*gomatrixserverlib.HeaderedEvent, *rsAPI.QueryLatestEventsAndStateResponse, error) { - eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder) - if err != nil { - return nil, nil, fmt.Errorf("gomatrixserverlib.StateNeededForEventBuilder: %w", err) - } - - if len(eventsNeeded.Tuples()) == 0 { - return nil, nil, errors.New("expecting state tuples for event builder, got none") - } - - var queryRes rsAPI.QueryLatestEventsAndStateResponse - err = helpers.QueryLatestEventsAndState(ctx, db, &rsAPI.QueryLatestEventsAndStateRequest{ - RoomID: builder.RoomID, - StateToFetch: eventsNeeded.Tuples(), - }, &queryRes) - if err != nil { - switch err.(type) { - case types.MissingStateError: - // We know something about the room but the state seems to be - // insufficient to actually build a new event, so in effect we - // had might as well treat the room as if it doesn't exist. - return nil, nil, eventutil.ErrRoomNoExists - default: - return nil, nil, fmt.Errorf("QueryLatestEventsAndState: %w", err) - } - } - - identity, err := cfg.SigningIdentityFor(senderDomain) - if err != nil { - return nil, nil, err - } - - ev, err := eventutil.BuildEvent(ctx, builder, cfg, identity, time.Now(), &eventsNeeded, &queryRes) - if err != nil { - return nil, nil, err - } - return ev, &queryRes, nil -} diff --git a/roomserver/internal/perform/perform_leave.go b/roomserver/internal/perform/perform_leave.go index 86f1dfaee..00b6c50c3 100644 --- a/roomserver/internal/perform/perform_leave.go +++ b/roomserver/internal/perform/perform_leave.go @@ -19,8 +19,10 @@ import ( "encoding/json" "fmt" "strings" + "time" "github.com/matrix-org/dendrite/clientapi/jsonerror" + "github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -28,6 +30,7 @@ import ( fsAPI "github.com/matrix-org/dendrite/federationapi/api" "github.com/matrix-org/dendrite/roomserver/api" + rsAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/internal/helpers" "github.com/matrix-org/dendrite/roomserver/internal/input" "github.com/matrix-org/dendrite/roomserver/storage" @@ -39,6 +42,7 @@ type Leaver struct { Cfg *config.RoomServer DB storage.Database FSAPI fsAPI.RoomserverFederationAPI + RSAPI rsAPI.RoomserverInternalAPI UserAPI userapi.RoomserverUserAPI Inputer *input.Inputer } @@ -173,9 +177,15 @@ func (r *Leaver) performLeaveRoomByID( // a leave event. // TODO: Check what happens if the room exists on the server // but everyone has since left. I suspect it does the wrong thing. - event, buildRes, err := buildEvent(ctx, r.DB, r.Cfg.Matrix, senderDomain, &eb) + + var buildRes rsAPI.QueryLatestEventsAndStateResponse + identity, err := r.Cfg.Matrix.SigningIdentityFor(senderDomain) if err != nil { - return nil, fmt.Errorf("eventutil.BuildEvent: %w", err) + return nil, fmt.Errorf("SigningIdentityFor: %w", err) + } + event, err := eventutil.QueryAndBuildEvent(ctx, &eb, r.Cfg.Matrix, identity, time.Now(), r.RSAPI, &buildRes) + if err != nil { + return nil, fmt.Errorf("eventutil.QueryAndBuildEvent: %w", err) } // Give our leave event to the roomserver input stream. The diff --git a/roomserver/internal/perform/perform_upgrade.go b/roomserver/internal/perform/perform_upgrade.go index 02a19911c..b739b0676 100644 --- a/roomserver/internal/perform/perform_upgrade.go +++ b/roomserver/internal/perform/perform_upgrade.go @@ -526,7 +526,7 @@ func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, user builder.PrevEvents = []gomatrixserverlib.EventReference{builtEvents[i-1].EventReference()} } var event *gomatrixserverlib.Event - event, err = r.buildEvent(&builder, userDomain, &authEvents, evTime, gomatrixserverlib.RoomVersion(newVersion)) + event, err = builder.AddAuthEventsAndBuild(userDomain, &authEvents, evTime, gomatrixserverlib.RoomVersion(newVersion), r.Cfg.Matrix.KeyID, r.Cfg.Matrix.PrivateKey) if err != nil { return &api.PerformError{ Msg: fmt.Sprintf("Failed to build new %q event: %s", builder.Type, err), @@ -707,29 +707,3 @@ func (r *Upgrader) sendHeaderedEvent( return nil } - -func (r *Upgrader) buildEvent( - builder *gomatrixserverlib.EventBuilder, - serverName gomatrixserverlib.ServerName, - provider gomatrixserverlib.AuthEventProvider, - evTime time.Time, - roomVersion gomatrixserverlib.RoomVersion, -) (*gomatrixserverlib.Event, error) { - eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder) - if err != nil { - return nil, err - } - refs, err := eventsNeeded.AuthEventReferences(provider) - if err != nil { - return nil, err - } - builder.AuthEvents = refs - event, err := builder.Build( - evTime, serverName, r.Cfg.Matrix.KeyID, - r.Cfg.Matrix.PrivateKey, roomVersion, - ) - if err != nil { - return nil, err - } - return event, nil -} diff --git a/roomserver/internal/query/query.go b/roomserver/internal/query/query.go index c5b74422f..cac8d995f 100644 --- a/roomserver/internal/query/query.go +++ b/roomserver/internal/query/query.go @@ -26,6 +26,7 @@ import ( "github.com/sirupsen/logrus" "github.com/matrix-org/dendrite/roomserver/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/internal/caching" @@ -35,7 +36,6 @@ import ( "github.com/matrix-org/dendrite/roomserver/state" "github.com/matrix-org/dendrite/roomserver/storage" "github.com/matrix-org/dendrite/roomserver/types" - "github.com/matrix-org/dendrite/roomserver/version" ) type Queryer struct { @@ -346,7 +346,7 @@ func (r *Queryer) QueryMembershipsForRoom( return fmt.Errorf("r.DB.Events: %w", err) } for _, event := range events { - clientEvent := gomatrixserverlib.ToClientEvent(event.Event, gomatrixserverlib.FormatAll) + clientEvent := synctypes.ToClientEvent(event.Event, synctypes.FormatAll) response.JoinEvents = append(response.JoinEvents, clientEvent) } return nil @@ -366,7 +366,7 @@ func (r *Queryer) QueryMembershipsForRoom( } response.HasBeenInRoom = true - response.JoinEvents = []gomatrixserverlib.ClientEvent{} + response.JoinEvents = []synctypes.ClientEvent{} var events []types.Event var stateEntries []types.StateEntry @@ -395,7 +395,7 @@ func (r *Queryer) QueryMembershipsForRoom( } for _, event := range events { - clientEvent := gomatrixserverlib.ToClientEvent(event.Event, gomatrixserverlib.FormatAll) + clientEvent := synctypes.ToClientEvent(event.Event, synctypes.FormatAll) response.JoinEvents = append(response.JoinEvents, clientEvent) } @@ -694,25 +694,7 @@ func GetAuthChain( return authEvents, nil } -// QueryRoomVersionCapabilities implements api.RoomserverInternalAPI -func (r *Queryer) QueryRoomVersionCapabilities( - ctx context.Context, - request *api.QueryRoomVersionCapabilitiesRequest, - response *api.QueryRoomVersionCapabilitiesResponse, -) error { - response.DefaultRoomVersion = version.DefaultRoomVersion() - response.AvailableRoomVersions = make(map[gomatrixserverlib.RoomVersion]string) - for v, desc := range version.SupportedRoomVersions() { - if desc.Stable { - response.AvailableRoomVersions[v] = "stable" - } else { - response.AvailableRoomVersions[v] = "unstable" - } - } - return nil -} - -// QueryRoomVersionCapabilities implements api.RoomserverInternalAPI +// QueryRoomVersionForRoom implements api.RoomserverInternalAPI func (r *Queryer) QueryRoomVersionForRoom( ctx context.Context, request *api.QueryRoomVersionForRoomRequest, diff --git a/setup/base/base.go b/setup/base/base.go index e5fd6fed1..d6c350109 100644 --- a/setup/base/base.go +++ b/setup/base/base.go @@ -32,7 +32,7 @@ import ( "time" sentryhttp "github.com/getsentry/sentry-go/http" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/atomic" @@ -54,42 +54,42 @@ const HTTPServerTimeout = time.Minute * 5 // CreateClient creates a new client (normally used for media fetch requests). // Should only be called once per component. -func CreateClient(cfg *config.Dendrite, dnsCache *gomatrixserverlib.DNSCache) *gomatrixserverlib.Client { +func CreateClient(cfg *config.Dendrite, dnsCache *fclient.DNSCache) *fclient.Client { if cfg.Global.DisableFederation { - return gomatrixserverlib.NewClient( - gomatrixserverlib.WithTransport(noOpHTTPTransport), + return fclient.NewClient( + fclient.WithTransport(noOpHTTPTransport), ) } - opts := []gomatrixserverlib.ClientOption{ - gomatrixserverlib.WithSkipVerify(cfg.FederationAPI.DisableTLSValidation), - gomatrixserverlib.WithWellKnownSRVLookups(true), + opts := []fclient.ClientOption{ + fclient.WithSkipVerify(cfg.FederationAPI.DisableTLSValidation), + fclient.WithWellKnownSRVLookups(true), } if cfg.Global.DNSCache.Enabled && dnsCache != nil { - opts = append(opts, gomatrixserverlib.WithDNSCache(dnsCache)) + opts = append(opts, fclient.WithDNSCache(dnsCache)) } - client := gomatrixserverlib.NewClient(opts...) + client := fclient.NewClient(opts...) client.SetUserAgent(fmt.Sprintf("Dendrite/%s", internal.VersionString())) return client } // CreateFederationClient creates a new federation client. Should only be called // once per component. -func CreateFederationClient(cfg *config.Dendrite, dnsCache *gomatrixserverlib.DNSCache) *gomatrixserverlib.FederationClient { +func CreateFederationClient(cfg *config.Dendrite, dnsCache *fclient.DNSCache) *fclient.FederationClient { identities := cfg.Global.SigningIdentities() if cfg.Global.DisableFederation { - return gomatrixserverlib.NewFederationClient( - identities, gomatrixserverlib.WithTransport(noOpHTTPTransport), + return fclient.NewFederationClient( + identities, fclient.WithTransport(noOpHTTPTransport), ) } - opts := []gomatrixserverlib.ClientOption{ - gomatrixserverlib.WithTimeout(time.Minute * 5), - gomatrixserverlib.WithSkipVerify(cfg.FederationAPI.DisableTLSValidation), - gomatrixserverlib.WithKeepAlives(!cfg.FederationAPI.DisableHTTPKeepalives), + opts := []fclient.ClientOption{ + fclient.WithTimeout(time.Minute * 5), + fclient.WithSkipVerify(cfg.FederationAPI.DisableTLSValidation), + fclient.WithKeepAlives(!cfg.FederationAPI.DisableHTTPKeepalives), } if cfg.Global.DNSCache.Enabled { - opts = append(opts, gomatrixserverlib.WithDNSCache(dnsCache)) + opts = append(opts, fclient.WithDNSCache(dnsCache)) } - client := gomatrixserverlib.NewFederationClient( + client := fclient.NewFederationClient( identities, opts..., ) client.SetUserAgent(fmt.Sprintf("Dendrite/%s", internal.VersionString())) diff --git a/setup/config/config_appservice.go b/setup/config/config_appservice.go index 37e20a978..ef10649d2 100644 --- a/setup/config/config_appservice.go +++ b/setup/config/config_appservice.go @@ -15,16 +15,23 @@ package config import ( + "context" + "crypto/tls" "fmt" + "net" + "net/http" "os" "path/filepath" "regexp" "strings" + "time" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" ) +const UnixSocketPrefix = "unix://" + type AppServiceAPI struct { Matrix *Global `yaml:"-"` Derived *Derived `yaml:"-"` // TODO: Nuke Derived from orbit @@ -80,7 +87,41 @@ type ApplicationService struct { // Whether rate limiting is applied to each application service user RateLimited bool `yaml:"rate_limited"` // Any custom protocols that this application service provides (e.g. IRC) - Protocols []string `yaml:"protocols"` + Protocols []string `yaml:"protocols"` + HTTPClient *http.Client + isUnixSocket bool + unixSocket string +} + +func (a *ApplicationService) CreateHTTPClient(insecureSkipVerify bool) { + client := &http.Client{ + Timeout: time.Second * 30, + Transport: &http.Transport{ + DisableKeepAlives: true, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: insecureSkipVerify, + }, + Proxy: http.ProxyFromEnvironment, + }, + } + if strings.HasPrefix(a.URL, UnixSocketPrefix) { + a.isUnixSocket = true + a.unixSocket = "http://unix" + client.Transport = &http.Transport{ + DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { + return net.Dial("unix", strings.TrimPrefix(a.URL, UnixSocketPrefix)) + }, + } + } + a.HTTPClient = client +} + +func (a *ApplicationService) RequestUrl() string { + if a.isUnixSocket { + return a.unixSocket + } else { + return a.URL + } } // IsInterestedInRoomID returns a bool on whether an application service's @@ -152,7 +193,7 @@ func (a *ApplicationService) IsInterestedInRoomAlias( func loadAppServices(config *AppServiceAPI, derived *Derived) error { for _, configPath := range config.ConfigFiles { // Create a new application service with default options - appservice := ApplicationService{ + appservice := &ApplicationService{ RateLimited: true, } @@ -169,13 +210,13 @@ func loadAppServices(config *AppServiceAPI, derived *Derived) error { } // Load the config data into our struct - if err = yaml.Unmarshal(configData, &appservice); err != nil { + if err = yaml.Unmarshal(configData, appservice); err != nil { return err } - + appservice.CreateHTTPClient(config.DisableTLSValidation) // Append the parsed application service to the global config derived.ApplicationServices = append( - derived.ApplicationServices, appservice, + derived.ApplicationServices, *appservice, ) } diff --git a/setup/config/config_global.go b/setup/config/config_global.go index 7d3ab6a40..0687e9d35 100644 --- a/setup/config/config_global.go +++ b/setup/config/config_global.go @@ -8,13 +8,14 @@ import ( "time" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "golang.org/x/crypto/ed25519" ) type Global struct { // Signing identity contains the server name, private key and key ID of // the deployment. - gomatrixserverlib.SigningIdentity `yaml:",inline"` + fclient.SigningIdentity `yaml:",inline"` // The secondary server names, used for virtual hosting. VirtualHosts []*VirtualHost `yaml:"-"` @@ -167,7 +168,7 @@ func (c *Global) VirtualHostForHTTPHost(serverName gomatrixserverlib.ServerName) return nil } -func (c *Global) SigningIdentityFor(serverName gomatrixserverlib.ServerName) (*gomatrixserverlib.SigningIdentity, error) { +func (c *Global) SigningIdentityFor(serverName gomatrixserverlib.ServerName) (*fclient.SigningIdentity, error) { for _, id := range c.SigningIdentities() { if id.ServerName == serverName { return id, nil @@ -176,8 +177,8 @@ func (c *Global) SigningIdentityFor(serverName gomatrixserverlib.ServerName) (*g return nil, fmt.Errorf("no signing identity for %q", serverName) } -func (c *Global) SigningIdentities() []*gomatrixserverlib.SigningIdentity { - identities := make([]*gomatrixserverlib.SigningIdentity, 0, len(c.VirtualHosts)+1) +func (c *Global) SigningIdentities() []*fclient.SigningIdentity { + identities := make([]*fclient.SigningIdentity, 0, len(c.VirtualHosts)+1) identities = append(identities, &c.SigningIdentity) for _, v := range c.VirtualHosts { identities = append(identities, &v.SigningIdentity) @@ -188,7 +189,7 @@ func (c *Global) SigningIdentities() []*gomatrixserverlib.SigningIdentity { type VirtualHost struct { // Signing identity contains the server name, private key and key ID of // the virtual host. - gomatrixserverlib.SigningIdentity `yaml:",inline"` + fclient.SigningIdentity `yaml:",inline"` // Path to the private key. If not specified, the default global private key // will be used instead. diff --git a/setup/config/config_test.go b/setup/config/config_test.go index 79407f30d..a0509aafb 100644 --- a/setup/config/config_test.go +++ b/setup/config/config_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" ) @@ -275,7 +276,7 @@ func Test_SigningIdentityFor(t *testing.T) { name string virtualHosts []*VirtualHost serverName gomatrixserverlib.ServerName - want *gomatrixserverlib.SigningIdentity + want *fclient.SigningIdentity wantErr bool }{ { @@ -290,23 +291,23 @@ func Test_SigningIdentityFor(t *testing.T) { { name: "found identity", serverName: gomatrixserverlib.ServerName("main"), - want: &gomatrixserverlib.SigningIdentity{ServerName: "main"}, + want: &fclient.SigningIdentity{ServerName: "main"}, }, { name: "identity found on virtual hosts", serverName: gomatrixserverlib.ServerName("vh2"), virtualHosts: []*VirtualHost{ - {SigningIdentity: gomatrixserverlib.SigningIdentity{ServerName: "vh1"}}, - {SigningIdentity: gomatrixserverlib.SigningIdentity{ServerName: "vh2"}}, + {SigningIdentity: fclient.SigningIdentity{ServerName: "vh1"}}, + {SigningIdentity: fclient.SigningIdentity{ServerName: "vh2"}}, }, - want: &gomatrixserverlib.SigningIdentity{ServerName: "vh2"}, + want: &fclient.SigningIdentity{ServerName: "vh2"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Global{ VirtualHosts: tt.virtualHosts, - SigningIdentity: gomatrixserverlib.SigningIdentity{ + SigningIdentity: fclient.SigningIdentity{ ServerName: "main", }, } diff --git a/setup/monolith.go b/setup/monolith.go index 51e40070b..e5af69853 100644 --- a/setup/monolith.go +++ b/setup/monolith.go @@ -34,6 +34,7 @@ import ( "github.com/matrix-org/dendrite/syncapi" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) // Monolith represents an instantiation of all dependencies required to build @@ -41,8 +42,8 @@ import ( type Monolith struct { Config *config.Dendrite KeyRing *gomatrixserverlib.KeyRing - Client *gomatrixserverlib.Client - FedClient *gomatrixserverlib.FederationClient + Client *fclient.Client + FedClient *fclient.FederationClient AppserviceAPI appserviceAPI.AppServiceInternalAPI FederationAPI federationAPI.FederationInternalAPI diff --git a/setup/mscs/msc2836/msc2836.go b/setup/mscs/msc2836/msc2836.go index 92248bc10..a12a28b18 100644 --- a/setup/mscs/msc2836/msc2836.go +++ b/setup/mscs/msc2836/msc2836.go @@ -34,8 +34,10 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" roomserver "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" + "github.com/matrix-org/dendrite/syncapi/synctypes" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" ) @@ -78,20 +80,20 @@ func (r *EventRelationshipRequest) Defaults() { } type EventRelationshipResponse struct { - Events []gomatrixserverlib.ClientEvent `json:"events"` - NextBatch string `json:"next_batch"` - Limited bool `json:"limited"` + Events []synctypes.ClientEvent `json:"events"` + NextBatch string `json:"next_batch"` + Limited bool `json:"limited"` } type MSC2836EventRelationshipsResponse struct { - gomatrixserverlib.MSC2836EventRelationshipsResponse + fclient.MSC2836EventRelationshipsResponse ParsedEvents []*gomatrixserverlib.Event ParsedAuthChain []*gomatrixserverlib.Event } func toClientResponse(res *MSC2836EventRelationshipsResponse) *EventRelationshipResponse { out := &EventRelationshipResponse{ - Events: gomatrixserverlib.ToClientEvents(res.ParsedEvents, gomatrixserverlib.FormatAll), + Events: synctypes.ToClientEvents(res.ParsedEvents, synctypes.FormatAll), Limited: res.Limited, NextBatch: res.NextBatch, } @@ -398,7 +400,7 @@ func (rc *reqCtx) includeChildren(db Database, parentID string, limit int, recen serversToQuery := rc.getServersForEventID(parentID) var result *MSC2836EventRelationshipsResponse for _, srv := range serversToQuery { - res, err := rc.fsAPI.MSC2836EventRelationships(rc.ctx, rc.serverName, srv, gomatrixserverlib.MSC2836EventRelationshipsRequest{ + res, err := rc.fsAPI.MSC2836EventRelationships(rc.ctx, rc.serverName, srv, fclient.MSC2836EventRelationshipsRequest{ EventID: parentID, Direction: "down", Limit: 100, @@ -485,7 +487,7 @@ func walkThread( // MSC2836EventRelationships performs an /event_relationships request to a remote server func (rc *reqCtx) MSC2836EventRelationships(eventID string, srv gomatrixserverlib.ServerName, ver gomatrixserverlib.RoomVersion) (*MSC2836EventRelationshipsResponse, error) { - res, err := rc.fsAPI.MSC2836EventRelationships(rc.ctx, rc.serverName, srv, gomatrixserverlib.MSC2836EventRelationshipsRequest{ + res, err := rc.fsAPI.MSC2836EventRelationships(rc.ctx, rc.serverName, srv, fclient.MSC2836EventRelationshipsRequest{ EventID: eventID, DepthFirst: rc.req.DepthFirst, Direction: rc.req.Direction, @@ -652,11 +654,11 @@ func (rc *reqCtx) injectResponseToRoomserver(res *MSC2836EventRelationshipsRespo messageEvents = append(messageEvents, ev) } } - respState := gomatrixserverlib.RespState{ + respState := &fclient.RespState{ AuthEvents: res.AuthChain, StateEvents: stateEvents, } - eventsInOrder := respState.Events(rc.roomVersion) + eventsInOrder := gomatrixserverlib.LineariseStateResponse(rc.roomVersion, respState) // everything gets sent as an outlier because auth chain events may be disjoint from the DAG // as may the threaded events. var ires []roomserver.InputRoomEvent diff --git a/setup/mscs/msc2836/msc2836_test.go b/setup/mscs/msc2836/msc2836_test.go index bfcabef64..3c4431489 100644 --- a/setup/mscs/msc2836/msc2836_test.go +++ b/setup/mscs/msc2836/msc2836_test.go @@ -16,6 +16,7 @@ import ( "github.com/gorilla/mux" "github.com/matrix-org/dendrite/setup/process" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/dendrite/internal/hooks" @@ -462,7 +463,7 @@ func assertContains(t *testing.T, result *msc2836.EventRelationshipResponse, wan } } -func assertUnsignedChildren(t *testing.T, ev gomatrixserverlib.ClientEvent, relType string, wantCount int, childrenEventIDs []string) { +func assertUnsignedChildren(t *testing.T, ev synctypes.ClientEvent, relType string, wantCount int, childrenEventIDs []string) { t.Helper() unsigned := struct { Children map[string]int `json:"children"` diff --git a/setup/mscs/msc2946/msc2946.go b/setup/mscs/msc2946/msc2946.go index d33897b93..965af9207 100644 --- a/setup/mscs/msc2946/msc2946.go +++ b/setup/mscs/msc2946/msc2946.go @@ -36,6 +36,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/tidwall/gjson" ) @@ -48,8 +49,8 @@ const ( ) type MSC2946ClientResponse struct { - Rooms []gomatrixserverlib.MSC2946Room `json:"rooms"` - NextBatch string `json:"next_batch,omitempty"` + Rooms []fclient.MSC2946Room `json:"rooms"` + NextBatch string `json:"next_batch,omitempty"` } // Enable this MSC @@ -222,7 +223,7 @@ func (w *walker) walk() util.JSONResponse { } } - var discoveredRooms []gomatrixserverlib.MSC2946Room + var discoveredRooms []fclient.MSC2946Room var cache *paginationInfo if w.paginationToken != "" { @@ -275,7 +276,7 @@ func (w *walker) walk() util.JSONResponse { } // Collect rooms/events to send back (either locally or fetched via federation) - var discoveredChildEvents []gomatrixserverlib.MSC2946StrippedEvent + var discoveredChildEvents []fclient.MSC2946StrippedEvent // If we know about this room and the caller is authorised (joined/world_readable) then pull // events locally @@ -306,7 +307,7 @@ func (w *walker) walk() util.JSONResponse { pubRoom := w.publicRoomsChunk(rv.roomID) - discoveredRooms = append(discoveredRooms, gomatrixserverlib.MSC2946Room{ + discoveredRooms = append(discoveredRooms, fclient.MSC2946Room{ PublicRoom: *pubRoom, RoomType: roomType, ChildrenState: events, @@ -379,7 +380,7 @@ func (w *walker) walk() util.JSONResponse { } return util.JSONResponse{ Code: 200, - JSON: gomatrixserverlib.MSC2946SpacesResponse{ + JSON: fclient.MSC2946SpacesResponse{ Room: discoveredRooms[0], Children: discoveredRooms[1:], }, @@ -402,7 +403,7 @@ func (w *walker) stateEvent(roomID, evType, stateKey string) *gomatrixserverlib. return queryRes.StateEvents[tuple] } -func (w *walker) publicRoomsChunk(roomID string) *gomatrixserverlib.PublicRoom { +func (w *walker) publicRoomsChunk(roomID string) *fclient.PublicRoom { pubRooms, err := roomserver.PopulatePublicRooms(w.ctx, []string{roomID}, w.rsAPI) if err != nil { util.GetLogger(w.ctx).WithError(err).Error("failed to PopulatePublicRooms") @@ -416,7 +417,7 @@ func (w *walker) publicRoomsChunk(roomID string) *gomatrixserverlib.PublicRoom { // federatedRoomInfo returns more of the spaces graph from another server. Returns nil if this was // unsuccessful. -func (w *walker) federatedRoomInfo(roomID string, vias []string) *gomatrixserverlib.MSC2946SpacesResponse { +func (w *walker) federatedRoomInfo(roomID string, vias []string) *fclient.MSC2946SpacesResponse { // only do federated requests for client requests if w.caller == nil { return nil @@ -440,12 +441,12 @@ func (w *walker) federatedRoomInfo(roomID string, vias []string) *gomatrixserver } // ensure nil slices are empty as we send this to the client sometimes if res.Room.ChildrenState == nil { - res.Room.ChildrenState = []gomatrixserverlib.MSC2946StrippedEvent{} + res.Room.ChildrenState = []fclient.MSC2946StrippedEvent{} } for i := 0; i < len(res.Children); i++ { child := res.Children[i] if child.ChildrenState == nil { - child.ChildrenState = []gomatrixserverlib.MSC2946StrippedEvent{} + child.ChildrenState = []fclient.MSC2946StrippedEvent{} } res.Children[i] = child } @@ -653,7 +654,7 @@ func (w *walker) restrictedJoinRuleAllowedRooms(joinRuleEv *gomatrixserverlib.He } // references returns all child references pointing to or from this room. -func (w *walker) childReferences(roomID string) ([]gomatrixserverlib.MSC2946StrippedEvent, error) { +func (w *walker) childReferences(roomID string) ([]fclient.MSC2946StrippedEvent, error) { createTuple := gomatrixserverlib.StateKeyTuple{ EventType: gomatrixserverlib.MRoomCreate, StateKey: "", @@ -678,12 +679,12 @@ func (w *walker) childReferences(roomID string) ([]gomatrixserverlib.MSC2946Stri // escape the `.`s so gjson doesn't think it's nested roomType := gjson.GetBytes(res.StateEvents[createTuple].Content(), strings.ReplaceAll(ConstCreateEventContentKey, ".", `\.`)).Str if roomType != ConstCreateEventContentValueSpace { - return []gomatrixserverlib.MSC2946StrippedEvent{}, nil + return []fclient.MSC2946StrippedEvent{}, nil } } delete(res.StateEvents, createTuple) - el := make([]gomatrixserverlib.MSC2946StrippedEvent, 0, len(res.StateEvents)) + el := make([]fclient.MSC2946StrippedEvent, 0, len(res.StateEvents)) for _, ev := range res.StateEvents { content := gjson.ParseBytes(ev.Content()) // only return events that have a `via` key as per MSC1772 @@ -720,11 +721,11 @@ func (s set) isSet(val string) bool { return ok } -func stripped(ev *gomatrixserverlib.Event) *gomatrixserverlib.MSC2946StrippedEvent { +func stripped(ev *gomatrixserverlib.Event) *fclient.MSC2946StrippedEvent { if ev.StateKey() == nil { return nil } - return &gomatrixserverlib.MSC2946StrippedEvent{ + return &fclient.MSC2946StrippedEvent{ Type: ev.Type(), StateKey: *ev.StateKey(), Content: ev.Content(), diff --git a/syncapi/internal/keychange.go b/syncapi/internal/keychange.go index e7f677c85..17d63708a 100644 --- a/syncapi/internal/keychange.go +++ b/syncapi/internal/keychange.go @@ -26,6 +26,7 @@ import ( roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/userapi/api" ) @@ -278,7 +279,7 @@ func leftRooms(res *types.Response) []string { return roomIDs } -func membershipEventPresent(events []gomatrixserverlib.ClientEvent, userID string) bool { +func membershipEventPresent(events []synctypes.ClientEvent, userID string) bool { for _, ev := range events { // it's enough to know that we have our member event here, don't need to check membership content // as it's implied by being in the respective section of the sync response. diff --git a/syncapi/internal/keychange_test.go b/syncapi/internal/keychange_test.go index 4bb851668..f775276fe 100644 --- a/syncapi/internal/keychange_test.go +++ b/syncapi/internal/keychange_test.go @@ -10,6 +10,7 @@ import ( "github.com/matrix-org/util" "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -159,7 +160,7 @@ func assertCatchup(t *testing.T, hasNew bool, syncResponse *types.Response, want func joinResponseWithRooms(syncResponse *types.Response, userID string, roomIDs []string) *types.Response { for _, roomID := range roomIDs { - roomEvents := []gomatrixserverlib.ClientEvent{ + roomEvents := []synctypes.ClientEvent{ { Type: "m.room.member", StateKey: &userID, @@ -182,7 +183,7 @@ func joinResponseWithRooms(syncResponse *types.Response, userID string, roomIDs func leaveResponseWithRooms(syncResponse *types.Response, userID string, roomIDs []string) *types.Response { for _, roomID := range roomIDs { - roomEvents := []gomatrixserverlib.ClientEvent{ + roomEvents := []synctypes.ClientEvent{ { Type: "m.room.member", StateKey: &userID, @@ -299,7 +300,7 @@ func TestKeyChangeCatchupNoNewJoinsButMessages(t *testing.T) { roomID := "!TestKeyChangeCatchupNoNewJoinsButMessages:bar" syncResponse := types.NewResponse() empty := "" - roomStateEvents := []gomatrixserverlib.ClientEvent{ + roomStateEvents := []synctypes.ClientEvent{ { Type: "m.room.name", StateKey: &empty, @@ -309,7 +310,7 @@ func TestKeyChangeCatchupNoNewJoinsButMessages(t *testing.T) { Content: []byte(`{"name":"The Room Name"}`), }, } - roomTimelineEvents := []gomatrixserverlib.ClientEvent{ + roomTimelineEvents := []synctypes.ClientEvent{ { Type: "m.room.message", EventID: "$something1:here", @@ -402,7 +403,7 @@ func TestKeyChangeCatchupChangeAndLeftSameRoom(t *testing.T) { newShareUser2 := "@bobby:localhost" roomID := "!join:bar" syncResponse := types.NewResponse() - roomEvents := []gomatrixserverlib.ClientEvent{ + roomEvents := []synctypes.ClientEvent{ { Type: "m.room.member", StateKey: &syncingUser, diff --git a/syncapi/routing/context.go b/syncapi/routing/context.go index 76f003671..dd42c7ac4 100644 --- a/syncapi/routing/context.go +++ b/syncapi/routing/context.go @@ -29,6 +29,7 @@ import ( roomserver "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/internal" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" @@ -37,12 +38,12 @@ import ( ) type ContextRespsonse struct { - End string `json:"end"` - Event *gomatrixserverlib.ClientEvent `json:"event,omitempty"` - EventsAfter []gomatrixserverlib.ClientEvent `json:"events_after,omitempty"` - EventsBefore []gomatrixserverlib.ClientEvent `json:"events_before,omitempty"` - Start string `json:"start"` - State []gomatrixserverlib.ClientEvent `json:"state,omitempty"` + End string `json:"end"` + Event *synctypes.ClientEvent `json:"event,omitempty"` + EventsAfter []synctypes.ClientEvent `json:"events_after,omitempty"` + EventsBefore []synctypes.ClientEvent `json:"events_before,omitempty"` + Start string `json:"start"` + State []synctypes.ClientEvent `json:"state,omitempty"` } func Context( @@ -94,7 +95,7 @@ func Context( } } - stateFilter := gomatrixserverlib.StateFilter{ + stateFilter := synctypes.StateFilter{ NotSenders: filter.NotSenders, NotTypes: filter.NotTypes, Senders: filter.Senders, @@ -167,14 +168,14 @@ func Context( return jsonerror.InternalServerError() } - eventsBeforeClient := gomatrixserverlib.HeaderedToClientEvents(eventsBeforeFiltered, gomatrixserverlib.FormatAll) - eventsAfterClient := gomatrixserverlib.HeaderedToClientEvents(eventsAfterFiltered, gomatrixserverlib.FormatAll) + eventsBeforeClient := synctypes.HeaderedToClientEvents(eventsBeforeFiltered, synctypes.FormatAll) + eventsAfterClient := synctypes.HeaderedToClientEvents(eventsAfterFiltered, synctypes.FormatAll) newState := state if filter.LazyLoadMembers { allEvents := append(eventsBeforeFiltered, eventsAfterFiltered...) allEvents = append(allEvents, &requestedEvent) - evs := gomatrixserverlib.HeaderedToClientEvents(allEvents, gomatrixserverlib.FormatAll) + evs := synctypes.HeaderedToClientEvents(allEvents, synctypes.FormatAll) newState, err = applyLazyLoadMembers(ctx, device, snapshot, roomID, evs, lazyLoadCache) if err != nil { logrus.WithError(err).Error("unable to load membership events") @@ -182,12 +183,12 @@ func Context( } } - ev := gomatrixserverlib.HeaderedToClientEvent(&requestedEvent, gomatrixserverlib.FormatAll) + ev := synctypes.HeaderedToClientEvent(&requestedEvent, synctypes.FormatAll) response := ContextRespsonse{ Event: &ev, EventsAfter: eventsAfterClient, EventsBefore: eventsBeforeClient, - State: gomatrixserverlib.HeaderedToClientEvents(newState, gomatrixserverlib.FormatAll), + State: synctypes.HeaderedToClientEvents(newState, synctypes.FormatAll), } if len(response.State) > filter.Limit { @@ -261,7 +262,7 @@ func applyLazyLoadMembers( device *userapi.Device, snapshot storage.DatabaseTransaction, roomID string, - events []gomatrixserverlib.ClientEvent, + events []synctypes.ClientEvent, lazyLoadCache caching.LazyLoadCache, ) ([]*gomatrixserverlib.HeaderedEvent, error) { eventSenders := make(map[string]struct{}) @@ -280,7 +281,7 @@ func applyLazyLoadMembers( } // Query missing membership events - filter := gomatrixserverlib.DefaultStateFilter() + filter := synctypes.DefaultStateFilter() filter.Senders = &wantUsers filter.Types = &[]string{gomatrixserverlib.MRoomMember} memberships, err := snapshot.GetStateEventsForRoom(ctx, roomID, &filter) @@ -296,9 +297,9 @@ func applyLazyLoadMembers( return memberships, nil } -func parseRoomEventFilter(req *http.Request) (*gomatrixserverlib.RoomEventFilter, error) { +func parseRoomEventFilter(req *http.Request) (*synctypes.RoomEventFilter, error) { // Default room filter - filter := &gomatrixserverlib.RoomEventFilter{Limit: 10} + filter := &synctypes.RoomEventFilter{Limit: 10} l := req.URL.Query().Get("limit") f := req.URL.Query().Get("filter") diff --git a/syncapi/routing/context_test.go b/syncapi/routing/context_test.go index e79a5d5f1..5c6682bd1 100644 --- a/syncapi/routing/context_test.go +++ b/syncapi/routing/context_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/dendrite/syncapi/synctypes" ) func Test_parseContextParams(t *testing.T) { @@ -19,28 +19,28 @@ func Test_parseContextParams(t *testing.T) { tests := []struct { name string req *http.Request - wantFilter *gomatrixserverlib.RoomEventFilter + wantFilter *synctypes.RoomEventFilter wantErr bool }{ { name: "no params set", req: noParamsReq, - wantFilter: &gomatrixserverlib.RoomEventFilter{Limit: 10}, + wantFilter: &synctypes.RoomEventFilter{Limit: 10}, }, { name: "limit 2 param set", req: limit2Req, - wantFilter: &gomatrixserverlib.RoomEventFilter{Limit: 2}, + wantFilter: &synctypes.RoomEventFilter{Limit: 2}, }, { name: "limit 10000 param set", req: limit10000Req, - wantFilter: &gomatrixserverlib.RoomEventFilter{Limit: 100}, + wantFilter: &synctypes.RoomEventFilter{Limit: 100}, }, { name: "filter lazy_load_members param set", req: lazyLoadReq, - wantFilter: &gomatrixserverlib.RoomEventFilter{Limit: 2, LazyLoadMembers: true}, + wantFilter: &synctypes.RoomEventFilter{Limit: 2, LazyLoadMembers: true}, }, { name: "invalid limit req", diff --git a/syncapi/routing/filter.go b/syncapi/routing/filter.go index f5acdbde3..266ad4adc 100644 --- a/syncapi/routing/filter.go +++ b/syncapi/routing/filter.go @@ -26,6 +26,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/syncapi/storage" "github.com/matrix-org/dendrite/syncapi/sync" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/userapi/api" ) @@ -45,7 +46,7 @@ func GetFilter( return jsonerror.InternalServerError() } - filter := gomatrixserverlib.DefaultFilter() + filter := synctypes.DefaultFilter() if err := syncDB.GetFilter(req.Context(), &filter, localpart, filterID); err != nil { //TODO better error handling. This error message is *probably* right, // but if there are obscure db errors, this will also be returned, @@ -85,7 +86,7 @@ func PutFilter( return jsonerror.InternalServerError() } - var filter gomatrixserverlib.Filter + var filter synctypes.Filter defer req.Body.Close() // nolint:errcheck body, err := io.ReadAll(req.Body) diff --git a/syncapi/routing/getevent.go b/syncapi/routing/getevent.go index d2cdc1b5f..84986d3b3 100644 --- a/syncapi/routing/getevent.go +++ b/syncapi/routing/getevent.go @@ -17,7 +17,6 @@ package routing import ( "net/http" - "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" "github.com/sirupsen/logrus" @@ -26,6 +25,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/syncapi/internal" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -97,6 +97,6 @@ func GetEvent( return util.JSONResponse{ Code: http.StatusOK, - JSON: gomatrixserverlib.HeaderedToClientEvent(events[0], gomatrixserverlib.FormatAll), + JSON: synctypes.HeaderedToClientEvent(events[0], synctypes.FormatAll), } } diff --git a/syncapi/routing/memberships.go b/syncapi/routing/memberships.go index 8efd77cef..9ea660f59 100644 --- a/syncapi/routing/memberships.go +++ b/syncapi/routing/memberships.go @@ -22,14 +22,14 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" ) type getMembershipResponse struct { - Chunk []gomatrixserverlib.ClientEvent `json:"chunk"` + Chunk []synctypes.ClientEvent `json:"chunk"` } // https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-joined-members @@ -134,6 +134,6 @@ func GetMemberships( } return util.JSONResponse{ Code: http.StatusOK, - JSON: getMembershipResponse{gomatrixserverlib.HeaderedToClientEvents(result, gomatrixserverlib.FormatAll)}, + JSON: getMembershipResponse{synctypes.HeaderedToClientEvents(result, synctypes.FormatAll)}, } } diff --git a/syncapi/routing/messages.go b/syncapi/routing/messages.go index 02d8fcc7e..3c4166272 100644 --- a/syncapi/routing/messages.go +++ b/syncapi/routing/messages.go @@ -34,6 +34,7 @@ import ( "github.com/matrix-org/dendrite/syncapi/internal" "github.com/matrix-org/dendrite/syncapi/storage" "github.com/matrix-org/dendrite/syncapi/sync" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -50,15 +51,15 @@ type messagesReq struct { device *userapi.Device wasToProvided bool backwardOrdering bool - filter *gomatrixserverlib.RoomEventFilter + filter *synctypes.RoomEventFilter } type messagesResp struct { - Start string `json:"start"` - StartStream string `json:"start_stream,omitempty"` // NOTSPEC: used by Cerulean, so clients can hit /messages then immediately /sync with a latest sync token - End string `json:"end,omitempty"` - Chunk []gomatrixserverlib.ClientEvent `json:"chunk"` - State []gomatrixserverlib.ClientEvent `json:"state,omitempty"` + Start string `json:"start"` + StartStream string `json:"start_stream,omitempty"` // NOTSPEC: used by Cerulean, so clients can hit /messages then immediately /sync with a latest sync token + End string `json:"end,omitempty"` + Chunk []synctypes.ClientEvent `json:"chunk"` + State []synctypes.ClientEvent `json:"state,omitempty"` } // OnIncomingMessagesRequest implements the /messages endpoint from the @@ -253,7 +254,7 @@ func OnIncomingMessagesRequest( util.GetLogger(req.Context()).WithError(err).Error("failed to apply lazy loading") return jsonerror.InternalServerError() } - res.State = append(res.State, gomatrixserverlib.HeaderedToClientEvents(membershipEvents, gomatrixserverlib.FormatAll)...) + res.State = append(res.State, synctypes.HeaderedToClientEvents(membershipEvents, synctypes.FormatAll)...) } // If we didn't return any events, set the end to an empty string, so it will be omitted @@ -291,7 +292,7 @@ func getMembershipForUser(ctx context.Context, roomID, userID string, rsAPI api. // Returns an error if there was an issue talking to the database or with the // remote homeserver. func (r *messagesReq) retrieveEvents() ( - clientEvents []gomatrixserverlib.ClientEvent, start, + clientEvents []synctypes.ClientEvent, start, end types.TopologyToken, err error, ) { // Retrieve the events from the local database. @@ -323,7 +324,7 @@ func (r *messagesReq) retrieveEvents() ( // If we didn't get any event, we don't need to proceed any further. if len(events) == 0 { - return []gomatrixserverlib.ClientEvent{}, *r.from, *r.to, nil + return []synctypes.ClientEvent{}, *r.from, *r.to, nil } // Get the position of the first and the last event in the room's topology. @@ -334,7 +335,7 @@ func (r *messagesReq) retrieveEvents() ( // only have to change it in one place, i.e. the database. start, end, err = r.getStartEnd(events) if err != nil { - return []gomatrixserverlib.ClientEvent{}, *r.from, *r.to, err + return []synctypes.ClientEvent{}, *r.from, *r.to, err } // Sort the events to ensure we send them in the right order. @@ -350,7 +351,7 @@ func (r *messagesReq) retrieveEvents() ( events = reversed(events) } if len(events) == 0 { - return []gomatrixserverlib.ClientEvent{}, *r.from, *r.to, nil + return []synctypes.ClientEvent{}, *r.from, *r.to, nil } // Apply room history visibility filter @@ -362,7 +363,7 @@ func (r *messagesReq) retrieveEvents() ( "events_before": len(events), "events_after": len(filteredEvents), }).Debug("applied history visibility (messages)") - return gomatrixserverlib.HeaderedToClientEvents(filteredEvents, gomatrixserverlib.FormatAll), start, end, err + return synctypes.HeaderedToClientEvents(filteredEvents, synctypes.FormatAll), start, end, err } func (r *messagesReq) getStartEnd(events []*gomatrixserverlib.HeaderedEvent) (start, end types.TopologyToken, err error) { diff --git a/syncapi/routing/relations.go b/syncapi/routing/relations.go index fee61b0df..79533883f 100644 --- a/syncapi/routing/relations.go +++ b/syncapi/routing/relations.go @@ -27,14 +27,15 @@ import ( "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/internal" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" ) type RelationsResponse struct { - Chunk []gomatrixserverlib.ClientEvent `json:"chunk"` - NextBatch string `json:"next_batch,omitempty"` - PrevBatch string `json:"prev_batch,omitempty"` + Chunk []synctypes.ClientEvent `json:"chunk"` + NextBatch string `json:"next_batch,omitempty"` + PrevBatch string `json:"prev_batch,omitempty"` } // nolint:gocyclo @@ -85,7 +86,7 @@ func Relations( defer sqlutil.EndTransactionWithCheck(snapshot, &succeeded, &err) res := &RelationsResponse{ - Chunk: []gomatrixserverlib.ClientEvent{}, + Chunk: []synctypes.ClientEvent{}, } var events []types.StreamEvent events, res.PrevBatch, res.NextBatch, err = snapshot.RelationsFor( @@ -108,11 +109,11 @@ func Relations( // Convert the events into client events, and optionally filter based on the event // type if it was specified. - res.Chunk = make([]gomatrixserverlib.ClientEvent, 0, len(filteredEvents)) + res.Chunk = make([]synctypes.ClientEvent, 0, len(filteredEvents)) for _, event := range filteredEvents { res.Chunk = append( res.Chunk, - gomatrixserverlib.ToClientEvent(event.Event, gomatrixserverlib.FormatAll), + synctypes.ToClientEvent(event.Event, synctypes.FormatAll), ) } diff --git a/syncapi/routing/search.go b/syncapi/routing/search.go index 69fa52942..15cb2f9b8 100644 --- a/syncapi/routing/search.go +++ b/syncapi/routing/search.go @@ -32,6 +32,7 @@ import ( "github.com/matrix-org/dendrite/internal/fulltext" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/userapi/api" ) @@ -145,7 +146,7 @@ func Search(req *http.Request, device *api.Device, syncDB storage.Database, fts // Filter on m.room.message, as otherwise we also get events like m.reaction // which "breaks" displaying results in Element Web. types := []string{"m.room.message"} - roomFilter := &gomatrixserverlib.RoomEventFilter{ + roomFilter := &synctypes.RoomEventFilter{ Rooms: &rooms, Types: &types, } @@ -166,7 +167,7 @@ func Search(req *http.Request, device *api.Device, syncDB storage.Database, fts }) } - stateForRooms := make(map[string][]gomatrixserverlib.ClientEvent) + stateForRooms := make(map[string][]synctypes.ClientEvent) for _, event := range evs { eventsBefore, eventsAfter, err := contextEvents(ctx, snapshot, event, roomFilter, searchReq) if err != nil { @@ -204,24 +205,24 @@ func Search(req *http.Request, device *api.Device, syncDB storage.Database, fts Context: SearchContextResponse{ Start: startToken.String(), End: endToken.String(), - EventsAfter: gomatrixserverlib.HeaderedToClientEvents(eventsAfter, gomatrixserverlib.FormatSync), - EventsBefore: gomatrixserverlib.HeaderedToClientEvents(eventsBefore, gomatrixserverlib.FormatSync), + EventsAfter: synctypes.HeaderedToClientEvents(eventsAfter, synctypes.FormatSync), + EventsBefore: synctypes.HeaderedToClientEvents(eventsBefore, synctypes.FormatSync), ProfileInfo: profileInfos, }, Rank: eventScore[event.EventID()].Score, - Result: gomatrixserverlib.HeaderedToClientEvent(event, gomatrixserverlib.FormatAll), + Result: synctypes.HeaderedToClientEvent(event, synctypes.FormatAll), }) roomGroup := groups[event.RoomID()] roomGroup.Results = append(roomGroup.Results, event.EventID()) groups[event.RoomID()] = roomGroup if _, ok := stateForRooms[event.RoomID()]; searchReq.SearchCategories.RoomEvents.IncludeState && !ok { - stateFilter := gomatrixserverlib.DefaultStateFilter() + stateFilter := synctypes.DefaultStateFilter() state, err := snapshot.CurrentState(ctx, event.RoomID(), &stateFilter, nil) if err != nil { logrus.WithError(err).Error("unable to get current state") return jsonerror.InternalServerError() } - stateForRooms[event.RoomID()] = gomatrixserverlib.HeaderedToClientEvents(state, gomatrixserverlib.FormatSync) + stateForRooms[event.RoomID()] = synctypes.HeaderedToClientEvents(state, synctypes.FormatSync) } } @@ -262,7 +263,7 @@ func contextEvents( ctx context.Context, snapshot storage.DatabaseTransaction, event *gomatrixserverlib.HeaderedEvent, - roomFilter *gomatrixserverlib.RoomEventFilter, + roomFilter *synctypes.RoomEventFilter, searchReq SearchRequest, ) ([]*gomatrixserverlib.HeaderedEvent, []*gomatrixserverlib.HeaderedEvent, error) { id, _, err := snapshot.SelectContextEvent(ctx, event.RoomID(), event.EventID()) @@ -300,13 +301,13 @@ type Groupings struct { } type RoomEvents struct { - EventContext EventContext `json:"event_context"` - Filter gomatrixserverlib.RoomEventFilter `json:"filter"` - Groupings Groupings `json:"groupings"` - IncludeState bool `json:"include_state"` - Keys []string `json:"keys"` - OrderBy string `json:"order_by"` - SearchTerm string `json:"search_term"` + EventContext EventContext `json:"event_context"` + Filter synctypes.RoomEventFilter `json:"filter"` + Groupings Groupings `json:"groupings"` + IncludeState bool `json:"include_state"` + Keys []string `json:"keys"` + OrderBy string `json:"order_by"` + SearchTerm string `json:"search_term"` } type SearchCategories struct { @@ -331,17 +332,17 @@ type Groups struct { } type Result struct { - Context SearchContextResponse `json:"context"` - Rank float64 `json:"rank"` - Result gomatrixserverlib.ClientEvent `json:"result"` + Context SearchContextResponse `json:"context"` + Rank float64 `json:"rank"` + Result synctypes.ClientEvent `json:"result"` } type SearchContextResponse struct { - End string `json:"end"` - EventsAfter []gomatrixserverlib.ClientEvent `json:"events_after"` - EventsBefore []gomatrixserverlib.ClientEvent `json:"events_before"` - Start string `json:"start"` - ProfileInfo map[string]ProfileInfoResponse `json:"profile_info"` + End string `json:"end"` + EventsAfter []synctypes.ClientEvent `json:"events_after"` + EventsBefore []synctypes.ClientEvent `json:"events_before"` + Start string `json:"start"` + ProfileInfo map[string]ProfileInfoResponse `json:"profile_info"` } type ProfileInfoResponse struct { @@ -350,12 +351,12 @@ type ProfileInfoResponse struct { } type RoomEventsResponse struct { - Count int `json:"count"` - Groups Groups `json:"groups"` - Highlights []string `json:"highlights"` - NextBatch *string `json:"next_batch,omitempty"` - Results []Result `json:"results"` - State map[string][]gomatrixserverlib.ClientEvent `json:"state,omitempty"` + Count int `json:"count"` + Groups Groups `json:"groups"` + Highlights []string `json:"highlights"` + NextBatch *string `json:"next_batch,omitempty"` + Results []Result `json:"results"` + State map[string][]synctypes.ClientEvent `json:"state,omitempty"` } type SearchCategoriesResponse struct { RoomEvents RoomEventsResponse `json:"room_events"` diff --git a/syncapi/routing/search_test.go b/syncapi/routing/search_test.go index 05479300e..716f3bb88 100644 --- a/syncapi/routing/search_test.go +++ b/syncapi/routing/search_test.go @@ -10,6 +10,7 @@ import ( "github.com/matrix-org/dendrite/internal/fulltext" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/test" "github.com/matrix-org/dendrite/test/testrig" @@ -96,7 +97,7 @@ func TestSearch(t *testing.T) { SearchCategories: SearchCategories{ RoomEvents: RoomEvents{ SearchTerm: "hello", - Filter: gomatrixserverlib.RoomEventFilter{ + Filter: synctypes.RoomEventFilter{ Rooms: &roomsFilter, }, }, @@ -111,7 +112,7 @@ func TestSearch(t *testing.T) { SearchCategories: SearchCategories{ RoomEvents: RoomEvents{ SearchTerm: "hello", - Filter: gomatrixserverlib.RoomEventFilter{ + Filter: synctypes.RoomEventFilter{ Rooms: &roomsFilterUnknown, }, }, @@ -126,7 +127,7 @@ func TestSearch(t *testing.T) { SearchCategories: SearchCategories{ RoomEvents: RoomEvents{ SearchTerm: "hello", - Filter: gomatrixserverlib.RoomEventFilter{ + Filter: synctypes.RoomEventFilter{ Rooms: &roomsFilter, }, IncludeState: true, @@ -143,7 +144,7 @@ func TestSearch(t *testing.T) { SearchCategories: SearchCategories{ RoomEvents: RoomEvents{ SearchTerm: "hello", - Filter: gomatrixserverlib.RoomEventFilter{ + Filter: synctypes.RoomEventFilter{ Rooms: &roomsFilter, }, }, @@ -160,7 +161,7 @@ func TestSearch(t *testing.T) { SearchCategories: SearchCategories{ RoomEvents: RoomEvents{ SearchTerm: "hello", - Filter: gomatrixserverlib.RoomEventFilter{ + Filter: synctypes.RoomEventFilter{ Rooms: &roomsFilter, }, }, @@ -176,7 +177,7 @@ func TestSearch(t *testing.T) { SearchCategories: SearchCategories{ RoomEvents: RoomEvents{ SearchTerm: "hello", - Filter: gomatrixserverlib.RoomEventFilter{ + Filter: synctypes.RoomEventFilter{ Rooms: &roomsFilter, }, }, diff --git a/syncapi/storage/interface.go b/syncapi/storage/interface.go index 04c2020a0..38c04e858 100644 --- a/syncapi/storage/interface.go +++ b/syncapi/storage/interface.go @@ -23,6 +23,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/storage/shared" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -40,13 +41,13 @@ type DatabaseTransaction interface { MaxStreamPositionForPresence(ctx context.Context) (types.StreamPosition, error) MaxStreamPositionForRelations(ctx context.Context) (types.StreamPosition, error) - CurrentState(ctx context.Context, roomID string, stateFilterPart *gomatrixserverlib.StateFilter, excludeEventIDs []string) ([]*gomatrixserverlib.HeaderedEvent, error) - GetStateDeltasForFullStateSync(ctx context.Context, device *userapi.Device, r types.Range, userID string, stateFilter *gomatrixserverlib.StateFilter) ([]types.StateDelta, []string, error) - GetStateDeltas(ctx context.Context, device *userapi.Device, r types.Range, userID string, stateFilter *gomatrixserverlib.StateFilter) ([]types.StateDelta, []string, error) + CurrentState(ctx context.Context, roomID string, stateFilterPart *synctypes.StateFilter, excludeEventIDs []string) ([]*gomatrixserverlib.HeaderedEvent, error) + GetStateDeltasForFullStateSync(ctx context.Context, device *userapi.Device, r types.Range, userID string, stateFilter *synctypes.StateFilter) ([]types.StateDelta, []string, error) + GetStateDeltas(ctx context.Context, device *userapi.Device, r types.Range, userID string, stateFilter *synctypes.StateFilter) ([]types.StateDelta, []string, error) RoomIDsWithMembership(ctx context.Context, userID string, membership string) ([]string, error) MembershipCount(ctx context.Context, roomID, membership string, pos types.StreamPosition) (int, error) GetRoomSummary(ctx context.Context, roomID, userID string) (summary *types.Summary, err error) - RecentEvents(ctx context.Context, roomIDs []string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error) + RecentEvents(ctx context.Context, roomIDs []string, r types.Range, eventFilter *synctypes.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error) GetBackwardTopologyPos(ctx context.Context, events []*gomatrixserverlib.HeaderedEvent) (types.TopologyToken, error) PositionInTopology(ctx context.Context, eventID string) (pos types.StreamPosition, spos types.StreamPosition, err error) InviteEventsInRange(ctx context.Context, targetUserID string, r types.Range) (map[string]*gomatrixserverlib.HeaderedEvent, map[string]*gomatrixserverlib.HeaderedEvent, types.StreamPosition, error) @@ -71,15 +72,15 @@ type DatabaseTransaction interface { // GetStateEventsForRoom fetches the state events for a given room. // Returns an empty slice if no state events could be found for this room. // Returns an error if there was an issue with the retrieval. - GetStateEventsForRoom(ctx context.Context, roomID string, stateFilterPart *gomatrixserverlib.StateFilter) (stateEvents []*gomatrixserverlib.HeaderedEvent, err error) + GetStateEventsForRoom(ctx context.Context, roomID string, stateFilterPart *synctypes.StateFilter) (stateEvents []*gomatrixserverlib.HeaderedEvent, err error) // GetAccountDataInRange returns all account data for a given user inserted or // updated between two given positions // Returns a map following the format data[roomID] = []dataTypes // If no data is retrieved, returns an empty map // If there was an issue with the retrieval, returns an error - GetAccountDataInRange(ctx context.Context, userID string, r types.Range, accountDataFilterPart *gomatrixserverlib.EventFilter) (map[string][]string, types.StreamPosition, error) + GetAccountDataInRange(ctx context.Context, userID string, r types.Range, accountDataFilterPart *synctypes.EventFilter) (map[string][]string, types.StreamPosition, error) // GetEventsInTopologicalRange retrieves all of the events on a given ordering using the given extremities and limit. If backwardsOrdering is true, the most recent event must be first, else last. - GetEventsInTopologicalRange(ctx context.Context, from, to *types.TopologyToken, roomID string, filter *gomatrixserverlib.RoomEventFilter, backwardOrdering bool) (events []types.StreamEvent, err error) + GetEventsInTopologicalRange(ctx context.Context, from, to *types.TopologyToken, roomID string, filter *synctypes.RoomEventFilter, backwardOrdering bool) (events []types.StreamEvent, err error) // EventPositionInTopology returns the depth and stream position of the given event. EventPositionInTopology(ctx context.Context, eventID string) (types.TopologyToken, error) // BackwardExtremitiesForRoom returns a map of backwards extremity event ID to a list of its prev_events. @@ -94,8 +95,8 @@ type DatabaseTransaction interface { // GetRoomReceipts gets all receipts for a given roomID GetRoomReceipts(ctx context.Context, roomIDs []string, streamPos types.StreamPosition) ([]types.OutputReceiptEvent, error) SelectContextEvent(ctx context.Context, roomID, eventID string) (int, gomatrixserverlib.HeaderedEvent, error) - SelectContextBeforeEvent(ctx context.Context, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter) ([]*gomatrixserverlib.HeaderedEvent, error) - SelectContextAfterEvent(ctx context.Context, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter) (int, []*gomatrixserverlib.HeaderedEvent, error) + SelectContextBeforeEvent(ctx context.Context, id int, roomID string, filter *synctypes.RoomEventFilter) ([]*gomatrixserverlib.HeaderedEvent, error) + SelectContextAfterEvent(ctx context.Context, id int, roomID string, filter *synctypes.RoomEventFilter) (int, []*gomatrixserverlib.HeaderedEvent, error) StreamToTopologicalPosition(ctx context.Context, roomID string, streamPos types.StreamPosition, backwardOrdering bool) (types.TopologyToken, error) IgnoresForUser(ctx context.Context, userID string) (*types.IgnoredUsers, error) // SelectMembershipForUser returns the membership of the user before and including the given position. If no membership can be found @@ -105,7 +106,7 @@ type DatabaseTransaction interface { // getUserUnreadNotificationCountsForRooms returns the unread notifications for the given rooms GetUserUnreadNotificationCountsForRooms(ctx context.Context, userID string, roomIDs map[string]string) (map[string]*eventutil.NotificationData, error) GetPresences(ctx context.Context, userID []string) ([]*types.PresenceInternal, error) - PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error) + PresenceAfter(ctx context.Context, after types.StreamPosition, filter synctypes.EventFilter) (map[string]*types.PresenceInternal, error) RelationsFor(ctx context.Context, roomID, eventID, relType, eventType string, from, to types.StreamPosition, backwards bool, limit int) (events []types.StreamEvent, prevBatch, nextBatch string, err error) } @@ -165,11 +166,11 @@ type Database interface { // GetFilter looks up the filter associated with a given local user and filter ID // and populates the target filter. Otherwise returns an error if no such filter exists // or if there was an error talking to the database. - GetFilter(ctx context.Context, target *gomatrixserverlib.Filter, localpart string, filterID string) error + GetFilter(ctx context.Context, target *synctypes.Filter, localpart string, filterID string) error // PutFilter puts the passed filter into the database. // Returns the filterID as a string. Otherwise returns an error if something // goes wrong. - PutFilter(ctx context.Context, localpart string, filter *gomatrixserverlib.Filter) (string, error) + PutFilter(ctx context.Context, localpart string, filter *synctypes.Filter) (string, error) // RedactEvent wipes an event in the database and sets the unsigned.redacted_because key to the redaction event RedactEvent(ctx context.Context, redactedEventID string, redactedBecause *gomatrixserverlib.HeaderedEvent) error // StoreReceipt stores new receipt events diff --git a/syncapi/storage/postgres/account_data_table.go b/syncapi/storage/postgres/account_data_table.go index 8147b4868..44d735bb4 100644 --- a/syncapi/storage/postgres/account_data_table.go +++ b/syncapi/storage/postgres/account_data_table.go @@ -23,8 +23,8 @@ import ( "github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" - "github.com/matrix-org/gomatrixserverlib" ) const accountDataSchema = ` @@ -97,7 +97,7 @@ func (s *accountDataStatements) SelectAccountDataInRange( ctx context.Context, txn *sql.Tx, userID string, r types.Range, - accountDataEventFilter *gomatrixserverlib.EventFilter, + accountDataEventFilter *synctypes.EventFilter, ) (data map[string][]string, pos types.StreamPosition, err error) { data = make(map[string][]string) diff --git a/syncapi/storage/postgres/current_room_state_table.go b/syncapi/storage/postgres/current_room_state_table.go index 0d607b7c0..b05477585 100644 --- a/syncapi/storage/postgres/current_room_state_table.go +++ b/syncapi/storage/postgres/current_room_state_table.go @@ -26,6 +26,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/syncapi/storage/postgres/deltas" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/gomatrixserverlib" ) @@ -270,7 +271,7 @@ func (s *currentRoomStateStatements) SelectRoomIDsWithAnyMembership( // SelectCurrentState returns all the current state events for the given room. func (s *currentRoomStateStatements) SelectCurrentState( ctx context.Context, txn *sql.Tx, roomID string, - stateFilter *gomatrixserverlib.StateFilter, + stateFilter *synctypes.StateFilter, excludeEventIDs []string, ) ([]*gomatrixserverlib.HeaderedEvent, error) { stmt := sqlutil.TxStmt(txn, s.selectCurrentStateStmt) diff --git a/syncapi/storage/postgres/filter_table.go b/syncapi/storage/postgres/filter_table.go index 44c3de72e..089382b89 100644 --- a/syncapi/storage/postgres/filter_table.go +++ b/syncapi/storage/postgres/filter_table.go @@ -21,6 +21,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/gomatrixserverlib" ) @@ -69,7 +70,7 @@ func NewPostgresFilterTable(db *sql.DB) (tables.Filter, error) { } func (s *filterStatements) SelectFilter( - ctx context.Context, txn *sql.Tx, target *gomatrixserverlib.Filter, localpart string, filterID string, + ctx context.Context, txn *sql.Tx, target *synctypes.Filter, localpart string, filterID string, ) error { // Retrieve filter from database (stored as canonical JSON) var filterData []byte @@ -86,7 +87,7 @@ func (s *filterStatements) SelectFilter( } func (s *filterStatements) InsertFilter( - ctx context.Context, txn *sql.Tx, filter *gomatrixserverlib.Filter, localpart string, + ctx context.Context, txn *sql.Tx, filter *synctypes.Filter, localpart string, ) (filterID string, err error) { var existingFilterID string diff --git a/syncapi/storage/postgres/filtering.go b/syncapi/storage/postgres/filtering.go index a2ca42156..39ef11086 100644 --- a/syncapi/storage/postgres/filtering.go +++ b/syncapi/storage/postgres/filtering.go @@ -17,7 +17,7 @@ package postgres import ( "strings" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/dendrite/syncapi/synctypes" ) // filterConvertWildcardToSQL converts wildcards as defined in @@ -39,7 +39,7 @@ func filterConvertTypeWildcardToSQL(values *[]string) []string { } // TODO: Replace when Dendrite uses Go 1.18 -func getSendersRoomEventFilter(filter *gomatrixserverlib.RoomEventFilter) (senders []string, notSenders []string) { +func getSendersRoomEventFilter(filter *synctypes.RoomEventFilter) (senders []string, notSenders []string) { if filter.Senders != nil { senders = *filter.Senders } @@ -49,7 +49,7 @@ func getSendersRoomEventFilter(filter *gomatrixserverlib.RoomEventFilter) (sende return senders, notSenders } -func getSendersStateFilterFilter(filter *gomatrixserverlib.StateFilter) (senders []string, notSenders []string) { +func getSendersStateFilterFilter(filter *synctypes.StateFilter) (senders []string, notSenders []string) { if filter.Senders != nil { senders = *filter.Senders } diff --git a/syncapi/storage/postgres/output_room_events_table.go b/syncapi/storage/postgres/output_room_events_table.go index 59fb99aa3..8ee5098c7 100644 --- a/syncapi/storage/postgres/output_room_events_table.go +++ b/syncapi/storage/postgres/output_room_events_table.go @@ -28,6 +28,7 @@ import ( "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/storage/postgres/deltas" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/gomatrixserverlib" ) @@ -135,15 +136,6 @@ FROM room_ids, ) AS x ` -const selectEarlyEventsSQL = "" + - "SELECT event_id, id, headered_event_json, session_id, exclude_from_sync, transaction_id, history_visibility FROM syncapi_output_room_events" + - " WHERE room_id = $1 AND id > $2 AND id <= $3" + - " AND ( $4::text[] IS NULL OR sender = ANY($4) )" + - " AND ( $5::text[] IS NULL OR NOT(sender = ANY($5)) )" + - " AND ( $6::text[] IS NULL OR type LIKE ANY($6) )" + - " AND ( $7::text[] IS NULL OR NOT(type LIKE ANY($7)) )" + - " ORDER BY id ASC LIMIT $8" - const selectMaxEventIDSQL = "" + "SELECT MAX(id) FROM syncapi_output_room_events" @@ -205,7 +197,6 @@ type outputRoomEventsStatements struct { selectMaxEventIDStmt *sql.Stmt selectRecentEventsStmt *sql.Stmt selectRecentEventsForSyncStmt *sql.Stmt - selectEarlyEventsStmt *sql.Stmt selectStateInRangeFilteredStmt *sql.Stmt selectStateInRangeStmt *sql.Stmt updateEventJSONStmt *sql.Stmt @@ -261,7 +252,6 @@ func NewPostgresEventsTable(db *sql.DB) (tables.Events, error) { {&s.selectMaxEventIDStmt, selectMaxEventIDSQL}, {&s.selectRecentEventsStmt, selectRecentEventsSQL}, {&s.selectRecentEventsForSyncStmt, selectRecentEventsForSyncSQL}, - {&s.selectEarlyEventsStmt, selectEarlyEventsSQL}, {&s.selectStateInRangeFilteredStmt, selectStateInRangeFilteredSQL}, {&s.selectStateInRangeStmt, selectStateInRangeSQL}, {&s.updateEventJSONStmt, updateEventJSONSQL}, @@ -288,7 +278,7 @@ func (s *outputRoomEventsStatements) UpdateEventJSON(ctx context.Context, txn *s // two positions, only the most recent state is returned. func (s *outputRoomEventsStatements) SelectStateInRange( ctx context.Context, txn *sql.Tx, r types.Range, - stateFilter *gomatrixserverlib.StateFilter, roomIDs []string, + stateFilter *synctypes.StateFilter, roomIDs []string, ) (map[string]map[string]bool, map[string]types.StreamEvent, error) { var rows *sql.Rows var err error @@ -433,7 +423,7 @@ func (s *outputRoomEventsStatements) InsertEvent( // from sync. func (s *outputRoomEventsStatements) SelectRecentEvents( ctx context.Context, txn *sql.Tx, - roomIDs []string, ra types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, + roomIDs []string, ra types.Range, eventFilter *synctypes.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool, ) (map[string]types.RecentEvents, error) { var stmt *sql.Stmt @@ -529,43 +519,10 @@ func (s *outputRoomEventsStatements) SelectRecentEvents( return result, rows.Err() } -// selectEarlyEvents returns the earliest events in the given room, starting -// from a given position, up to a maximum of 'limit'. -func (s *outputRoomEventsStatements) SelectEarlyEvents( - ctx context.Context, txn *sql.Tx, - roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, -) ([]types.StreamEvent, error) { - senders, notSenders := getSendersRoomEventFilter(eventFilter) - stmt := sqlutil.TxStmt(txn, s.selectEarlyEventsStmt) - rows, err := stmt.QueryContext( - ctx, roomID, r.Low(), r.High(), - pq.StringArray(senders), - pq.StringArray(notSenders), - pq.StringArray(filterConvertTypeWildcardToSQL(eventFilter.Types)), - pq.StringArray(filterConvertTypeWildcardToSQL(eventFilter.NotTypes)), - eventFilter.Limit, - ) - if err != nil { - return nil, err - } - defer internal.CloseAndLogIfError(ctx, rows, "selectEarlyEvents: rows.close() failed") - events, err := rowsToStreamEvents(rows) - if err != nil { - return nil, err - } - // The events need to be returned from oldest to latest, which isn't - // necessarily the way the SQL query returns them, so a sort is necessary to - // ensure the events are in the right order in the slice. - sort.SliceStable(events, func(i int, j int) bool { - return events[i].StreamPosition < events[j].StreamPosition - }) - return events, nil -} - // selectEvents returns the events for the given event IDs. If an event is // missing from the database, it will be omitted. func (s *outputRoomEventsStatements) SelectEvents( - ctx context.Context, txn *sql.Tx, eventIDs []string, filter *gomatrixserverlib.RoomEventFilter, preserveOrder bool, + ctx context.Context, txn *sql.Tx, eventIDs []string, filter *synctypes.RoomEventFilter, preserveOrder bool, ) ([]types.StreamEvent, error) { var ( stmt *sql.Stmt @@ -637,7 +594,7 @@ func (s *outputRoomEventsStatements) SelectContextEvent(ctx context.Context, txn } func (s *outputRoomEventsStatements) SelectContextBeforeEvent( - ctx context.Context, txn *sql.Tx, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter, + ctx context.Context, txn *sql.Tx, id int, roomID string, filter *synctypes.RoomEventFilter, ) (evts []*gomatrixserverlib.HeaderedEvent, err error) { senders, notSenders := getSendersRoomEventFilter(filter) rows, err := sqlutil.TxStmt(txn, s.selectContextBeforeEventStmt).QueryContext( @@ -672,7 +629,7 @@ func (s *outputRoomEventsStatements) SelectContextBeforeEvent( } func (s *outputRoomEventsStatements) SelectContextAfterEvent( - ctx context.Context, txn *sql.Tx, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter, + ctx context.Context, txn *sql.Tx, id int, roomID string, filter *synctypes.RoomEventFilter, ) (lastID int, evts []*gomatrixserverlib.HeaderedEvent, err error) { senders, notSenders := getSendersRoomEventFilter(filter) rows, err := sqlutil.TxStmt(txn, s.selectContextAfterEventStmt).QueryContext( diff --git a/syncapi/storage/postgres/presence_table.go b/syncapi/storage/postgres/presence_table.go index a3f7c5213..3dba7756c 100644 --- a/syncapi/storage/postgres/presence_table.go +++ b/syncapi/storage/postgres/presence_table.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" ) @@ -156,7 +157,7 @@ func (p *presenceStatements) GetMaxPresenceID(ctx context.Context, txn *sql.Tx) func (p *presenceStatements) GetPresenceAfter( ctx context.Context, txn *sql.Tx, after types.StreamPosition, - filter gomatrixserverlib.EventFilter, + filter synctypes.EventFilter, ) (presences map[string]*types.PresenceInternal, err error) { presences = make(map[string]*types.PresenceInternal) stmt := sqlutil.TxStmt(txn, p.selectPresenceAfterStmt) diff --git a/syncapi/storage/shared/storage_consumer.go b/syncapi/storage/shared/storage_consumer.go index 18802d0c4..1894a0946 100644 --- a/syncapi/storage/shared/storage_consumer.go +++ b/syncapi/storage/shared/storage_consumer.go @@ -31,6 +31,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" ) @@ -323,13 +324,13 @@ func (d *Database) updateRoomState( } func (d *Database) GetFilter( - ctx context.Context, target *gomatrixserverlib.Filter, localpart string, filterID string, + ctx context.Context, target *synctypes.Filter, localpart string, filterID string, ) error { return d.Filter.SelectFilter(ctx, nil, target, localpart, filterID) } func (d *Database) PutFilter( - ctx context.Context, localpart string, filter *gomatrixserverlib.Filter, + ctx context.Context, localpart string, filter *synctypes.Filter, ) (string, error) { var filterID string var err error @@ -523,10 +524,10 @@ func (d *Database) SelectContextEvent(ctx context.Context, roomID, eventID strin return d.OutputEvents.SelectContextEvent(ctx, nil, roomID, eventID) } -func (d *Database) SelectContextBeforeEvent(ctx context.Context, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter) ([]*gomatrixserverlib.HeaderedEvent, error) { +func (d *Database) SelectContextBeforeEvent(ctx context.Context, id int, roomID string, filter *synctypes.RoomEventFilter) ([]*gomatrixserverlib.HeaderedEvent, error) { return d.OutputEvents.SelectContextBeforeEvent(ctx, nil, id, roomID, filter) } -func (d *Database) SelectContextAfterEvent(ctx context.Context, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter) (int, []*gomatrixserverlib.HeaderedEvent, error) { +func (d *Database) SelectContextAfterEvent(ctx context.Context, id int, roomID string, filter *synctypes.RoomEventFilter) (int, []*gomatrixserverlib.HeaderedEvent, error) { return d.OutputEvents.SelectContextAfterEvent(ctx, nil, id, roomID, filter) } diff --git a/syncapi/storage/shared/storage_consumer_test.go b/syncapi/storage/shared/storage_consumer_test.go new file mode 100644 index 000000000..e5f734c96 --- /dev/null +++ b/syncapi/storage/shared/storage_consumer_test.go @@ -0,0 +1,103 @@ +package shared_test + +import ( + "context" + "reflect" + "testing" + + "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" + "github.com/matrix-org/dendrite/syncapi/types" + "github.com/matrix-org/dendrite/test" + "github.com/matrix-org/dendrite/test/testrig" +) + +func newSyncDB(t *testing.T, dbType test.DBType) (storage.Database, func()) { + t.Helper() + + cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType) + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + syncDB, err := storage.NewSyncServerDatasource(processCtx.Context(), cm, &cfg.SyncAPI.Database) + if err != nil { + t.Fatalf("failed to create sync DB: %s", err) + } + + return syncDB, closeDB +} + +func TestFilterTable(t *testing.T) { + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + tab, closeDB := newSyncDB(t, dbType) + defer closeDB() + + // initially create a filter + filter := &synctypes.Filter{} + filterID, err := tab.PutFilter(context.Background(), "alice", filter) + if err != nil { + t.Fatal(err) + } + + // create the same filter again, we should receive the existing filter + secondFilterID, err := tab.PutFilter(context.Background(), "alice", filter) + if err != nil { + t.Fatal(err) + } + + if secondFilterID != filterID { + t.Fatalf("expected second filter to be the same as the first: %s vs %s", filterID, secondFilterID) + } + + // query the filter again + targetFilter := &synctypes.Filter{} + if err = tab.GetFilter(context.Background(), targetFilter, "alice", filterID); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(filter, targetFilter) { + t.Fatalf("%#v vs %#v", filter, targetFilter) + } + + // query non-existent filter + if err = tab.GetFilter(context.Background(), targetFilter, "bob", filterID); err == nil { + t.Fatalf("expected filter to not exist, but it does exist: %v", targetFilter) + } + }) +} + +func TestIgnores(t *testing.T) { + alice := test.NewUser(t) + bob := test.NewUser(t) + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + syncDB, closeDB := newSyncDB(t, dbType) + defer closeDB() + + tab, err := syncDB.NewDatabaseTransaction(context.Background()) + if err != nil { + t.Fatal(err) + } + defer tab.Rollback() // nolint: errcheck + + ignoredUsers := &types.IgnoredUsers{List: map[string]interface{}{ + bob.ID: "", + }} + if err = tab.UpdateIgnoresForUser(context.Background(), alice.ID, ignoredUsers); err != nil { + t.Fatal(err) + } + + gotIgnoredUsers, err := tab.IgnoresForUser(context.Background(), alice.ID) + if err != nil { + t.Fatal(err) + } + + // verify the ignored users matches those we stored + if !reflect.DeepEqual(gotIgnoredUsers, ignoredUsers) { + t.Fatalf("%#v vs %#v", gotIgnoredUsers, ignoredUsers) + } + + // Bob doesn't have any ignored users, so should receive sql.ErrNoRows + if _, err = tab.IgnoresForUser(context.Background(), bob.ID); err == nil { + t.Fatalf("expected an error but got none") + } + }) +} diff --git a/syncapi/storage/shared/storage_sync.go b/syncapi/storage/shared/storage_sync.go index 931bc9e23..a614544b5 100644 --- a/syncapi/storage/shared/storage_sync.go +++ b/syncapi/storage/shared/storage_sync.go @@ -10,6 +10,7 @@ import ( "github.com/tidwall/gjson" "github.com/matrix-org/dendrite/internal/eventutil" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -82,7 +83,7 @@ func (d *DatabaseTransaction) MaxStreamPositionForNotificationData(ctx context.C return types.StreamPosition(id), nil } -func (d *DatabaseTransaction) CurrentState(ctx context.Context, roomID string, stateFilterPart *gomatrixserverlib.StateFilter, excludeEventIDs []string) ([]*gomatrixserverlib.HeaderedEvent, error) { +func (d *DatabaseTransaction) CurrentState(ctx context.Context, roomID string, stateFilterPart *synctypes.StateFilter, excludeEventIDs []string) ([]*gomatrixserverlib.HeaderedEvent, error) { return d.CurrentRoomState.SelectCurrentState(ctx, d.txn, roomID, stateFilterPart, excludeEventIDs) } @@ -109,7 +110,7 @@ func (d *DatabaseTransaction) GetRoomSummary(ctx context.Context, roomID, userID summary.JoinedMemberCount = &joinCount // Get the room name and canonical alias, if any - filter := gomatrixserverlib.DefaultStateFilter() + filter := synctypes.DefaultStateFilter() filterTypes := []string{gomatrixserverlib.MRoomName, gomatrixserverlib.MRoomCanonicalAlias} filterRooms := []string{roomID} @@ -151,7 +152,7 @@ func (d *DatabaseTransaction) GetRoomSummary(ctx context.Context, roomID, userID return summary, nil } -func (d *DatabaseTransaction) RecentEvents(ctx context.Context, roomIDs []string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error) { +func (d *DatabaseTransaction) RecentEvents(ctx context.Context, roomIDs []string, r types.Range, eventFilter *synctypes.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error) { return d.OutputEvents.SelectRecentEvents(ctx, d.txn, roomIDs, r, eventFilter, chronologicalOrder, onlySyncEvents) } @@ -210,7 +211,7 @@ func (d *DatabaseTransaction) GetStateEvent( } func (d *DatabaseTransaction) GetStateEventsForRoom( - ctx context.Context, roomID string, stateFilter *gomatrixserverlib.StateFilter, + ctx context.Context, roomID string, stateFilter *synctypes.StateFilter, ) (stateEvents []*gomatrixserverlib.HeaderedEvent, err error) { stateEvents, err = d.CurrentRoomState.SelectCurrentState(ctx, d.txn, roomID, stateFilter, nil) return @@ -223,7 +224,7 @@ func (d *DatabaseTransaction) GetStateEventsForRoom( // If there was an issue with the retrieval, returns an error func (d *DatabaseTransaction) GetAccountDataInRange( ctx context.Context, userID string, r types.Range, - accountDataFilterPart *gomatrixserverlib.EventFilter, + accountDataFilterPart *synctypes.EventFilter, ) (map[string][]string, types.StreamPosition, error) { return d.AccountData.SelectAccountDataInRange(ctx, d.txn, userID, r, accountDataFilterPart) } @@ -232,7 +233,7 @@ func (d *DatabaseTransaction) GetEventsInTopologicalRange( ctx context.Context, from, to *types.TopologyToken, roomID string, - filter *gomatrixserverlib.RoomEventFilter, + filter *synctypes.RoomEventFilter, backwardOrdering bool, ) (events []types.StreamEvent, err error) { var minDepth, maxDepth, maxStreamPosForMaxDepth types.StreamPosition @@ -323,7 +324,7 @@ func (d *DatabaseTransaction) GetBackwardTopologyPos( func (d *DatabaseTransaction) GetStateDeltas( ctx context.Context, device *userapi.Device, r types.Range, userID string, - stateFilter *gomatrixserverlib.StateFilter, + stateFilter *synctypes.StateFilter, ) (deltas []types.StateDelta, joinedRoomsIDs []string, err error) { // Implement membership change algorithm: https://github.com/matrix-org/synapse/blob/v0.19.3/synapse/handlers/sync.py#L821 // - Get membership list changes for this user in this sync response @@ -488,7 +489,7 @@ func (d *DatabaseTransaction) GetStateDeltas( func (d *DatabaseTransaction) GetStateDeltasForFullStateSync( ctx context.Context, device *userapi.Device, r types.Range, userID string, - stateFilter *gomatrixserverlib.StateFilter, + stateFilter *synctypes.StateFilter, ) ([]types.StateDelta, []string, error) { // Look up all memberships for the user. We only care about rooms that a // user has ever interacted with — joined to, kicked/banned from, left. @@ -597,7 +598,7 @@ func (d *DatabaseTransaction) GetStateDeltasForFullStateSync( func (d *DatabaseTransaction) currentStateStreamEventsForRoom( ctx context.Context, roomID string, - stateFilter *gomatrixserverlib.StateFilter, + stateFilter *synctypes.StateFilter, ) ([]types.StreamEvent, error) { allState, err := d.CurrentRoomState.SelectCurrentState(ctx, d.txn, roomID, stateFilter, nil) if err != nil { @@ -647,7 +648,7 @@ func (d *DatabaseTransaction) GetPresences(ctx context.Context, userIDs []string return d.Presence.GetPresenceForUsers(ctx, d.txn, userIDs) } -func (d *DatabaseTransaction) PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error) { +func (d *DatabaseTransaction) PresenceAfter(ctx context.Context, after types.StreamPosition, filter synctypes.EventFilter) (map[string]*types.PresenceInternal, error) { return d.Presence.GetPresenceAfter(ctx, d.txn, after, filter) } @@ -707,7 +708,7 @@ func (d *DatabaseTransaction) MaxStreamPositionForRelations(ctx context.Context) return types.StreamPosition(id), err } -func isStatefilterEmpty(filter *gomatrixserverlib.StateFilter) bool { +func isStatefilterEmpty(filter *synctypes.StateFilter) bool { if filter == nil { return true } diff --git a/syncapi/storage/shared/storage_sync_test.go b/syncapi/storage/shared/storage_sync_test.go index c56720db7..4468a7728 100644 --- a/syncapi/storage/shared/storage_sync_test.go +++ b/syncapi/storage/shared/storage_sync_test.go @@ -3,7 +3,7 @@ package shared import ( "testing" - "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/dendrite/syncapi/synctypes" ) func Test_isStatefilterEmpty(t *testing.T) { @@ -12,7 +12,7 @@ func Test_isStatefilterEmpty(t *testing.T) { tests := []struct { name string - filter *gomatrixserverlib.StateFilter + filter *synctypes.StateFilter want bool }{ { @@ -22,42 +22,42 @@ func Test_isStatefilterEmpty(t *testing.T) { }, { name: "Empty filter is empty", - filter: &gomatrixserverlib.StateFilter{}, + filter: &synctypes.StateFilter{}, want: true, }, { name: "NotTypes is set", - filter: &gomatrixserverlib.StateFilter{ + filter: &synctypes.StateFilter{ NotTypes: &filterSet, }, }, { name: "Types is set", - filter: &gomatrixserverlib.StateFilter{ + filter: &synctypes.StateFilter{ Types: &filterSet, }, }, { name: "Senders is set", - filter: &gomatrixserverlib.StateFilter{ + filter: &synctypes.StateFilter{ Senders: &filterSet, }, }, { name: "NotSenders is set", - filter: &gomatrixserverlib.StateFilter{ + filter: &synctypes.StateFilter{ NotSenders: &filterSet, }, }, { name: "NotRooms is set", - filter: &gomatrixserverlib.StateFilter{ + filter: &synctypes.StateFilter{ NotRooms: &filterSet, }, }, { name: "ContainsURL is set", - filter: &gomatrixserverlib.StateFilter{ + filter: &synctypes.StateFilter{ ContainsURL: &boolValue, }, }, diff --git a/syncapi/storage/sqlite3/account_data_table.go b/syncapi/storage/sqlite3/account_data_table.go index eaf522340..b49c2f701 100644 --- a/syncapi/storage/sqlite3/account_data_table.go +++ b/syncapi/storage/sqlite3/account_data_table.go @@ -22,8 +22,8 @@ import ( "github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" - "github.com/matrix-org/gomatrixserverlib" ) const accountDataSchema = ` @@ -89,7 +89,7 @@ func (s *accountDataStatements) SelectAccountDataInRange( ctx context.Context, txn *sql.Tx, userID string, r types.Range, - filter *gomatrixserverlib.EventFilter, + filter *synctypes.EventFilter, ) (data map[string][]string, pos types.StreamPosition, err error) { data = make(map[string][]string) stmt, params, err := prepareWithFilters( diff --git a/syncapi/storage/sqlite3/current_room_state_table.go b/syncapi/storage/sqlite3/current_room_state_table.go index 35b746c5c..c681933d5 100644 --- a/syncapi/storage/sqlite3/current_room_state_table.go +++ b/syncapi/storage/sqlite3/current_room_state_table.go @@ -27,6 +27,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/syncapi/storage/sqlite3/deltas" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/gomatrixserverlib" ) @@ -264,7 +265,7 @@ func (s *currentRoomStateStatements) SelectRoomIDsWithAnyMembership( // CurrentState returns all the current state events for the given room. func (s *currentRoomStateStatements) SelectCurrentState( ctx context.Context, txn *sql.Tx, roomID string, - stateFilter *gomatrixserverlib.StateFilter, + stateFilter *synctypes.StateFilter, excludeEventIDs []string, ) ([]*gomatrixserverlib.HeaderedEvent, error) { // We're going to query members later, so remove them from this request diff --git a/syncapi/storage/sqlite3/filter_table.go b/syncapi/storage/sqlite3/filter_table.go index 0132a7664..8da4f299c 100644 --- a/syncapi/storage/sqlite3/filter_table.go +++ b/syncapi/storage/sqlite3/filter_table.go @@ -22,6 +22,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/gomatrixserverlib" ) @@ -73,7 +74,7 @@ func NewSqliteFilterTable(db *sql.DB) (tables.Filter, error) { } func (s *filterStatements) SelectFilter( - ctx context.Context, txn *sql.Tx, target *gomatrixserverlib.Filter, localpart string, filterID string, + ctx context.Context, txn *sql.Tx, target *synctypes.Filter, localpart string, filterID string, ) error { // Retrieve filter from database (stored as canonical JSON) var filterData []byte @@ -90,7 +91,7 @@ func (s *filterStatements) SelectFilter( } func (s *filterStatements) InsertFilter( - ctx context.Context, txn *sql.Tx, filter *gomatrixserverlib.Filter, localpart string, + ctx context.Context, txn *sql.Tx, filter *synctypes.Filter, localpart string, ) (filterID string, err error) { var existingFilterID string diff --git a/syncapi/storage/sqlite3/output_room_events_table.go b/syncapi/storage/sqlite3/output_room_events_table.go index 23bc68a41..b5b6ea883 100644 --- a/syncapi/storage/sqlite3/output_room_events_table.go +++ b/syncapi/storage/sqlite3/output_room_events_table.go @@ -27,8 +27,8 @@ import ( "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/storage/sqlite3/deltas" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" - "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/dendrite/internal/sqlutil" @@ -81,12 +81,6 @@ const selectRecentEventsForSyncSQL = "" + // WHEN, ORDER BY and LIMIT are appended by prepareWithFilters -const selectEarlyEventsSQL = "" + - "SELECT event_id, id, headered_event_json, session_id, exclude_from_sync, transaction_id, history_visibility FROM syncapi_output_room_events" + - " WHERE room_id = $1 AND id > $2 AND id <= $3" - -// WHEN, ORDER BY and LIMIT are appended by prepareWithFilters - const selectMaxEventIDSQL = "" + "SELECT MAX(id) FROM syncapi_output_room_events" @@ -118,7 +112,7 @@ const selectContextAfterEventSQL = "" + // WHEN, ORDER BY and LIMIT are appended by prepareWithFilters -const selectSearchSQL = "SELECT id, event_id, headered_event_json FROM syncapi_output_room_events WHERE type IN ($1) AND id > $2 LIMIT $3 ORDER BY id ASC" +const selectSearchSQL = "SELECT id, event_id, headered_event_json FROM syncapi_output_room_events WHERE id > $1 AND type IN ($2)" const purgeEventsSQL = "" + "DELETE FROM syncapi_output_room_events WHERE room_id = $1" @@ -186,7 +180,7 @@ func (s *outputRoomEventsStatements) UpdateEventJSON(ctx context.Context, txn *s // two positions, only the most recent state is returned. func (s *outputRoomEventsStatements) SelectStateInRange( ctx context.Context, txn *sql.Tx, r types.Range, - stateFilter *gomatrixserverlib.StateFilter, roomIDs []string, + stateFilter *synctypes.StateFilter, roomIDs []string, ) (map[string]map[string]bool, map[string]types.StreamEvent, error) { stmtSQL := strings.Replace(selectStateInRangeSQL, "($3)", sqlutil.QueryVariadicOffset(len(roomIDs), 2), 1) inputParams := []interface{}{ @@ -368,7 +362,7 @@ func (s *outputRoomEventsStatements) InsertEvent( func (s *outputRoomEventsStatements) SelectRecentEvents( ctx context.Context, txn *sql.Tx, - roomIDs []string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, + roomIDs []string, r types.Range, eventFilter *synctypes.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool, ) (map[string]types.RecentEvents, error) { var query string @@ -429,46 +423,10 @@ func (s *outputRoomEventsStatements) SelectRecentEvents( return result, nil } -func (s *outputRoomEventsStatements) SelectEarlyEvents( - ctx context.Context, txn *sql.Tx, - roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, -) ([]types.StreamEvent, error) { - stmt, params, err := prepareWithFilters( - s.db, txn, selectEarlyEventsSQL, - []interface{}{ - roomID, r.Low(), r.High(), - }, - eventFilter.Senders, eventFilter.NotSenders, - eventFilter.Types, eventFilter.NotTypes, - nil, eventFilter.ContainsURL, eventFilter.Limit, FilterOrderAsc, - ) - if err != nil { - return nil, fmt.Errorf("s.prepareWithFilters: %w", err) - } - defer internal.CloseAndLogIfError(ctx, stmt, "SelectEarlyEvents: stmt.close() failed") - - rows, err := stmt.QueryContext(ctx, params...) - if err != nil { - return nil, err - } - defer internal.CloseAndLogIfError(ctx, rows, "selectEarlyEvents: rows.close() failed") - events, err := rowsToStreamEvents(rows) - if err != nil { - return nil, err - } - // The events need to be returned from oldest to latest, which isn't - // necessarily the way the SQL query returns them, so a sort is necessary to - // ensure the events are in the right order in the slice. - sort.SliceStable(events, func(i int, j int) bool { - return events[i].StreamPosition < events[j].StreamPosition - }) - return events, nil -} - // selectEvents returns the events for the given event IDs. If an event is // missing from the database, it will be omitted. func (s *outputRoomEventsStatements) SelectEvents( - ctx context.Context, txn *sql.Tx, eventIDs []string, filter *gomatrixserverlib.RoomEventFilter, preserveOrder bool, + ctx context.Context, txn *sql.Tx, eventIDs []string, filter *synctypes.RoomEventFilter, preserveOrder bool, ) ([]types.StreamEvent, error) { iEventIDs := make([]interface{}, len(eventIDs)) for i := range eventIDs { @@ -477,7 +435,7 @@ func (s *outputRoomEventsStatements) SelectEvents( selectSQL := strings.Replace(selectEventsSQL, "($1)", sqlutil.QueryVariadic(len(eventIDs)), 1) if filter == nil { - filter = &gomatrixserverlib.RoomEventFilter{Limit: 20} + filter = &synctypes.RoomEventFilter{Limit: 20} } stmt, params, err := prepareWithFilters( s.db, txn, selectSQL, iEventIDs, @@ -581,7 +539,7 @@ func (s *outputRoomEventsStatements) SelectContextEvent( } func (s *outputRoomEventsStatements) SelectContextBeforeEvent( - ctx context.Context, txn *sql.Tx, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter, + ctx context.Context, txn *sql.Tx, id int, roomID string, filter *synctypes.RoomEventFilter, ) (evts []*gomatrixserverlib.HeaderedEvent, err error) { stmt, params, err := prepareWithFilters( s.db, txn, selectContextBeforeEventSQL, @@ -623,7 +581,7 @@ func (s *outputRoomEventsStatements) SelectContextBeforeEvent( } func (s *outputRoomEventsStatements) SelectContextAfterEvent( - ctx context.Context, txn *sql.Tx, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter, + ctx context.Context, txn *sql.Tx, id int, roomID string, filter *synctypes.RoomEventFilter, ) (lastID int, evts []*gomatrixserverlib.HeaderedEvent, err error) { stmt, params, err := prepareWithFilters( s.db, txn, selectContextAfterEventSQL, @@ -685,18 +643,18 @@ func (s *outputRoomEventsStatements) PurgeEvents( } func (s *outputRoomEventsStatements) ReIndex(ctx context.Context, txn *sql.Tx, limit, afterID int64, types []string) (map[int64]gomatrixserverlib.HeaderedEvent, error) { - params := make([]interface{}, len(types)) + params := make([]interface{}, len(types)+1) + params[0] = afterID for i := range types { - params[i] = types[i] + params[i+1] = types[i] } - params = append(params, afterID) - params = append(params, limit) - selectSQL := strings.Replace(selectSearchSQL, "($1)", sqlutil.QueryVariadic(len(types)), 1) - stmt, err := s.db.Prepare(selectSQL) + selectSQL := strings.Replace(selectSearchSQL, "($2)", sqlutil.QueryVariadicOffset(len(types), 1), 1) + stmt, params, err := prepareWithFilters(s.db, txn, selectSQL, params, nil, nil, nil, nil, nil, nil, int(limit), FilterOrderAsc) if err != nil { return nil, err } + defer internal.CloseAndLogIfError(ctx, stmt, "selectEvents: stmt.close() failed") rows, err := sqlutil.TxStmt(txn, stmt).QueryContext(ctx, params...) if err != nil { diff --git a/syncapi/storage/sqlite3/presence_table.go b/syncapi/storage/sqlite3/presence_table.go index 7641de92f..5f76b3700 100644 --- a/syncapi/storage/sqlite3/presence_table.go +++ b/syncapi/storage/sqlite3/presence_table.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" ) @@ -180,7 +181,7 @@ func (p *presenceStatements) GetMaxPresenceID(ctx context.Context, txn *sql.Tx) // GetPresenceAfter returns the changes presences after a given stream id func (p *presenceStatements) GetPresenceAfter( ctx context.Context, txn *sql.Tx, - after types.StreamPosition, filter gomatrixserverlib.EventFilter, + after types.StreamPosition, filter synctypes.EventFilter, ) (presences map[string]*types.PresenceInternal, err error) { presences = make(map[string]*types.PresenceInternal) stmt := sqlutil.TxStmt(txn, p.selectPresenceAfterStmt) diff --git a/syncapi/storage/storage_test.go b/syncapi/storage/storage_test.go index e81a341f1..fa3f54c0e 100644 --- a/syncapi/storage/storage_test.go +++ b/syncapi/storage/storage_test.go @@ -12,6 +12,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/test" "github.com/matrix-org/gomatrixserverlib" @@ -153,7 +154,7 @@ func TestRecentEventsPDU(t *testing.T) { for i := range testCases { tc := testCases[i] t.Run(tc.Name, func(st *testing.T) { - var filter gomatrixserverlib.RoomEventFilter + var filter synctypes.RoomEventFilter var gotEvents map[string]types.RecentEvents var limited bool filter.Limit = tc.Limit @@ -206,7 +207,7 @@ func TestGetEventsInRangeWithTopologyToken(t *testing.T) { to := types.TopologyToken{} // backpaginate 5 messages starting at the latest position. - filter := &gomatrixserverlib.RoomEventFilter{Limit: 5} + filter := &synctypes.RoomEventFilter{Limit: 5} paginatedEvents, err := snapshot.GetEventsInTopologicalRange(ctx, &from, &to, r.ID, filter, true) if err != nil { t.Fatalf("GetEventsInTopologicalRange returned an error: %s", err) @@ -932,7 +933,7 @@ func TestRecentEvents(t *testing.T) { } test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { - filter := gomatrixserverlib.DefaultRoomEventFilter() + filter := synctypes.DefaultRoomEventFilter() db, close := MustCreateDatabase(t, dbType) t.Cleanup(close) diff --git a/syncapi/storage/tables/current_room_state_test.go b/syncapi/storage/tables/current_room_state_test.go index c7af4f977..5fe06c3ce 100644 --- a/syncapi/storage/tables/current_room_state_test.go +++ b/syncapi/storage/tables/current_room_state_test.go @@ -11,6 +11,7 @@ import ( "github.com/matrix-org/dendrite/syncapi/storage/postgres" "github.com/matrix-org/dendrite/syncapi/storage/sqlite3" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/test" "github.com/matrix-org/gomatrixserverlib" @@ -94,7 +95,7 @@ func TestCurrentRoomStateTable(t *testing.T) { func testCurrentState(t *testing.T, ctx context.Context, txn *sql.Tx, tab tables.CurrentRoomState, room *test.Room) { t.Run("test currentState", func(t *testing.T) { // returns the complete state of the room with a default filter - filter := gomatrixserverlib.DefaultStateFilter() + filter := synctypes.DefaultStateFilter() evs, err := tab.SelectCurrentState(ctx, txn, room.ID, &filter, nil) if err != nil { t.Fatal(err) diff --git a/syncapi/storage/tables/interface.go b/syncapi/storage/tables/interface.go index 727d6bf2c..94c9c4028 100644 --- a/syncapi/storage/tables/interface.go +++ b/syncapi/storage/tables/interface.go @@ -22,13 +22,14 @@ import ( "github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" ) type AccountData interface { InsertAccountData(ctx context.Context, txn *sql.Tx, userID, roomID, dataType string) (pos types.StreamPosition, err error) // SelectAccountDataInRange returns a map of room ID to a list of `dataType`. - SelectAccountDataInRange(ctx context.Context, txn *sql.Tx, userID string, r types.Range, accountDataEventFilter *gomatrixserverlib.EventFilter) (data map[string][]string, pos types.StreamPosition, err error) + SelectAccountDataInRange(ctx context.Context, txn *sql.Tx, userID string, r types.Range, accountDataEventFilter *synctypes.EventFilter) (data map[string][]string, pos types.StreamPosition, err error) SelectMaxAccountDataID(ctx context.Context, txn *sql.Tx) (id int64, err error) } @@ -53,7 +54,7 @@ type Peeks interface { } type Events interface { - SelectStateInRange(ctx context.Context, txn *sql.Tx, r types.Range, stateFilter *gomatrixserverlib.StateFilter, roomIDs []string) (map[string]map[string]bool, map[string]types.StreamEvent, error) + SelectStateInRange(ctx context.Context, txn *sql.Tx, r types.Range, stateFilter *synctypes.StateFilter, roomIDs []string) (map[string]map[string]bool, map[string]types.StreamEvent, error) SelectMaxEventID(ctx context.Context, txn *sql.Tx) (id int64, err error) InsertEvent( ctx context.Context, txn *sql.Tx, @@ -66,17 +67,15 @@ type Events interface { // SelectRecentEvents returns events between the two stream positions: exclusive of low and inclusive of high. // If onlySyncEvents has a value of true, only returns the events that aren't marked as to exclude from sync. // Returns up to `limit` events. Returns `limited=true` if there are more events in this range but we hit the `limit`. - SelectRecentEvents(ctx context.Context, txn *sql.Tx, roomIDs []string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error) - // SelectEarlyEvents returns the earliest events in the given room. - SelectEarlyEvents(ctx context.Context, txn *sql.Tx, roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter) ([]types.StreamEvent, error) - SelectEvents(ctx context.Context, txn *sql.Tx, eventIDs []string, filter *gomatrixserverlib.RoomEventFilter, preserveOrder bool) ([]types.StreamEvent, error) + SelectRecentEvents(ctx context.Context, txn *sql.Tx, roomIDs []string, r types.Range, eventFilter *synctypes.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error) + SelectEvents(ctx context.Context, txn *sql.Tx, eventIDs []string, filter *synctypes.RoomEventFilter, preserveOrder bool) ([]types.StreamEvent, error) UpdateEventJSON(ctx context.Context, txn *sql.Tx, event *gomatrixserverlib.HeaderedEvent) error // DeleteEventsForRoom removes all event information for a room. This should only be done when removing the room entirely. DeleteEventsForRoom(ctx context.Context, txn *sql.Tx, roomID string) (err error) SelectContextEvent(ctx context.Context, txn *sql.Tx, roomID, eventID string) (int, gomatrixserverlib.HeaderedEvent, error) - SelectContextBeforeEvent(ctx context.Context, txn *sql.Tx, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter) ([]*gomatrixserverlib.HeaderedEvent, error) - SelectContextAfterEvent(ctx context.Context, txn *sql.Tx, id int, roomID string, filter *gomatrixserverlib.RoomEventFilter) (int, []*gomatrixserverlib.HeaderedEvent, error) + SelectContextBeforeEvent(ctx context.Context, txn *sql.Tx, id int, roomID string, filter *synctypes.RoomEventFilter) ([]*gomatrixserverlib.HeaderedEvent, error) + SelectContextAfterEvent(ctx context.Context, txn *sql.Tx, id int, roomID string, filter *synctypes.RoomEventFilter) (int, []*gomatrixserverlib.HeaderedEvent, error) PurgeEvents(ctx context.Context, txn *sql.Tx, roomID string) error ReIndex(ctx context.Context, txn *sql.Tx, limit, offset int64, types []string) (map[int64]gomatrixserverlib.HeaderedEvent, error) @@ -107,7 +106,7 @@ type CurrentRoomState interface { DeleteRoomStateByEventID(ctx context.Context, txn *sql.Tx, eventID string) error DeleteRoomStateForRoom(ctx context.Context, txn *sql.Tx, roomID string) error // SelectCurrentState returns all the current state events for the given room. - SelectCurrentState(ctx context.Context, txn *sql.Tx, roomID string, stateFilter *gomatrixserverlib.StateFilter, excludeEventIDs []string) ([]*gomatrixserverlib.HeaderedEvent, error) + SelectCurrentState(ctx context.Context, txn *sql.Tx, roomID string, stateFilter *synctypes.StateFilter, excludeEventIDs []string) ([]*gomatrixserverlib.HeaderedEvent, error) // SelectRoomIDsWithMembership returns the list of room IDs which have the given user in the given membership state. SelectRoomIDsWithMembership(ctx context.Context, txn *sql.Tx, userID string, membership string) ([]string, error) // SelectRoomIDsWithAnyMembership returns a map of all memberships for the given user. @@ -179,8 +178,8 @@ type SendToDevice interface { } type Filter interface { - SelectFilter(ctx context.Context, txn *sql.Tx, target *gomatrixserverlib.Filter, localpart string, filterID string) error - InsertFilter(ctx context.Context, txn *sql.Tx, filter *gomatrixserverlib.Filter, localpart string) (filterID string, err error) + SelectFilter(ctx context.Context, txn *sql.Tx, target *synctypes.Filter, localpart string, filterID string) error + InsertFilter(ctx context.Context, txn *sql.Tx, filter *synctypes.Filter, localpart string) (filterID string, err error) } type Receipts interface { @@ -218,7 +217,7 @@ type Presence interface { UpsertPresence(ctx context.Context, txn *sql.Tx, userID string, statusMsg *string, presence types.Presence, lastActiveTS gomatrixserverlib.Timestamp, fromSync bool) (pos types.StreamPosition, err error) GetPresenceForUsers(ctx context.Context, txn *sql.Tx, userIDs []string) (presence []*types.PresenceInternal, err error) GetMaxPresenceID(ctx context.Context, txn *sql.Tx) (pos types.StreamPosition, err error) - GetPresenceAfter(ctx context.Context, txn *sql.Tx, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (presences map[string]*types.PresenceInternal, err error) + GetPresenceAfter(ctx context.Context, txn *sql.Tx, after types.StreamPosition, filter synctypes.EventFilter) (presences map[string]*types.PresenceInternal, err error) } type Relations interface { diff --git a/syncapi/storage/tables/output_room_events_test.go b/syncapi/storage/tables/output_room_events_test.go index bdb17ae20..695878008 100644 --- a/syncapi/storage/tables/output_room_events_test.go +++ b/syncapi/storage/tables/output_room_events_test.go @@ -12,6 +12,7 @@ import ( "github.com/matrix-org/dendrite/syncapi/storage/postgres" "github.com/matrix-org/dendrite/syncapi/storage/sqlite3" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/test" "github.com/matrix-org/gomatrixserverlib" ) @@ -84,7 +85,7 @@ func TestOutputRoomEventsTable(t *testing.T) { } wantEventID := []string{urlEv.EventID()} t := true - gotEvents, err = tab.SelectEvents(ctx, txn, wantEventID, &gomatrixserverlib.RoomEventFilter{Limit: 1, ContainsURL: &t}, true) + gotEvents, err = tab.SelectEvents(ctx, txn, wantEventID, &synctypes.RoomEventFilter{Limit: 1, ContainsURL: &t}, true) if err != nil { return fmt.Errorf("failed to SelectEvents: %s", err) } @@ -103,3 +104,53 @@ func TestOutputRoomEventsTable(t *testing.T) { } }) } + +func TestReindex(t *testing.T) { + ctx := context.Background() + alice := test.NewUser(t) + room := test.NewRoom(t, alice) + + room.CreateAndInsert(t, alice, gomatrixserverlib.MRoomName, map[string]interface{}{ + "name": "my new room name", + }, test.WithStateKey("")) + + room.CreateAndInsert(t, alice, gomatrixserverlib.MRoomTopic, map[string]interface{}{ + "topic": "my new room topic", + }, test.WithStateKey("")) + + room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{ + "msgbody": "my room message", + "type": "m.text", + }) + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + tab, db, close := newOutputRoomEventsTable(t, dbType) + defer close() + err := sqlutil.WithTransaction(db, func(txn *sql.Tx) error { + for _, ev := range room.Events() { + _, err := tab.InsertEvent(ctx, txn, ev, nil, nil, nil, false, gomatrixserverlib.HistoryVisibilityShared) + if err != nil { + return fmt.Errorf("failed to InsertEvent: %s", err) + } + } + + return nil + }) + if err != nil { + t.Fatalf("err: %s", err) + } + + events, err := tab.ReIndex(ctx, nil, 10, 0, []string{ + gomatrixserverlib.MRoomName, + gomatrixserverlib.MRoomTopic, + "m.room.message"}) + if err != nil { + t.Fatal(err) + } + + wantEventCount := 3 + if len(events) != wantEventCount { + t.Fatalf("expected %d events, got %d", wantEventCount, len(events)) + } + }) +} diff --git a/syncapi/storage/tables/presence_table_test.go b/syncapi/storage/tables/presence_table_test.go index dce0c695a..cb7a4dee9 100644 --- a/syncapi/storage/tables/presence_table_test.go +++ b/syncapi/storage/tables/presence_table_test.go @@ -14,6 +14,7 @@ import ( "github.com/matrix-org/dendrite/syncapi/storage/postgres" "github.com/matrix-org/dendrite/syncapi/storage/sqlite3" "github.com/matrix-org/dendrite/syncapi/storage/tables" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/test" ) @@ -96,7 +97,7 @@ func TestPresence(t *testing.T) { } // This should return only Bobs status - presences, err := tab.GetPresenceAfter(ctx, txn, maxPos, gomatrixserverlib.EventFilter{Limit: 10}) + presences, err := tab.GetPresenceAfter(ctx, txn, maxPos, synctypes.EventFilter{Limit: 10}) if err != nil { t.Error(err) } diff --git a/syncapi/streams/stream_accountdata.go b/syncapi/streams/stream_accountdata.go index 3593a6563..22953b8c1 100644 --- a/syncapi/streams/stream_accountdata.go +++ b/syncapi/streams/stream_accountdata.go @@ -6,6 +6,7 @@ import ( "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -82,7 +83,7 @@ func (p *AccountDataStreamProvider) IncrementalSync( if globalData, ok := dataRes.GlobalAccountData[dataType]; ok { req.Response.AccountData.Events = append( req.Response.AccountData.Events, - gomatrixserverlib.ClientEvent{ + synctypes.ClientEvent{ Type: dataType, Content: gomatrixserverlib.RawJSON(globalData), }, @@ -96,7 +97,7 @@ func (p *AccountDataStreamProvider) IncrementalSync( } joinData.AccountData.Events = append( joinData.AccountData.Events, - gomatrixserverlib.ClientEvent{ + synctypes.ClientEvent{ Type: dataType, Content: gomatrixserverlib.RawJSON(roomData), }, diff --git a/syncapi/streams/stream_invite.go b/syncapi/streams/stream_invite.go index e4de30e1c..a4414f315 100644 --- a/syncapi/streams/stream_invite.go +++ b/syncapi/streams/stream_invite.go @@ -11,6 +11,7 @@ import ( "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" ) @@ -85,7 +86,7 @@ func (p *InviteStreamProvider) IncrementalSync( lr := types.NewLeaveResponse() h := sha256.Sum256(append([]byte(roomID), []byte(strconv.FormatInt(int64(to), 10))...)) - lr.Timeline.Events = append(lr.Timeline.Events, gomatrixserverlib.ClientEvent{ + lr.Timeline.Events = append(lr.Timeline.Events, synctypes.ClientEvent{ // fake event ID which muxes in the to position EventID: "$" + base64.RawURLEncoding.EncodeToString(h[:]), OriginServerTS: gomatrixserverlib.AsTimestamp(time.Now()), diff --git a/syncapi/streams/stream_pdu.go b/syncapi/streams/stream_pdu.go index 6af25c028..e29e29f7a 100644 --- a/syncapi/streams/stream_pdu.go +++ b/syncapi/streams/stream_pdu.go @@ -10,6 +10,7 @@ import ( roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/internal" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" @@ -242,8 +243,8 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse( device *userapi.Device, r types.Range, delta types.StateDelta, - eventFilter *gomatrixserverlib.RoomEventFilter, - stateFilter *gomatrixserverlib.StateFilter, + eventFilter *synctypes.RoomEventFilter, + stateFilter *synctypes.StateFilter, req *types.SyncRequest, ) (types.StreamPosition, error) { var err error @@ -365,20 +366,20 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse( } } jr.Timeline.PrevBatch = &prevBatch - jr.Timeline.Events = gomatrixserverlib.HeaderedToClientEvents(events, gomatrixserverlib.FormatSync) + jr.Timeline.Events = synctypes.HeaderedToClientEvents(events, synctypes.FormatSync) // If we are limited by the filter AND the history visibility filter // didn't "remove" events, return that the response is limited. jr.Timeline.Limited = (limited && len(events) == len(recentEvents)) || delta.NewlyJoined - jr.State.Events = gomatrixserverlib.HeaderedToClientEvents(delta.StateEvents, gomatrixserverlib.FormatSync) + jr.State.Events = synctypes.HeaderedToClientEvents(delta.StateEvents, synctypes.FormatSync) req.Response.Rooms.Join[delta.RoomID] = jr case gomatrixserverlib.Peek: jr := types.NewJoinResponse() jr.Timeline.PrevBatch = &prevBatch // TODO: Apply history visibility on peeked rooms - jr.Timeline.Events = gomatrixserverlib.HeaderedToClientEvents(recentEvents, gomatrixserverlib.FormatSync) + jr.Timeline.Events = synctypes.HeaderedToClientEvents(recentEvents, synctypes.FormatSync) jr.Timeline.Limited = limited - jr.State.Events = gomatrixserverlib.HeaderedToClientEvents(delta.StateEvents, gomatrixserverlib.FormatSync) + jr.State.Events = synctypes.HeaderedToClientEvents(delta.StateEvents, synctypes.FormatSync) req.Response.Rooms.Peek[delta.RoomID] = jr case gomatrixserverlib.Leave: @@ -387,11 +388,11 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse( case gomatrixserverlib.Ban: lr := types.NewLeaveResponse() lr.Timeline.PrevBatch = &prevBatch - lr.Timeline.Events = gomatrixserverlib.HeaderedToClientEvents(events, gomatrixserverlib.FormatSync) + lr.Timeline.Events = synctypes.HeaderedToClientEvents(events, synctypes.FormatSync) // If we are limited by the filter AND the history visibility filter // didn't "remove" events, return that the response is limited. lr.Timeline.Limited = limited && len(events) == len(recentEvents) - lr.State.Events = gomatrixserverlib.HeaderedToClientEvents(delta.StateEvents, gomatrixserverlib.FormatSync) + lr.State.Events = synctypes.HeaderedToClientEvents(delta.StateEvents, synctypes.FormatSync) req.Response.Rooms.Leave[delta.RoomID] = lr } @@ -420,7 +421,7 @@ func applyHistoryVisibilityFilter( // Only get the state again if there are state events in the timeline if len(stateTypes) > 0 { - filter := gomatrixserverlib.DefaultStateFilter() + filter := synctypes.DefaultStateFilter() filter.Types = &stateTypes filter.Senders = &senders stateEvents, err := snapshot.CurrentState(ctx, roomID, &filter, nil) @@ -451,7 +452,7 @@ func (p *PDUStreamProvider) getJoinResponseForCompleteSync( ctx context.Context, snapshot storage.DatabaseTransaction, roomID string, - stateFilter *gomatrixserverlib.StateFilter, + stateFilter *synctypes.StateFilter, wantFullState bool, device *userapi.Device, isPeek bool, @@ -541,17 +542,17 @@ func (p *PDUStreamProvider) getJoinResponseForCompleteSync( } jr.Timeline.PrevBatch = prevBatch - jr.Timeline.Events = gomatrixserverlib.HeaderedToClientEvents(events, gomatrixserverlib.FormatSync) + jr.Timeline.Events = synctypes.HeaderedToClientEvents(events, synctypes.FormatSync) // If we are limited by the filter AND the history visibility filter // didn't "remove" events, return that the response is limited. jr.Timeline.Limited = limited && len(events) == len(recentEvents) - jr.State.Events = gomatrixserverlib.HeaderedToClientEvents(stateEvents, gomatrixserverlib.FormatSync) + jr.State.Events = synctypes.HeaderedToClientEvents(stateEvents, synctypes.FormatSync) return jr, nil } func (p *PDUStreamProvider) lazyLoadMembers( ctx context.Context, snapshot storage.DatabaseTransaction, roomID string, - incremental, limited bool, stateFilter *gomatrixserverlib.StateFilter, + incremental, limited bool, stateFilter *synctypes.StateFilter, device *userapi.Device, timelineEvents, stateEvents []*gomatrixserverlib.HeaderedEvent, ) ([]*gomatrixserverlib.HeaderedEvent, error) { @@ -595,7 +596,7 @@ func (p *PDUStreamProvider) lazyLoadMembers( wantUsers = append(wantUsers, userID) } // Query missing membership events - filter := gomatrixserverlib.DefaultStateFilter() + filter := synctypes.DefaultStateFilter() filter.Senders = &wantUsers filter.Types = &[]string{gomatrixserverlib.MRoomMember} memberships, err := snapshot.GetStateEventsForRoom(ctx, roomID, &filter) @@ -612,7 +613,7 @@ func (p *PDUStreamProvider) lazyLoadMembers( // addIgnoredUsersToFilter adds ignored users to the eventfilter and // the syncreq itself for further use in streams. -func (p *PDUStreamProvider) addIgnoredUsersToFilter(ctx context.Context, snapshot storage.DatabaseTransaction, req *types.SyncRequest, eventFilter *gomatrixserverlib.RoomEventFilter) error { +func (p *PDUStreamProvider) addIgnoredUsersToFilter(ctx context.Context, snapshot storage.DatabaseTransaction, req *types.SyncRequest, eventFilter *synctypes.RoomEventFilter) error { ignores, err := snapshot.IgnoresForUser(ctx, req.Device.UserID) if err != nil { if err == sql.ErrNoRows { diff --git a/syncapi/streams/stream_presence.go b/syncapi/streams/stream_presence.go index 445e46b3a..81f33207c 100644 --- a/syncapi/streams/stream_presence.go +++ b/syncapi/streams/stream_presence.go @@ -25,6 +25,7 @@ import ( "github.com/matrix-org/dendrite/syncapi/notifier" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" ) @@ -65,7 +66,7 @@ func (p *PresenceStreamProvider) IncrementalSync( from, to types.StreamPosition, ) types.StreamPosition { // We pull out a larger number than the filter asks for, since we're filtering out events later - presences, err := snapshot.PresenceAfter(ctx, from, gomatrixserverlib.EventFilter{Limit: 1000}) + presences, err := snapshot.PresenceAfter(ctx, from, synctypes.EventFilter{Limit: 1000}) if err != nil { req.Log.WithError(err).Error("p.DB.PresenceAfter failed") return from @@ -130,7 +131,7 @@ func (p *PresenceStreamProvider) IncrementalSync( return from } - req.Response.Presence.Events = append(req.Response.Presence.Events, gomatrixserverlib.ClientEvent{ + req.Response.Presence.Events = append(req.Response.Presence.Events, synctypes.ClientEvent{ Content: content, Sender: presence.UserID, Type: gomatrixserverlib.MPresence, @@ -202,7 +203,7 @@ func joinedRooms(res *types.Response, userID string) []string { return roomIDs } -func membershipEventPresent(events []gomatrixserverlib.ClientEvent, userID string) bool { +func membershipEventPresent(events []synctypes.ClientEvent, userID string) bool { for _, ev := range events { // it's enough to know that we have our member event here, don't need to check membership content // as it's implied by being in the respective section of the sync response. diff --git a/syncapi/streams/stream_receipt.go b/syncapi/streams/stream_receipt.go index 16a81e833..88db0054e 100644 --- a/syncapi/streams/stream_receipt.go +++ b/syncapi/streams/stream_receipt.go @@ -7,6 +7,7 @@ import ( "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" ) @@ -86,7 +87,7 @@ func (p *ReceiptStreamProvider) IncrementalSync( jr = types.NewJoinResponse() } - ev := gomatrixserverlib.ClientEvent{ + ev := synctypes.ClientEvent{ Type: gomatrixserverlib.MReceipt, } content := make(map[string]ReceiptMRead) diff --git a/syncapi/streams/stream_typing.go b/syncapi/streams/stream_typing.go index 84c199b39..b0e7d9e7c 100644 --- a/syncapi/streams/stream_typing.go +++ b/syncapi/streams/stream_typing.go @@ -8,6 +8,7 @@ import ( "github.com/matrix-org/dendrite/internal/caching" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" ) @@ -51,7 +52,7 @@ func (p *TypingStreamProvider) IncrementalSync( typingUsers = append(typingUsers, users[i]) } } - ev := gomatrixserverlib.ClientEvent{ + ev := synctypes.ClientEvent{ Type: gomatrixserverlib.MTyping, } ev.Content, err = json.Marshal(map[string]interface{}{ diff --git a/syncapi/sync/request.go b/syncapi/sync/request.go index e5e5fdb5b..617342331 100644 --- a/syncapi/sync/request.go +++ b/syncapi/sync/request.go @@ -28,6 +28,7 @@ import ( "github.com/sirupsen/logrus" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -49,7 +50,7 @@ func newSyncRequest(req *http.Request, device userapi.Device, syncDB storage.Dat } // Create a default filter and apply a stored filter on top of it (if specified) - filter := gomatrixserverlib.DefaultFilter() + filter := synctypes.DefaultFilter() filterQuery := req.URL.Query().Get("filter") if filterQuery != "" { if filterQuery[0] == '{' { diff --git a/syncapi/sync/requestpool_test.go b/syncapi/sync/requestpool_test.go index faa0b49c6..7bce0a0c7 100644 --- a/syncapi/sync/requestpool_test.go +++ b/syncapi/sync/requestpool_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/matrix-org/dendrite/setup/config" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/gomatrixserverlib" ) @@ -33,7 +34,7 @@ func (d dummyDB) GetPresences(ctx context.Context, userID []string) ([]*types.Pr return []*types.PresenceInternal{}, nil } -func (d dummyDB) PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error) { +func (d dummyDB) PresenceAfter(ctx context.Context, after types.StreamPosition, filter synctypes.EventFilter) (map[string]*types.PresenceInternal, error) { return map[string]*types.PresenceInternal{}, nil } diff --git a/syncapi/syncapi_test.go b/syncapi/syncapi_test.go index 584782afb..6496a6070 100644 --- a/syncapi/syncapi_test.go +++ b/syncapi/syncapi_test.go @@ -20,6 +20,7 @@ import ( "github.com/matrix-org/dendrite/syncapi/routing" "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/clientapi/producers" "github.com/matrix-org/dendrite/roomserver" @@ -473,7 +474,7 @@ func testHistoryVisibility(t *testing.T, dbType test.DBType) { } // We only care about the returned events at this point var res struct { - Chunk []gomatrixserverlib.ClientEvent `json:"chunk"` + Chunk []synctypes.ClientEvent `json:"chunk"` } if err := json.NewDecoder(w.Body).Decode(&res); err != nil { t.Errorf("failed to decode response body: %s", err) @@ -521,7 +522,7 @@ func testHistoryVisibility(t *testing.T, dbType test.DBType) { } } -func verifyEventVisible(t *testing.T, wantVisible bool, wantVisibleEvent *gomatrixserverlib.HeaderedEvent, chunk []gomatrixserverlib.ClientEvent) { +func verifyEventVisible(t *testing.T, wantVisible bool, wantVisibleEvent *gomatrixserverlib.HeaderedEvent, chunk []synctypes.ClientEvent) { t.Helper() if wantVisible { for _, ev := range chunk { diff --git a/syncapi/synctypes/clientevent.go b/syncapi/synctypes/clientevent.go new file mode 100644 index 000000000..0d1e85bcc --- /dev/null +++ b/syncapi/synctypes/clientevent.go @@ -0,0 +1,88 @@ +/* Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package synctypes + +import "github.com/matrix-org/gomatrixserverlib" + +type ClientEventFormat int + +const ( + // FormatAll will include all client event keys + FormatAll ClientEventFormat = iota + // FormatSync will include only the event keys required by the /sync API. Notably, this + // means the 'room_id' will be missing from the events. + FormatSync +) + +// ClientEvent is an event which is fit for consumption by clients, in accordance with the specification. +type ClientEvent struct { + Content gomatrixserverlib.RawJSON `json:"content"` + EventID string `json:"event_id,omitempty"` // EventID is omitted on receipt events + OriginServerTS gomatrixserverlib.Timestamp `json:"origin_server_ts,omitempty"` // OriginServerTS is omitted on receipt events + RoomID string `json:"room_id,omitempty"` // RoomID is omitted on /sync responses + Sender string `json:"sender,omitempty"` // Sender is omitted on receipt events + StateKey *string `json:"state_key,omitempty"` + Type string `json:"type"` + Unsigned gomatrixserverlib.RawJSON `json:"unsigned,omitempty"` + Redacts string `json:"redacts,omitempty"` +} + +// ToClientEvents converts server events to client events. +func ToClientEvents(serverEvs []*gomatrixserverlib.Event, format ClientEventFormat) []ClientEvent { + evs := make([]ClientEvent, 0, len(serverEvs)) + for _, se := range serverEvs { + if se == nil { + continue // TODO: shouldn't happen? + } + evs = append(evs, ToClientEvent(se, format)) + } + return evs +} + +// HeaderedToClientEvents converts headered server events to client events. +func HeaderedToClientEvents(serverEvs []*gomatrixserverlib.HeaderedEvent, format ClientEventFormat) []ClientEvent { + evs := make([]ClientEvent, 0, len(serverEvs)) + for _, se := range serverEvs { + if se == nil { + continue // TODO: shouldn't happen? + } + evs = append(evs, HeaderedToClientEvent(se, format)) + } + return evs +} + +// ToClientEvent converts a single server event to a client event. +func ToClientEvent(se *gomatrixserverlib.Event, format ClientEventFormat) ClientEvent { + ce := ClientEvent{ + Content: gomatrixserverlib.RawJSON(se.Content()), + Sender: se.Sender(), + Type: se.Type(), + StateKey: se.StateKey(), + Unsigned: gomatrixserverlib.RawJSON(se.Unsigned()), + OriginServerTS: se.OriginServerTS(), + EventID: se.EventID(), + Redacts: se.Redacts(), + } + if format == FormatAll { + ce.RoomID = se.RoomID() + } + return ce +} + +// HeaderedToClientEvent converts a single headered server event to a client event. +func HeaderedToClientEvent(se *gomatrixserverlib.HeaderedEvent, format ClientEventFormat) ClientEvent { + return ToClientEvent(se.Event, format) +} diff --git a/syncapi/synctypes/clientevent_test.go b/syncapi/synctypes/clientevent_test.go new file mode 100644 index 000000000..ac07917ab --- /dev/null +++ b/syncapi/synctypes/clientevent_test.go @@ -0,0 +1,105 @@ +/* Copyright 2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package synctypes + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/matrix-org/gomatrixserverlib" +) + +func TestToClientEvent(t *testing.T) { // nolint: gocyclo + ev, err := gomatrixserverlib.NewEventFromTrustedJSON([]byte(`{ + "type": "m.room.name", + "state_key": "", + "event_id": "$test:localhost", + "room_id": "!test:localhost", + "sender": "@test:localhost", + "content": { + "name": "Hello World" + }, + "origin_server_ts": 123456, + "unsigned": { + "prev_content": { + "name": "Goodbye World" + } + } + }`), false, gomatrixserverlib.RoomVersionV1) + if err != nil { + t.Fatalf("failed to create Event: %s", err) + } + ce := ToClientEvent(ev, FormatAll) + if ce.EventID != ev.EventID() { + t.Errorf("ClientEvent.EventID: wanted %s, got %s", ev.EventID(), ce.EventID) + } + if ce.OriginServerTS != ev.OriginServerTS() { + t.Errorf("ClientEvent.OriginServerTS: wanted %d, got %d", ev.OriginServerTS(), ce.OriginServerTS) + } + if ce.StateKey == nil || *ce.StateKey != "" { + t.Errorf("ClientEvent.StateKey: wanted '', got %v", ce.StateKey) + } + if ce.Type != ev.Type() { + t.Errorf("ClientEvent.Type: wanted %s, got %s", ev.Type(), ce.Type) + } + if !bytes.Equal(ce.Content, ev.Content()) { + t.Errorf("ClientEvent.Content: wanted %s, got %s", string(ev.Content()), string(ce.Content)) + } + if !bytes.Equal(ce.Unsigned, ev.Unsigned()) { + t.Errorf("ClientEvent.Unsigned: wanted %s, got %s", string(ev.Unsigned()), string(ce.Unsigned)) + } + if ce.Sender != ev.Sender() { + t.Errorf("ClientEvent.Sender: wanted %s, got %s", ev.Sender(), ce.Sender) + } + j, err := json.Marshal(ce) + if err != nil { + t.Fatalf("failed to Marshal ClientEvent: %s", err) + } + // Marshal sorts keys in structs by the order they are defined in the struct, which is alphabetical + out := `{"content":{"name":"Hello World"},"event_id":"$test:localhost","origin_server_ts":123456,` + + `"room_id":"!test:localhost","sender":"@test:localhost","state_key":"","type":"m.room.name",` + + `"unsigned":{"prev_content":{"name":"Goodbye World"}}}` + if !bytes.Equal([]byte(out), j) { + t.Errorf("ClientEvent marshalled to wrong bytes: wanted %s, got %s", out, string(j)) + } +} + +func TestToClientFormatSync(t *testing.T) { + ev, err := gomatrixserverlib.NewEventFromTrustedJSON([]byte(`{ + "type": "m.room.name", + "state_key": "", + "event_id": "$test:localhost", + "room_id": "!test:localhost", + "sender": "@test:localhost", + "content": { + "name": "Hello World" + }, + "origin_server_ts": 123456, + "unsigned": { + "prev_content": { + "name": "Goodbye World" + } + } + }`), false, gomatrixserverlib.RoomVersionV1) + if err != nil { + t.Fatalf("failed to create Event: %s", err) + } + ce := ToClientEvent(ev, FormatSync) + if ce.RoomID != "" { + t.Errorf("ClientEvent.RoomID: wanted '', got %s", ce.RoomID) + } +} diff --git a/syncapi/synctypes/filter.go b/syncapi/synctypes/filter.go new file mode 100644 index 000000000..c994ddb96 --- /dev/null +++ b/syncapi/synctypes/filter.go @@ -0,0 +1,152 @@ +// Copyright 2017 Jan Christian Grünhage +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package synctypes + +import ( + "errors" +) + +// Filter is used by clients to specify how the server should filter responses to e.g. sync requests +// Specified by: https://spec.matrix.org/v1.6/client-server-api/#filtering +type Filter struct { + EventFields []string `json:"event_fields,omitempty"` + EventFormat string `json:"event_format,omitempty"` + Presence EventFilter `json:"presence,omitempty"` + AccountData EventFilter `json:"account_data,omitempty"` + Room RoomFilter `json:"room,omitempty"` +} + +// EventFilter is used to define filtering rules for events +type EventFilter struct { + Limit int `json:"limit,omitempty"` + NotSenders *[]string `json:"not_senders,omitempty"` + NotTypes *[]string `json:"not_types,omitempty"` + Senders *[]string `json:"senders,omitempty"` + Types *[]string `json:"types,omitempty"` +} + +// RoomFilter is used to define filtering rules for room-related events +type RoomFilter struct { + NotRooms *[]string `json:"not_rooms,omitempty"` + Rooms *[]string `json:"rooms,omitempty"` + Ephemeral RoomEventFilter `json:"ephemeral,omitempty"` + IncludeLeave bool `json:"include_leave,omitempty"` + State StateFilter `json:"state,omitempty"` + Timeline RoomEventFilter `json:"timeline,omitempty"` + AccountData RoomEventFilter `json:"account_data,omitempty"` +} + +// StateFilter is used to define filtering rules for state events +type StateFilter struct { + NotSenders *[]string `json:"not_senders,omitempty"` + NotTypes *[]string `json:"not_types,omitempty"` + Senders *[]string `json:"senders,omitempty"` + Types *[]string `json:"types,omitempty"` + LazyLoadMembers bool `json:"lazy_load_members,omitempty"` + IncludeRedundantMembers bool `json:"include_redundant_members,omitempty"` + NotRooms *[]string `json:"not_rooms,omitempty"` + Rooms *[]string `json:"rooms,omitempty"` + Limit int `json:"limit,omitempty"` + UnreadThreadNotifications bool `json:"unread_thread_notifications,omitempty"` + ContainsURL *bool `json:"contains_url,omitempty"` +} + +// RoomEventFilter is used to define filtering rules for events in rooms +type RoomEventFilter struct { + Limit int `json:"limit,omitempty"` + NotSenders *[]string `json:"not_senders,omitempty"` + NotTypes *[]string `json:"not_types,omitempty"` + Senders *[]string `json:"senders,omitempty"` + Types *[]string `json:"types,omitempty"` + LazyLoadMembers bool `json:"lazy_load_members,omitempty"` + IncludeRedundantMembers bool `json:"include_redundant_members,omitempty"` + NotRooms *[]string `json:"not_rooms,omitempty"` + Rooms *[]string `json:"rooms,omitempty"` + UnreadThreadNotifications bool `json:"unread_thread_notifications,omitempty"` + ContainsURL *bool `json:"contains_url,omitempty"` +} + +// Validate checks if the filter contains valid property values +func (filter *Filter) Validate() error { + if filter.EventFormat != "" && filter.EventFormat != "client" && filter.EventFormat != "federation" { + return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]") + } + return nil +} + +// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in +// the request +func DefaultFilter() Filter { + return Filter{ + AccountData: DefaultEventFilter(), + EventFields: nil, + EventFormat: "client", + Presence: DefaultEventFilter(), + Room: RoomFilter{ + AccountData: DefaultRoomEventFilter(), + Ephemeral: DefaultRoomEventFilter(), + IncludeLeave: false, + NotRooms: nil, + Rooms: nil, + State: DefaultStateFilter(), + Timeline: DefaultRoomEventFilter(), + }, + } +} + +// DefaultEventFilter returns the default event filter used by the Matrix server if no filter is +// provided in the request +func DefaultEventFilter() EventFilter { + return EventFilter{ + // parity with synapse: https://github.com/matrix-org/synapse/blob/v1.80.0/synapse/api/filtering.py#L336 + Limit: 10, + NotSenders: nil, + NotTypes: nil, + Senders: nil, + Types: nil, + } +} + +// DefaultStateFilter returns the default state event filter used by the Matrix server if no filter +// is provided in the request +func DefaultStateFilter() StateFilter { + return StateFilter{ + NotSenders: nil, + NotTypes: nil, + Senders: nil, + Types: nil, + LazyLoadMembers: false, + IncludeRedundantMembers: false, + NotRooms: nil, + Rooms: nil, + ContainsURL: nil, + } +} + +// DefaultRoomEventFilter returns the default room event filter used by the Matrix server if no +// filter is provided in the request +func DefaultRoomEventFilter() RoomEventFilter { + return RoomEventFilter{ + // parity with synapse: https://github.com/matrix-org/synapse/blob/v1.80.0/synapse/api/filtering.py#L336 + Limit: 10, + NotSenders: nil, + NotTypes: nil, + Senders: nil, + Types: nil, + NotRooms: nil, + Rooms: nil, + ContainsURL: nil, + } +} diff --git a/syncapi/synctypes/filter_test.go b/syncapi/synctypes/filter_test.go new file mode 100644 index 000000000..d00ee69d6 --- /dev/null +++ b/syncapi/synctypes/filter_test.go @@ -0,0 +1,60 @@ +package synctypes + +import ( + "encoding/json" + "reflect" + "testing" +) + +func Test_Filter(t *testing.T) { + tests := []struct { + name string + input []byte + want RoomEventFilter + }{ + { + name: "empty types filter", + input: []byte(`{ "types": [] }`), + want: RoomEventFilter{ + Limit: 0, + NotSenders: nil, + NotTypes: nil, + Senders: nil, + Types: &[]string{}, + LazyLoadMembers: false, + IncludeRedundantMembers: false, + NotRooms: nil, + Rooms: nil, + ContainsURL: nil, + }, + }, + { + name: "absent types filter", + input: []byte(`{}`), + want: RoomEventFilter{ + Limit: 0, + NotSenders: nil, + NotTypes: nil, + Senders: nil, + Types: nil, + LazyLoadMembers: false, + IncludeRedundantMembers: false, + NotRooms: nil, + Rooms: nil, + ContainsURL: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var f RoomEventFilter + if err := json.Unmarshal(tt.input, &f); err != nil { + t.Fatalf("unable to parse filter: %v", err) + } + if !reflect.DeepEqual(f, tt.want) { + t.Fatalf("Expected %+v\ngot %+v", tt.want, f) + } + }) + } + +} diff --git a/syncapi/types/provider.go b/syncapi/types/provider.go index 9a533002b..f77f83422 100644 --- a/syncapi/types/provider.go +++ b/syncapi/types/provider.go @@ -7,6 +7,7 @@ import ( "github.com/matrix-org/gomatrixserverlib" "github.com/sirupsen/logrus" + "github.com/matrix-org/dendrite/syncapi/synctypes" userapi "github.com/matrix-org/dendrite/userapi/api" ) @@ -15,7 +16,7 @@ type SyncRequest struct { Log *logrus.Entry Device *userapi.Device Response *Response - Filter gomatrixserverlib.Filter + Filter synctypes.Filter Since StreamingToken Timeout time.Duration WantFullState bool diff --git a/syncapi/types/types.go b/syncapi/types/types.go index 6495dd535..0c522fc3b 100644 --- a/syncapi/types/types.go +++ b/syncapi/types/types.go @@ -25,6 +25,7 @@ import ( "github.com/tidwall/gjson" "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/syncapi/synctypes" ) var ( @@ -451,13 +452,13 @@ type UnreadNotifications struct { } type ClientEvents struct { - Events []gomatrixserverlib.ClientEvent `json:"events,omitempty"` + Events []synctypes.ClientEvent `json:"events,omitempty"` } type Timeline struct { - Events []gomatrixserverlib.ClientEvent `json:"events"` - Limited bool `json:"limited"` - PrevBatch *TopologyToken `json:"prev_batch,omitempty"` + Events []synctypes.ClientEvent `json:"events"` + Limited bool `json:"limited"` + PrevBatch *TopologyToken `json:"prev_batch,omitempty"` } type Summary struct { @@ -549,7 +550,7 @@ func NewInviteResponse(event *gomatrixserverlib.HeaderedEvent) *InviteResponse { // Then we'll see if we can create a partial of the invite event itself. // This is needed for clients to work out *who* sent the invite. - inviteEvent := gomatrixserverlib.ToClientEvent(event.Unwrap(), gomatrixserverlib.FormatSync) + inviteEvent := synctypes.ToClientEvent(event.Unwrap(), synctypes.FormatSync) inviteEvent.Unsigned = nil if ev, err := json.Marshal(inviteEvent); err == nil { res.InviteState.Events = append(res.InviteState.Events, ev) diff --git a/syncapi/types/types_test.go b/syncapi/types/types_test.go index 74246d964..8bb887ffe 100644 --- a/syncapi/types/types_test.go +++ b/syncapi/types/types_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/gomatrixserverlib" ) @@ -125,7 +126,7 @@ func TestJoinResponse_MarshalJSON(t *testing.T) { { name: "unread notifications are NOT removed, if state is set", fields: fields{ - State: &ClientEvents{Events: []gomatrixserverlib.ClientEvent{{Content: []byte("{}")}}}, + State: &ClientEvents{Events: []synctypes.ClientEvent{{Content: []byte("{}")}}}, UnreadNotifications: &UnreadNotifications{NotificationCount: 1}, }, want: []byte(`{"state":{"events":[{"content":{},"type":""}]},"unread_notifications":{"highlight_count":0,"notification_count":1}}`), @@ -134,7 +135,7 @@ func TestJoinResponse_MarshalJSON(t *testing.T) { name: "roomID is removed from EDUs", fields: fields{ Ephemeral: &ClientEvents{ - Events: []gomatrixserverlib.ClientEvent{ + Events: []synctypes.ClientEvent{ {RoomID: "!someRandomRoomID:test", Content: []byte("{}")}, }, }, diff --git a/test/user.go b/test/user.go index 95a8f83e6..63206fa16 100644 --- a/test/user.go +++ b/test/user.go @@ -17,6 +17,7 @@ package test import ( "crypto/ed25519" "fmt" + "strconv" "sync/atomic" "testing" @@ -47,6 +48,7 @@ var ( type User struct { ID string + Localpart string AccountType api.AccountType // key ID and private key of the server who has this user, if known. keyID gomatrixserverlib.KeyID @@ -81,6 +83,7 @@ func NewUser(t *testing.T, opts ...UserOpt) *User { WithSigningServer(serverName, keyID, privateKey)(&u) } u.ID = fmt.Sprintf("@%d:%s", counter, u.srvName) + u.Localpart = strconv.Itoa(int(counter)) t.Logf("NewUser: created user %s", u.ID) return &u } diff --git a/userapi/api/api.go b/userapi/api/api.go index fa297f773..7c47efd24 100644 --- a/userapi/api/api.go +++ b/userapi/api/api.go @@ -21,8 +21,10 @@ import ( "strings" "time" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/internal/pushrules" @@ -58,7 +60,7 @@ type MediaUserAPI interface { type FederationUserAPI interface { UploadDeviceKeysAPI QueryOpenIDToken(ctx context.Context, req *QueryOpenIDTokenRequest, res *QueryOpenIDTokenResponse) error - QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error + QueryProfile(ctx context.Context, userID string) (*authtypes.Profile, error) QueryDevices(ctx context.Context, req *QueryDevicesRequest, res *QueryDevicesResponse) error QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse) error QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse) error @@ -83,12 +85,12 @@ type ClientUserAPI interface { LoginTokenInternalAPI UserLoginAPI ClientKeyAPI + ProfileAPI QueryNumericLocalpart(ctx context.Context, req *QueryNumericLocalpartRequest, res *QueryNumericLocalpartResponse) error QueryDevices(ctx context.Context, req *QueryDevicesRequest, res *QueryDevicesResponse) error - QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error QueryPushers(ctx context.Context, req *QueryPushersRequest, res *QueryPushersResponse) error - QueryPushRules(ctx context.Context, req *QueryPushRulesRequest, res *QueryPushRulesResponse) error + QueryPushRules(ctx context.Context, userID string) (*pushrules.AccountRuleSets, error) QueryAccountAvailability(ctx context.Context, req *QueryAccountAvailabilityRequest, res *QueryAccountAvailabilityResponse) error PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error @@ -97,11 +99,9 @@ type ClientUserAPI interface { PerformPasswordUpdate(ctx context.Context, req *PerformPasswordUpdateRequest, res *PerformPasswordUpdateResponse) error PerformPusherDeletion(ctx context.Context, req *PerformPusherDeletionRequest, res *struct{}) error PerformPusherSet(ctx context.Context, req *PerformPusherSetRequest, res *struct{}) error - PerformPushRulesPut(ctx context.Context, req *PerformPushRulesPutRequest, res *struct{}) error + PerformPushRulesPut(ctx context.Context, userID string, ruleSets *pushrules.AccountRuleSets) error PerformAccountDeactivation(ctx context.Context, req *PerformAccountDeactivationRequest, res *PerformAccountDeactivationResponse) error PerformOpenIDTokenCreation(ctx context.Context, req *PerformOpenIDTokenCreationRequest, res *PerformOpenIDTokenCreationResponse) error - SetAvatarURL(ctx context.Context, req *PerformSetAvatarURLRequest, res *PerformSetAvatarURLResponse) error - SetDisplayName(ctx context.Context, req *PerformUpdateDisplayNameRequest, res *PerformUpdateDisplayNameResponse) error QueryNotifications(ctx context.Context, req *QueryNotificationsRequest, res *QueryNotificationsResponse) error InputAccountData(ctx context.Context, req *InputAccountDataRequest, res *InputAccountDataResponse) error PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest, res *PerformKeyBackupResponse) error @@ -113,6 +113,12 @@ type ClientUserAPI interface { PerformSaveThreePIDAssociation(ctx context.Context, req *PerformSaveThreePIDAssociationRequest, res *struct{}) error } +type ProfileAPI interface { + QueryProfile(ctx context.Context, userID string) (*authtypes.Profile, error) + SetAvatarURL(ctx context.Context, localpart string, serverName gomatrixserverlib.ServerName, avatarURL string) (*authtypes.Profile, bool, error) + SetDisplayName(ctx context.Context, localpart string, serverName gomatrixserverlib.ServerName, displayName string) (*authtypes.Profile, bool, error) +} + // custom api functions required by pinecone / p2p demos type QuerySearchProfilesAPI interface { QuerySearchProfiles(ctx context.Context, req *QuerySearchProfilesRequest, res *QuerySearchProfilesResponse) error @@ -224,7 +230,6 @@ type PerformDeviceUpdateRequest struct { } type PerformDeviceUpdateResponse struct { DeviceExists bool - Forbidden bool } type PerformDeviceDeletionRequest struct { @@ -291,22 +296,6 @@ type QueryDevicesResponse struct { Devices []Device } -// QueryProfileRequest is the request for QueryProfile -type QueryProfileRequest struct { - // The user ID to query - UserID string -} - -// QueryProfileResponse is the response for QueryProfile -type QueryProfileResponse struct { - // True if the user exists. Querying for a profile does not create them. - UserExists bool - // The current display name if set. - DisplayName string - // The current avatar URL if set. - AvatarURL string -} - // QuerySearchProfilesRequest is the request for QueryProfile type QuerySearchProfilesRequest struct { // The search string to match @@ -566,19 +555,6 @@ const ( HTTPKind PusherKind = "http" ) -type PerformPushRulesPutRequest struct { - UserID string `json:"user_id"` - RuleSets *pushrules.AccountRuleSets `json:"rule_sets"` -} - -type QueryPushRulesRequest struct { - UserID string `json:"user_id"` -} - -type QueryPushRulesResponse struct { - RuleSets *pushrules.AccountRuleSets `json:"rule_sets"` -} - type QueryNotificationsRequest struct { Localpart string `json:"localpart"` // Required. ServerName gomatrixserverlib.ServerName `json:"server_name"` // Required. @@ -593,22 +569,12 @@ type QueryNotificationsResponse struct { } type Notification struct { - Actions []*pushrules.Action `json:"actions"` // Required. - Event gomatrixserverlib.ClientEvent `json:"event"` // Required. - ProfileTag string `json:"profile_tag"` // Required by Sytest, but actually optional. - Read bool `json:"read"` // Required. - RoomID string `json:"room_id"` // Required. - TS gomatrixserverlib.Timestamp `json:"ts"` // Required. -} - -type PerformSetAvatarURLRequest struct { - Localpart string - ServerName gomatrixserverlib.ServerName - AvatarURL string -} -type PerformSetAvatarURLResponse struct { - Profile *authtypes.Profile `json:"profile"` - Changed bool `json:"changed"` + Actions []*pushrules.Action `json:"actions"` // Required. + Event synctypes.ClientEvent `json:"event"` // Required. + ProfileTag string `json:"profile_tag"` // Required by Sytest, but actually optional. + Read bool `json:"read"` // Required. + RoomID string `json:"room_id"` // Required. + TS gomatrixserverlib.Timestamp `json:"ts"` // Required. } type QueryNumericLocalpartRequest struct { @@ -639,17 +605,6 @@ type QueryAccountByPasswordResponse struct { Exists bool } -type PerformUpdateDisplayNameRequest struct { - Localpart string - ServerName gomatrixserverlib.ServerName - DisplayName string -} - -type PerformUpdateDisplayNameResponse struct { - Profile *authtypes.Profile `json:"profile"` - Changed bool `json:"changed"` -} - type QueryLocalpartForThreePIDRequest struct { ThreePID, Medium string } @@ -752,9 +707,9 @@ type OutputCrossSigningKeyUpdate struct { } type CrossSigningKeyUpdate struct { - MasterKey *gomatrixserverlib.CrossSigningKey `json:"master_key,omitempty"` - SelfSigningKey *gomatrixserverlib.CrossSigningKey `json:"self_signing_key,omitempty"` - UserID string `json:"user_id"` + MasterKey *fclient.CrossSigningKey `json:"master_key,omitempty"` + SelfSigningKey *fclient.CrossSigningKey `json:"self_signing_key,omitempty"` + UserID string `json:"user_id"` } // DeviceKeysEqual returns true if the device keys updates contain the @@ -887,7 +842,7 @@ type PerformClaimKeysResponse struct { } type PerformUploadDeviceKeysRequest struct { - gomatrixserverlib.CrossSigningKeys + fclient.CrossSigningKeys // The user that uploaded the key, should be populated by the clientapi. UserID string } @@ -897,7 +852,7 @@ type PerformUploadDeviceKeysResponse struct { } type PerformUploadDeviceSignaturesRequest struct { - Signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice + Signatures map[string]map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice // The user that uploaded the sig, should be populated by the clientapi. UserID string } @@ -921,9 +876,9 @@ type QueryKeysResponse struct { // Map of user_id to device_id to device_key DeviceKeys map[string]map[string]json.RawMessage // Maps of user_id to cross signing key - MasterKeys map[string]gomatrixserverlib.CrossSigningKey - SelfSigningKeys map[string]gomatrixserverlib.CrossSigningKey - UserSigningKeys map[string]gomatrixserverlib.CrossSigningKey + MasterKeys map[string]fclient.CrossSigningKey + SelfSigningKeys map[string]fclient.CrossSigningKey + UserSigningKeys map[string]fclient.CrossSigningKey // Set if there was a fatal error processing this query Error *KeyError } @@ -978,11 +933,11 @@ type QuerySignaturesResponse struct { // A map of target user ID -> target key/device ID -> origin user ID -> origin key/device ID -> signatures Signatures map[string]map[gomatrixserverlib.KeyID]types.CrossSigningSigMap // A map of target user ID -> cross-signing master key - MasterKeys map[string]gomatrixserverlib.CrossSigningKey + MasterKeys map[string]fclient.CrossSigningKey // A map of target user ID -> cross-signing self-signing key - SelfSigningKeys map[string]gomatrixserverlib.CrossSigningKey + SelfSigningKeys map[string]fclient.CrossSigningKey // A map of target user ID -> cross-signing user-signing key - UserSigningKeys map[string]gomatrixserverlib.CrossSigningKey + UserSigningKeys map[string]fclient.CrossSigningKey // The request error, if any Error *KeyError } diff --git a/userapi/consumers/roomserver.go b/userapi/consumers/roomserver.go index 47d330959..6704658df 100644 --- a/userapi/consumers/roomserver.go +++ b/userapi/consumers/roomserver.go @@ -23,6 +23,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/process" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/producers" @@ -298,7 +299,7 @@ func (s *OutputRoomEventConsumer) processMessage(ctx context.Context, event *gom switch { case event.Type() == gomatrixserverlib.MRoomMember: - cevent := gomatrixserverlib.HeaderedToClientEvent(event, gomatrixserverlib.FormatAll) + cevent := synctypes.HeaderedToClientEvent(event, synctypes.FormatAll) var member *localMembership member, err = newLocalMembership(&cevent) if err != nil { @@ -358,7 +359,7 @@ type localMembership struct { Domain gomatrixserverlib.ServerName } -func newLocalMembership(event *gomatrixserverlib.ClientEvent) (*localMembership, error) { +func newLocalMembership(event *synctypes.ClientEvent) (*localMembership, error) { if event.StateKey == nil { return nil, fmt.Errorf("missing state_key") } @@ -531,7 +532,7 @@ func (s *OutputRoomEventConsumer) notifyLocal(ctx context.Context, event *gomatr // UNSPEC: the spec doesn't say this is a ClientEvent, but the // fields seem to match. room_id should be missing, which // matches the behaviour of FormatSync. - Event: gomatrixserverlib.HeaderedToClientEvent(event, gomatrixserverlib.FormatSync), + Event: synctypes.HeaderedToClientEvent(event, synctypes.FormatSync), // TODO: this is per-device, but it's not part of the primary // key. So inserting one notification per profile tag doesn't // make sense. What is this supposed to be? Sytests require it diff --git a/userapi/consumers/signingkeyupdate.go b/userapi/consumers/signingkeyupdate.go index f4ff017db..006ccb728 100644 --- a/userapi/consumers/signingkeyupdate.go +++ b/userapi/consumers/signingkeyupdate.go @@ -19,6 +19,7 @@ import ( "encoding/json" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/nats-io/nats.go" "github.com/sirupsen/logrus" @@ -86,7 +87,7 @@ func (t *SigningKeyUpdateConsumer) onMessage(ctx context.Context, msgs []*nats.M return true } - keys := gomatrixserverlib.CrossSigningKeys{} + keys := fclient.CrossSigningKeys{} if updatePayload.MasterKey != nil { keys.MasterKey = *updatePayload.MasterKey } diff --git a/userapi/internal/cross_signing.go b/userapi/internal/cross_signing.go index 8b9704d1b..23b6207e2 100644 --- a/userapi/internal/cross_signing.go +++ b/userapi/internal/cross_signing.go @@ -25,11 +25,12 @@ import ( "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/sirupsen/logrus" "golang.org/x/crypto/curve25519" ) -func sanityCheckKey(key gomatrixserverlib.CrossSigningKey, userID string, purpose gomatrixserverlib.CrossSigningKeyPurpose) error { +func sanityCheckKey(key fclient.CrossSigningKey, userID string, purpose fclient.CrossSigningKeyPurpose) error { // Is there exactly one key? if len(key.Keys) != 1 { return fmt.Errorf("should contain exactly one key") @@ -105,12 +106,12 @@ func sanityCheckKey(key gomatrixserverlib.CrossSigningKey, userID string, purpos // nolint:gocyclo func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) error { // Find the keys to store. - byPurpose := map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey{} + byPurpose := map[fclient.CrossSigningKeyPurpose]fclient.CrossSigningKey{} toStore := types.CrossSigningKeyMap{} hasMasterKey := false if len(req.MasterKey.Keys) > 0 { - if err := sanityCheckKey(req.MasterKey, req.UserID, gomatrixserverlib.CrossSigningKeyPurposeMaster); err != nil { + if err := sanityCheckKey(req.MasterKey, req.UserID, fclient.CrossSigningKeyPurposeMaster); err != nil { res.Error = &api.KeyError{ Err: "Master key sanity check failed: " + err.Error(), IsInvalidParam: true, @@ -118,15 +119,15 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api. return nil } - byPurpose[gomatrixserverlib.CrossSigningKeyPurposeMaster] = req.MasterKey + byPurpose[fclient.CrossSigningKeyPurposeMaster] = req.MasterKey for _, key := range req.MasterKey.Keys { // iterates once, see sanityCheckKey - toStore[gomatrixserverlib.CrossSigningKeyPurposeMaster] = key + toStore[fclient.CrossSigningKeyPurposeMaster] = key } hasMasterKey = true } if len(req.SelfSigningKey.Keys) > 0 { - if err := sanityCheckKey(req.SelfSigningKey, req.UserID, gomatrixserverlib.CrossSigningKeyPurposeSelfSigning); err != nil { + if err := sanityCheckKey(req.SelfSigningKey, req.UserID, fclient.CrossSigningKeyPurposeSelfSigning); err != nil { res.Error = &api.KeyError{ Err: "Self-signing key sanity check failed: " + err.Error(), IsInvalidParam: true, @@ -134,14 +135,14 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api. return nil } - byPurpose[gomatrixserverlib.CrossSigningKeyPurposeSelfSigning] = req.SelfSigningKey + byPurpose[fclient.CrossSigningKeyPurposeSelfSigning] = req.SelfSigningKey for _, key := range req.SelfSigningKey.Keys { // iterates once, see sanityCheckKey - toStore[gomatrixserverlib.CrossSigningKeyPurposeSelfSigning] = key + toStore[fclient.CrossSigningKeyPurposeSelfSigning] = key } } if len(req.UserSigningKey.Keys) > 0 { - if err := sanityCheckKey(req.UserSigningKey, req.UserID, gomatrixserverlib.CrossSigningKeyPurposeUserSigning); err != nil { + if err := sanityCheckKey(req.UserSigningKey, req.UserID, fclient.CrossSigningKeyPurposeUserSigning); err != nil { res.Error = &api.KeyError{ Err: "User-signing key sanity check failed: " + err.Error(), IsInvalidParam: true, @@ -149,9 +150,9 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api. return nil } - byPurpose[gomatrixserverlib.CrossSigningKeyPurposeUserSigning] = req.UserSigningKey + byPurpose[fclient.CrossSigningKeyPurposeUserSigning] = req.UserSigningKey for _, key := range req.UserSigningKey.Keys { // iterates once, see sanityCheckKey - toStore[gomatrixserverlib.CrossSigningKeyPurposeUserSigning] = key + toStore[fclient.CrossSigningKeyPurposeUserSigning] = key } } @@ -180,7 +181,7 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api. // If we still can't find a master key for the user then stop the upload. // This satisfies the "Fails to upload self-signing key without master key" test. if !hasMasterKey { - if _, hasMasterKey = existingKeys[gomatrixserverlib.CrossSigningKeyPurposeMaster]; !hasMasterKey { + if _, hasMasterKey = existingKeys[fclient.CrossSigningKeyPurposeMaster]; !hasMasterKey { res.Error = &api.KeyError{ Err: "No master key was found", IsMissingParam: true, @@ -191,10 +192,10 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api. // Check if anything actually changed compared to what we have in the database. changed := false - for _, purpose := range []gomatrixserverlib.CrossSigningKeyPurpose{ - gomatrixserverlib.CrossSigningKeyPurposeMaster, - gomatrixserverlib.CrossSigningKeyPurposeSelfSigning, - gomatrixserverlib.CrossSigningKeyPurposeUserSigning, + for _, purpose := range []fclient.CrossSigningKeyPurpose{ + fclient.CrossSigningKeyPurposeMaster, + fclient.CrossSigningKeyPurposeSelfSigning, + fclient.CrossSigningKeyPurposeUserSigning, } { old, gotOld := existingKeys[purpose] new, gotNew := toStore[purpose] @@ -248,10 +249,10 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api. update := api.CrossSigningKeyUpdate{ UserID: req.UserID, } - if mk, ok := byPurpose[gomatrixserverlib.CrossSigningKeyPurposeMaster]; ok { + if mk, ok := byPurpose[fclient.CrossSigningKeyPurposeMaster]; ok { update.MasterKey = &mk } - if ssk, ok := byPurpose[gomatrixserverlib.CrossSigningKeyPurposeSelfSigning]; ok { + if ssk, ok := byPurpose[fclient.CrossSigningKeyPurposeSelfSigning]; ok { update.SelfSigningKey = &ssk } if update.MasterKey == nil && update.SelfSigningKey == nil { @@ -279,36 +280,36 @@ func (a *UserInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req } _ = a.QueryKeys(ctx, queryReq, queryRes) - selfSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{} - otherSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{} + selfSignatures := map[string]map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice{} + otherSignatures := map[string]map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice{} // Sort signatures into two groups: one where people have signed their own // keys and one where people have signed someone elses for userID, forUserID := range req.Signatures { for keyID, keyOrDevice := range forUserID { switch key := keyOrDevice.CrossSigningBody.(type) { - case *gomatrixserverlib.CrossSigningKey: + case *fclient.CrossSigningKey: if key.UserID == req.UserID { if _, ok := selfSignatures[userID]; !ok { - selfSignatures[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{} + selfSignatures[userID] = map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice{} } selfSignatures[userID][keyID] = keyOrDevice } else { if _, ok := otherSignatures[userID]; !ok { - otherSignatures[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{} + otherSignatures[userID] = map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice{} } otherSignatures[userID][keyID] = keyOrDevice } - case *gomatrixserverlib.DeviceKeys: + case *fclient.DeviceKeys: if key.UserID == req.UserID { if _, ok := selfSignatures[userID]; !ok { - selfSignatures[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{} + selfSignatures[userID] = map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice{} } selfSignatures[userID][keyID] = keyOrDevice } else { if _, ok := otherSignatures[userID]; !ok { - otherSignatures[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{} + otherSignatures[userID] = map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice{} } otherSignatures[userID][keyID] = keyOrDevice } @@ -354,7 +355,7 @@ func (a *UserInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req func (a *UserInternalAPI) processSelfSignatures( ctx context.Context, - signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice, + signatures map[string]map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice, ) error { // Here we will process: // * The user signing their own devices using their self-signing key @@ -363,7 +364,7 @@ func (a *UserInternalAPI) processSelfSignatures( for targetUserID, forTargetUserID := range signatures { for targetKeyID, signature := range forTargetUserID { switch sig := signature.CrossSigningBody.(type) { - case *gomatrixserverlib.CrossSigningKey: + case *fclient.CrossSigningKey: for keyID := range sig.Keys { split := strings.SplitN(string(keyID), ":", 2) if len(split) > 1 && gomatrixserverlib.KeyID(split[1]) == targetKeyID { @@ -381,7 +382,7 @@ func (a *UserInternalAPI) processSelfSignatures( } } - case *gomatrixserverlib.DeviceKeys: + case *fclient.DeviceKeys: for originUserID, forOriginUserID := range sig.Signatures { for originKeyID, originSig := range forOriginUserID { if err := a.KeyDatabase.StoreCrossSigningSigsForTarget( @@ -403,7 +404,7 @@ func (a *UserInternalAPI) processSelfSignatures( func (a *UserInternalAPI) processOtherSignatures( ctx context.Context, userID string, queryRes *api.QueryKeysResponse, - signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice, + signatures map[string]map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice, ) error { // Here we will process: // * A user signing someone else's master keys using their user-signing keys @@ -411,7 +412,7 @@ func (a *UserInternalAPI) processOtherSignatures( for targetUserID, forTargetUserID := range signatures { for _, signature := range forTargetUserID { switch sig := signature.CrossSigningBody.(type) { - case *gomatrixserverlib.CrossSigningKey: + case *fclient.CrossSigningKey: // Find the local copy of the master key. We'll use this to be // sure that the supplied stanza matches the key that we think it // should be. @@ -509,13 +510,13 @@ func (a *UserInternalAPI) crossSigningKeysFromDatabase( } switch keyType { - case gomatrixserverlib.CrossSigningKeyPurposeMaster: + case fclient.CrossSigningKeyPurposeMaster: res.MasterKeys[targetUserID] = key - case gomatrixserverlib.CrossSigningKeyPurposeSelfSigning: + case fclient.CrossSigningKeyPurposeSelfSigning: res.SelfSigningKeys[targetUserID] = key - case gomatrixserverlib.CrossSigningKeyPurposeUserSigning: + case fclient.CrossSigningKeyPurposeUserSigning: res.UserSigningKeys[targetUserID] = key } } @@ -534,21 +535,21 @@ func (a *UserInternalAPI) QuerySignatures(ctx context.Context, req *api.QuerySig for targetPurpose, targetKey := range keyMap { switch targetPurpose { - case gomatrixserverlib.CrossSigningKeyPurposeMaster: + case fclient.CrossSigningKeyPurposeMaster: if res.MasterKeys == nil { - res.MasterKeys = map[string]gomatrixserverlib.CrossSigningKey{} + res.MasterKeys = map[string]fclient.CrossSigningKey{} } res.MasterKeys[targetUserID] = targetKey - case gomatrixserverlib.CrossSigningKeyPurposeSelfSigning: + case fclient.CrossSigningKeyPurposeSelfSigning: if res.SelfSigningKeys == nil { - res.SelfSigningKeys = map[string]gomatrixserverlib.CrossSigningKey{} + res.SelfSigningKeys = map[string]fclient.CrossSigningKey{} } res.SelfSigningKeys[targetUserID] = targetKey - case gomatrixserverlib.CrossSigningKeyPurposeUserSigning: + case fclient.CrossSigningKeyPurposeUserSigning: if res.UserSigningKeys == nil { - res.UserSigningKeys = map[string]gomatrixserverlib.CrossSigningKey{} + res.UserSigningKeys = map[string]fclient.CrossSigningKey{} } res.UserSigningKeys[targetUserID] = targetKey } diff --git a/userapi/internal/device_list_update.go b/userapi/internal/device_list_update.go index 3b4dcf98e..a274e1ae3 100644 --- a/userapi/internal/device_list_update.go +++ b/userapi/internal/device_list_update.go @@ -25,6 +25,7 @@ import ( "time" rsapi "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" @@ -508,12 +509,12 @@ func (u *DeviceListUpdater) processServerUser(ctx context.Context, serverName go } uploadRes := &api.PerformUploadDeviceKeysResponse{} if res.MasterKey != nil { - if err = sanityCheckKey(*res.MasterKey, userID, gomatrixserverlib.CrossSigningKeyPurposeMaster); err == nil { + if err = sanityCheckKey(*res.MasterKey, userID, fclient.CrossSigningKeyPurposeMaster); err == nil { uploadReq.MasterKey = *res.MasterKey } } if res.SelfSigningKey != nil { - if err = sanityCheckKey(*res.SelfSigningKey, userID, gomatrixserverlib.CrossSigningKeyPurposeSelfSigning); err == nil { + if err = sanityCheckKey(*res.SelfSigningKey, userID, fclient.CrossSigningKeyPurposeSelfSigning); err == nil { uploadReq.SelfSigningKey = *res.SelfSigningKey } } @@ -527,7 +528,7 @@ func (u *DeviceListUpdater) processServerUser(ctx context.Context, serverName go return defaultWaitTime, nil } -func (u *DeviceListUpdater) updateDeviceList(res *gomatrixserverlib.RespUserDevices) error { +func (u *DeviceListUpdater) updateDeviceList(res *fclient.RespUserDevices) error { ctx := context.Background() // we've got the keys, don't time out when persisting them to the database. keys := make([]api.DeviceMessage, len(res.Devices)) existingKeys := make([]api.DeviceMessage, len(res.Devices)) diff --git a/userapi/internal/device_list_update_test.go b/userapi/internal/device_list_update_test.go index c0965a2c2..47b31c685 100644 --- a/userapi/internal/device_list_update_test.go +++ b/userapi/internal/device_list_update_test.go @@ -29,6 +29,7 @@ import ( "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" roomserver "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" @@ -135,10 +136,10 @@ func (t *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return t.fn(req) } -func newFedClient(tripper func(*http.Request) (*http.Response, error)) *gomatrixserverlib.FederationClient { +func newFedClient(tripper func(*http.Request) (*http.Response, error)) *fclient.FederationClient { _, pkey, _ := ed25519.GenerateKey(nil) - fedClient := gomatrixserverlib.NewFederationClient( - []*gomatrixserverlib.SigningIdentity{ + fedClient := fclient.NewFederationClient( + []*fclient.SigningIdentity{ { ServerName: gomatrixserverlib.ServerName("example.test"), KeyID: gomatrixserverlib.KeyID("ed25519:test"), @@ -146,8 +147,8 @@ func newFedClient(tripper func(*http.Request) (*http.Response, error)) *gomatrix }, }, ) - fedClient.Client = *gomatrixserverlib.NewClient( - gomatrixserverlib.WithTransport(&roundTripper{tripper}), + fedClient.Client = *fclient.NewClient( + fclient.WithTransport(&roundTripper{tripper}), ) return fedClient } diff --git a/userapi/internal/key_api.go b/userapi/internal/key_api.go index be816fe5d..043028725 100644 --- a/userapi/internal/key_api.go +++ b/userapi/internal/key_api.go @@ -24,6 +24,7 @@ import ( "time" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/sirupsen/logrus" "github.com/tidwall/gjson" @@ -229,9 +230,9 @@ func (a *UserInternalAPI) PerformMarkAsStaleIfNeeded(ctx context.Context, req *a func (a *UserInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) error { var respMu sync.Mutex res.DeviceKeys = make(map[string]map[string]json.RawMessage) - res.MasterKeys = make(map[string]gomatrixserverlib.CrossSigningKey) - res.SelfSigningKeys = make(map[string]gomatrixserverlib.CrossSigningKey) - res.UserSigningKeys = make(map[string]gomatrixserverlib.CrossSigningKey) + res.MasterKeys = make(map[string]fclient.CrossSigningKey) + res.SelfSigningKeys = make(map[string]fclient.CrossSigningKey) + res.UserSigningKeys = make(map[string]fclient.CrossSigningKey) res.Failures = make(map[string]interface{}) // make a map from domain to device keys @@ -362,7 +363,7 @@ func (a *UserInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysReque if len(sigMap) == 0 { continue } - var deviceKey gomatrixserverlib.DeviceKeys + var deviceKey fclient.DeviceKeys if err = json.Unmarshal(key, &deviceKey); err != nil { continue } @@ -415,7 +416,7 @@ func (a *UserInternalAPI) queryRemoteKeys( ctx context.Context, timeout time.Duration, res *api.QueryKeysResponse, domainToDeviceKeys map[string]map[string][]string, domainToCrossSigningKeys map[string]map[string]struct{}, ) { - resultCh := make(chan *gomatrixserverlib.RespQueryKeys, len(domainToDeviceKeys)) + resultCh := make(chan *fclient.RespQueryKeys, len(domainToDeviceKeys)) // allows us to wait until all federation servers have been poked var wg sync.WaitGroup // mutex for writing directly to res (e.g failures) @@ -450,7 +451,7 @@ func (a *UserInternalAPI) queryRemoteKeys( close(resultCh) }() - processResult := func(result *gomatrixserverlib.RespQueryKeys) { + processResult := func(result *fclient.RespQueryKeys) { respMu.Lock() defer respMu.Unlock() for userID, nest := range result.DeviceKeys { @@ -483,7 +484,7 @@ func (a *UserInternalAPI) queryRemoteKeys( func (a *UserInternalAPI) queryRemoteKeysOnServer( ctx context.Context, serverName string, devKeys map[string][]string, crossSigningKeys map[string]struct{}, - wg *sync.WaitGroup, respMu *sync.Mutex, timeout time.Duration, resultCh chan<- *gomatrixserverlib.RespQueryKeys, + wg *sync.WaitGroup, respMu *sync.Mutex, timeout time.Duration, resultCh chan<- *fclient.RespQueryKeys, res *api.QueryKeysResponse, ) { defer wg.Done() diff --git a/userapi/internal/user_api.go b/userapi/internal/user_api.go index 8977697b5..139ca7580 100644 --- a/userapi/internal/user_api.go +++ b/userapi/internal/user_api.go @@ -23,7 +23,10 @@ import ( "strconv" "time" + appserviceAPI "github.com/matrix-org/dendrite/appservice/api" + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" fedsenderapi "github.com/matrix-org/dendrite/federationapi/api" + "github.com/matrix-org/dendrite/internal/pushrules" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" "github.com/sirupsen/logrus" @@ -386,11 +389,6 @@ func (a *UserInternalAPI) PerformDeviceUpdate(ctx context.Context, req *api.Perf } res.DeviceExists = true - if dev.UserID != req.RequestingUserID { - res.Forbidden = true - return nil - } - err = a.DB.UpdateDevice(ctx, localpart, domain, req.DeviceID, req.DisplayName) if err != nil { util.GetLogger(ctx).WithError(err).Error("deviceDB.UpdateDevice failed") @@ -423,25 +421,26 @@ func (a *UserInternalAPI) PerformDeviceUpdate(ctx context.Context, req *api.Perf return nil } -func (a *UserInternalAPI) QueryProfile(ctx context.Context, req *api.QueryProfileRequest, res *api.QueryProfileResponse) error { - local, domain, err := gomatrixserverlib.SplitID('@', req.UserID) +var ( + ErrIsRemoteServer = errors.New("cannot query profile of remote users") +) + +func (a *UserInternalAPI) QueryProfile(ctx context.Context, userID string) (*authtypes.Profile, error) { + local, domain, err := gomatrixserverlib.SplitID('@', userID) if err != nil { - return err + return nil, err } if !a.Config.Matrix.IsLocalServerName(domain) { - return fmt.Errorf("cannot query profile of remote users (server name %s)", domain) + return nil, ErrIsRemoteServer } prof, err := a.DB.GetProfileByLocalpart(ctx, local, domain) if err != nil { if err == sql.ErrNoRows { - return nil + return nil, appserviceAPI.ErrProfileNotExists } - return err + return nil, err } - res.UserExists = true - res.AvatarURL = prof.AvatarURL - res.DisplayName = prof.DisplayName - return nil + return prof, nil } func (a *UserInternalAPI) QuerySearchProfiles(ctx context.Context, req *api.QuerySearchProfilesRequest, res *api.QuerySearchProfilesResponse) error { @@ -874,43 +873,32 @@ func (a *UserInternalAPI) QueryPushers(ctx context.Context, req *api.QueryPusher func (a *UserInternalAPI) PerformPushRulesPut( ctx context.Context, - req *api.PerformPushRulesPutRequest, - _ *struct{}, + userID string, + ruleSets *pushrules.AccountRuleSets, ) error { - bs, err := json.Marshal(&req.RuleSets) + bs, err := json.Marshal(ruleSets) if err != nil { return err } userReq := api.InputAccountDataRequest{ - UserID: req.UserID, + UserID: userID, DataType: pushRulesAccountDataType, AccountData: json.RawMessage(bs), } var userRes api.InputAccountDataResponse // empty - if err := a.InputAccountData(ctx, &userReq, &userRes); err != nil { - return err - } - return nil + return a.InputAccountData(ctx, &userReq, &userRes) } -func (a *UserInternalAPI) QueryPushRules(ctx context.Context, req *api.QueryPushRulesRequest, res *api.QueryPushRulesResponse) error { - localpart, domain, err := gomatrixserverlib.SplitID('@', req.UserID) +func (a *UserInternalAPI) QueryPushRules(ctx context.Context, userID string) (*pushrules.AccountRuleSets, error) { + localpart, domain, err := gomatrixserverlib.SplitID('@', userID) if err != nil { - return fmt.Errorf("failed to split user ID %q for push rules", req.UserID) + return nil, fmt.Errorf("failed to split user ID %q for push rules", userID) } - pushRules, err := a.DB.QueryPushRules(ctx, localpart, domain) - if err != nil { - return fmt.Errorf("failed to query push rules: %w", err) - } - res.RuleSets = pushRules - return nil + return a.DB.QueryPushRules(ctx, localpart, domain) } -func (a *UserInternalAPI) SetAvatarURL(ctx context.Context, req *api.PerformSetAvatarURLRequest, res *api.PerformSetAvatarURLResponse) error { - profile, changed, err := a.DB.SetAvatarURL(ctx, req.Localpart, req.ServerName, req.AvatarURL) - res.Profile = profile - res.Changed = changed - return err +func (a *UserInternalAPI) SetAvatarURL(ctx context.Context, localpart string, serverName gomatrixserverlib.ServerName, avatarURL string) (*authtypes.Profile, bool, error) { + return a.DB.SetAvatarURL(ctx, localpart, serverName, avatarURL) } func (a *UserInternalAPI) QueryNumericLocalpart(ctx context.Context, req *api.QueryNumericLocalpartRequest, res *api.QueryNumericLocalpartResponse) error { @@ -944,11 +932,8 @@ func (a *UserInternalAPI) QueryAccountByPassword(ctx context.Context, req *api.Q } } -func (a *UserInternalAPI) SetDisplayName(ctx context.Context, req *api.PerformUpdateDisplayNameRequest, res *api.PerformUpdateDisplayNameResponse) error { - profile, changed, err := a.DB.SetDisplayName(ctx, req.Localpart, req.ServerName, req.DisplayName) - res.Profile = profile - res.Changed = changed - return err +func (a *UserInternalAPI) SetDisplayName(ctx context.Context, localpart string, serverName gomatrixserverlib.ServerName, displayName string) (*authtypes.Profile, bool, error) { + return a.DB.SetDisplayName(ctx, localpart, serverName, displayName) } func (a *UserInternalAPI) QueryLocalpartForThreePID(ctx context.Context, req *api.QueryLocalpartForThreePIDRequest, res *api.QueryLocalpartForThreePIDResponse) error { diff --git a/userapi/storage/interface.go b/userapi/storage/interface.go index 278378861..4ffb126a7 100644 --- a/userapi/storage/interface.go +++ b/userapi/storage/interface.go @@ -20,6 +20,7 @@ import ( "errors" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/internal/pushrules" @@ -203,7 +204,7 @@ type KeyDatabase interface { // MarkDeviceListStale sets the stale bit for this user to isStale. MarkDeviceListStale(ctx context.Context, userID string, isStale bool) error - CrossSigningKeysForUser(ctx context.Context, userID string) (map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey, error) + CrossSigningKeysForUser(ctx context.Context, userID string) (map[fclient.CrossSigningKeyPurpose]fclient.CrossSigningKey, error) CrossSigningKeysDataForUser(ctx context.Context, userID string) (types.CrossSigningKeyMap, error) CrossSigningSigsForTarget(ctx context.Context, originUserID, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error) diff --git a/userapi/storage/postgres/cross_signing_keys_table.go b/userapi/storage/postgres/cross_signing_keys_table.go index c0ecbd303..b6fe6d721 100644 --- a/userapi/storage/postgres/cross_signing_keys_table.go +++ b/userapi/storage/postgres/cross_signing_keys_table.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/dendrite/userapi/storage/tables" "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) var crossSigningKeysSchema = ` @@ -89,7 +90,7 @@ func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUser( } func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser( - ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes, + ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes, ) error { keyTypeInt, ok := types.KeyTypePurposeToInt[keyType] if !ok { diff --git a/userapi/storage/shared/storage.go b/userapi/storage/shared/storage.go index d3272a032..a03d022ad 100644 --- a/userapi/storage/shared/storage.go +++ b/userapi/storage/shared/storage.go @@ -27,6 +27,7 @@ import ( "time" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "golang.org/x/crypto/bcrypt" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" @@ -1026,17 +1027,17 @@ func (d *KeyDatabase) DeleteDeviceKeys(ctx context.Context, userID string, devic } // CrossSigningKeysForUser returns the latest known cross-signing keys for a user, if any. -func (d *KeyDatabase) CrossSigningKeysForUser(ctx context.Context, userID string) (map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey, error) { +func (d *KeyDatabase) CrossSigningKeysForUser(ctx context.Context, userID string) (map[fclient.CrossSigningKeyPurpose]fclient.CrossSigningKey, error) { keyMap, err := d.CrossSigningKeysTable.SelectCrossSigningKeysForUser(ctx, nil, userID) if err != nil { return nil, fmt.Errorf("d.CrossSigningKeysTable.SelectCrossSigningKeysForUser: %w", err) } - results := map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey{} + results := map[fclient.CrossSigningKeyPurpose]fclient.CrossSigningKey{} for purpose, key := range keyMap { keyID := gomatrixserverlib.KeyID("ed25519:" + key.Encode()) - result := gomatrixserverlib.CrossSigningKey{ + result := fclient.CrossSigningKey{ UserID: userID, - Usage: []gomatrixserverlib.CrossSigningKeyPurpose{purpose}, + Usage: []fclient.CrossSigningKeyPurpose{purpose}, Keys: map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{ keyID: key, }, diff --git a/userapi/storage/sqlite3/cross_signing_keys_table.go b/userapi/storage/sqlite3/cross_signing_keys_table.go index 10721fcc8..e1c45c411 100644 --- a/userapi/storage/sqlite3/cross_signing_keys_table.go +++ b/userapi/storage/sqlite3/cross_signing_keys_table.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/dendrite/userapi/storage/tables" "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) var crossSigningKeysSchema = ` @@ -88,7 +89,7 @@ func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUser( } func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser( - ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes, + ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes, ) error { keyTypeInt, ok := types.KeyTypePurposeToInt[keyType] if !ok { diff --git a/userapi/storage/storage_test.go b/userapi/storage/storage_test.go index cf7c5144e..7afcda6e9 100644 --- a/userapi/storage/storage_test.go +++ b/userapi/storage/storage_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -356,12 +357,12 @@ func Test_OpenID(t *testing.T) { expiresAtMS := time.Now().UnixNano()/int64(time.Millisecond) + openIDLifetimeMS expires, err := db.CreateOpenIDToken(ctx, token, alice.ID) assert.NoError(t, err, "unable to create OpenID token") - assert.Equal(t, expiresAtMS, expires) + assert.InDelta(t, expiresAtMS, expires, 2) // 2ms leeway attributes, err := db.GetOpenIDTokenAttributes(ctx, token) assert.NoError(t, err, "unable to get OpenID token attributes") assert.Equal(t, alice.ID, attributes.UserID) - assert.Equal(t, expiresAtMS, attributes.ExpiresAtMS) + assert.InDelta(t, expiresAtMS, attributes.ExpiresAtMS, 2) // 2ms leeway }) } @@ -526,7 +527,7 @@ func Test_Notification(t *testing.T) { Actions: []*pushrules.Action{ {}, }, - Event: gomatrixserverlib.ClientEvent{ + Event: synctypes.ClientEvent{ Content: gomatrixserverlib.RawJSON("{}"), }, Read: false, diff --git a/userapi/storage/tables/interface.go b/userapi/storage/tables/interface.go index 693e73038..2d1339282 100644 --- a/userapi/storage/tables/interface.go +++ b/userapi/storage/tables/interface.go @@ -22,6 +22,7 @@ import ( "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/userapi/types" @@ -181,7 +182,7 @@ type StaleDeviceLists interface { type CrossSigningKeys interface { SelectCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string) (r types.CrossSigningKeyMap, err error) - UpsertCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes) error + UpsertCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes) error } type CrossSigningSigs interface { diff --git a/userapi/types/storage.go b/userapi/types/storage.go index 7fb90454e..a910f7f10 100644 --- a/userapi/types/storage.go +++ b/userapi/types/storage.go @@ -18,6 +18,7 @@ import ( "math" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" ) const ( @@ -29,22 +30,22 @@ const ( // KeyTypePurposeToInt maps a purpose to an integer, which is used in the // database to reduce the amount of space taken up by this column. -var KeyTypePurposeToInt = map[gomatrixserverlib.CrossSigningKeyPurpose]int16{ - gomatrixserverlib.CrossSigningKeyPurposeMaster: 1, - gomatrixserverlib.CrossSigningKeyPurposeSelfSigning: 2, - gomatrixserverlib.CrossSigningKeyPurposeUserSigning: 3, +var KeyTypePurposeToInt = map[fclient.CrossSigningKeyPurpose]int16{ + fclient.CrossSigningKeyPurposeMaster: 1, + fclient.CrossSigningKeyPurposeSelfSigning: 2, + fclient.CrossSigningKeyPurposeUserSigning: 3, } // KeyTypeIntToPurpose maps an integer to a purpose, which is used in the // database to reduce the amount of space taken up by this column. -var KeyTypeIntToPurpose = map[int16]gomatrixserverlib.CrossSigningKeyPurpose{ - 1: gomatrixserverlib.CrossSigningKeyPurposeMaster, - 2: gomatrixserverlib.CrossSigningKeyPurposeSelfSigning, - 3: gomatrixserverlib.CrossSigningKeyPurposeUserSigning, +var KeyTypeIntToPurpose = map[int16]fclient.CrossSigningKeyPurpose{ + 1: fclient.CrossSigningKeyPurposeMaster, + 2: fclient.CrossSigningKeyPurposeSelfSigning, + 3: fclient.CrossSigningKeyPurposeUserSigning, } // Map of purpose -> public key -type CrossSigningKeyMap map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.Base64Bytes +type CrossSigningKeyMap map[fclient.CrossSigningKeyPurpose]gomatrixserverlib.Base64Bytes // Map of user ID -> key ID -> signature type CrossSigningSigMap map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes diff --git a/userapi/userapi_test.go b/userapi/userapi_test.go index 03e656354..9d068ca3b 100644 --- a/userapi/userapi_test.go +++ b/userapi/userapi_test.go @@ -22,9 +22,12 @@ import ( "testing" "time" + api2 "github.com/matrix-org/dendrite/appservice/api" + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/userapi/producers" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/util" "github.com/nats-io/nats.go" "golang.org/x/crypto/bcrypt" @@ -85,7 +88,7 @@ func MustMakeInternalAPI(t *testing.T, opts apiTestOpts, dbType test.DBType, pub t.Fatalf("failed to create key DB: %s", err) } - cfg.Global.SigningIdentity = gomatrixserverlib.SigningIdentity{ + cfg.Global.SigningIdentity = fclient.SigningIdentity{ ServerName: sName, } @@ -111,33 +114,26 @@ func TestQueryProfile(t *testing.T) { aliceDisplayName := "Alice" testCases := []struct { - req api.QueryProfileRequest - wantRes api.QueryProfileResponse + userID string + wantRes *authtypes.Profile wantErr error }{ { - req: api.QueryProfileRequest{ - UserID: fmt.Sprintf("@alice:%s", serverName), - }, - wantRes: api.QueryProfileResponse{ - UserExists: true, - AvatarURL: aliceAvatarURL, + userID: fmt.Sprintf("@alice:%s", serverName), + wantRes: &authtypes.Profile{ + Localpart: "alice", DisplayName: aliceDisplayName, + AvatarURL: aliceAvatarURL, + ServerName: string(serverName), }, }, { - req: api.QueryProfileRequest{ - UserID: fmt.Sprintf("@bob:%s", serverName), - }, - wantRes: api.QueryProfileResponse{ - UserExists: false, - }, + userID: fmt.Sprintf("@bob:%s", serverName), + wantErr: api2.ErrProfileNotExists, }, { - req: api.QueryProfileRequest{ - UserID: "@alice:wrongdomain.com", - }, - wantErr: fmt.Errorf("wrong domain"), + userID: "@alice:wrongdomain.com", + wantErr: api2.ErrProfileNotExists, }, } @@ -147,14 +143,14 @@ func TestQueryProfile(t *testing.T) { mode = "HTTP" } for _, tc := range testCases { - var gotRes api.QueryProfileResponse - gotErr := testAPI.QueryProfile(context.TODO(), &tc.req, &gotRes) + + profile, gotErr := testAPI.QueryProfile(context.TODO(), tc.userID) if tc.wantErr == nil && gotErr != nil || tc.wantErr != nil && gotErr == nil { t.Errorf("QueryProfile %s error, got %s want %s", mode, gotErr, tc.wantErr) continue } - if !reflect.DeepEqual(tc.wantRes, gotRes) { - t.Errorf("QueryProfile %s response got %+v want %+v", mode, gotRes, tc.wantRes) + if !reflect.DeepEqual(tc.wantRes, profile) { + t.Errorf("QueryProfile %s response got %+v want %+v", mode, profile, tc.wantRes) } } } diff --git a/userapi/util/notify_test.go b/userapi/util/notify_test.go index 69461ddd1..d6cbad7db 100644 --- a/userapi/util/notify_test.go +++ b/userapi/util/notify_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/syncapi/synctypes" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" "golang.org/x/crypto/bcrypt" @@ -99,7 +100,7 @@ func TestNotifyUserCountsAsync(t *testing.T) { // Insert a dummy event if err := db.InsertNotification(ctx, aliceLocalpart, serverName, dummyEvent.EventID(), 0, nil, &api.Notification{ - Event: gomatrixserverlib.HeaderedToClientEvent(dummyEvent, gomatrixserverlib.FormatAll), + Event: synctypes.HeaderedToClientEvent(dummyEvent, synctypes.FormatAll), }); err != nil { t.Error(err) }