diff --git a/.github/workflows/dendrite.yml b/.github/workflows/dendrite.yml index 5edb1924e7..d9c883dabb 100644 --- a/.github/workflows/dendrite.yml +++ b/.github/workflows/dendrite.yml @@ -33,7 +33,7 @@ jobs: - name: Install Go uses: actions/setup-go@v4 with: - go-version: "stable" + go-version-file: 'go.mod' cache: true - name: Install Node @@ -72,7 +72,7 @@ jobs: - name: Install Go uses: actions/setup-go@v4 with: - go-version: "stable" + go-version-file: 'go.mod' - name: golangci-lint uses: golangci/golangci-lint-action@v3 @@ -108,7 +108,7 @@ jobs: - name: Setup go uses: actions/setup-go@v4 with: - go-version: "stable" + go-version-file: 'go.mod' - uses: actions/cache@v4 # manually set up caches, as they otherwise clash with different steps using setup-go with cache=true with: @@ -145,7 +145,7 @@ jobs: - name: Setup go uses: actions/setup-go@v4 with: - go-version: "stable" + go-version-file: 'go.mod' - uses: actions/cache@v4 with: path: | @@ -178,7 +178,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v4 with: - go-version: "stable" + go-version-file: 'go.mod' - uses: actions/cache@v4 with: path: | @@ -241,7 +241,7 @@ jobs: - name: Setup go uses: actions/setup-go@v4 with: - go-version: "stable" + go-version-file: 'go.mod' - name: Set up gotestfmt uses: gotesttools/gotestfmt-action@v2 with: @@ -279,7 +279,7 @@ jobs: - name: Setup go uses: actions/setup-go@v4 with: - go-version: "stable" + go-version-file: 'go.mod' cache: true - uses: actions/cache@v4 with: @@ -309,7 +309,7 @@ jobs: - name: Setup go uses: actions/setup-go@v4 with: - go-version: "stable" + go-version-file: 'go.mod' cache: true - uses: actions/cache@v4 with: diff --git a/.golangci.yml b/.golangci.yml index 5bee0a8858..6f3fd3627e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,7 +6,7 @@ run: concurrency: 4 # timeout for analysis, e.g. 30s, 5m, default is 1m - deadline: 30m + timeout: 5m # exit code when at least one issue was found, default is 1 issues-exit-code: 1 @@ -18,24 +18,6 @@ run: #build-tags: # - mytag - # which dirs to skip: they won't be analyzed; - # can use regexp here: generated.*, regexp is applied on full path; - # default value is empty list, but next dirs are always skipped independently - # from this option's value: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - skip-dirs: - - bin - - docs - - # which files to skip: they will be analyzed, but issues from them - # won't be reported. Default value is empty list, but there is - # no need to include all autogenerated files, we confidently recognize - # autogenerated files. If it's not please let us know. - skip-files: - - ".*\\.md$" - - ".*\\.sh$" - - "^cmd/syncserver-integration-tests/testdata.go$" - # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": # If invoked with -mod=readonly, the go command is disallowed from the implicit # automatic updating of go.mod described above. Instead, it fails when any changes @@ -50,7 +32,8 @@ run: # output configuration options output: # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" - format: colored-line-number + formats: + - format: colored-line-number # print lines of code with issue, default is true print-issued-lines: true @@ -79,9 +62,8 @@ linters-settings: # see https://github.com/kisielk/errcheck#excluding-functions for details #exclude: /path/to/file.txt govet: - # report about shadowed variables - check-shadowing: true - + enable: + - shadow # settings per analyzer settings: printf: # analyzer name, run `go tool vet help` to see all analyzers @@ -217,6 +199,24 @@ linters: issues: + # which files to skip: they will be analyzed, but issues from them + # won't be reported. Default value is empty list, but there is + # no need to include all autogenerated files, we confidently recognize + # autogenerated files. If it's not please let us know. + exclude-files: + - ".*\\.md$" + - ".*\\.sh$" + - "^cmd/syncserver-integration-tests/testdata.go$" + + # which dirs to skip: they won't be analyzed; + # can use regexp here: generated.*, regexp is applied on full path; + # default value is empty list, but next dirs are always skipped independently + # from this option's value: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + exclude-dirs: + - bin + - docs + # List of regexps of issue texts to exclude, empty list by default. # But independently from this option we use default exclude patterns, # it can be disabled by `exclude-use-default: false`. To list all diff --git a/CHANGES.md b/CHANGES.md index c41025e915..240b0ceeca 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,28 @@ # Changelog +## Dendrite 0.xx.x + +### Other + + - Bump required Go version to 1.21 + +## Dendrite 0.13.7 (2024-04-09) + +### Fixes + +- Fixed an issue where the displayname/avatar of an invited user was replaced with the inviter's details +- Improved server startup performance by avoiding unnecessary room ACL queries + - This change reduces memory footprint as it caches ACL regex patterns once instead of for each room + - Unnecessary Relay related queries have been removed. **Note**: To use relays, you now need to explicitly enable them using the `federation_api.enable_relays` config +- Fixed space summaries over federation +- Improved usage of external NATS JetStream by reusing existing connections instead of opening new ones unnecessarily + +### Features + +- Modernized Appservices (contributed by [tulir](https://github.com/tulir)) +- Added event reporting with Synapse Admin endpoints for querying them +- Updated dependencies + ## Dendrite 0.13.6 (2024-01-26) Upgrading to this version is **highly** recommended, as it contains several QoL improvements. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..0284244065 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing to Dendrite + +Thank you for taking the time to contribute to Matrix! + +This is the repository for Dendrite, a second-generation Matrix homeserver written in Go. + +## Sign off + +We ask that everybody who contributes to this project signs off their contributions, as explained below. + +We follow a simple 'inbound=outbound' model for contributions: the act of submitting an 'inbound' contribution means that the contributor agrees to license their contribution under the same terms as the project's overall 'outbound' license - in our case, this is Apache Software License v2 (see [LICENSE](./LICENSE)). + +In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as the project's license, we've adopted the same lightweight approach used by the [Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html), [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other projects: the [Developer Certificate of Origin](https://developercertificate.org/) (DCO). This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to Matrix: + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +If you agree to this for your contribution, then all that's needed is to include the line in your commit or pull request comment: + +``` +Signed-off-by: Your Name +``` + +Git allows you to add this signoff automatically when using the `-s` flag to `git commit`, which uses the name and email set in your `user.name` and `user.email` git configs. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 523857ccc6..bf80692131 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,7 @@ # # base installs required dependencies and runs go mod download to cache dependencies # -# Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved -FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21-alpine3.18 AS base +FROM --platform=${BUILDPLATFORM} docker.io/golang:1.22-alpine AS base RUN apk --update --no-cache add bash build-base curl git # diff --git a/README.md b/README.md index bde19b07ee..2bae96fec8 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ If you have further questions, please take a look at [our FAQ](docs/FAQ.md) or j See the [Planning your Installation](https://matrix-org.github.io/dendrite/installation/planning) page for more information on requirements. -To build Dendrite, you will need Go 1.20 or later. +To build Dendrite, you will need Go 1.21 or later. For a usable federating Dendrite deployment, you will also need: diff --git a/build/docker/Dockerfile.demo-pinecone b/build/docker/Dockerfile.demo-pinecone index fae8ca036f..f295044fcf 100644 --- a/build/docker/Dockerfile.demo-pinecone +++ b/build/docker/Dockerfile.demo-pinecone @@ -1,5 +1,4 @@ -# Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved -FROM docker.io/golang:1.21-alpine3.18 AS base +FROM docker.io/golang:1.22 AS base # # Needs to be separate from the main Dockerfile for OpenShift, diff --git a/build/docker/Dockerfile.demo-yggdrasil b/build/docker/Dockerfile.demo-yggdrasil index 502233ea97..6d6b37ea3c 100644 --- a/build/docker/Dockerfile.demo-yggdrasil +++ b/build/docker/Dockerfile.demo-yggdrasil @@ -1,5 +1,4 @@ -# Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved -FROM docker.io/golang:1.21-alpine3.18 AS base +FROM docker.io/golang:1.22 AS base # # Needs to be separate from the main Dockerfile for OpenShift, diff --git a/build/scripts/Complement.Dockerfile b/build/scripts/Complement.Dockerfile index 453d897658..660b84a466 100644 --- a/build/scripts/Complement.Dockerfile +++ b/build/scripts/Complement.Dockerfile @@ -1,6 +1,6 @@ #syntax=docker/dockerfile:1.2 -FROM golang:1.20-bullseye as build +FROM golang:1.22-bookworm as build RUN apt-get update && apt-get install -y sqlite3 WORKDIR /build diff --git a/build/scripts/ComplementLocal.Dockerfile b/build/scripts/ComplementLocal.Dockerfile index 0b80cfc405..8fc8476500 100644 --- a/build/scripts/ComplementLocal.Dockerfile +++ b/build/scripts/ComplementLocal.Dockerfile @@ -8,7 +8,7 @@ # # Use these mounts to make use of this dockerfile: # COMPLEMENT_HOST_MOUNTS='/your/local/dendrite:/dendrite:ro;/your/go/path:/go:ro' -FROM golang:1.18-stretch +FROM golang:1.22-bookworm RUN apt-get update && apt-get install -y sqlite3 ENV SERVER_NAME=localhost diff --git a/build/scripts/ComplementPostgres.Dockerfile b/build/scripts/ComplementPostgres.Dockerfile index 77071b4506..0026842d85 100644 --- a/build/scripts/ComplementPostgres.Dockerfile +++ b/build/scripts/ComplementPostgres.Dockerfile @@ -1,19 +1,19 @@ #syntax=docker/dockerfile:1.2 -FROM golang:1.20-bullseye as build +FROM golang:1.22-bookworm as build RUN apt-get update && apt-get install -y postgresql WORKDIR /build -# No password when connecting over localhost -RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/postgresql/13/main/pg_hba.conf && \ +# No password when connecting to Postgres +RUN sed -i "s%peer%trust%g" /etc/postgresql/15/main/pg_hba.conf && \ # Bump up max conns for moar concurrency - sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/13/main/postgresql.conf + sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/15/main/postgresql.conf # This entry script starts postgres, waits for it to be up then starts dendrite RUN echo '\ #!/bin/bash -eu \n\ pg_lsclusters \n\ - pg_ctlcluster 13 main start \n\ + pg_ctlcluster 15 main start \n\ \n\ until pg_isready \n\ do \n\ @@ -50,7 +50,7 @@ EXPOSE 8008 8448 # At runtime, generate TLS cert based on the CA now mounted at /ca # At runtime, replace the SERVER_NAME with what we are told CMD /build/run_postgres.sh && ./generate-keys --keysize 1024 --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key && \ - ./generate-config -server $SERVER_NAME --ci --db postgresql://postgres@localhost/postgres?sslmode=disable > dendrite.yaml && \ + ./generate-config -server $SERVER_NAME --ci --db "user=postgres database=postgres host=/var/run/postgresql/" > dendrite.yaml && \ # Bump max_open_conns up here in the global database config sed -i 's/max_open_conns:.*$/max_open_conns: 1990/g' dendrite.yaml && \ cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \ diff --git a/clientapi/admin_test.go b/clientapi/admin_test.go index f0e5f004d2..b2adeb757b 100644 --- a/clientapi/admin_test.go +++ b/clientapi/admin_test.go @@ -2,10 +2,12 @@ package clientapi import ( "context" + "encoding/json" "fmt" "net/http" "net/http/httptest" "reflect" + "strings" "testing" "time" @@ -1092,3 +1094,382 @@ func TestAdminMarkAsStale(t *testing.T) { } }) } + +func TestAdminQueryEventReports(t *testing.T) { + alice := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin)) + bob := test.NewUser(t) + room := test.NewRoom(t, alice) + room2 := test.NewRoom(t, alice) + + // room2 has a name and canonical alias + room2.CreateAndInsert(t, alice, spec.MRoomName, map[string]string{"name": "Testing"}, test.WithStateKey("")) + room2.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": "#testing"}, test.WithStateKey("")) + + // Join the rooms with Bob + room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{ + "membership": "join", + }, test.WithStateKey(bob.ID)) + room2.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{ + "membership": "join", + }, test.WithStateKey(bob.ID)) + + // Create a few events to report + eventsToReportPerRoom := make(map[string][]string) + for i := 0; i < 10; i++ { + ev1 := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"}) + ev2 := room2.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"}) + eventsToReportPerRoom[room.ID] = append(eventsToReportPerRoom[room.ID], ev1.EventID()) + eventsToReportPerRoom[room2.ID] = append(eventsToReportPerRoom[room2.ID], ev2.EventID()) + } + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + /*if dbType == test.DBTypeSQLite { + t.Skip() + }*/ + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + defer close() + natsInstance := jetstream.NATSInstance{} + jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream) + defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream) + + // Use an actual roomserver for this + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff) + + if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil { + t.Fatalf("failed to send events: %v", err) + } + if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room2.Events(), "test", "test", "test", nil, false); err != nil { + t.Fatalf("failed to send events: %v", err) + } + + // 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) + + reqBody := map[string]any{ + "reason": "baaad", + "score": -100, + } + body, err := json.Marshal(reqBody) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + + var req *http.Request + // Report all events + for roomID, eventIDs := range eventsToReportPerRoom { + for _, eventID := range eventIDs { + req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", roomID, eventID), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken) + + routers.Client.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected report to succeed, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + } + } + + type response struct { + EventReports []api.QueryAdminEventReportsResponse `json:"event_reports"` + Total int64 `json:"total"` + NextToken *int64 `json:"next_token,omitempty"` + } + + t.Run("Can query all reports", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports", strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + var resp response + if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { + t.Fatal(err) + } + wantCount := 20 + // Only validating the count + if len(resp.EventReports) != wantCount { + t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports)) + } + if resp.Total != int64(wantCount) { + t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total) + } + }) + + t.Run("Can filter on room", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s", room.ID), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + var resp response + if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { + t.Fatal(err) + } + wantCount := 10 + // Only validating the count + if len(resp.EventReports) != wantCount { + t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports)) + } + if resp.Total != int64(wantCount) { + t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total) + } + }) + + t.Run("Can filter on user_id", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?user_id=%s", "@doesnotexist:test"), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + var resp response + if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { + t.Fatal(err) + } + + // The user does not exist, so we expect no results + wantCount := 0 + // Only validating the count + if len(resp.EventReports) != wantCount { + t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports)) + } + if resp.Total != int64(wantCount) { + t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total) + } + }) + + t.Run("Can set direction=f", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s&dir=f", room.ID), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + var resp response + if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { + t.Fatal(err) + } + wantCount := 10 + // Only validating the count + if len(resp.EventReports) != wantCount { + t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports)) + } + if resp.Total != int64(wantCount) { + t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total) + } + // we now should have the first reported event + wantEventID := eventsToReportPerRoom[room.ID][0] + gotEventID := resp.EventReports[0].EventID + if gotEventID != wantEventID { + t.Fatalf("expected eventID to be %v, got %v", wantEventID, gotEventID) + } + }) + + t.Run("Can limit and paginate", func(t *testing.T) { + var from int64 = 0 + var limit int64 = 5 + var wantTotal int64 = 10 // We expect there to be 10 events in total + var resp response + for from+limit <= wantTotal { + resp = response{} + t.Logf("Getting reports starting from %d", from) + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s&limit=%d&from=%d", room2.ID, limit, from), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + + if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { + t.Fatal(err) + } + + wantCount := 5 // we are limited to 5 + if len(resp.EventReports) != wantCount { + t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports)) + } + if resp.Total != int64(wantTotal) { + t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total) + } + + // We've reached the end + if (from + int64(len(resp.EventReports))) == wantTotal { + return + } + + // The next_token should be set + if resp.NextToken == nil { + t.Fatal("expected nextToken to be set") + } + from = *resp.NextToken + } + }) + }) +} + +func TestEventReportsGetDelete(t *testing.T) { + alice := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin)) + bob := test.NewUser(t) + room := test.NewRoom(t, alice) + + // Add a name and alias + roomName := "Testing" + alias := "#testing" + room.CreateAndInsert(t, alice, spec.MRoomName, map[string]string{"name": roomName}, test.WithStateKey("")) + room.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": alias}, test.WithStateKey("")) + + // Join the rooms with Bob + room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{ + "membership": "join", + }, test.WithStateKey(bob.ID)) + + // Create a few events to report + + eventIDToReport := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"}) + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + defer close() + natsInstance := jetstream.NATSInstance{} + jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream) + defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream) + + // Use an actual roomserver for this + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff) + + if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil { + t.Fatalf("failed to send events: %v", err) + } + + // 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) + + reqBody := map[string]any{ + "reason": "baaad", + "score": -100, + } + body, err := json.Marshal(reqBody) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + + var req *http.Request + // Report the event + req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventIDToReport.EventID()), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken) + + routers.Client.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected report to succeed, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + + t.Run("Can not query with invalid ID", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/abc", strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code != http.StatusBadRequest { + t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + }) + + t.Run("Can query with valid ID", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + resp := api.QueryAdminEventReportResponse{} + if err = json.Unmarshal(w.Body.Bytes(), &resp); err != nil { + t.Fatal(err) + } + // test a few things + if resp.EventID != eventIDToReport.EventID() { + t.Fatalf("expected eventID to be %s, got %s instead", eventIDToReport.EventID(), resp.EventID) + } + if resp.RoomName != roomName { + t.Fatalf("expected roomName to be %s, got %s instead", roomName, resp.RoomName) + } + if resp.CanonicalAlias != alias { + t.Fatalf("expected alias to be %s, got %s instead", alias, resp.CanonicalAlias) + } + if reflect.DeepEqual(resp.EventJSON, eventIDToReport.JSON()) { + t.Fatal("mismatching eventJSON") + } + }) + + t.Run("Can delete with a valid ID", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodDelete, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + }) + + t.Run("Can not query deleted report", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken) + + routers.SynapseAdmin.ServeHTTP(w, req) + + if w.Code == http.StatusOK { + t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + }) + }) +} diff --git a/clientapi/clientapi_test.go b/clientapi/clientapi_test.go index fffe4b6b8e..c550b20835 100644 --- a/clientapi/clientapi_test.go +++ b/clientapi/clientapi_test.go @@ -2346,3 +2346,92 @@ func TestCreateRoomInvite(t *testing.T) { } }) } + +func TestReportEvent(t *testing.T) { + alice := test.NewUser(t) + bob := test.NewUser(t) + charlie := test.NewUser(t) + room := test.NewRoom(t, alice) + + room.CreateAndInsert(t, charlie, spec.MRoomMember, map[string]interface{}{ + "membership": "join", + }, test.WithStateKey(charlie.ID)) + eventToReport := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"}) + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + routers := httputil.NewRouters() + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + defer close() + natsInstance := jetstream.NATSInstance{} + jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream) + defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream) + + // Use an actual roomserver for this + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff) + + if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil { + t.Fatalf("failed to send events: %v", err) + } + + // 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: {}, + charlie: {}, + } + createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers) + + reqBody := map[string]any{ + "reason": "baaad", + "score": -100, + } + body, err := json.Marshal(reqBody) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + + var req *http.Request + t.Run("Bob is not joined and should not be able to report the event", func(t *testing.T) { + req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventToReport.EventID()), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken) + + routers.Client.ServeHTTP(w, req) + + if w.Code != http.StatusNotFound { + t.Fatalf("expected report to fail, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + }) + + t.Run("Charlie is joined but the event does not exist", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/$doesNotExist", room.ID), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[charlie].accessToken) + + routers.Client.ServeHTTP(w, req) + + if w.Code != http.StatusNotFound { + t.Fatalf("expected report to fail, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + }) + + t.Run("Charlie is joined and allowed to report the event", func(t *testing.T) { + w = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventToReport.EventID()), strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer "+accessTokens[charlie].accessToken) + + routers.Client.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected report to be successful, got HTTP %d instead: %s", w.Code, w.Body.String()) + } + }) + }) +} diff --git a/clientapi/httputil/parse.go b/clientapi/httputil/parse.go index c83583345e..a952d1778d 100644 --- a/clientapi/httputil/parse.go +++ b/clientapi/httputil/parse.go @@ -35,5 +35,5 @@ func ParseTSParam(req *http.Request) (time.Time, error) { return time.Time{}, fmt.Errorf("param 'ts' is no valid int (%s)", err.Error()) } - return time.Unix(ts/1000, 0), nil + return time.UnixMilli(ts), nil } diff --git a/clientapi/routing/admin.go b/clientapi/routing/admin.go index 519666076e..73a0afc32c 100644 --- a/clientapi/routing/admin.go +++ b/clientapi/routing/admin.go @@ -328,7 +328,7 @@ func AdminPurgeRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) } } -func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI api.ClientUserAPI) util.JSONResponse { +func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { if req.Body == nil { return util.JSONResponse{ Code: http.StatusBadRequest, @@ -423,7 +423,7 @@ func AdminReindex(req *http.Request, cfg *config.ClientAPI, device *api.Device, } } -func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI api.ClientKeyAPI) util.JSONResponse { +func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI userapi.ClientKeyAPI) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -495,3 +495,93 @@ func AdminDownloadState(req *http.Request, device *api.Device, rsAPI roomserverA JSON: struct{}{}, } } + +// GetEventReports returns reported events for a given user/room. +func GetEventReports( + req *http.Request, + rsAPI roomserverAPI.ClientRoomserverAPI, + from, limit uint64, + backwards bool, + userID, roomID string, +) util.JSONResponse { + + eventReports, count, err := rsAPI.QueryAdminEventReports(req.Context(), from, limit, backwards, userID, roomID) + if err != nil { + logrus.WithError(err).Error("failed to query event reports") + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.InternalServerError{}, + } + } + + resp := map[string]any{ + "event_reports": eventReports, + "total": count, + } + + // Add a next_token if there are still reports + if int64(from+limit) < count { + resp["next_token"] = int(from) + len(eventReports) + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: resp, + } +} + +func GetEventReport(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, reportID string) util.JSONResponse { + parsedReportID, err := strconv.ParseUint(reportID, 10, 64) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + // Given this is an admin endpoint, let them know what didn't work. + JSON: spec.InvalidParam(err.Error()), + } + } + + report, err := rsAPI.QueryAdminEventReport(req.Context(), parsedReportID) + if err != nil { + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.Unknown(err.Error()), + } + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: report, + } +} + +func DeleteEventReport(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, reportID string) util.JSONResponse { + parsedReportID, err := strconv.ParseUint(reportID, 10, 64) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + // Given this is an admin endpoint, let them know what didn't work. + JSON: spec.InvalidParam(err.Error()), + } + } + + err = rsAPI.PerformAdminDeleteEventReport(req.Context(), parsedReportID) + if err != nil { + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.Unknown(err.Error()), + } + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: struct{}{}, + } +} + +func parseUint64OrDefault(input string, defaultValue uint64) uint64 { + v, err := strconv.ParseUint(input, 10, 64) + if err != nil { + return defaultValue + } + return v +} diff --git a/clientapi/routing/pushrules.go b/clientapi/routing/pushrules.go index 74873d5c91..43c034f9d9 100644 --- a/clientapi/routing/pushrules.go +++ b/clientapi/routing/pushrules.go @@ -70,7 +70,7 @@ func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi } rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) // Even if rulesPtr is not nil, there may not be any rules for this kind - if rulesPtr == nil || (rulesPtr != nil && len(*rulesPtr) == 0) { + if rulesPtr == nil || len(*rulesPtr) == 0 { return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed") } return util.JSONResponse{ diff --git a/clientapi/routing/report_event.go b/clientapi/routing/report_event.go new file mode 100644 index 0000000000..4dc6498d81 --- /dev/null +++ b/clientapi/routing/report_event.go @@ -0,0 +1,93 @@ +// Copyright 2023 The Matrix.org Foundation C.I.C. +// +// 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 routing + +import ( + "net/http" + + "github.com/matrix-org/dendrite/clientapi/httputil" + "github.com/matrix-org/dendrite/roomserver/api" + userAPI "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/gomatrixserverlib/spec" + "github.com/matrix-org/util" +) + +type reportEventRequest struct { + Reason string `json:"reason"` + Score int64 `json:"score"` +} + +func ReportEvent( + req *http.Request, + device *userAPI.Device, + roomID, eventID string, + rsAPI api.ClientRoomserverAPI, +) util.JSONResponse { + defer req.Body.Close() // nolint: errcheck + + deviceUserID, err := spec.NewUserID(device.UserID, true) + if err != nil { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: spec.NotFound("You don't have permission to report this event, bad userID"), + } + } + // The requesting user must be a member of the room + errRes := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID) + if errRes != nil { + return util.JSONResponse{ + Code: http.StatusNotFound, // Spec demands this... + JSON: spec.NotFound("The event was not found or you are not joined to the room."), + } + } + + // Parse the request + report := reportEventRequest{} + if resErr := httputil.UnmarshalJSONRequest(req, &report); resErr != nil { + return *resErr + } + + queryRes := &api.QueryEventsByIDResponse{} + if err = rsAPI.QueryEventsByID(req.Context(), &api.QueryEventsByIDRequest{ + RoomID: roomID, + EventIDs: []string{eventID}, + }, queryRes); err != nil { + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.InternalServerError{Err: err.Error()}, + } + } + + // No event was found or it was already redacted + if len(queryRes.Events) == 0 || queryRes.Events[0].Redacted() { + return util.JSONResponse{ + Code: http.StatusNotFound, + JSON: spec.NotFound("The event was not found or you are not joined to the room."), + } + } + + _, err = rsAPI.InsertReportedEvent(req.Context(), roomID, eventID, device.UserID, report.Reason, report.Score) + if err != nil { + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.InternalServerError{Err: err.Error()}, + } + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: struct{}{}, + } +} diff --git a/clientapi/routing/room_hierarchy.go b/clientapi/routing/room_hierarchy.go index 2884d2c32d..cf9d43dd1f 100644 --- a/clientapi/routing/room_hierarchy.go +++ b/clientapi/routing/room_hierarchy.go @@ -138,7 +138,7 @@ func QueryRoomHierarchy(req *http.Request, device *userapi.Device, roomIDStr str walker = *cachedWalker } - discoveredRooms, nextWalker, err := rsAPI.QueryNextRoomHierarchyPage(req.Context(), walker, limit) + discoveredRooms, _, nextWalker, err := rsAPI.QueryNextRoomHierarchyPage(req.Context(), walker, limit) if err != nil { switch err.(type) { diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 3e23ab405d..60dad54331 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -255,7 +255,7 @@ func Setup( logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice") serverNotificationSender, err := getSenderDevice(context.Background(), rsAPI, userAPI, cfg) if err != nil { - logrus.WithError(err).Fatal("unable to get account for sending sending server notices") + logrus.WithError(err).Fatal("unable to get account for sending server notices") } synapseAdminRouter.Handle("/admin/v1/send_server_notice/{txnID}", @@ -1523,4 +1523,48 @@ func Setup( return GetJoinedMembers(req, device, vars["roomID"], rsAPI) }), ).Methods(http.MethodGet, http.MethodOptions) + + v3mux.Handle("/rooms/{roomID}/report/{eventID}", + httputil.MakeAuthAPI("report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return ReportEvent(req, device, vars["roomID"], vars["eventID"], rsAPI) + }), + ).Methods(http.MethodPost, http.MethodOptions) + + synapseAdminRouter.Handle("/admin/v1/event_reports", + httputil.MakeAdminAPI("admin_report_events", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + from := parseUint64OrDefault(req.URL.Query().Get("from"), 0) + limit := parseUint64OrDefault(req.URL.Query().Get("limit"), 100) + dir := req.URL.Query().Get("dir") + userID := req.URL.Query().Get("user_id") + roomID := req.URL.Query().Get("room_id") + + // Go backwards if direction is empty or "b" + backwards := dir == "" || dir == "b" + return GetEventReports(req, rsAPI, from, limit, backwards, userID, roomID) + }), + ).Methods(http.MethodGet, http.MethodOptions) + + synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}", + httputil.MakeAdminAPI("admin_report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return GetEventReport(req, rsAPI, vars["reportID"]) + }), + ).Methods(http.MethodGet, http.MethodOptions) + + synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}", + httputil.MakeAdminAPI("admin_report_event_delete", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return DeleteEventReport(req, rsAPI, vars["reportID"]) + }), + ).Methods(http.MethodDelete, http.MethodOptions) } diff --git a/clientapi/routing/sendevent_test.go b/clientapi/routing/sendevent_test.go index 9cdd753587..00d19154ad 100644 --- a/clientapi/routing/sendevent_test.go +++ b/clientapi/routing/sendevent_test.go @@ -265,7 +265,7 @@ func createEvents(eventsJSON []string, roomVer gomatrixserverlib.RoomVersion) ([ for i, eventJSON := range eventsJSON { pdu, evErr := roomVerImpl.NewEventFromTrustedJSON([]byte(eventJSON), false) if evErr != nil { - return nil, fmt.Errorf("failed to make event: %s", err.Error()) + return nil, fmt.Errorf("failed to make event: %s", evErr.Error()) } ev := types.HeaderedEvent{PDU: pdu} events[i] = &ev diff --git a/cmd/dendrite-demo-pinecone/relay/retriever.go b/cmd/dendrite-demo-pinecone/relay/retriever.go index 3c76ad6007..9c918fb67b 100644 --- a/cmd/dendrite-demo-pinecone/relay/retriever.go +++ b/cmd/dendrite-demo-pinecone/relay/retriever.go @@ -17,13 +17,13 @@ package relay import ( "context" "sync" + "sync/atomic" "time" federationAPI "github.com/matrix-org/dendrite/federationapi/api" relayServerAPI "github.com/matrix-org/dendrite/relayapi/api" "github.com/matrix-org/gomatrixserverlib/spec" "github.com/sirupsen/logrus" - "go.uber.org/atomic" ) const ( @@ -54,7 +54,7 @@ func NewRelayServerRetriever( federationAPI: federationAPI, relayAPI: relayAPI, relayServersQueried: make(map[spec.ServerName]bool), - running: *atomic.NewBool(false), + running: atomic.Bool{}, quit: quit, } } diff --git a/cmd/dendrite-demo-yggdrasil/README.md b/cmd/dendrite-demo-yggdrasil/README.md index 23304c214a..a950720dd2 100644 --- a/cmd/dendrite-demo-yggdrasil/README.md +++ b/cmd/dendrite-demo-yggdrasil/README.md @@ -1,6 +1,6 @@ # Yggdrasil Demo -This is the Dendrite Yggdrasil demo! It's easy to get started - all you need is Go 1.20 or later. +This is the Dendrite Yggdrasil demo! It's easy to get started - all you need is Go 1.21 or later. To run the homeserver, start at the root of the Dendrite repository and run: diff --git a/cmd/dendrite-upgrade-tests/main.go b/cmd/dendrite-upgrade-tests/main.go index b78c5f6055..871a605d45 100644 --- a/cmd/dendrite-upgrade-tests/main.go +++ b/cmd/dendrite-upgrade-tests/main.go @@ -54,7 +54,7 @@ var latest, _ = semver.NewVersion("v6.6.6") // Dummy version, used as "HEAD" // due to the error: // When using COPY with more than one source file, the destination must be a directory and end with a / // We need to run a postgres anyway, so use the dockerfile associated with Complement instead. -const DockerfilePostgreSQL = `FROM golang:1.20-bookworm as build +const DockerfilePostgreSQL = `FROM golang:1.22-bookworm as build RUN apt-get update && apt-get install -y postgresql WORKDIR /build ARG BINARY @@ -67,13 +67,11 @@ RUN go build ./cmd/${BINARY} RUN go build ./cmd/generate-keys RUN go build ./cmd/generate-config RUN go build ./cmd/create-account -RUN ./generate-config --ci > dendrite.yaml +RUN ./generate-config --ci --db "user=postgres database=postgres host=/var/run/postgresql/" > dendrite.yaml RUN ./generate-keys --private-key matrix_key.pem --tls-cert server.crt --tls-key server.key -# Replace the connection string with a single postgres DB, using user/db = 'postgres' and no password -RUN sed -i "s%connection_string:.*$%connection_string: postgresql://postgres@localhost/postgres?sslmode=disable%g" dendrite.yaml -# No password when connecting over localhost -RUN sed -i "s%127.0.0.1/32 scram-sha-256%127.0.0.1/32 trust%g" /etc/postgresql/15/main/pg_hba.conf +# No password when connecting to Postgres +RUN sed -i "s%peer%trust%g" /etc/postgresql/15/main/pg_hba.conf # Bump up max conns for moar concurrency RUN sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/15/main/postgresql.conf RUN sed -i 's/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml @@ -100,7 +98,7 @@ ENV BINARY=dendrite EXPOSE 8008 8448 CMD /build/run_dendrite.sh` -const DockerfileSQLite = `FROM golang:1.20-bookworm as build +const DockerfileSQLite = `FROM golang:1.22-bookworm as build RUN apt-get update && apt-get install -y postgresql WORKDIR /build ARG BINARY @@ -410,7 +408,7 @@ func runImage(dockerClient *client.Client, volumeName string, branchNameToImageI } containerID = body.ID - err = dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}) + err = dockerClient.ContainerStart(ctx, containerID, container.StartOptions{}) if err != nil { return "", "", fmt.Errorf("failed to ContainerStart: %s", err) } @@ -442,7 +440,7 @@ func runImage(dockerClient *client.Client, volumeName string, branchNameToImageI lastErr = nil break } - logs, err := dockerClient.ContainerLogs(context.Background(), containerID, types.ContainerLogsOptions{ + logs, err := dockerClient.ContainerLogs(context.Background(), containerID, container.LogsOptions{ ShowStdout: true, ShowStderr: true, Follow: true, @@ -463,7 +461,7 @@ func runImage(dockerClient *client.Client, volumeName string, branchNameToImageI } func destroyContainer(dockerClient *client.Client, containerID string) { - err := dockerClient.ContainerRemove(context.TODO(), containerID, types.ContainerRemoveOptions{ + err := dockerClient.ContainerRemove(context.TODO(), containerID, container.RemoveOptions{ Force: true, }) if err != nil { @@ -550,7 +548,7 @@ func verifyTests(dockerClient *client.Client, volumeName string, versions []*sem // cleanup old containers/volumes from a previous run func cleanup(dockerClient *client.Client) { // ignore all errors, we are just cleaning up and don't want to fail just because we fail to cleanup - containers, _ := dockerClient.ContainerList(context.Background(), types.ContainerListOptions{ + containers, _ := dockerClient.ContainerList(context.Background(), container.ListOptions{ Filters: label(dendriteUpgradeTestLabel), All: true, }) @@ -558,7 +556,7 @@ func cleanup(dockerClient *client.Client) { log.Printf("Removing container: %v %v\n", c.ID, c.Names) timeout := 1 _ = dockerClient.ContainerStop(context.Background(), c.ID, container.StopOptions{Timeout: &timeout}) - _ = dockerClient.ContainerRemove(context.Background(), c.ID, types.ContainerRemoveOptions{ + _ = dockerClient.ContainerRemove(context.Background(), c.ID, container.RemoveOptions{ Force: true, }) } diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 791d0f8571..bf123b1659 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -245,7 +245,8 @@ GEM rb-fsevent (0.11.1) rb-inotify (0.10.1) ffi (~> 1.0) - rexml (3.2.5) + rexml (3.3.2) + strscan rouge (3.26.0) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -260,6 +261,7 @@ GEM faraday (> 0.8, < 2.0) simpleidn (0.2.1) unf (~> 0.1.4) + strscan (3.1.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) thread_safe (0.3.6) diff --git a/docs/installation/1_planning.md b/docs/installation/1_planning.md index 37ca5702a3..e113ca2d3c 100644 --- a/docs/installation/1_planning.md +++ b/docs/installation/1_planning.md @@ -59,7 +59,7 @@ In order to install Dendrite, you will need to satisfy the following dependencie ### Go -At this time, Dendrite supports being built with Go 1.20 or later. We do not support building +At this time, Dendrite supports being built with Go 1.21 or later. We do not support building Dendrite with older versions of Go than this. If you are installing Go using a package manager, you should check (by running `go version`) that you are using a suitable version before you start. diff --git a/federationapi/queue/destinationqueue.go b/federationapi/queue/destinationqueue.go index f51e849fa3..be43aaf1c7 100644 --- a/federationapi/queue/destinationqueue.go +++ b/federationapi/queue/destinationqueue.go @@ -19,6 +19,7 @@ import ( "encoding/json" "fmt" "sync" + "sync/atomic" "time" "github.com/matrix-org/gomatrix" @@ -26,7 +27,6 @@ import ( "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/spec" "github.com/sirupsen/logrus" - "go.uber.org/atomic" "github.com/matrix-org/dendrite/federationapi/statistics" "github.com/matrix-org/dendrite/federationapi/storage" @@ -294,6 +294,10 @@ func (oq *destinationQueue) checkNotificationsOnClose() { // backgroundSend is the worker goroutine for sending events. func (oq *destinationQueue) backgroundSend() { + // Don't try to send transactions if we are shutting down. + if oq.process.Context().Err() != nil { + return + } // Check if a worker is already running, and if it isn't, then // mark it as started. if !oq.running.CompareAndSwap(false, true) { diff --git a/federationapi/queue/queue_test.go b/federationapi/queue/queue_test.go index 6da8634272..7d21a3bb3f 100644 --- a/federationapi/queue/queue_test.go +++ b/federationapi/queue/queue_test.go @@ -18,6 +18,7 @@ import ( "context" "encoding/json" "fmt" + "sync/atomic" "testing" "time" @@ -26,7 +27,6 @@ import ( "github.com/matrix-org/dendrite/test/testrig" "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/spec" - "go.uber.org/atomic" "gotest.tools/v3/poll" "github.com/matrix-org/gomatrixserverlib" @@ -113,8 +113,8 @@ func testSetup(failuresUntilBlacklist uint32, failuresUntilAssumedOffline uint32 fc := &stubFederationClient{ shouldTxSucceed: shouldTxSucceed, shouldTxRelaySucceed: shouldTxRelaySucceed, - txCount: *atomic.NewUint32(0), - txRelayCount: *atomic.NewUint32(0), + txCount: atomic.Uint32{}, + txRelayCount: atomic.Uint32{}, } stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline, false) diff --git a/federationapi/routing/query.go b/federationapi/routing/query.go index 327ba9b08a..dac9b1b34c 100644 --- a/federationapi/routing/query.go +++ b/federationapi/routing/query.go @@ -146,7 +146,7 @@ func QueryRoomHierarchy(httpReq *http.Request, request *fclient.FederationReques } walker := roomserverAPI.NewRoomHierarchyWalker(types.NewServerNameNotDevice(request.Origin()), roomID, suggestedOnly, 1) - discoveredRooms, _, err := rsAPI.QueryNextRoomHierarchyPage(httpReq.Context(), walker, -1) + discoveredRooms, inaccessibleRooms, _, err := rsAPI.QueryNextRoomHierarchyPage(httpReq.Context(), walker, -1) if err != nil { switch err.(type) { @@ -175,8 +175,9 @@ func QueryRoomHierarchy(httpReq *http.Request, request *fclient.FederationReques return util.JSONResponse{ Code: 200, JSON: fclient.RoomHierarchyResponse{ - Room: discoveredRooms[0], - Children: discoveredRooms[1:], + Room: discoveredRooms[0], + Children: discoveredRooms[1:], + InaccessibleChildren: inaccessibleRooms, }, } } diff --git a/federationapi/statistics/statistics.go b/federationapi/statistics/statistics.go index e133fc9c98..750c57fd7f 100644 --- a/federationapi/statistics/statistics.go +++ b/federationapi/statistics/statistics.go @@ -5,10 +5,10 @@ import ( "math" "math/rand" "sync" + "sync/atomic" "time" "github.com/sirupsen/logrus" - "go.uber.org/atomic" "github.com/matrix-org/dendrite/federationapi/storage" "github.com/matrix-org/gomatrixserverlib/spec" @@ -169,7 +169,7 @@ func (s *ServerStatistics) Success(method SendMethod) { // NOTE : Sending to the final destination vs. a relay server has // slightly different semantics. if method == SendDirect { - s.successCounter.Inc() + s.successCounter.Add(1) if s.blacklisted.Load() && s.statistics.DB != nil { if err := s.statistics.DB.RemoveServerFromBlacklist(s.serverName); err != nil { logrus.WithError(err).Errorf("Failed to remove %q from blacklist", s.serverName) @@ -195,7 +195,7 @@ func (s *ServerStatistics) Failure() (time.Time, bool) { // start a goroutine which will wait out the backoff and // unset the backoffStarted flag when done. if s.backoffStarted.CompareAndSwap(false, true) { - backoffCount := s.backoffCount.Inc() + backoffCount := s.backoffCount.Add(1) if backoffCount >= s.statistics.FailuresUntilAssumedOffline { s.assumedOffline.CompareAndSwap(false, true) diff --git a/federationapi/storage/cache/keydb.go b/federationapi/storage/cache/keydb.go index b53695ca47..d63c889d56 100644 --- a/federationapi/storage/cache/keydb.go +++ b/federationapi/storage/cache/keydb.go @@ -46,6 +46,10 @@ func (d *KeyDatabase) FetchKeys( delete(requests, req) } } + // Don't bother hitting the DB if we got everything from cache. + if len(requests) == 0 { + return results, nil + } fromDB, err := d.inner.FetchKeys(ctx, requests) if err != nil { return results, err diff --git a/go.mod b/go.mod index 04a5a97951..8aff23d474 100644 --- a/go.mod +++ b/go.mod @@ -6,18 +6,18 @@ require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/MFAshby/stdemuxerhook v1.0.0 github.com/Masterminds/semver/v3 v3.1.1 - github.com/blevesearch/bleve/v2 v2.3.8 + github.com/blevesearch/bleve/v2 v2.4.0 github.com/codeclysm/extract v2.2.0+incompatible github.com/cretz/bine v0.2.0 github.com/dgraph-io/ristretto v0.1.1 - github.com/docker/docker v24.0.7+incompatible + github.com/docker/docker v25.0.6+incompatible github.com/docker/go-connections v0.4.0 github.com/eyedeekay/goSam v0.32.54 github.com/eyedeekay/onramp v0.33.7 github.com/getsentry/sentry-go v0.14.0 github.com/gologme/log v1.3.0 - github.com/google/go-cmp v0.5.9 - github.com/google/uuid v1.3.0 + github.com/google/go-cmp v0.6.0 + github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/kardianos/minwinsvc v1.0.2 @@ -25,10 +25,10 @@ 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-20240109180417-3495e573f2b7 + github.com/matrix-org/gomatrixserverlib v0.0.0-20240328203753-c2391f7113a5 github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 - github.com/mattn/go-sqlite3 v1.14.17 + github.com/mattn/go-sqlite3 v1.14.22 github.com/nats-io/nats-server/v2 v2.10.7 github.com/nats-io/nats.go v1.31.0 github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9 @@ -36,26 +36,26 @@ require ( github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_golang v1.19.0 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/tidwall/gjson v1.17.0 github.com/tidwall/sjson v1.2.5 github.com/uber/jaeger-client-go v2.30.0+incompatible 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.18.0 - golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 + golang.org/x/crypto v0.24.0 + golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 golang.org/x/image v0.10.0 golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e - golang.org/x/sync v0.3.0 - golang.org/x/term v0.16.0 + golang.org/x/sync v0.7.0 + golang.org/x/term v0.21.0 gopkg.in/h2non/bimg.v1 v1.1.9 gopkg.in/yaml.v2 v2.4.0 gotest.tools/v3 v3.4.0 maunium.net/go/mautrix v0.15.1 - modernc.org/sqlite v1.23.1 + modernc.org/sqlite v1.29.5 nhooyr.io/websocket v1.8.7 ) @@ -65,28 +65,34 @@ require ( github.com/RoaringBitmap/roaring v1.2.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.5.0 // indirect - github.com/blevesearch/bleve_index_api v1.0.5 // indirect - github.com/blevesearch/geo v0.1.17 // indirect + github.com/blevesearch/bleve_index_api v1.1.6 // indirect + github.com/blevesearch/geo v0.1.20 // indirect + github.com/blevesearch/go-faiss v1.0.13 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect github.com/blevesearch/gtreap v0.1.1 // indirect github.com/blevesearch/mmap-go v1.0.4 // indirect - github.com/blevesearch/scorch_segment_api/v2 v2.1.4 // indirect + github.com/blevesearch/scorch_segment_api/v2 v2.2.9 // indirect github.com/blevesearch/segment v0.9.1 // indirect github.com/blevesearch/snowballstem v0.9.0 // indirect github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect - github.com/blevesearch/vellum v1.0.9 // indirect - github.com/blevesearch/zapx/v11 v11.3.7 // indirect - github.com/blevesearch/zapx/v12 v12.3.7 // indirect - github.com/blevesearch/zapx/v13 v13.3.7 // indirect - github.com/blevesearch/zapx/v14 v14.3.7 // indirect - github.com/blevesearch/zapx/v15 v15.3.10 // indirect + github.com/blevesearch/vellum v1.0.10 // indirect + github.com/blevesearch/zapx/v11 v11.3.10 // indirect + github.com/blevesearch/zapx/v12 v12.3.10 // indirect + github.com/blevesearch/zapx/v13 v13.3.10 // indirect + github.com/blevesearch/zapx/v14 v14.3.10 // indirect + github.com/blevesearch/zapx/v15 v15.3.13 // indirect + github.com/blevesearch/zapx/v16 v16.0.12 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/eyedeekay/i2pkeys v0.33.7 // indirect github.com/eyedeekay/sam3 v0.33.7 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect @@ -96,56 +102,58 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/pprof v0.0.0-20230808223545-4887780b67fb // indirect github.com/h2non/filetype v1.1.3 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/juju/errors v1.0.0 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/compress v1.17.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/mschoch/smat v0.2.0 // indirect - github.com/nats-io/jwt/v2 v2.5.3 // indirect - github.com/nats-io/nkeys v0.4.6 // indirect + github.com/nats-io/jwt/v2 v2.5.5 // indirect + github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect github.com/onsi/ginkgo/v2 v2.11.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.10.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/quic-go/qtls-go1-20 v0.3.2 // indirect github.com/quic-go/quic-go v0.37.7 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/rs/zerolog v1.29.1 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect - go.etcd.io/bbolt v1.3.6 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect + go.etcd.io/bbolt v1.3.7 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.12.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/macaroon.v2 v2.1.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/uint128 v1.2.0 // indirect maunium.net/go/maulogger/v2 v2.4.1 // indirect - modernc.org/cc/v3 v3.40.0 // indirect - modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.22.5 // indirect - modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.5.0 // indirect - modernc.org/opt v0.1.3 // indirect - modernc.org/strutil v1.1.3 // indirect - modernc.org/token v1.0.1 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.41.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect ) -go 1.20 +go 1.21 diff --git a/go.sum b/go.sum index 254a9038de..9906bf57d1 100644 --- a/go.sum +++ b/go.sum @@ -36,47 +36,55 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/blevesearch/bleve/v2 v2.3.8 h1:IqFyMJ73n4gY8AmVqM8Sa6EtAZ5beE8yramVqCvs2kQ= -github.com/blevesearch/bleve/v2 v2.3.8/go.mod h1:Lh9aZEHrLKxwPnW4z4lsBEGnflZQ1V/aWP/t+htsiDw= -github.com/blevesearch/bleve_index_api v1.0.5 h1:Lc986kpC4Z0/n1g3gg8ul7H+lxgOQPcXb9SxvQGu+tw= -github.com/blevesearch/bleve_index_api v1.0.5/go.mod h1:YXMDwaXFFXwncRS8UobWs7nvo0DmusriM1nztTlj1ms= -github.com/blevesearch/geo v0.1.17 h1:AguzI6/5mHXapzB0gE9IKWo+wWPHZmXZoscHcjFgAFA= -github.com/blevesearch/geo v0.1.17/go.mod h1:uRMGWG0HJYfWfFJpK3zTdnnr1K+ksZTuWKhXeSokfnM= +github.com/blevesearch/bleve/v2 v2.4.0 h1:2xyg+Wv60CFHYccXc+moGxbL+8QKT/dZK09AewHgKsg= +github.com/blevesearch/bleve/v2 v2.4.0/go.mod h1:IhQHoFAbHgWKYavb9rQgQEJJVMuY99cKdQ0wPpst2aY= +github.com/blevesearch/bleve_index_api v1.1.6 h1:orkqDFCBuNU2oHW9hN2YEJmet+TE9orml3FCGbl1cKk= +github.com/blevesearch/bleve_index_api v1.1.6/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8= +github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM= +github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w= +github.com/blevesearch/go-faiss v1.0.13 h1:zfFs7ZYD0NqXVSY37j0JZjZT1BhE9AE4peJfcx/NB4A= +github.com/blevesearch/go-faiss v1.0.13/go.mod h1:jrxHrbl42X/RnDPI+wBoZU8joxxuRwedrxqswQ3xfU8= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= -github.com/blevesearch/scorch_segment_api/v2 v2.1.4 h1:LmGmo5twU3gV+natJbKmOktS9eMhokPGKWuR+jX84vk= -github.com/blevesearch/scorch_segment_api/v2 v2.1.4/go.mod h1:PgVnbbg/t1UkgezPDu8EHLi1BHQ17xUwsFdU6NnOYS0= +github.com/blevesearch/scorch_segment_api/v2 v2.2.9 h1:3nBaSBRFokjE4FtPW3eUDgcAu3KphBg1GP07zy/6Uyk= +github.com/blevesearch/scorch_segment_api/v2 v2.2.9/go.mod h1:ckbeb7knyOOvAdZinn/ASbB7EA3HoagnJkmEV3J7+sg= github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= -github.com/blevesearch/vellum v1.0.9 h1:PL+NWVk3dDGPCV0hoDu9XLLJgqU4E5s/dOeEJByQ2uQ= -github.com/blevesearch/vellum v1.0.9/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k= -github.com/blevesearch/zapx/v11 v11.3.7 h1:Y6yIAF/DVPiqZUA/jNgSLXmqewfzwHzuwfKyfdG+Xaw= -github.com/blevesearch/zapx/v11 v11.3.7/go.mod h1:Xk9Z69AoAWIOvWudNDMlxJDqSYGf90LS0EfnaAIvXCA= -github.com/blevesearch/zapx/v12 v12.3.7 h1:DfQ6rsmZfEK4PzzJJRXjiM6AObG02+HWvprlXQ1Y7eI= -github.com/blevesearch/zapx/v12 v12.3.7/go.mod h1:SgEtYIBGvM0mgIBn2/tQE/5SdrPXaJUaT/kVqpAPxm0= -github.com/blevesearch/zapx/v13 v13.3.7 h1:igIQg5eKmjw168I7av0Vtwedf7kHnQro/M+ubM4d2l8= -github.com/blevesearch/zapx/v13 v13.3.7/go.mod h1:yyrB4kJ0OT75UPZwT/zS+Ru0/jYKorCOOSY5dBzAy+s= -github.com/blevesearch/zapx/v14 v14.3.7 h1:gfe+fbWslDWP/evHLtp/GOvmNM3sw1BbqD7LhycBX20= -github.com/blevesearch/zapx/v14 v14.3.7/go.mod h1:9J/RbOkqZ1KSjmkOes03AkETX7hrXT0sFMpWH4ewC4w= -github.com/blevesearch/zapx/v15 v15.3.10 h1:bQ9ZxJCj6rKp873EuVJu2JPxQ+EWQZI1cjJGeroovaQ= -github.com/blevesearch/zapx/v15 v15.3.10/go.mod h1:m7Y6m8soYUvS7MjN9eKlz1xrLCcmqfFadmu7GhWIrLY= +github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI= +github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k= +github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk= +github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ= +github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s= +github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs= +github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8= +github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk= +github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU= +github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns= +github.com/blevesearch/zapx/v15 v15.3.13 h1:6EkfaZiPlAxqXz0neniq35my6S48QI94W/wyhnpDHHQ= +github.com/blevesearch/zapx/v15 v15.3.13/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg= +github.com/blevesearch/zapx/v16 v16.0.12 h1:Uccxvjmn+hQ6ywQP+wIiTpdq9LnAviGoryJOmGwAo/I= +github.com/blevesearch/zapx/v16 v16.0.12/go.mod h1:MYnOshRfSm4C4drxx1LGRI+MVFByykJ2anDY1fxdk9Q= github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/codeclysm/extract v2.2.0+incompatible h1:q3wyckoA30bhUSiwdQezMqVhwd8+WGE64/GL//LtUhI= github.com/codeclysm/extract v2.2.0+incompatible/go.mod h1:2nhFMPHiU9At61hz+12bfrlpXSUrOnK+wR+KlGO4Uks= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -89,10 +97,10 @@ github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWa github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= +github.com/docker/docker v25.0.6+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= @@ -111,9 +119,12 @@ github.com/eyedeekay/onramp v0.33.7 h1:LkPklut7Apa6CPGdIoOJpyIpzP9H/Jw7RKvrVxEEY github.com/eyedeekay/onramp v0.33.7/go.mod h1:+Dutoc91mCHLJlYNE3Ir6kSfmpEcQA6/RNHnmVVznWg= github.com/eyedeekay/sam3 v0.33.7 h1:GPYHG4NHxvhqPbGNJ3wKvUQyZSTCmX17f5L5QvyefGs= github.com/eyedeekay/sam3 v0.33.7/go.mod h1:25cRGEFawSkbiPNSh7vTUIpRtEYLVLg/4J4He6LndAY= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= github.com/getlantern/fdcount v0.0.0-20210503151800-5decd65b3731/go.mod h1:XZwE+iIlAgr64OFbXKFNCllBwV4wEipPx8Hlo2gZdbM= @@ -132,18 +143,27 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= @@ -154,6 +174,7 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -165,7 +186,6 @@ github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -182,22 +202,27 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20230808223545-4887780b67fb h1:oqpb3Cwpc7EOml5PVGMYbSGmwNui2R7i8IW83gs4W0c= github.com/google/pprof v0.0.0-20230808223545-4887780b67fb/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -209,21 +234,21 @@ github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e h1:DP5RC0Z3XdyBEW5dKt8YPeN6vZbm6OzVaGVp7f1BQRM= @@ -232,8 +257,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-20240109180417-3495e573f2b7 h1:EaUvK2ay6cxMxeshC1p6QswS9+rQFbUc2YerkRFyVXQ= -github.com/matrix-org/gomatrixserverlib v0.0.0-20240109180417-3495e573f2b7/go.mod h1:HZGsVJ3bUE+DkZtufkH9H0mlsvbhEGK5CpX0Zlavylg= +github.com/matrix-org/gomatrixserverlib v0.0.0-20240328203753-c2391f7113a5 h1:GuxmpyjZQoqb6UFQgKq8Td3wIITlXln/sItqp1jbTTA= +github.com/matrix-org/gomatrixserverlib v0.0.0-20240328203753-c2391f7113a5/go.mod h1:HZGsVJ3bUE+DkZtufkH9H0mlsvbhEGK5CpX0Zlavylg= github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 h1:6t8kJr8i1/1I5nNttw6nn1ryQJgzVlBmSGgPiiaTdw4= github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7/go.mod h1:ReWMS/LoVnOiRAdq9sNUC2NZnd1mZkMNB52QhpTRWjg= github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y= @@ -246,11 +271,10 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= -github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= @@ -266,16 +290,18 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= -github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo= -github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4= +github.com/nats-io/jwt/v2 v2.5.5 h1:ROfXb50elFq5c9+1ztaUbdlrArNFl2+fQWP6B8HGEq4= +github.com/nats-io/jwt/v2 v2.5.5/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A= github.com/nats-io/nats-server/v2 v2.10.7 h1:f5VDy+GMu7JyuFA0Fef+6TfulfCs5nBTgq7MMkFJx5Y= github.com/nats-io/nats-server/v2 v2.10.7/go.mod h1:V2JHOvPiPdtfDXTuEUsthUnCvSDeFrK4Xn9hRo6du7c= github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E= github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8= -github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY= -github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts= +github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= +github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9 h1:lrVQzBtkeQEGGYUHwSX1XPe1E5GL6U3KYCNe2G4bncQ= github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9/go.mod h1:NPHGhPc0/wudcaCqL/H5AOddkRf8GPRhzOujuUKGQu8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= @@ -284,6 +310,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= @@ -294,30 +321,31 @@ github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwU github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= -github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI= github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/quic-go v0.37.7 h1:AgKsQLZ1+YCwZd2GYhBUsJDYZwEkA5gENtAjb+MxONU= github.com/quic-go/quic-go v0.37.7/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= @@ -329,7 +357,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -337,8 +366,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -358,6 +387,7 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/yggdrasil-network/yggdrasil-go v0.4.6 h1:GALUDV9QPz/5FVkbazpkTc9EABHufA556JwUJZr41j4= github.com/yggdrasil-network/yggdrasil-go v0.4.6/go.mod h1:PBMoAOvQjA9geNEeGyMXA9QgCS6Bu+9V+1VkWM84wpw= @@ -365,8 +395,24 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -377,15 +423,15 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= 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-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= -golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -400,8 +446,8 @@ 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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -412,24 +458,22 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -447,13 +491,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc 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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -461,8 +505,8 @@ 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/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= @@ -478,8 +522,8 @@ 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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= @@ -488,17 +532,25 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/h2non/bimg.v1 v1.1.9 h1:wZIUbeOnwr37Ta4aofhIv8OI8v4ujpjXC9mXnAGpQjM= gopkg.in/h2non/bimg.v1 v1.1.9/go.mod h1:PgsZL7dLwUbsGm1NYps320GxGgvQNTnecMCZqxV11So= gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI= gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -511,34 +563,26 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8= maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= maunium.net/go/mautrix v0.15.1 h1:pmCtMjYRpd83+2UL+KTRFYQo5to0373yulimvLK+1k0= maunium.net/go/mautrix v0.15.1/go.mod h1:icQIrvz2NldkRLTuzSGzmaeuMUmw+fzO7UVycPeauN8= -modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= -modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= -modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= -modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= +modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE= +modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/helm/dendrite/Chart.yaml b/helm/dendrite/Chart.yaml index 2ede0cfe9e..a4088872ea 100644 --- a/helm/dendrite/Chart.yaml +++ b/helm/dendrite/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: dendrite -version: "0.14.0" -appVersion: "0.13.6" +version: "0.14.1" +appVersion: "0.13.7" description: Dendrite Matrix Homeserver type: application icon: https://avatars.githubusercontent.com/u/8418310?s=48&v=4 diff --git a/helm/dendrite/README.md b/helm/dendrite/README.md index 091f575f04..9259c7903b 100644 --- a/helm/dendrite/README.md +++ b/helm/dendrite/README.md @@ -1,7 +1,7 @@ # dendrite -![Version: 0.14.0](https://img.shields.io/badge/Version-0.14.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.13.6](https://img.shields.io/badge/AppVersion-0.13.6-informational?style=flat-square) +![Version: 0.14.0](https://img.shields.io/badge/Version-0.14.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.13.7](https://img.shields.io/badge/AppVersion-0.13.7-informational?style=flat-square) Dendrite Matrix Homeserver Status: **NOT PRODUCTION READY** @@ -190,4 +190,4 @@ grafana: 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`) ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.13.0](https://github.com/norwoodj/helm-docs/releases/v1.13.0) \ No newline at end of file +Autogenerated from chart metadata using [helm-docs v1.13.1](https://github.com/norwoodj/helm-docs/releases/v1.13.1) \ No newline at end of file diff --git a/internal/httputil/routing.go b/internal/httputil/routing.go index 2052c798f5..f5f1c6528e 100644 --- a/internal/httputil/routing.go +++ b/internal/httputil/routing.go @@ -66,15 +66,15 @@ func NewRouters() Routers { } var NotAllowedHandler = WrapHandlerInCORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusMethodNotAllowed) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusMethodNotAllowed) unrecognizedErr, _ := json.Marshal(spec.Unrecognized("Unrecognized request")) // nolint:misspell _, _ = w.Write(unrecognizedErr) // nolint:misspell })) var NotFoundCORSHandler = WrapHandlerInCORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotFound) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) unrecognizedErr, _ := json.Marshal(spec.Unrecognized("Unrecognized request")) // nolint:misspell _, _ = w.Write(unrecognizedErr) // nolint:misspell })) diff --git a/internal/httputil/routing_test.go b/internal/httputil/routing_test.go index 21e2bf48ad..39ccd6213d 100644 --- a/internal/httputil/routing_test.go +++ b/internal/httputil/routing_test.go @@ -17,7 +17,7 @@ func TestRoutersError(t *testing.T) { if rec.Code != http.StatusNotFound { t.Fatalf("unexpected status code: %d - %s", rec.Code, rec.Body.String()) } - if ct := rec.Header().Get("Content-Type"); ct != "application/json" { + if ct := rec.Result().Header.Get("Content-Type"); ct != "application/json" { t.Fatalf("unexpected content-type: %s", ct) } @@ -32,7 +32,7 @@ func TestRoutersError(t *testing.T) { if rec.Code != http.StatusMethodNotAllowed { t.Fatalf("unexpected status code: %d - %s", rec.Code, rec.Body.String()) } - if ct := rec.Header().Get("Content-Type"); ct != "application/json" { + if ct := rec.Result().Header.Get("Content-Type"); ct != "application/json" { t.Fatalf("unexpected content-type: %s", ct) } } diff --git a/internal/sqlutil/writer_exclusive.go b/internal/sqlutil/writer_exclusive.go index c6a271c1c0..69eb8609c9 100644 --- a/internal/sqlutil/writer_exclusive.go +++ b/internal/sqlutil/writer_exclusive.go @@ -3,8 +3,7 @@ package sqlutil import ( "database/sql" "errors" - - "go.uber.org/atomic" + "sync/atomic" ) // ExclusiveWriter implements sqlutil.Writer. diff --git a/internal/transactionrequest_test.go b/internal/transactionrequest_test.go index ffc1cd89ab..8dd100d118 100644 --- a/internal/transactionrequest_test.go +++ b/internal/transactionrequest_test.go @@ -19,6 +19,7 @@ import ( "encoding/json" "fmt" "strconv" + "sync/atomic" "testing" "time" @@ -26,7 +27,6 @@ import ( "github.com/matrix-org/gomatrixserverlib/spec" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" - "go.uber.org/atomic" "gotest.tools/v3/poll" "github.com/matrix-org/dendrite/federationapi/producers" @@ -228,7 +228,7 @@ func TestProcessTransactionRequestEDUTyping(t *testing.T) { ctx := process.NewProcessContext() defer ctx.ShutdownDendrite() txn, js, cfg := createTransactionWithEDU(ctx, edus) - received := atomic.NewBool(false) + received := atomic.Bool{} onMessage := func(ctx context.Context, msgs []*nats.Msg) bool { msg := msgs[0] // Guaranteed to exist if onMessage is called room := msg.Header.Get(jetstream.RoomID) @@ -294,7 +294,7 @@ func TestProcessTransactionRequestEDUToDevice(t *testing.T) { ctx := process.NewProcessContext() defer ctx.ShutdownDendrite() txn, js, cfg := createTransactionWithEDU(ctx, edus) - received := atomic.NewBool(false) + received := atomic.Bool{} onMessage := func(ctx context.Context, msgs []*nats.Msg) bool { msg := msgs[0] // Guaranteed to exist if onMessage is called @@ -371,7 +371,7 @@ func TestProcessTransactionRequestEDUDeviceListUpdate(t *testing.T) { ctx := process.NewProcessContext() defer ctx.ShutdownDendrite() txn, js, cfg := createTransactionWithEDU(ctx, edus) - received := atomic.NewBool(false) + received := atomic.Bool{} onMessage := func(ctx context.Context, msgs []*nats.Msg) bool { msg := msgs[0] // Guaranteed to exist if onMessage is called @@ -468,7 +468,7 @@ func TestProcessTransactionRequestEDUReceipt(t *testing.T) { ctx := process.NewProcessContext() defer ctx.ShutdownDendrite() txn, js, cfg := createTransactionWithEDU(ctx, edus) - received := atomic.NewBool(false) + received := atomic.Bool{} onMessage := func(ctx context.Context, msgs []*nats.Msg) bool { msg := msgs[0] // Guaranteed to exist if onMessage is called @@ -512,7 +512,7 @@ func TestProcessTransactionRequestEDUSigningKeyUpdate(t *testing.T) { ctx := process.NewProcessContext() defer ctx.ShutdownDendrite() txn, js, cfg := createTransactionWithEDU(ctx, edus) - received := atomic.NewBool(false) + received := atomic.Bool{} onMessage := func(ctx context.Context, msgs []*nats.Msg) bool { msg := msgs[0] // Guaranteed to exist if onMessage is called @@ -569,7 +569,7 @@ func TestProcessTransactionRequestEDUPresence(t *testing.T) { ctx := process.NewProcessContext() defer ctx.ShutdownDendrite() txn, js, cfg := createTransactionWithEDU(ctx, edus) - received := atomic.NewBool(false) + received := atomic.Bool{} onMessage := func(ctx context.Context, msgs []*nats.Msg) bool { msg := msgs[0] // Guaranteed to exist if onMessage is called diff --git a/internal/version.go b/internal/version.go index 1a75750909..8616b82af9 100644 --- a/internal/version.go +++ b/internal/version.go @@ -18,7 +18,7 @@ var build string const ( VersionMajor = 0 VersionMinor = 13 - VersionPatch = 6 + VersionPatch = 7 VersionTag = "" // example: "rc1" gitRevLen = 7 // 7 matches the displayed characters on github.com diff --git a/mediaapi/storage/shared/mediaapi.go b/mediaapi/storage/shared/mediaapi.go index 867405fb37..bdd7f317b6 100644 --- a/mediaapi/storage/shared/mediaapi.go +++ b/mediaapi/storage/shared/mediaapi.go @@ -17,6 +17,7 @@ package shared import ( "context" "database/sql" + "errors" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/mediaapi/storage/tables" @@ -33,7 +34,7 @@ type Database struct { // StoreMediaMetadata inserts the metadata about the uploaded media into the database. // Returns an error if the combination of MediaID and Origin are not unique in the table. -func (d Database) StoreMediaMetadata(ctx context.Context, mediaMetadata *types.MediaMetadata) error { +func (d *Database) StoreMediaMetadata(ctx context.Context, mediaMetadata *types.MediaMetadata) error { return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { return d.MediaRepository.InsertMedia(ctx, txn, mediaMetadata) }) @@ -42,9 +43,9 @@ func (d Database) StoreMediaMetadata(ctx context.Context, mediaMetadata *types.M // GetMediaMetadata returns metadata about media stored on this server. // The media could have been uploaded to this server or fetched from another server and cached here. // Returns nil metadata if there is no metadata associated with this media. -func (d Database) GetMediaMetadata(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName) (*types.MediaMetadata, error) { +func (d *Database) GetMediaMetadata(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName) (*types.MediaMetadata, error) { mediaMetadata, err := d.MediaRepository.SelectMedia(ctx, nil, mediaID, mediaOrigin) - if err != nil && err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } return mediaMetadata, err @@ -53,9 +54,9 @@ func (d Database) GetMediaMetadata(ctx context.Context, mediaID types.MediaID, m // GetMediaMetadataByHash returns metadata about media stored on this server. // The media could have been uploaded to this server or fetched from another server and cached here. // Returns nil metadata if there is no metadata associated with this media. -func (d Database) GetMediaMetadataByHash(ctx context.Context, mediaHash types.Base64Hash, mediaOrigin spec.ServerName) (*types.MediaMetadata, error) { +func (d *Database) GetMediaMetadataByHash(ctx context.Context, mediaHash types.Base64Hash, mediaOrigin spec.ServerName) (*types.MediaMetadata, error) { mediaMetadata, err := d.MediaRepository.SelectMediaByHash(ctx, nil, mediaHash, mediaOrigin) - if err != nil && err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } return mediaMetadata, err @@ -63,7 +64,7 @@ func (d Database) GetMediaMetadataByHash(ctx context.Context, mediaHash types.Ba // StoreThumbnail inserts the metadata about the thumbnail into the database. // Returns an error if the combination of MediaID and Origin are not unique in the table. -func (d Database) StoreThumbnail(ctx context.Context, thumbnailMetadata *types.ThumbnailMetadata) error { +func (d *Database) StoreThumbnail(ctx context.Context, thumbnailMetadata *types.ThumbnailMetadata) error { return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { return d.Thumbnails.InsertThumbnail(ctx, txn, thumbnailMetadata) }) @@ -72,13 +73,10 @@ func (d Database) StoreThumbnail(ctx context.Context, thumbnailMetadata *types.T // GetThumbnail returns metadata about a specific thumbnail. // The media could have been uploaded to this server or fetched from another server and cached here. // Returns nil metadata if there is no metadata associated with this thumbnail. -func (d Database) GetThumbnail(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName, width, height int, resizeMethod string) (*types.ThumbnailMetadata, error) { +func (d *Database) GetThumbnail(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName, width, height int, resizeMethod string) (*types.ThumbnailMetadata, error) { metadata, err := d.Thumbnails.SelectThumbnail(ctx, nil, mediaID, mediaOrigin, width, height, resizeMethod) - if err != nil { - if err == sql.ErrNoRows { - return nil, nil - } - return nil, err + if errors.Is(err, sql.ErrNoRows) { + return nil, nil } return metadata, err } @@ -86,13 +84,10 @@ func (d Database) GetThumbnail(ctx context.Context, mediaID types.MediaID, media // GetThumbnails returns metadata about all thumbnails for a specific media stored on this server. // The media could have been uploaded to this server or fetched from another server and cached here. // Returns nil metadata if there are no thumbnails associated with this media. -func (d Database) GetThumbnails(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName) ([]*types.ThumbnailMetadata, error) { +func (d *Database) GetThumbnails(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName) ([]*types.ThumbnailMetadata, error) { metadatas, err := d.Thumbnails.SelectThumbnails(ctx, nil, mediaID, mediaOrigin) - if err != nil { - if err == sql.ErrNoRows { - return nil, nil - } - return nil, err + if errors.Is(err, sql.ErrNoRows) { + return nil, nil } return metadatas, err } diff --git a/roomserver/acls/acls.go b/roomserver/acls/acls.go index 017682e05a..4950e6231a 100644 --- a/roomserver/acls/acls.go +++ b/roomserver/acls/acls.go @@ -32,8 +32,8 @@ import ( const MRoomServerACL = "m.room.server_acl" type ServerACLDatabase interface { - // GetKnownRooms returns a list of all rooms we know about. - GetKnownRooms(ctx context.Context) ([]string, error) + // RoomsWithACLs returns all room IDs for rooms with ACLs + RoomsWithACLs(ctx context.Context) ([]string, error) // GetBulkStateContent returns all state events which match a given room ID and a given state key tuple. Both must be satisfied for a match. // If a tuple has the StateKey of '*' and allowWildcards=true then all state events with the EventType should be returned. @@ -57,7 +57,7 @@ func NewServerACLs(db ServerACLDatabase) *ServerACLs { } // Look up all of the rooms that the current state server knows about. - rooms, err := db.GetKnownRooms(ctx) + rooms, err := db.RoomsWithACLs(ctx) if err != nil { logrus.WithError(err).Fatalf("Failed to get known rooms") } diff --git a/roomserver/acls/acls_test.go b/roomserver/acls/acls_test.go index efe1d20936..09920308c5 100644 --- a/roomserver/acls/acls_test.go +++ b/roomserver/acls/acls_test.go @@ -116,7 +116,7 @@ var ( type dummyACLDB struct{} -func (d dummyACLDB) GetKnownRooms(ctx context.Context) ([]string, error) { +func (d dummyACLDB) RoomsWithACLs(ctx context.Context) ([]string, error) { return []string{"1", "2"}, nil } diff --git a/roomserver/api/api.go b/roomserver/api/api.go index ef5bc3d17f..b2b3192447 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -86,6 +86,9 @@ type RoomserverInternalAPI interface { req *QueryAuthChainRequest, res *QueryAuthChainResponse, ) error + + // RoomsWithACLs returns all room IDs for rooms with ACLs + RoomsWithACLs(ctx context.Context) ([]string, error) } type UserRoomPrivateKeyCreator interface { @@ -138,7 +141,12 @@ type QueryRoomHierarchyAPI interface { // // If returned walker is nil, then there are no more rooms left to traverse. This method does not modify the provided walker, so it // can be cached. - QueryNextRoomHierarchyPage(ctx context.Context, walker RoomHierarchyWalker, limit int) ([]fclient.RoomHierarchyRoom, *RoomHierarchyWalker, error) + QueryNextRoomHierarchyPage(ctx context.Context, walker RoomHierarchyWalker, limit int) ( + hierarchyRooms []fclient.RoomHierarchyRoom, + inaccessibleRooms []string, + hierarchyWalker *RoomHierarchyWalker, + err error, + ) } type QueryMembershipAPI interface { @@ -220,6 +228,7 @@ type ClientRoomserverAPI interface { UserRoomPrivateKeyCreator QueryRoomHierarchyAPI DefaultRoomVersionAPI + QueryMembershipForUser(ctx context.Context, req *QueryMembershipForUserRequest, res *QueryMembershipForUserResponse) error QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error QueryRoomsForUser(ctx context.Context, userID spec.UserID, desiredMembership string) ([]spec.RoomID, error) @@ -261,6 +270,15 @@ type ClientRoomserverAPI interface { RemoveRoomAlias(ctx context.Context, senderID spec.SenderID, alias string) (aliasFound bool, aliasRemoved bool, err error) SigningIdentityFor(ctx context.Context, roomID spec.RoomID, senderID spec.UserID) (fclient.SigningIdentity, error) + + InsertReportedEvent( + ctx context.Context, + roomID, eventID, reportingUserID, reason string, + score int64, + ) (int64, error) + QueryAdminEventReports(ctx context.Context, from, limit uint64, backwards bool, userID, roomID string) ([]QueryAdminEventReportsResponse, int64, error) + QueryAdminEventReport(ctx context.Context, reportID uint64) (QueryAdminEventReportResponse, error) + PerformAdminDeleteEventReport(ctx context.Context, reportID uint64) error } type UserRoomserverAPI interface { diff --git a/roomserver/api/query.go b/roomserver/api/query.go index 893d5dccfa..c4c019f99a 100644 --- a/roomserver/api/query.go +++ b/roomserver/api/query.go @@ -346,6 +346,28 @@ type QueryServerBannedFromRoomResponse struct { Banned bool `json:"banned"` } +type QueryAdminEventReportsResponse struct { + ID int64 `json:"id"` + Score int64 `json:"score"` + EventNID types.EventNID `json:"-"` // only used to query the state + RoomNID types.RoomNID `json:"-"` // only used to query the state + ReportingUserNID types.EventStateKeyNID `json:"-"` // only used in the DB + SenderNID types.EventStateKeyNID `json:"-"` // only used in the DB + RoomID string `json:"room_id"` + EventID string `json:"event_id"` + UserID string `json:"user_id"` // the user reporting the event + Reason string `json:"reason"` + Sender string `json:"sender"` // the user sending the reported event + CanonicalAlias string `json:"canonical_alias"` + RoomName string `json:"name"` + ReceivedTS spec.Timestamp `json:"received_ts"` +} + +type QueryAdminEventReportResponse struct { + QueryAdminEventReportsResponse + EventJSON json.RawMessage `json:"event_json"` +} + // MarshalJSON stringifies the room ID and StateKeyTuple keys so they can be sent over the wire in HTTP API mode. func (r *QueryBulkStateContentResponse) MarshalJSON() ([]byte, error) { se := make(map[string]string) diff --git a/roomserver/api/wrapper.go b/roomserver/api/wrapper.go index 0ad5d20130..4979d18c70 100644 --- a/roomserver/api/wrapper.go +++ b/roomserver/api/wrapper.go @@ -189,7 +189,7 @@ func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkS RoomID: roomID, } joinCount := 0 - var joinRule, guestAccess string + var guestAccess string for tuple, contentVal := range data { if tuple.EventType == spec.MRoomMember && contentVal == "join" { joinCount++ @@ -210,12 +210,12 @@ func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkS pub.WorldReadable = contentVal == "world_readable" // need both of these to determine whether guests can join case joinRuleTuple: - joinRule = contentVal + pub.JoinRule = contentVal case guestTuple: guestAccess = contentVal } } - if joinRule == spec.Public && guestAccess == "can_join" { + if pub.JoinRule == spec.Public && guestAccess == "can_join" { pub.GuestCanJoin = true } pub.JoinedMembersCount = joinCount diff --git a/roomserver/internal/api.go b/roomserver/internal/api.go index 1e08f6a3a2..a71fd2d15b 100644 --- a/roomserver/internal/api.go +++ b/roomserver/internal/api.go @@ -340,3 +340,11 @@ func (r *RoomserverInternalAPI) SigningIdentityFor(ctx context.Context, roomID s func (r *RoomserverInternalAPI) AssignRoomNID(ctx context.Context, roomID spec.RoomID, roomVersion gomatrixserverlib.RoomVersion) (roomNID types.RoomNID, err error) { return r.DB.AssignRoomNID(ctx, roomID, roomVersion) } + +func (r *RoomserverInternalAPI) InsertReportedEvent( + ctx context.Context, + roomID, eventID, reportingUserID, reason string, + score int64, +) (int64, error) { + return r.DB.InsertReportedEvent(ctx, roomID, eventID, reportingUserID, reason, score) +} diff --git a/roomserver/internal/perform/perform_admin.go b/roomserver/internal/perform/perform_admin.go index ae203854b6..1b88172343 100644 --- a/roomserver/internal/perform/perform_admin.go +++ b/roomserver/internal/perform/perform_admin.go @@ -354,3 +354,7 @@ func (r *Admin) PerformAdminDownloadState( return nil } + +func (r *Admin) PerformAdminDeleteEventReport(ctx context.Context, reportID uint64) error { + return r.DB.AdminDeleteEventReport(ctx, reportID) +} diff --git a/roomserver/internal/query/query.go b/roomserver/internal/query/query.go index 74b0102811..886d004920 100644 --- a/roomserver/internal/query/query.go +++ b/roomserver/internal/query/query.go @@ -1099,3 +1099,18 @@ func (r *Queryer) QueryUserIDForSender(ctx context.Context, roomID spec.RoomID, return nil, nil } + +// RoomsWithACLs returns all room IDs for rooms with ACLs +func (r *Queryer) RoomsWithACLs(ctx context.Context) ([]string, error) { + return r.DB.RoomsWithACLs(ctx) +} + +// QueryAdminEventReports returns event reports given a filter. +func (r *Queryer) QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error) { + return r.DB.QueryAdminEventReports(ctx, from, limit, backwards, userID, roomID) +} + +// QueryAdminEventReport returns a single event report. +func (r *Queryer) QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error) { + return r.DB.QueryAdminEventReport(ctx, reportID) +} diff --git a/roomserver/internal/query/query_room_hierarchy.go b/roomserver/internal/query/query_room_hierarchy.go index 5f55980f04..3fc6131925 100644 --- a/roomserver/internal/query/query_room_hierarchy.go +++ b/roomserver/internal/query/query_room_hierarchy.go @@ -39,9 +39,14 @@ import ( // // If returned walker is nil, then there are no more rooms left to traverse. This method does not modify the provided walker, so it // can be cached. -func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker roomserver.RoomHierarchyWalker, limit int) ([]fclient.RoomHierarchyRoom, *roomserver.RoomHierarchyWalker, error) { - if authorised, _ := authorised(ctx, querier, walker.Caller, walker.RootRoomID, nil); !authorised { - return nil, nil, roomserver.ErrRoomUnknownOrNotAllowed{Err: fmt.Errorf("room is unknown/forbidden")} +func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker roomserver.RoomHierarchyWalker, limit int) ( + []fclient.RoomHierarchyRoom, + []string, + *roomserver.RoomHierarchyWalker, + error, +) { + if authorised, _, _ := authorised(ctx, querier, walker.Caller, walker.RootRoomID, nil); !authorised { + return nil, []string{walker.RootRoomID.String()}, nil, roomserver.ErrRoomUnknownOrNotAllowed{Err: fmt.Errorf("room is unknown/forbidden")} } discoveredRooms := []fclient.RoomHierarchyRoom{} @@ -50,6 +55,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r unvisited := make([]roomserver.RoomHierarchyWalkerQueuedRoom, len(walker.Unvisited)) copy(unvisited, walker.Unvisited) processed := walker.Processed.Copy() + inaccessible := []string{} // Depth first -> stack data structure for len(unvisited) > 0 { @@ -108,7 +114,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r // as these children may be rooms we do know about. roomType = spec.MSpace } - } else if authorised, isJoinedOrInvited := authorised(ctx, querier, walker.Caller, queuedRoom.RoomID, queuedRoom.ParentRoomID); authorised { + } else if authorised, isJoinedOrInvited, allowedRoomIDs := authorised(ctx, querier, walker.Caller, queuedRoom.RoomID, queuedRoom.ParentRoomID); authorised { // Get all `m.space.child` state events for this room events, err := childReferences(ctx, querier, walker.SuggestedOnly, queuedRoom.RoomID) if err != nil { @@ -125,14 +131,18 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r } discoveredRooms = append(discoveredRooms, fclient.RoomHierarchyRoom{ - PublicRoom: *pubRoom, - RoomType: roomType, - ChildrenState: events, + PublicRoom: *pubRoom, + RoomType: roomType, + ChildrenState: events, + AllowedRoomIDs: allowedRoomIDs, }) // don't walk children if the user is not joined/invited to the space if !isJoinedOrInvited { continue } + } else if !authorised { + inaccessible = append(inaccessible, queuedRoom.RoomID.String()) + continue } else { // room exists but user is not authorised continue @@ -149,6 +159,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r // We need to invert the order here because the child events are lo->hi on the timestamp, // so we need to ensure we pop in the same lo->hi order, which won't be the case if we // insert the highest timestamp last in a stack. + extendQueueLoop: for i := len(discoveredChildEvents) - 1; i >= 0; i-- { spaceContent := struct { Via []string `json:"via"` @@ -161,6 +172,12 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r if err != nil { util.GetLogger(ctx).WithError(err).WithField("invalid_room_id", ev.StateKey).WithField("parent_room_id", queuedRoom.RoomID).Warn("Invalid room ID in m.space.child state event") } else { + // Make sure not to queue inaccessible rooms + for _, inaccessibleRoomID := range inaccessible { + if inaccessibleRoomID == childRoomID.String() { + continue extendQueueLoop + } + } unvisited = append(unvisited, roomserver.RoomHierarchyWalkerQueuedRoom{ RoomID: *childRoomID, ParentRoomID: &queuedRoom.RoomID, @@ -173,7 +190,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r if len(unvisited) == 0 { // If no more rooms to walk, then don't return a walker for future pages - return discoveredRooms, nil, nil + return discoveredRooms, inaccessible, nil, nil } else { // If there are more rooms to walk, then return a new walker to resume walking from (for querying more pages) newWalker := roomserver.RoomHierarchyWalker{ @@ -185,22 +202,25 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r Processed: processed, } - return discoveredRooms, &newWalker, nil + return discoveredRooms, inaccessible, &newWalker, nil } } // authorised returns true iff the user is joined this room or the room is world_readable -func authorised(ctx context.Context, querier *Queryer, caller types.DeviceOrServerName, roomID spec.RoomID, parentRoomID *spec.RoomID) (authed, isJoinedOrInvited bool) { +func authorised(ctx context.Context, querier *Queryer, caller types.DeviceOrServerName, roomID spec.RoomID, parentRoomID *spec.RoomID) (authed, isJoinedOrInvited bool, resultAllowedRoomIDs []string) { if clientCaller := caller.Device(); clientCaller != nil { return authorisedUser(ctx, querier, clientCaller, roomID, parentRoomID) - } else { - return authorisedServer(ctx, querier, roomID, *caller.ServerName()), false } + if serverCaller := caller.ServerName(); serverCaller != nil { + authed, resultAllowedRoomIDs = authorisedServer(ctx, querier, roomID, *serverCaller) + return authed, false, resultAllowedRoomIDs + } + return false, false, resultAllowedRoomIDs } // authorisedServer returns true iff the server is joined this room or the room is world_readable, public, or knockable -func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID, callerServerName spec.ServerName) bool { +func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID, callerServerName spec.ServerName) (bool, []string) { // Check history visibility / join rules first hisVisTuple := gomatrixserverlib.StateKeyTuple{ EventType: spec.MRoomHistoryVisibility, @@ -219,13 +239,13 @@ func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID, }, &queryRoomRes) if err != nil { util.GetLogger(ctx).WithError(err).Error("failed to QueryCurrentState") - return false + return false, []string{} } hisVisEv := queryRoomRes.StateEvents[hisVisTuple] if hisVisEv != nil { hisVis, _ := hisVisEv.HistoryVisibility() if hisVis == "world_readable" { - return true + return true, []string{} } } @@ -238,19 +258,23 @@ func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID, rule, ruleErr := joinRuleEv.JoinRule() if ruleErr != nil { util.GetLogger(ctx).WithError(ruleErr).WithField("parent_room_id", roomID).Warn("failed to get join rule") - return false + return false, []string{} } if rule == spec.Public || rule == spec.Knock { - return true + return true, []string{} } - if rule == spec.Restricted { + if rule == spec.Restricted || rule == spec.KnockRestricted { allowJoinedToRoomIDs = append(allowJoinedToRoomIDs, restrictedJoinRuleAllowedRooms(ctx, joinRuleEv)...) } } // check if server is joined to any allowed room + resultAllowedRoomIDs := make([]string, 0, len(allowJoinedToRoomIDs)) + for _, allowedRoomID := range allowJoinedToRoomIDs { + resultAllowedRoomIDs = append(resultAllowedRoomIDs, allowedRoomID.String()) + } for _, allowedRoomID := range allowJoinedToRoomIDs { var queryRes fs.QueryJoinedHostServerNamesInRoomResponse err = querier.FSAPI.QueryJoinedHostServerNamesInRoom(ctx, &fs.QueryJoinedHostServerNamesInRoomRequest{ @@ -262,18 +286,18 @@ func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID, } for _, srv := range queryRes.ServerNames { if srv == callerServerName { - return true + return true, resultAllowedRoomIDs[1:] } } } - return false + return false, resultAllowedRoomIDs[1:] } // authorisedUser returns true iff the user is invited/joined this room or the room is world_readable // or if the room has a public or knock join rule. // Failing that, if the room has a restricted join rule and belongs to the space parent listed, it will return true. -func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi.Device, roomID spec.RoomID, parentRoomID *spec.RoomID) (authed bool, isJoinedOrInvited bool) { +func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi.Device, roomID spec.RoomID, parentRoomID *spec.RoomID) (authed bool, isJoinedOrInvited bool, resultAllowedRoomIDs []string) { hisVisTuple := gomatrixserverlib.StateKeyTuple{ EventType: spec.MRoomHistoryVisibility, StateKey: "", @@ -295,20 +319,20 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi }, &queryRes) if err != nil { util.GetLogger(ctx).WithError(err).Error("failed to QueryCurrentState") - return false, false + return false, false, resultAllowedRoomIDs } memberEv := queryRes.StateEvents[roomMemberTuple] if memberEv != nil { membership, _ := memberEv.Membership() if membership == spec.Join || membership == spec.Invite { - return true, true + return true, true, resultAllowedRoomIDs } } hisVisEv := queryRes.StateEvents[hisVisTuple] if hisVisEv != nil { hisVis, _ := hisVisEv.HistoryVisibility() if hisVis == "world_readable" { - return true, false + return true, false, resultAllowedRoomIDs } } joinRuleEv := queryRes.StateEvents[joinRuleTuple] @@ -323,6 +347,7 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi allowedRoomIDs := restrictedJoinRuleAllowedRooms(ctx, joinRuleEv) // check parent is in the allowed set for _, a := range allowedRoomIDs { + resultAllowedRoomIDs = append(resultAllowedRoomIDs, a.String()) if *parentRoomID == a { allowed = true break @@ -345,13 +370,13 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi if memberEv != nil { membership, _ := memberEv.Membership() if membership == spec.Join { - return true, false + return true, false, resultAllowedRoomIDs } } } } } - return false, false + return false, false, resultAllowedRoomIDs } // helper function to fetch a state event diff --git a/roomserver/roomserver_test.go b/roomserver/roomserver_test.go index 88e3357117..85312efd91 100644 --- a/roomserver/roomserver_test.go +++ b/roomserver/roomserver_test.go @@ -1284,3 +1284,38 @@ func TestRoomConsumerRecreation(t *testing.T) { wantAckWait := input.MaximumMissingProcessingTime + (time.Second * 10) assert.Equal(t, wantAckWait, info.Config.AckWait) } + +func TestRoomsWithACLs(t *testing.T) { + ctx := context.Background() + alice := test.NewUser(t) + noACLRoom := test.NewRoom(t, alice) + aclRoom := test.NewRoom(t, alice) + + aclRoom.CreateAndInsert(t, alice, "m.room.server_acl", map[string]any{ + "deny": []string{"evilhost.test"}, + "allow": []string{"*"}, + }, test.WithStateKey("")) + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType) + defer closeDB() + + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + natsInstance := &jetstream.NATSInstance{} + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + // start JetStream listeners + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + + for _, room := range []*test.Room{noACLRoom, aclRoom} { + // Create the rooms + err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false) + assert.NoError(t, err) + } + + // Validate that we only have one ACLd room. + roomsWithACLs, err := rsAPI.RoomsWithACLs(ctx) + assert.NoError(t, err) + assert.Equal(t, []string{aclRoom.ID}, roomsWithACLs) + }) +} diff --git a/roomserver/storage/interface.go b/roomserver/storage/interface.go index 0638252b2e..ab105e6f9b 100644 --- a/roomserver/storage/interface.go +++ b/roomserver/storage/interface.go @@ -30,6 +30,7 @@ import ( type Database interface { UserRoomKeys + ReportedEvents // Do we support processing input events for more than one room at a time? SupportsConcurrentRoomInputs() bool AssignRoomNID(ctx context.Context, roomID spec.RoomID, roomVersion gomatrixserverlib.RoomVersion) (roomNID types.RoomNID, err error) @@ -170,8 +171,6 @@ type Database interface { GetServerInRoom(ctx context.Context, roomNID types.RoomNID, serverName spec.ServerName) (bool, error) // GetKnownUsers searches all users that userID knows about. GetKnownUsers(ctx context.Context, userID, searchString string, limit int) ([]string, error) - // GetKnownRooms returns a list of all rooms we know about. - GetKnownRooms(ctx context.Context) ([]string, error) // ForgetRoom sets a flag in the membership table, that the user wishes to forget a specific room ForgetRoom(ctx context.Context, userID, roomID string, forget bool) error @@ -193,6 +192,12 @@ type Database interface { MaybeRedactEvent( ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI, ) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error) + + // RoomsWithACLs returns all room IDs for rooms with ACLs + RoomsWithACLs(ctx context.Context) ([]string, error) + QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID string, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error) + QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error) + AdminDeleteEventReport(ctx context.Context, reportID uint64) error } type UserRoomKeys interface { @@ -256,3 +261,11 @@ type EventDatabase interface { ) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error) StoreEvent(ctx context.Context, event gomatrixserverlib.PDU, roomInfo *types.RoomInfo, eventTypeNID types.EventTypeNID, eventStateKeyNID types.EventStateKeyNID, authEventNIDs []types.EventNID, isRejected bool) (types.EventNID, types.StateAtEvent, error) } + +type ReportedEvents interface { + InsertReportedEvent( + ctx context.Context, + roomID, eventID, reportingUserID, reason string, + score int64, + ) (int64, error) +} diff --git a/roomserver/storage/postgres/events_table.go b/roomserver/storage/postgres/events_table.go index 1c9cd15994..180a03cd6d 100644 --- a/roomserver/storage/postgres/events_table.go +++ b/roomserver/storage/postgres/events_table.go @@ -68,6 +68,10 @@ CREATE TABLE IF NOT EXISTS roomserver_events ( -- Create an index which helps in resolving membership events (event_type_nid = 5) - (used for history visibility) CREATE INDEX IF NOT EXISTS roomserver_events_memberships_idx ON roomserver_events (room_nid, event_state_key_nid) WHERE (event_type_nid = 5); + +-- The following indexes are used by bulkSelectStateEventByNIDSQL +CREATE INDEX IF NOT EXISTS roomserver_event_event_type_nid_idx ON roomserver_events (event_type_nid); +CREATE INDEX IF NOT EXISTS roomserver_event_state_key_nid_idx ON roomserver_events (event_state_key_nid); ` const insertEventSQL = "" + @@ -147,6 +151,8 @@ const selectRoomNIDsForEventNIDsSQL = "" + const selectEventRejectedSQL = "" + "SELECT is_rejected FROM roomserver_events WHERE room_nid = $1 AND event_id = $2" +const selectRoomsWithEventTypeNIDSQL = `SELECT DISTINCT room_nid FROM roomserver_events WHERE event_type_nid = $1` + type eventStatements struct { insertEventStmt *sql.Stmt selectEventStmt *sql.Stmt @@ -166,6 +172,7 @@ type eventStatements struct { selectMaxEventDepthStmt *sql.Stmt selectRoomNIDsForEventNIDsStmt *sql.Stmt selectEventRejectedStmt *sql.Stmt + selectRoomsWithEventTypeNIDStmt *sql.Stmt } func CreateEventsTable(db *sql.DB) error { @@ -206,6 +213,7 @@ func PrepareEventsTable(db *sql.DB) (tables.Events, error) { {&s.selectMaxEventDepthStmt, selectMaxEventDepthSQL}, {&s.selectRoomNIDsForEventNIDsStmt, selectRoomNIDsForEventNIDsSQL}, {&s.selectEventRejectedStmt, selectEventRejectedSQL}, + {&s.selectRoomsWithEventTypeNIDStmt, selectRoomsWithEventTypeNIDSQL}, }.Prepare(db) } @@ -582,3 +590,25 @@ func (s *eventStatements) SelectEventRejected( err = stmt.QueryRowContext(ctx, roomNID, eventID).Scan(&rejected) return } + +func (s *eventStatements) SelectRoomsWithEventTypeNID( + ctx context.Context, txn *sql.Tx, eventTypeNID types.EventTypeNID, +) ([]types.RoomNID, error) { + stmt := sqlutil.TxStmt(txn, s.selectRoomsWithEventTypeNIDStmt) + rows, err := stmt.QueryContext(ctx, eventTypeNID) + defer internal.CloseAndLogIfError(ctx, rows, "SelectRoomsWithEventTypeNID: rows.close() failed") + if err != nil { + return nil, err + } + + var roomNIDs []types.RoomNID + var roomNID types.RoomNID + for rows.Next() { + if err := rows.Scan(&roomNID); err != nil { + return nil, err + } + roomNIDs = append(roomNIDs, roomNID) + } + + return roomNIDs, rows.Err() +} diff --git a/roomserver/storage/postgres/reported_events_table.go b/roomserver/storage/postgres/reported_events_table.go new file mode 100644 index 0000000000..c46f47b344 --- /dev/null +++ b/roomserver/storage/postgres/reported_events_table.go @@ -0,0 +1,221 @@ +// Copyright 2023 The Matrix.org Foundation C.I.C. +// +// 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 postgres + +import ( + "context" + "database/sql" + "time" + + "github.com/matrix-org/dendrite/internal" + "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/roomserver/storage/tables" + "github.com/matrix-org/dendrite/roomserver/types" + "github.com/matrix-org/gomatrixserverlib/spec" +) + +const reportedEventsScheme = ` +CREATE SEQUENCE IF NOT EXISTS roomserver_reported_events_id_seq; +CREATE TABLE IF NOT EXISTS roomserver_reported_events +( + id BIGINT PRIMARY KEY DEFAULT nextval('roomserver_reported_events_id_seq'), + room_nid BIGINT NOT NULL, + event_nid BIGINT NOT NULL, + reporting_user_nid BIGINT NOT NULL, -- the user reporting the event + event_sender_nid BIGINT NOT NULL, -- the user who sent the reported event + reason TEXT, + score INTEGER, + received_ts BIGINT NOT NULL +);` + +const insertReportedEventSQL = ` + INSERT INTO roomserver_reported_events (room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING id +` + +const selectReportedEventsDescSQL = ` +WITH countReports AS ( + SELECT count(*) as report_count + FROM roomserver_reported_events + WHERE ($1::BIGINT IS NULL OR room_nid = $1::BIGINT) AND ($2::TEXT IS NULL OR reporting_user_nid = $2::BIGINT) +) +SELECT report_count, id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts +FROM roomserver_reported_events, countReports +WHERE ($1::BIGINT IS NULL OR room_nid = $1::BIGINT) AND ($2::TEXT IS NULL OR reporting_user_nid = $2::BIGINT) +ORDER BY received_ts DESC +OFFSET $3 +LIMIT $4 +` + +const selectReportedEventsAscSQL = ` +WITH countReports AS ( + SELECT count(*) as report_count + FROM roomserver_reported_events + WHERE ($1::BIGINT IS NULL OR room_nid = $1::BIGINT) AND ($2::TEXT IS NULL OR reporting_user_nid = $2::BIGINT) +) +SELECT report_count, id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts +FROM roomserver_reported_events, countReports +WHERE ($1::BIGINT IS NULL OR room_nid = $1::BIGINT) AND ($2::TEXT IS NULL OR reporting_user_nid = $2::BIGINT) +ORDER BY received_ts ASC +OFFSET $3 +LIMIT $4 +` + +const selectReportedEventSQL = ` +SELECT id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts +FROM roomserver_reported_events +WHERE id = $1 +` + +const deleteReportedEventSQL = `DELETE FROM roomserver_reported_events WHERE id = $1` + +type reportedEventsStatements struct { + insertReportedEventsStmt *sql.Stmt + selectReportedEventsDescStmt *sql.Stmt + selectReportedEventsAscStmt *sql.Stmt + selectReportedEventStmt *sql.Stmt + deleteReportedEventStmt *sql.Stmt +} + +func CreateReportedEventsTable(db *sql.DB) error { + _, err := db.Exec(reportedEventsScheme) + return err +} + +func PrepareReportedEventsTable(db *sql.DB) (tables.ReportedEvents, error) { + s := &reportedEventsStatements{} + + return s, sqlutil.StatementList{ + {&s.insertReportedEventsStmt, insertReportedEventSQL}, + {&s.selectReportedEventsDescStmt, selectReportedEventsDescSQL}, + {&s.selectReportedEventsAscStmt, selectReportedEventsAscSQL}, + {&s.selectReportedEventStmt, selectReportedEventSQL}, + {&s.deleteReportedEventStmt, deleteReportedEventSQL}, + }.Prepare(db) +} + +func (r *reportedEventsStatements) InsertReportedEvent( + ctx context.Context, + txn *sql.Tx, + roomNID types.RoomNID, + eventNID types.EventNID, + reportingUserID types.EventStateKeyNID, + eventSenderID types.EventStateKeyNID, + reason string, + score int64, +) (int64, error) { + stmt := sqlutil.TxStmt(txn, r.insertReportedEventsStmt) + + var reportID int64 + err := stmt.QueryRowContext(ctx, + roomNID, + eventNID, + reportingUserID, + eventSenderID, + reason, + score, + spec.AsTimestamp(time.Now()), + ).Scan(&reportID) + return reportID, err +} + +func (r *reportedEventsStatements) SelectReportedEvents( + ctx context.Context, + txn *sql.Tx, + from, limit uint64, + backwards bool, + reportingUserID types.EventStateKeyNID, + roomNID types.RoomNID, +) ([]api.QueryAdminEventReportsResponse, int64, error) { + var stmt *sql.Stmt + if backwards { + stmt = sqlutil.TxStmt(txn, r.selectReportedEventsDescStmt) + } else { + stmt = sqlutil.TxStmt(txn, r.selectReportedEventsAscStmt) + } + + var qryRoomNID *types.RoomNID + if roomNID > 0 { + qryRoomNID = &roomNID + } + var qryReportingUser *types.EventStateKeyNID + if reportingUserID > 0 { + qryReportingUser = &reportingUserID + } + + rows, err := stmt.QueryContext(ctx, + qryRoomNID, + qryReportingUser, + from, + limit, + ) + if err != nil { + return nil, 0, err + } + defer internal.CloseAndLogIfError(ctx, rows, "SelectReportedEvents: failed to close rows") + + var result []api.QueryAdminEventReportsResponse + var row api.QueryAdminEventReportsResponse + var count int64 + for rows.Next() { + if err = rows.Scan( + &count, + &row.ID, + &row.RoomNID, + &row.EventNID, + &row.ReportingUserNID, + &row.SenderNID, + &row.Reason, + &row.Score, + &row.ReceivedTS, + ); err != nil { + return nil, 0, err + } + result = append(result, row) + } + + return result, count, rows.Err() +} + +func (r *reportedEventsStatements) SelectReportedEvent( + ctx context.Context, + txn *sql.Tx, + reportID uint64, +) (api.QueryAdminEventReportResponse, error) { + stmt := sqlutil.TxStmt(txn, r.selectReportedEventStmt) + + var row api.QueryAdminEventReportResponse + if err := stmt.QueryRowContext(ctx, reportID).Scan( + &row.ID, + &row.RoomNID, + &row.EventNID, + &row.ReportingUserNID, + &row.SenderNID, + &row.Reason, + &row.Score, + &row.ReceivedTS, + ); err != nil { + return api.QueryAdminEventReportResponse{}, err + } + return row, nil +} + +func (r *reportedEventsStatements) DeleteReportedEvent(ctx context.Context, txn *sql.Tx, reportID uint64) error { + stmt := sqlutil.TxStmt(txn, r.deleteReportedEventStmt) + _, err := stmt.ExecContext(ctx, reportID) + return err +} diff --git a/roomserver/storage/postgres/rooms_table.go b/roomserver/storage/postgres/rooms_table.go index bc3820b2c8..4de6dee46b 100644 --- a/roomserver/storage/postgres/rooms_table.go +++ b/roomserver/storage/postgres/rooms_table.go @@ -76,9 +76,6 @@ const selectRoomVersionsForRoomNIDsSQL = "" + const selectRoomInfoSQL = "" + "SELECT room_version, room_nid, state_snapshot_nid, latest_event_nids FROM roomserver_rooms WHERE room_id = $1" -const selectRoomIDsSQL = "" + - "SELECT room_id FROM roomserver_rooms WHERE array_length(latest_event_nids, 1) > 0" - const bulkSelectRoomIDsSQL = "" + "SELECT room_id FROM roomserver_rooms WHERE room_nid = ANY($1)" @@ -94,7 +91,6 @@ type roomStatements struct { updateLatestEventNIDsStmt *sql.Stmt selectRoomVersionsForRoomNIDsStmt *sql.Stmt selectRoomInfoStmt *sql.Stmt - selectRoomIDsStmt *sql.Stmt bulkSelectRoomIDsStmt *sql.Stmt bulkSelectRoomNIDsStmt *sql.Stmt } @@ -116,29 +112,11 @@ func PrepareRoomsTable(db *sql.DB) (tables.Rooms, error) { {&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL}, {&s.selectRoomVersionsForRoomNIDsStmt, selectRoomVersionsForRoomNIDsSQL}, {&s.selectRoomInfoStmt, selectRoomInfoSQL}, - {&s.selectRoomIDsStmt, selectRoomIDsSQL}, {&s.bulkSelectRoomIDsStmt, bulkSelectRoomIDsSQL}, {&s.bulkSelectRoomNIDsStmt, bulkSelectRoomNIDsSQL}, }.Prepare(db) } -func (s *roomStatements) SelectRoomIDsWithEvents(ctx context.Context, txn *sql.Tx) ([]string, error) { - stmt := sqlutil.TxStmt(txn, s.selectRoomIDsStmt) - rows, err := stmt.QueryContext(ctx) - if err != nil { - return nil, err - } - defer internal.CloseAndLogIfError(ctx, rows, "selectRoomIDsStmt: rows.close() failed") - var roomIDs []string - var roomID string - for rows.Next() { - if err = rows.Scan(&roomID); err != nil { - return nil, err - } - roomIDs = append(roomIDs, roomID) - } - return roomIDs, rows.Err() -} func (s *roomStatements) InsertRoomNID( ctx context.Context, txn *sql.Tx, roomID string, roomVersion gomatrixserverlib.RoomVersion, diff --git a/roomserver/storage/postgres/storage.go b/roomserver/storage/postgres/storage.go index c5c206cfb6..1068230f75 100644 --- a/roomserver/storage/postgres/storage.go +++ b/roomserver/storage/postgres/storage.go @@ -134,6 +134,9 @@ func (d *Database) create(db *sql.DB) error { if err := CreateUserRoomKeysTable(db); err != nil { return err } + if err := CreateReportedEventsTable(db); err != nil { + return err + } return nil } @@ -199,6 +202,10 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room if err != nil { return err } + reportedEvents, err := PrepareReportedEventsTable(db) + if err != nil { + return err + } d.Database = shared.Database{ DB: db, @@ -212,6 +219,7 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room EventStateKeysTable: eventStateKeys, PrevEventsTable: prevEvents, RedactionsTable: redactions, + ReportedEventsTable: reportedEvents, }, Cache: cache, Writer: writer, diff --git a/roomserver/storage/postgres/user_room_keys_table.go b/roomserver/storage/postgres/user_room_keys_table.go index 57e8f213bb..f8befc46b2 100644 --- a/roomserver/storage/postgres/user_room_keys_table.go +++ b/roomserver/storage/postgres/user_room_keys_table.go @@ -162,6 +162,9 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context, if errors.Is(err, sql.ErrNoRows) { return nil, nil } + if err != nil { + return nil, err + } defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows") resultMap := make(map[types.RoomNID]ed25519.PublicKey) diff --git a/roomserver/storage/shared/storage.go b/roomserver/storage/shared/storage.go index 682cead6c2..7b04641bf7 100644 --- a/roomserver/storage/shared/storage.go +++ b/roomserver/storage/shared/storage.go @@ -61,6 +61,7 @@ type EventDatabase struct { EventStateKeysTable tables.EventStateKeys PrevEventsTable tables.PreviousEvents RedactionsTable tables.Redactions + ReportedEventsTable tables.ReportedEvents } func (d *Database) SupportsConcurrentRoomInputs() bool { @@ -1625,9 +1626,24 @@ func (d *Database) GetKnownUsers(ctx context.Context, userID, searchString strin return d.MembershipTable.SelectKnownUsers(ctx, nil, stateKeyNID, searchString, limit) } -// GetKnownRooms returns a list of all rooms we know about. -func (d *Database) GetKnownRooms(ctx context.Context) ([]string, error) { - return d.RoomsTable.SelectRoomIDsWithEvents(ctx, nil) +func (d *Database) RoomsWithACLs(ctx context.Context) ([]string, error) { + + eventTypeNID, err := d.GetOrCreateEventTypeNID(ctx, "m.room.server_acl") + if err != nil { + return nil, err + } + + roomNIDs, err := d.EventsTable.SelectRoomsWithEventTypeNID(ctx, nil, eventTypeNID) + if err != nil { + return nil, err + } + + roomIDs, err := d.RoomsTable.BulkSelectRoomIDs(ctx, nil, roomNIDs) + if err != nil { + return nil, err + } + + return roomIDs, nil } // ForgetRoom sets a users room to forgotten @@ -1867,6 +1883,252 @@ func (d *Database) SelectUserIDsForPublicKeys(ctx context.Context, publicKeys ma return result, err } +// InsertReportedEvent stores a reported event. +func (d *Database) InsertReportedEvent( + ctx context.Context, + roomID, eventID, reportingUserID, reason string, + score int64, +) (int64, error) { + roomInfo, err := d.roomInfo(ctx, nil, roomID) + if err != nil { + return 0, err + } + if roomInfo == nil { + return 0, fmt.Errorf("room does not exist") + } + + events, err := d.eventsFromIDs(ctx, nil, roomInfo, []string{eventID}, NoFilter) + if err != nil { + return 0, err + } + if len(events) == 0 { + return 0, fmt.Errorf("unable to find requested event") + } + + stateKeyNIDs, err := d.EventStateKeyNIDs(ctx, []string{reportingUserID, events[0].SenderID().ToUserID().String()}) + if err != nil { + return 0, fmt.Errorf("failed to query eventStateKeyNIDs: %w", err) + } + + // We expect exactly 2 stateKeyNIDs + if len(stateKeyNIDs) != 2 { + return 0, fmt.Errorf("expected 2 stateKeyNIDs, received %d", len(stateKeyNIDs)) + } + + var reportID int64 + err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { + reportID, err = d.ReportedEventsTable.InsertReportedEvent( + ctx, + txn, + roomInfo.RoomNID, + events[0].EventNID, + stateKeyNIDs[reportingUserID], + stateKeyNIDs[events[0].SenderID().ToUserID().String()], + reason, + score, + ) + if err != nil { + return err + } + return nil + }) + + return reportID, err +} + +// QueryAdminEventReports returns event reports given a filter. +func (d *Database) QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID string, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error) { + // Filter on roomID, if requested + var roomNID types.RoomNID + if roomID != "" { + roomInfo, err := d.RoomInfo(ctx, roomID) + if err != nil { + return nil, 0, err + } + roomNID = roomInfo.RoomNID + } + + // Same as above, but for userID + var userNID types.EventStateKeyNID + if userID != "" { + stateKeysMap, err := d.EventStateKeyNIDs(ctx, []string{userID}) + if err != nil { + return nil, 0, err + } + if len(stateKeysMap) != 1 { + return nil, 0, fmt.Errorf("failed to get eventStateKeyNID for %s", userID) + } + userNID = stateKeysMap[userID] + } + + // Query all reported events matching the filters + reports, count, err := d.ReportedEventsTable.SelectReportedEvents(ctx, nil, from, limit, backwards, userNID, roomNID) + if err != nil { + return nil, 0, fmt.Errorf("failed to SelectReportedEvents: %w", err) + } + + // TODO: The below code may be inefficient due to many DB round trips and needs to be revisited. + // For the time being, this is "good enough". + qryRoomNIDs := make([]types.RoomNID, 0, len(reports)) + qryEventNIDs := make([]types.EventNID, 0, len(reports)) + qryStateKeyNIDs := make([]types.EventStateKeyNID, 0, len(reports)) + for _, report := range reports { + qryRoomNIDs = append(qryRoomNIDs, report.RoomNID) + qryEventNIDs = append(qryEventNIDs, report.EventNID) + qryStateKeyNIDs = append(qryStateKeyNIDs, report.ReportingUserNID, report.SenderNID) + } + + // This also de-dupes the roomIDs, otherwise we would query the same + // roomIDs in GetBulkStateContent multiple times + roomIDs, err := d.RoomsTable.BulkSelectRoomIDs(ctx, nil, qryRoomNIDs) + if err != nil { + return nil, 0, err + } + + // TODO: replace this with something more efficient, as it loads the entire state snapshot. + stateContent, err := d.GetBulkStateContent(ctx, roomIDs, []gomatrixserverlib.StateKeyTuple{ + {EventType: spec.MRoomName, StateKey: ""}, + {EventType: spec.MRoomCanonicalAlias, StateKey: ""}, + }, false) + if err != nil { + return nil, 0, err + } + + eventIDMap, err := d.EventIDs(ctx, qryEventNIDs) + if err != nil { + logrus.WithError(err).Error("unable to map eventNIDs to eventIDs") + return nil, 0, err + } + if len(eventIDMap) != len(qryEventNIDs) { + return nil, 0, fmt.Errorf("expected %d eventIDs, got %d", len(qryEventNIDs), len(eventIDMap)) + } + + // Get a map from EventStateKeyNID to userID + userNIDMap, err := d.EventStateKeys(ctx, qryStateKeyNIDs) + if err != nil { + logrus.WithError(err).Error("unable to map userNIDs to userIDs") + return nil, 0, err + } + + // Create a cache from roomNID to roomID to avoid hitting the DB again + roomNIDIDCache := make(map[types.RoomNID]string, len(roomIDs)) + for i := 0; i < len(reports); i++ { + cachedRoomID := roomNIDIDCache[reports[i].RoomNID] + if cachedRoomID == "" { + // We need to query this again, as we otherwise don't have a way to match roomNID -> roomID + roomIDs, err = d.RoomsTable.BulkSelectRoomIDs(ctx, nil, []types.RoomNID{reports[i].RoomNID}) + if err != nil { + return nil, 0, err + } + if len(roomIDs) == 0 || len(roomIDs) > 1 { + logrus.Warnf("unable to map roomNID %d to a roomID, was this room deleted?", roomNID) + continue + } + roomNIDIDCache[reports[i].RoomNID] = roomIDs[0] + cachedRoomID = roomIDs[0] + } + + reports[i].EventID = eventIDMap[reports[i].EventNID] + reports[i].RoomID = cachedRoomID + roomName, canonicalAlias := findRoomNameAndCanonicalAlias(stateContent, cachedRoomID) + reports[i].RoomName = roomName + reports[i].CanonicalAlias = canonicalAlias + reports[i].Sender = userNIDMap[reports[i].SenderNID] + reports[i].UserID = userNIDMap[reports[i].ReportingUserNID] + } + + return reports, count, nil +} + +func (d *Database) QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error) { + + report, err := d.ReportedEventsTable.SelectReportedEvent(ctx, nil, reportID) + if err != nil { + return api.QueryAdminEventReportResponse{}, err + } + + // Get a map from EventStateKeyNID to userID + userNIDMap, err := d.EventStateKeys(ctx, []types.EventStateKeyNID{report.ReportingUserNID, report.SenderNID}) + if err != nil { + logrus.WithError(err).Error("unable to map userNIDs to userIDs") + return report, err + } + + roomIDs, err := d.RoomsTable.BulkSelectRoomIDs(ctx, nil, []types.RoomNID{report.RoomNID}) + if err != nil { + return report, err + } + + if len(roomIDs) != 1 { + return report, fmt.Errorf("expected one roomID, got %d", len(roomIDs)) + } + + // TODO: replace this with something more efficient, as it loads the entire state snapshot. + stateContent, err := d.GetBulkStateContent(ctx, roomIDs, []gomatrixserverlib.StateKeyTuple{ + {EventType: spec.MRoomName, StateKey: ""}, + {EventType: spec.MRoomCanonicalAlias, StateKey: ""}, + }, false) + if err != nil { + return report, err + } + + eventIDMap, err := d.EventIDs(ctx, []types.EventNID{report.EventNID}) + if err != nil { + logrus.WithError(err).Error("unable to map eventNIDs to eventIDs") + return report, err + } + if len(eventIDMap) != 1 { + return report, fmt.Errorf("expected %d eventIDs, got %d", 1, len(eventIDMap)) + } + + eventJSONs, err := d.EventJSONTable.BulkSelectEventJSON(ctx, nil, []types.EventNID{report.EventNID}) + if err != nil { + return report, err + } + if len(eventJSONs) != 1 { + return report, fmt.Errorf("expected %d eventJSONs, got %d", 1, len(eventJSONs)) + } + + roomName, canonicalAlias := findRoomNameAndCanonicalAlias(stateContent, roomIDs[0]) + + report.Sender = userNIDMap[report.SenderNID] + report.UserID = userNIDMap[report.ReportingUserNID] + report.RoomID = roomIDs[0] + report.RoomName = roomName + report.CanonicalAlias = canonicalAlias + report.EventID = eventIDMap[report.EventNID] + report.EventJSON = eventJSONs[0].EventJSON + + return report, nil +} + +func (d *Database) AdminDeleteEventReport(ctx context.Context, reportID uint64) error { + return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { + return d.ReportedEventsTable.DeleteReportedEvent(ctx, txn, reportID) + }) +} + +// findRoomNameAndCanonicalAlias loops over events to find the corresponding room name and canonicalAlias +// for a given roomID. +func findRoomNameAndCanonicalAlias(events []tables.StrippedEvent, roomID string) (name, canonicalAlias string) { + for _, ev := range events { + if ev.RoomID != roomID { + continue + } + if ev.EventType == spec.MRoomName { + name = ev.ContentValue + } + if ev.EventType == spec.MRoomCanonicalAlias { + canonicalAlias = ev.ContentValue + } + // We found both wanted values, break the loop + if name != "" && canonicalAlias != "" { + break + } + } + return name, canonicalAlias +} + // FIXME TODO: Remove all this - horrible dupe with roomserver/state. Can't use the original impl because of circular loops // it should live in this package! diff --git a/roomserver/storage/sqlite3/events_table.go b/roomserver/storage/sqlite3/events_table.go index 2c269bced1..26401e45d2 100644 --- a/roomserver/storage/sqlite3/events_table.go +++ b/roomserver/storage/sqlite3/events_table.go @@ -44,6 +44,14 @@ const eventsSchema = ` auth_event_nids TEXT NOT NULL DEFAULT '[]', is_rejected BOOLEAN NOT NULL DEFAULT FALSE ); + +-- Create an index which helps in resolving membership events (event_type_nid = 5) - (used for history visibility) +CREATE INDEX IF NOT EXISTS roomserver_events_memberships_idx ON roomserver_events (room_nid, event_state_key_nid) WHERE (event_type_nid = 5); + +-- The following indexes are used by bulkSelectStateEventByNIDSQL +CREATE INDEX IF NOT EXISTS roomserver_event_event_type_nid_idx ON roomserver_events (event_type_nid); +CREATE INDEX IF NOT EXISTS roomserver_event_state_key_nid_idx ON roomserver_events (event_state_key_nid); + ` const insertEventSQL = ` @@ -120,6 +128,8 @@ const selectRoomNIDsForEventNIDsSQL = "" + const selectEventRejectedSQL = "" + "SELECT is_rejected FROM roomserver_events WHERE room_nid = $1 AND event_id = $2" +const selectRoomsWithEventTypeNIDSQL = `SELECT DISTINCT room_nid FROM roomserver_events WHERE event_type_nid = $1` + type eventStatements struct { db *sql.DB insertEventStmt *sql.Stmt @@ -135,6 +145,7 @@ type eventStatements struct { bulkSelectStateAtEventAndReferenceStmt *sql.Stmt bulkSelectEventIDStmt *sql.Stmt selectEventRejectedStmt *sql.Stmt + selectRoomsWithEventTypeNIDStmt *sql.Stmt //bulkSelectEventNIDStmt *sql.Stmt //bulkSelectUnsentEventNIDStmt *sql.Stmt //selectRoomNIDsForEventNIDsStmt *sql.Stmt @@ -192,6 +203,7 @@ func PrepareEventsTable(db *sql.DB) (tables.Events, error) { //{&s.bulkSelectUnsentEventNIDStmt, bulkSelectUnsentEventNIDSQL}, //{&s.selectRoomNIDForEventNIDStmt, selectRoomNIDForEventNIDSQL}, {&s.selectEventRejectedStmt, selectEventRejectedSQL}, + {&s.selectRoomsWithEventTypeNIDStmt, selectRoomsWithEventTypeNIDSQL}, }.Prepare(db) } @@ -682,3 +694,25 @@ func (s *eventStatements) SelectEventRejected( err = stmt.QueryRowContext(ctx, roomNID, eventID).Scan(&rejected) return } + +func (s *eventStatements) SelectRoomsWithEventTypeNID( + ctx context.Context, txn *sql.Tx, eventTypeNID types.EventTypeNID, +) ([]types.RoomNID, error) { + stmt := sqlutil.TxStmt(txn, s.selectRoomsWithEventTypeNIDStmt) + rows, err := stmt.QueryContext(ctx, eventTypeNID) + defer internal.CloseAndLogIfError(ctx, rows, "SelectRoomsWithEventTypeNID: rows.close() failed") + if err != nil { + return nil, err + } + + var roomNIDs []types.RoomNID + var roomNID types.RoomNID + for rows.Next() { + if err := rows.Scan(&roomNID); err != nil { + return nil, err + } + roomNIDs = append(roomNIDs, roomNID) + } + + return roomNIDs, rows.Err() +} diff --git a/roomserver/storage/sqlite3/reported_events_table.go b/roomserver/storage/sqlite3/reported_events_table.go new file mode 100644 index 0000000000..b72cb06851 --- /dev/null +++ b/roomserver/storage/sqlite3/reported_events_table.go @@ -0,0 +1,221 @@ +// Copyright 2023 The Matrix.org Foundation C.I.C. +// +// 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 sqlite3 + +import ( + "context" + "database/sql" + "time" + + "github.com/matrix-org/dendrite/internal" + "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/roomserver/storage/tables" + "github.com/matrix-org/dendrite/roomserver/types" + "github.com/matrix-org/gomatrixserverlib/spec" +) + +const reportedEventsScheme = ` +CREATE TABLE IF NOT EXISTS roomserver_reported_events +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + room_nid INTEGER NOT NULL, + event_nid INTEGER NOT NULL, + reporting_user_nid INTEGER NOT NULL, -- the user reporting the event + event_sender_nid INTEGER NOT NULL, -- the user who sent the reported event + reason TEXT, + score INTEGER, + received_ts INTEGER NOT NULL +);` + +const insertReportedEventSQL = ` + INSERT INTO roomserver_reported_events (room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING id +` + +const selectReportedEventsDescSQL = ` +WITH countReports AS ( + SELECT count(*) as report_count + FROM roomserver_reported_events + WHERE ($1 IS NULL OR room_nid = $1) AND ($2 IS NULL OR reporting_user_nid = $2) +) +SELECT report_count, id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts +FROM roomserver_reported_events, countReports +WHERE ($1 IS NULL OR room_nid = $1) AND ($2 IS NULL OR reporting_user_nid = $2) +ORDER BY received_ts DESC +LIMIT $3 +OFFSET $4 +` + +const selectReportedEventsAscSQL = ` +WITH countReports AS ( + SELECT count(*) as report_count + FROM roomserver_reported_events + WHERE ($1 IS NULL OR room_nid = $1) AND ($2 IS NULL OR reporting_user_nid = $2) +) +SELECT report_count, id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts +FROM roomserver_reported_events, countReports +WHERE ($1 IS NULL OR room_nid = $1) AND ($2 IS NULL OR reporting_user_nid = $2) +ORDER BY received_ts ASC +LIMIT $3 +OFFSET $4 +` + +const selectReportedEventSQL = ` +SELECT id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts +FROM roomserver_reported_events +WHERE id = $1 +` + +const deleteReportedEventSQL = `DELETE FROM roomserver_reported_events WHERE id = $1` + +type reportedEventsStatements struct { + insertReportedEventsStmt *sql.Stmt + selectReportedEventsDescStmt *sql.Stmt + selectReportedEventsAscStmt *sql.Stmt + selectReportedEventStmt *sql.Stmt + deleteReportedEventStmt *sql.Stmt +} + +func CreateReportedEventsTable(db *sql.DB) error { + _, err := db.Exec(reportedEventsScheme) + return err +} + +func PrepareReportedEventsTable(db *sql.DB) (tables.ReportedEvents, error) { + s := &reportedEventsStatements{} + + return s, sqlutil.StatementList{ + {&s.insertReportedEventsStmt, insertReportedEventSQL}, + {&s.selectReportedEventsDescStmt, selectReportedEventsDescSQL}, + {&s.selectReportedEventsAscStmt, selectReportedEventsAscSQL}, + {&s.selectReportedEventStmt, selectReportedEventSQL}, + {&s.deleteReportedEventStmt, deleteReportedEventSQL}, + }.Prepare(db) +} + +func (r *reportedEventsStatements) InsertReportedEvent( + ctx context.Context, + txn *sql.Tx, + roomNID types.RoomNID, + eventNID types.EventNID, + reportingUserID types.EventStateKeyNID, + eventSenderID types.EventStateKeyNID, + reason string, + score int64, +) (int64, error) { + stmt := sqlutil.TxStmt(txn, r.insertReportedEventsStmt) + + var reportID int64 + err := stmt.QueryRowContext(ctx, + roomNID, + eventNID, + reportingUserID, + eventSenderID, + reason, + score, + spec.AsTimestamp(time.Now()), + ).Scan(&reportID) + return reportID, err +} + +func (r *reportedEventsStatements) SelectReportedEvents( + ctx context.Context, + txn *sql.Tx, + from, limit uint64, + backwards bool, + reportingUserID types.EventStateKeyNID, + roomNID types.RoomNID, +) ([]api.QueryAdminEventReportsResponse, int64, error) { + + var stmt *sql.Stmt + if backwards { + stmt = sqlutil.TxStmt(txn, r.selectReportedEventsDescStmt) + } else { + stmt = sqlutil.TxStmt(txn, r.selectReportedEventsAscStmt) + } + + var qryRoomNID *types.RoomNID + if roomNID > 0 { + qryRoomNID = &roomNID + } + var qryReportingUser *types.EventStateKeyNID + if reportingUserID > 0 { + qryReportingUser = &reportingUserID + } + + rows, err := stmt.QueryContext(ctx, + qryRoomNID, + qryReportingUser, + limit, + from, + ) + if err != nil { + return nil, 0, err + } + defer internal.CloseAndLogIfError(ctx, rows, "SelectReportedEvents: failed to close rows") + + var result []api.QueryAdminEventReportsResponse + var row api.QueryAdminEventReportsResponse + var count int64 + for rows.Next() { + if err = rows.Scan( + &count, + &row.ID, + &row.RoomNID, + &row.EventNID, + &row.ReportingUserNID, + &row.SenderNID, + &row.Reason, + &row.Score, + &row.ReceivedTS, + ); err != nil { + return nil, 0, err + } + result = append(result, row) + } + + return result, count, rows.Err() +} + +func (r *reportedEventsStatements) SelectReportedEvent( + ctx context.Context, + txn *sql.Tx, + reportID uint64, +) (api.QueryAdminEventReportResponse, error) { + stmt := sqlutil.TxStmt(txn, r.selectReportedEventStmt) + + var row api.QueryAdminEventReportResponse + if err := stmt.QueryRowContext(ctx, reportID).Scan( + &row.ID, + &row.RoomNID, + &row.EventNID, + &row.ReportingUserNID, + &row.SenderNID, + &row.Reason, + &row.Score, + &row.ReceivedTS, + ); err != nil { + return api.QueryAdminEventReportResponse{}, err + } + return row, nil +} + +func (r *reportedEventsStatements) DeleteReportedEvent(ctx context.Context, txn *sql.Tx, reportID uint64) error { + stmt := sqlutil.TxStmt(txn, r.deleteReportedEventStmt) + _, err := stmt.ExecContext(ctx, reportID) + return err +} diff --git a/roomserver/storage/sqlite3/rooms_table.go b/roomserver/storage/sqlite3/rooms_table.go index 22700a7103..5034b24250 100644 --- a/roomserver/storage/sqlite3/rooms_table.go +++ b/roomserver/storage/sqlite3/rooms_table.go @@ -65,9 +65,6 @@ const selectRoomVersionsForRoomNIDsSQL = "" + const selectRoomInfoSQL = "" + "SELECT room_version, room_nid, state_snapshot_nid, latest_event_nids FROM roomserver_rooms WHERE room_id = $1" -const selectRoomIDsSQL = "" + - "SELECT room_id FROM roomserver_rooms WHERE latest_event_nids != '[]'" - const bulkSelectRoomIDsSQL = "" + "SELECT room_id FROM roomserver_rooms WHERE room_nid IN ($1)" @@ -87,7 +84,6 @@ type roomStatements struct { updateLatestEventNIDsStmt *sql.Stmt //selectRoomVersionForRoomNIDStmt *sql.Stmt selectRoomInfoStmt *sql.Stmt - selectRoomIDsStmt *sql.Stmt } func CreateRoomsTable(db *sql.DB) error { @@ -108,29 +104,10 @@ func PrepareRoomsTable(db *sql.DB) (tables.Rooms, error) { {&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL}, //{&s.selectRoomVersionForRoomNIDsStmt, selectRoomVersionForRoomNIDsSQL}, {&s.selectRoomInfoStmt, selectRoomInfoSQL}, - {&s.selectRoomIDsStmt, selectRoomIDsSQL}, {&s.selectRoomNIDForUpdateStmt, selectRoomNIDForUpdateSQL}, }.Prepare(db) } -func (s *roomStatements) SelectRoomIDsWithEvents(ctx context.Context, txn *sql.Tx) ([]string, error) { - stmt := sqlutil.TxStmt(txn, s.selectRoomIDsStmt) - rows, err := stmt.QueryContext(ctx) - if err != nil { - return nil, err - } - defer internal.CloseAndLogIfError(ctx, rows, "selectRoomIDsStmt: rows.close() failed") - var roomIDs []string - var roomID string - for rows.Next() { - if err = rows.Scan(&roomID); err != nil { - return nil, err - } - roomIDs = append(roomIDs, roomID) - } - return roomIDs, rows.Err() -} - func (s *roomStatements) SelectRoomInfo(ctx context.Context, txn *sql.Tx, roomID string) (*types.RoomInfo, error) { var info types.RoomInfo var latestNIDsJSON string diff --git a/roomserver/storage/sqlite3/storage.go b/roomserver/storage/sqlite3/storage.go index 98d88f923f..191c072232 100644 --- a/roomserver/storage/sqlite3/storage.go +++ b/roomserver/storage/sqlite3/storage.go @@ -141,7 +141,9 @@ func (d *Database) create(db *sql.DB) error { if err := CreateUserRoomKeysTable(db); err != nil { return err } - + if err := CreateReportedEventsTable(db); err != nil { + return err + } return nil } @@ -206,6 +208,10 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room if err != nil { return err } + reportedEvents, err := PrepareReportedEventsTable(db) + if err != nil { + return err + } d.Database = shared.Database{ DB: db, @@ -219,6 +225,7 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room EventJSONTable: eventJSON, PrevEventsTable: prevEvents, RedactionsTable: redactions, + ReportedEventsTable: reportedEvents, }, Cache: cache, Writer: writer, diff --git a/roomserver/storage/sqlite3/user_room_keys_table.go b/roomserver/storage/sqlite3/user_room_keys_table.go index 13906f771b..ef3b8fe202 100644 --- a/roomserver/storage/sqlite3/user_room_keys_table.go +++ b/roomserver/storage/sqlite3/user_room_keys_table.go @@ -177,6 +177,9 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context, if errors.Is(err, sql.ErrNoRows) { return nil, nil } + if err != nil { + return nil, err + } defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows") resultMap := make(map[types.RoomNID]ed25519.PublicKey) diff --git a/roomserver/storage/tables/events_table_test.go b/roomserver/storage/tables/events_table_test.go index 5ed805648d..52aeacc2f1 100644 --- a/roomserver/storage/tables/events_table_test.go +++ b/roomserver/storage/tables/events_table_test.go @@ -2,6 +2,7 @@ package tables_test import ( "context" + "fmt" "testing" "github.com/matrix-org/dendrite/internal/sqlutil" @@ -147,3 +148,38 @@ func Test_EventsTable(t *testing.T) { assert.Equal(t, int64(len(room.Events())+1), maxDepth) }) } + +func TestRoomsWithACL(t *testing.T) { + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + eventStateKeys, closeEventStateKeys := mustCreateEventTypesTable(t, dbType) + defer closeEventStateKeys() + + eventsTable, closeEventsTable := mustCreateEventsTable(t, dbType) + defer closeEventsTable() + + ctx := context.Background() + + // insert the m.room.server_acl event type + eventTypeNID, err := eventStateKeys.InsertEventTypeNID(ctx, nil, "m.room.server_acl") + assert.Nil(t, err) + + // Create ACL'd rooms + var wantRoomNIDs []types.RoomNID + for i := 0; i < 10; i++ { + _, _, err = eventsTable.InsertEvent(ctx, nil, types.RoomNID(i), eventTypeNID, types.EmptyStateKeyNID, fmt.Sprintf("$1337+%d", i), nil, 0, false) + assert.Nil(t, err) + wantRoomNIDs = append(wantRoomNIDs, types.RoomNID(i)) + } + + // Create non-ACL'd rooms (eventTypeNID+1) + for i := 10; i < 20; i++ { + _, _, err = eventsTable.InsertEvent(ctx, nil, types.RoomNID(i), eventTypeNID+1, types.EmptyStateKeyNID, fmt.Sprintf("$1337+%d", i), nil, 0, false) + assert.Nil(t, err) + } + + gotRoomNIDs, err := eventsTable.SelectRoomsWithEventTypeNID(ctx, nil, eventTypeNID) + assert.Nil(t, err) + assert.Equal(t, wantRoomNIDs, gotRoomNIDs) + }) +} diff --git a/roomserver/storage/tables/interface.go b/roomserver/storage/tables/interface.go index b3cb31880f..02f6992c4f 100644 --- a/roomserver/storage/tables/interface.go +++ b/roomserver/storage/tables/interface.go @@ -6,6 +6,7 @@ import ( "database/sql" "errors" + "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/spec" "github.com/tidwall/gjson" @@ -69,6 +70,8 @@ type Events interface { SelectMaxEventDepth(ctx context.Context, txn *sql.Tx, eventNIDs []types.EventNID) (int64, error) SelectRoomNIDsForEventNIDs(ctx context.Context, txn *sql.Tx, eventNIDs []types.EventNID) (roomNIDs map[types.EventNID]types.RoomNID, err error) SelectEventRejected(ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, eventID string) (rejected bool, err error) + + SelectRoomsWithEventTypeNID(ctx context.Context, txn *sql.Tx, eventTypeNID types.EventTypeNID) ([]types.RoomNID, error) } type Rooms interface { @@ -80,7 +83,6 @@ type Rooms interface { UpdateLatestEventNIDs(ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, eventNIDs []types.EventNID, lastEventSentNID types.EventNID, stateSnapshotNID types.StateSnapshotNID) error SelectRoomVersionsForRoomNIDs(ctx context.Context, txn *sql.Tx, roomNID []types.RoomNID) (map[types.RoomNID]gomatrixserverlib.RoomVersion, error) SelectRoomInfo(ctx context.Context, txn *sql.Tx, roomID string) (*types.RoomInfo, error) - SelectRoomIDsWithEvents(ctx context.Context, txn *sql.Tx) ([]string, error) BulkSelectRoomIDs(ctx context.Context, txn *sql.Tx, roomNIDs []types.RoomNID) ([]string, error) BulkSelectRoomNIDs(ctx context.Context, txn *sql.Tx, roomIDs []string) ([]types.RoomNID, error) } @@ -126,6 +128,33 @@ type Invites interface { SelectInviteActiveForUserInRoom(ctx context.Context, txn *sql.Tx, targetUserNID types.EventStateKeyNID, roomNID types.RoomNID) ([]types.EventStateKeyNID, []string, []byte, error) } +type ReportedEvents interface { + InsertReportedEvent( + ctx context.Context, + txn *sql.Tx, + roomNID types.RoomNID, + eventNID types.EventNID, + reportingUserID types.EventStateKeyNID, + eventSenderID types.EventStateKeyNID, + reason string, + score int64, + ) (int64, error) + SelectReportedEvents( + ctx context.Context, + txn *sql.Tx, + from, limit uint64, + backwards bool, + reportingUserID types.EventStateKeyNID, + roomNID types.RoomNID, + ) ([]api.QueryAdminEventReportsResponse, int64, error) + SelectReportedEvent( + ctx context.Context, + txn *sql.Tx, + reportID uint64, + ) (api.QueryAdminEventReportResponse, error) + DeleteReportedEvent(ctx context.Context, txn *sql.Tx, reportID uint64) error +} + type MembershipState int64 const ( diff --git a/roomserver/storage/tables/rooms_table_test.go b/roomserver/storage/tables/rooms_table_test.go index eddd012c8b..e97e3e3392 100644 --- a/roomserver/storage/tables/rooms_table_test.go +++ b/roomserver/storage/tables/rooms_table_test.go @@ -74,11 +74,6 @@ func TestRoomsTable(t *testing.T) { assert.NoError(t, err) assert.Nil(t, roomInfo) - // There are no rooms with latestEventNIDs yet - roomIDs, err := tab.SelectRoomIDsWithEvents(ctx, nil) - assert.NoError(t, err) - assert.Equal(t, 0, len(roomIDs)) - roomVersions, err := tab.SelectRoomVersionsForRoomNIDs(ctx, nil, []types.RoomNID{wantRoomNID, 1337}) assert.NoError(t, err) assert.Equal(t, roomVersions[wantRoomNID], room.Version) @@ -86,7 +81,7 @@ func TestRoomsTable(t *testing.T) { _, ok := roomVersions[1337] assert.False(t, ok) - roomIDs, err = tab.BulkSelectRoomIDs(ctx, nil, []types.RoomNID{wantRoomNID, 1337}) + roomIDs, err := tab.BulkSelectRoomIDs(ctx, nil, []types.RoomNID{wantRoomNID, 1337}) assert.NoError(t, err) assert.Equal(t, []string{room.ID}, roomIDs) diff --git a/setup/base/base.go b/setup/base/base.go index 455337e595..82068aa920 100644 --- a/setup/base/base.go +++ b/setup/base/base.go @@ -28,13 +28,13 @@ import ( _ "net/http/pprof" "os" "os/signal" + "sync/atomic" "syscall" "time" sentryhttp "github.com/getsentry/sentry-go/http" "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/prometheus/client_golang/prometheus/promhttp" - "go.uber.org/atomic" "github.com/gorilla/mux" "github.com/kardianos/minwinsvc" diff --git a/setup/base/sanity_other.go b/setup/base/sanity_other.go index d35c2e8727..38e2b941f8 100644 --- a/setup/base/sanity_other.go +++ b/setup/base/sanity_other.go @@ -1,5 +1,5 @@ -//go:build !linux && !darwin && !netbsd && !freebsd && !openbsd && !solaris && !dragonfly && !aix -// +build !linux,!darwin,!netbsd,!freebsd,!openbsd,!solaris,!dragonfly,!aix +//go:build !unix +// +build !unix package base diff --git a/setup/base/sanity_unix.go b/setup/base/sanity_unix.go index 0403df1a8f..90e38a6db9 100644 --- a/setup/base/sanity_unix.go +++ b/setup/base/sanity_unix.go @@ -1,5 +1,5 @@ -//go:build linux || darwin || netbsd || freebsd || openbsd || solaris || dragonfly || aix -// +build linux darwin netbsd freebsd openbsd solaris dragonfly aix +//go:build unix +// +build unix package base diff --git a/setup/jetstream/nats.go b/setup/jetstream/nats.go index 8820e86b2b..8630a14119 100644 --- a/setup/jetstream/nats.go +++ b/setup/jetstream/nats.go @@ -38,7 +38,12 @@ func (s *NATSInstance) Prepare(process *process.ProcessContext, cfg *config.JetS defer natsLock.Unlock() // check if we need an in-process NATS Server if len(cfg.Addresses) != 0 { - return setupNATS(process, cfg, nil) + // reuse existing connections + if s.nc != nil { + return s.js, s.nc + } + s.js, s.nc = setupNATS(process, cfg, nil) + return s.js, s.nc } if s.Server == nil { var err error diff --git a/syncapi/consumers/roomserver.go b/syncapi/consumers/roomserver.go index 81c532f19e..abf8888299 100644 --- a/syncapi/consumers/roomserver.go +++ b/syncapi/consumers/roomserver.go @@ -601,9 +601,11 @@ func (s *OutputRoomEventConsumer) writeFTS(ev *rstypes.HeaderedEvent, pduPositio } e.SetContentType(ev.Type()) + var relatesTo gjson.Result switch ev.Type() { case "m.room.message": e.Content = gjson.GetBytes(ev.Content(), "body").String() + relatesTo = gjson.GetBytes(ev.Content(), "m\\.relates_to") case spec.MRoomName: e.Content = gjson.GetBytes(ev.Content(), "name").String() case spec.MRoomTopic: @@ -622,6 +624,22 @@ func (s *OutputRoomEventConsumer) writeFTS(ev *rstypes.HeaderedEvent, pduPositio if err := s.fts.Index(e); err != nil { return err } + // If the event is an edited message we remove the original event from the index + // to avoid duplicates in the search results. + if relatesTo.Exists() { + relatedData := relatesTo.Map() + if _, ok := relatedData["rel_type"]; ok && relatedData["rel_type"].Str == "m.replace" { + // We remove the original event from the index + if srcEventID, ok := relatedData["event_id"]; ok { + if err := s.fts.Delete(srcEventID.Str); err != nil { + log.WithFields(log.Fields{ + "event_id": ev.EventID(), + "src_id": srcEventID.Str, + }).WithError(err).Error("Failed to delete edited message from the fulltext index") + } + } + } + } } return nil } diff --git a/syncapi/syncapi_test.go b/syncapi/syncapi_test.go index 0392f209ab..d360e10d91 100644 --- a/syncapi/syncapi_test.go +++ b/syncapi/syncapi_test.go @@ -4,12 +4,14 @@ import ( "context" "encoding/json" "fmt" + "io" "net/http" "net/http/httptest" "reflect" "testing" "time" + "github.com/gorilla/mux" "github.com/matrix-org/dendrite/internal/caching" "github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/dendrite/internal/sqlutil" @@ -17,6 +19,7 @@ import ( "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/spec" "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" "github.com/tidwall/gjson" rstypes "github.com/matrix-org/dendrite/roomserver/types" @@ -1324,6 +1327,95 @@ func TestUpdateRelations(t *testing.T) { }) } +func TestRemoveEditedEventFromSearchIndex(t *testing.T) { + user := test.NewUser(t) + alice := userapi.Device{ + ID: "ALICEID", + UserID: user.ID, + AccessToken: "ALICE_BEARER_TOKEN", + DisplayName: "Alice", + AccountType: userapi.AccountTypeUser, + } + + routers := httputil.NewRouters() + + cfg, processCtx, close := testrig.CreateConfig(t, test.DBTypeSQLite) + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + defer close() + + // Use an actual roomserver for this + natsInstance := jetstream.NATSInstance{} + jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream) + defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream) + + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + + room := test.NewRoom(t, user) + AddPublicRoutes(processCtx, routers, cfg, cm, &natsInstance, &syncUserAPI{accounts: []userapi.Device{alice}}, &syncRoomserverAPI{rooms: []*test.Room{room}}, caches, caching.DisableMetrics) + + if err := api.SendEvents(processCtx.Context(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil { + t.Fatalf("failed to send events: %v", err) + } + + ev1 := room.CreateAndInsert(t, user, "m.room.message", map[string]interface{}{"body": "first"}) + ev2 := room.CreateAndInsert(t, user, "m.room.message", map[string]interface{}{ + "body": " * first", + "m.new_content": map[string]interface{}{ + "body": "first", + "msgtype": "m.text", + }, + "m.relates_to": map[string]interface{}{ + "event_id": ev1.EventID(), + "rel_type": "m.replace", + }, + }) + events := []*rstypes.HeaderedEvent{ev1, ev2} + + for _, e := range events { + roomEvents := append([]*rstypes.HeaderedEvent{}, e) + if err := api.SendEvents(processCtx.Context(), rsAPI, api.KindNew, roomEvents, "test", "test", "test", nil, false); err != nil { + t.Fatalf("failed to send events: %v", err) + } + + syncUntil(t, routers, alice.AccessToken, false, func(syncBody string) bool { + // wait for the last sent eventID to come down sync + path := fmt.Sprintf(`rooms.join.%s.timeline.events.#(event_id=="%s")`, room.ID, e.EventID()) + + return gjson.Get(syncBody, path).Exists() + }) + + // We search that event is the only one nad is the exact event we sent + searchResult := searchRequest(t, routers.Client, alice.AccessToken, "first", []string{room.ID}) + results := gjson.GetBytes(searchResult, fmt.Sprintf(`search_categories.room_events.groups.room_id.%s.results`, room.ID)) + assert.True(t, results.Exists(), "Should be a search response") + assert.Equal(t, 1, len(results.Array()), "Should be exactly one result") + assert.Equal(t, e.EventID(), results.Array()[0].String(), "Should be only found exact event") + } +} + +func searchRequest(t *testing.T, router *mux.Router, accessToken, searchTerm string, roomList []string) []byte { + t.Helper() + w := httptest.NewRecorder() + rq := test.NewRequest(t, "POST", "/_matrix/client/v3/search", test.WithQueryParams(map[string]string{ + "access_token": accessToken, + }), test.WithJSONBody(t, map[string]interface{}{ + "search_categories": map[string]interface{}{ + "room_events": map[string]interface{}{ + "filters": roomList, + "search_term": searchTerm, + }, + }, + })) + + router.ServeHTTP(w, rq) + assert.Equal(t, 200, w.Code) + defer w.Result().Body.Close() + body, err := io.ReadAll(w.Result().Body) + assert.NoError(t, err) + return body +} func syncUntil(t *testing.T, routers httputil.Routers, accessToken string, skip bool, diff --git a/sytest-whitelist b/sytest-whitelist index 492c756baf..35d700d0af 100644 --- a/sytest-whitelist +++ b/sytest-whitelist @@ -540,7 +540,6 @@ Will not back up to an old backup version Can create more than 10 backup versions Can delete backup Deleted & recreated backups are empty -Can upload self-signing keys Fails to upload self-signing keys with no auth Fails to upload self-signing key without master key can fetch self-signing keys over federation @@ -633,7 +632,6 @@ Trying to add push rule with no scope fails with 400 Trying to add push rule with invalid scope fails with 400 Forward extremities remain so even after the next events are populated as outliers uploading self-signing key notifies over federation -uploading signed devices gets propagated over federation Device list doesn't change if remote server is down /context/ on joined room works /context/ on non world readable room does not work diff --git a/test/testrig/base.go b/test/testrig/base.go index 9537045959..a21cfe8027 100644 --- a/test/testrig/base.go +++ b/test/testrig/base.go @@ -71,6 +71,7 @@ func CreateConfig(t *testing.T, dbType test.DBType) (*config.Dendrite, *process. SingleDatabase: false, }) cfg.Global.ServerName = "test" + cfg.SyncAPI.Fulltext.Enabled = true cfg.SyncAPI.Fulltext.InMemory = true // use a distinct prefix else concurrent postgres/sqlite runs will clash since NATS will use // the file system event with InMemory=true :( diff --git a/test/wasm/package-lock.json b/test/wasm/package-lock.json index f26d55ab7e..c9ea15407f 100644 --- a/test/wasm/package-lock.json +++ b/test/wasm/package-lock.json @@ -7,7 +7,7 @@ "dependencies": { "isomorphic-ws": "^4.0.1", "sql.js": "github:neilalexander/sql.js#252a72bf57b0538cbd49bbd6f70af71e516966ae", - "ws": "^7.5.2" + "ws": "^7.5.10" } }, "node_modules/isomorphic-ws": { @@ -25,9 +25,9 @@ "license": "MIT" }, "node_modules/ws": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.2.tgz", - "integrity": "sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "engines": { "node": ">=8.3.0" }, @@ -58,9 +58,9 @@ "from": "sql.js@github:neilalexander/sql.js#252a72bf57b0538cbd49bbd6f70af71e516966ae" }, "ws": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.2.tgz", - "integrity": "sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "requires": {} } } diff --git a/test/wasm/package.json b/test/wasm/package.json index b28c30b1d5..6a2cc43630 100644 --- a/test/wasm/package.json +++ b/test/wasm/package.json @@ -2,6 +2,6 @@ "dependencies": { "isomorphic-ws": "^4.0.1", "sql.js": "github:neilalexander/sql.js#252a72bf57b0538cbd49bbd6f70af71e516966ae", - "ws": "^7.5.2" + "ws": "^7.5.10" } } diff --git a/userapi/internal/user_api.go b/userapi/internal/user_api.go index a126dc871d..fd73bf62fd 100644 --- a/userapi/internal/user_api.go +++ b/userapi/internal/user_api.go @@ -939,11 +939,12 @@ func (a *UserInternalAPI) QueryAccountByPassword(ctx context.Context, req *api.Q return nil case bcrypt.ErrHashTooShort: // user exists, but probably a passwordless account return nil - default: + case nil: res.Exists = true res.Account = acc return nil } + return err } func (a *UserInternalAPI) SetDisplayName(ctx context.Context, localpart string, serverName spec.ServerName, displayName string) (*authtypes.Profile, bool, error) {