mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-10 23:53:09 -06:00
Merge branch 'matrix-org:main' into brianathere/test_race_https_setup
This commit is contained in:
commit
065a02b31b
23
.github/workflows/dendrite.yml
vendored
23
.github/workflows/dendrite.yml
vendored
|
|
@ -7,6 +7,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|
@ -19,10 +20,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ false }} # disable for now
|
if: ${{ false }} # disable for now
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.18
|
go-version: 1.18
|
||||||
|
|
||||||
|
|
@ -66,8 +67,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.18
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
|
||||||
# run go test with different go versions
|
# run go test with different go versions
|
||||||
test:
|
test:
|
||||||
|
|
@ -101,7 +106,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
|
|
@ -133,7 +138,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
- name: Install dependencies x86
|
- name: Install dependencies x86
|
||||||
|
|
@ -167,7 +172,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Go ${{ matrix.go }}
|
- name: Setup Go ${{ matrix.go }}
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|
@ -208,7 +213,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: "1.18"
|
go-version: "1.18"
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
|
|
@ -233,7 +238,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: "1.18"
|
go-version: "1.18"
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
|
|
@ -371,6 +376,8 @@ jobs:
|
||||||
# Build initial Dendrite image
|
# Build initial Dendrite image
|
||||||
- run: docker build -t complement-dendrite -f build/scripts/Complement${{ matrix.postgres }}.Dockerfile .
|
- run: docker build -t complement-dendrite -f build/scripts/Complement${{ matrix.postgres }}.Dockerfile .
|
||||||
working-directory: dendrite
|
working-directory: dendrite
|
||||||
|
env:
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
|
||||||
# Run Complement
|
# Run Complement
|
||||||
- run: |
|
- run: |
|
||||||
|
|
|
||||||
60
CHANGES.md
60
CHANGES.md
|
|
@ -1,5 +1,65 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Dendrite 0.9.5 (2022-08-25)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* The roomserver will now correctly unreject previously rejected events if necessary when reprocessing
|
||||||
|
* The handling of event soft-failure has been improved on the roomserver input by no longer applying rejection rules and still calculating state before the event if possible
|
||||||
|
* The federation `/state` and `/state_ids` endpoints should now return the correct error code when the state isn't known instead of returning a HTTP 500
|
||||||
|
* The federation `/event` should now return outlier events correctly instead of returning a HTTP 500
|
||||||
|
* A bug in the federation backoff allowing zero intervals has been corrected
|
||||||
|
* The `create-account` utility will no longer error if the homeserver URL ends in a trailing slash
|
||||||
|
* A regression in `/sync` introduced in 0.9.4 should be fixed
|
||||||
|
|
||||||
|
## Dendrite 0.9.4 (2022-08-19)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* A bug in the roomserver around handling rejected outliers has been fixed
|
||||||
|
* Backfilled events will now use the correct history visibility where possible
|
||||||
|
* The device list updater backoff has been fixed, which should reduce the number of outbound HTTP requests and `Failed to query device keys for some users` log entries for dead servers
|
||||||
|
* The `/sync` endpoint will no longer incorrectly return room entries for retired invites which could cause some rooms to show up in the client "Historical" section
|
||||||
|
* The `/createRoom` endpoint will now correctly populate `is_direct` in invite membership events, which may help clients to classify direct messages correctly
|
||||||
|
* The `create-account` tool will now log an error if the shared secret is not set in the Dendrite config
|
||||||
|
* A couple of minor bugs have been fixed in the membership lazy-loading
|
||||||
|
* Queued EDUs in the federation API are now cached properly
|
||||||
|
|
||||||
|
## Dendrite 0.9.3 (2022-08-15)
|
||||||
|
|
||||||
|
### Important
|
||||||
|
|
||||||
|
* This is a **security release** to fix a vulnerability within event auth, affecting all versions of Dendrite before 0.9.3. Upgrading to this version is highly recommended. For more information, [see here](https://github.com/matrix-org/gomatrixserverlib/security/advisories/GHSA-grvv-h2f9-7v9c).
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Dendrite will now correctly parse the `"events_default"` power level value for event auth.
|
||||||
|
|
||||||
|
## Dendrite 0.9.2 (2022-08-12)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Dendrite now supports history visibility on the `/sync`, `/messages` and `/context` endpoints
|
||||||
|
* It should now be possible to view the history of a room in more cases (as opposed to limiting scrollback to the join event or defaulting to the restrictive `"join"` visibility rule as before)
|
||||||
|
* The default room version for newly created rooms is now room version 9
|
||||||
|
* New admin endpoint `/_dendrite/admin/resetPassword/{userID}` has been added, which replaces the `-reset-password` flag in `create-account`
|
||||||
|
* The `create-account` binary now uses shared secret registration over HTTP to create new accounts, which fixes a number of problems with account data and push rules not being configured correctly for new accounts
|
||||||
|
* The internal HTTP APIs for polylith deployments have been refactored for correctness and consistency
|
||||||
|
* The federation API will now automatically clean up some EDUs that have failed to send within a certain period of time
|
||||||
|
* The `/hierarchy` endpoint will now return potentially joinable rooms (contributed by [texuf](https://github.com/texuf))
|
||||||
|
* The user directory will now show or hide users correctly
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Send-to-device messages should no longer be incorrectly duplicated in `/sync`
|
||||||
|
* The federation sender will no longer create unnecessary destination queues as a result of a logic error
|
||||||
|
* A bug where database migrations may not execute properly when upgrading from older versions has been fixed
|
||||||
|
* A crash when failing to update user account data has been fixed
|
||||||
|
* A race condition when generating notification counts has been fixed
|
||||||
|
* A race condition when setting up NATS has been fixed (contributed by [brianathere](https://github.com/brianathere))
|
||||||
|
* Stale cache data for membership lazy-loading is now correctly invalidated when doing a complete sync
|
||||||
|
* Data races within user-interactive authentication have been fixed (contributed by [tak-hntlabs](https://github.com/tak-hntlabs))
|
||||||
|
|
||||||
## Dendrite 0.9.1 (2022-08-03)
|
## Dendrite 0.9.1 (2022-08-03)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
|
||||||
63
README.md
63
README.md
|
|
@ -21,8 +21,7 @@ As of October 2020 (current [progress below](#progress)), Dendrite has now enter
|
||||||
This does not mean:
|
This does not mean:
|
||||||
|
|
||||||
- Dendrite is bug-free. It has not yet been battle-tested in the real world and so will be error prone initially.
|
- Dendrite is bug-free. It has not yet been battle-tested in the real world and so will be error prone initially.
|
||||||
- All of the CS/Federation APIs are implemented. We are tracking progress via a script called 'Are We Synapse Yet?'. In particular,
|
- Dendrite is feature-complete. There may be client or federation APIs that are not implemented.
|
||||||
presence and push notifications are entirely missing from Dendrite. See [CHANGES.md](CHANGES.md) for updates.
|
|
||||||
- Dendrite is ready for massive homeserver deployments. You cannot shard each microservice, only run each one on a different machine.
|
- Dendrite is ready for massive homeserver deployments. You cannot shard each microservice, only run each one on a different machine.
|
||||||
|
|
||||||
Currently, we expect Dendrite to function well for small (10s/100s of users) homeserver deployments as well as P2P Matrix nodes in-browser or on mobile devices.
|
Currently, we expect Dendrite to function well for small (10s/100s of users) homeserver deployments as well as P2P Matrix nodes in-browser or on mobile devices.
|
||||||
|
|
@ -36,6 +35,9 @@ If you have further questions, please take a look at [our FAQ](docs/FAQ.md) or j
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
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.18 or later.
|
To build Dendrite, you will need Go 1.18 or later.
|
||||||
|
|
||||||
For a usable federating Dendrite deployment, you will also need:
|
For a usable federating Dendrite deployment, you will also need:
|
||||||
|
|
@ -78,21 +80,21 @@ $ ./bin/dendrite-monolith-server --tls-cert server.crt --tls-key server.key --co
|
||||||
|
|
||||||
# Create an user account (add -admin for an admin user).
|
# Create an user account (add -admin for an admin user).
|
||||||
# Specify the localpart only, e.g. 'alice' for '@alice:domain.com'
|
# Specify the localpart only, e.g. 'alice' for '@alice:domain.com'
|
||||||
$ ./bin/create-account --config dendrite.yaml -username alice
|
$ ./bin/create-account --config dendrite.yaml --url http://localhost:8008 --username alice
|
||||||
```
|
```
|
||||||
|
|
||||||
Then point your favourite Matrix client at `http://localhost:8008` or `https://localhost:8448`.
|
Then point your favourite Matrix client at `http://localhost:8008` or `https://localhost:8448`.
|
||||||
|
|
||||||
## <a id="progress"></a> Progress
|
## Progress
|
||||||
|
|
||||||
We use a script called Are We Synapse Yet which checks Sytest compliance rates. Sytest is a black-box homeserver
|
We use a script called Are We Synapse Yet which checks Sytest compliance rates. Sytest is a black-box homeserver
|
||||||
test rig with around 900 tests. The script works out how many of these tests are passing on Dendrite and it
|
test rig with around 900 tests. The script works out how many of these tests are passing on Dendrite and it
|
||||||
updates with CI. As of April 2022 we're at around 83% CS API coverage and 95% Federation coverage, though check
|
updates with CI. As of August 2022 we're at around 90% CS API coverage and 95% Federation coverage, though check
|
||||||
CI for the latest numbers. In practice, this means you can communicate locally and via federation with Synapse
|
CI for the latest numbers. In practice, this means you can communicate locally and via federation with Synapse
|
||||||
servers such as matrix.org reasonably well, although there are still some missing features (like Search).
|
servers such as matrix.org reasonably well, although there are still some missing features (like Search).
|
||||||
|
|
||||||
We are prioritising features that will benefit single-user homeservers first (e.g Receipts, E2E) rather
|
We are prioritising features that will benefit single-user homeservers first (e.g Receipts, E2E) rather
|
||||||
than features that massive deployments may be interested in (User Directory, OpenID, Guests, Admin APIs, AS API).
|
than features that massive deployments may be interested in (OpenID, Guests, Admin APIs, AS API).
|
||||||
This means Dendrite supports amongst others:
|
This means Dendrite supports amongst others:
|
||||||
|
|
||||||
- Core room functionality (creating rooms, invites, auth rules)
|
- Core room functionality (creating rooms, invites, auth rules)
|
||||||
|
|
@ -119,53 +121,8 @@ We would be grateful for any help on issues marked as
|
||||||
all have related Sytests which need to pass in order for the issue to be closed. Once you've written your
|
all have related Sytests which need to pass in order for the issue to be closed. Once you've written your
|
||||||
code, you can quickly run Sytest to ensure that the test names are now passing.
|
code, you can quickly run Sytest to ensure that the test names are now passing.
|
||||||
|
|
||||||
For example, if the test `Local device key changes get to remote servers` was marked as failing, find the
|
If you're new to the project, see our
|
||||||
test file (e.g via `grep` or via the
|
[Contributing page](https://matrix-org.github.io/dendrite/development/contributing) to get up to speed, then
|
||||||
[CI log output](https://buildkite.com/matrix-dot-org/dendrite/builds/2826#39cff5de-e032-4ad0-ad26-f819e6919c42)
|
|
||||||
it's `tests/50federation/40devicelists.pl` ) then to run Sytest:
|
|
||||||
|
|
||||||
```
|
|
||||||
docker run --rm --name sytest
|
|
||||||
-v "/Users/kegan/github/sytest:/sytest"
|
|
||||||
-v "/Users/kegan/github/dendrite:/src"
|
|
||||||
-v "/Users/kegan/logs:/logs"
|
|
||||||
-v "/Users/kegan/go/:/gopath"
|
|
||||||
-e "POSTGRES=1" -e "DENDRITE_TRACE_HTTP=1"
|
|
||||||
matrixdotorg/sytest-dendrite:latest tests/50federation/40devicelists.pl
|
|
||||||
```
|
|
||||||
|
|
||||||
See [sytest.md](docs/sytest.md) for the full description of these flags.
|
|
||||||
|
|
||||||
You can try running sytest outside of docker for faster runs, but the dependencies can be temperamental
|
|
||||||
and we recommend using docker where possible.
|
|
||||||
|
|
||||||
```
|
|
||||||
cd sytest
|
|
||||||
export PERL5LIB=$HOME/lib/perl5
|
|
||||||
export PERL_MB_OPT=--install_base=$HOME
|
|
||||||
export PERL_MM_OPT=INSTALL_BASE=$HOME
|
|
||||||
./install-deps.pl
|
|
||||||
|
|
||||||
./run-tests.pl -I Dendrite::Monolith -d $PATH_TO_DENDRITE_BINARIES
|
|
||||||
```
|
|
||||||
|
|
||||||
Sometimes Sytest is testing the wrong thing or is flakey, so it will need to be patched.
|
|
||||||
Ask on `#dendrite-dev:matrix.org` if you think this is the case for you and we'll be happy to help.
|
|
||||||
|
|
||||||
If you're new to the project, see [CONTRIBUTING.md](docs/CONTRIBUTING.md) to get up to speed then
|
|
||||||
look for [Good First Issues](https://github.com/matrix-org/dendrite/labels/good%20first%20issue). If you're
|
look for [Good First Issues](https://github.com/matrix-org/dendrite/labels/good%20first%20issue). If you're
|
||||||
familiar with the project, look for [Help Wanted](https://github.com/matrix-org/dendrite/labels/help-wanted)
|
familiar with the project, look for [Help Wanted](https://github.com/matrix-org/dendrite/labels/help-wanted)
|
||||||
issues.
|
issues.
|
||||||
|
|
||||||
## Hardware requirements
|
|
||||||
|
|
||||||
Dendrite in Monolith + SQLite works in a range of environments including iOS and in-browser via WASM.
|
|
||||||
|
|
||||||
For small homeserver installations joined on ~10s rooms on matrix.org with ~100s of users in those rooms, including some
|
|
||||||
encrypted rooms:
|
|
||||||
|
|
||||||
- Memory: uses around 100MB of RAM, with peaks at around 200MB.
|
|
||||||
- Disk space: After a few months of usage, the database grew to around 2GB (in Monolith mode).
|
|
||||||
- CPU: Brief spikes when processing events, typically idles at 1% CPU.
|
|
||||||
|
|
||||||
This means Dendrite should comfortably work on things like Raspberry Pis.
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/appservice/api"
|
"github.com/matrix-org/dendrite/appservice/api"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/opentracing/opentracing-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP paths for the internal HTTP APIs
|
// HTTP paths for the internal HTTP APIs
|
||||||
|
|
@ -42,11 +41,10 @@ func (h *httpAppServiceQueryAPI) RoomAliasExists(
|
||||||
request *api.RoomAliasExistsRequest,
|
request *api.RoomAliasExistsRequest,
|
||||||
response *api.RoomAliasExistsResponse,
|
response *api.RoomAliasExistsResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "appserviceRoomAliasExists")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"RoomAliasExists", h.appserviceURL+AppServiceRoomAliasExistsPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.appserviceURL + AppServiceRoomAliasExistsPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserIDExists implements AppServiceQueryAPI
|
// UserIDExists implements AppServiceQueryAPI
|
||||||
|
|
@ -55,9 +53,8 @@ func (h *httpAppServiceQueryAPI) UserIDExists(
|
||||||
request *api.UserIDExistsRequest,
|
request *api.UserIDExistsRequest,
|
||||||
response *api.UserIDExistsResponse,
|
response *api.UserIDExistsResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "appserviceUserIDExists")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"UserIDExists", h.appserviceURL+AppServiceUserIDExistsPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.appserviceURL + AppServiceUserIDExistsPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,20 @@
|
||||||
package inthttp
|
package inthttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/appservice/api"
|
"github.com/matrix-org/dendrite/appservice/api"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddRoutes adds the AppServiceQueryAPI handlers to the http.ServeMux.
|
// AddRoutes adds the AppServiceQueryAPI handlers to the http.ServeMux.
|
||||||
func AddRoutes(a api.AppServiceInternalAPI, internalAPIMux *mux.Router) {
|
func AddRoutes(a api.AppServiceInternalAPI, internalAPIMux *mux.Router) {
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
AppServiceRoomAliasExistsPath,
|
AppServiceRoomAliasExistsPath,
|
||||||
httputil.MakeInternalAPI("appserviceRoomAliasExists", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalRPCAPI("AppserviceRoomAliasExists", a.RoomAliasExists),
|
||||||
var request api.RoomAliasExistsRequest
|
|
||||||
var response api.RoomAliasExistsResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
if err := a.RoomAliasExists(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
AppServiceUserIDExistsPath,
|
AppServiceUserIDExistsPath,
|
||||||
httputil.MakeInternalAPI("appserviceUserIDExists", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalRPCAPI("AppserviceUserIDExists", a.UserIDExists),
|
||||||
var request api.UserIDExistsRequest
|
|
||||||
var response api.UserIDExistsResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
if err := a.UserIDExists(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#syntax=docker/dockerfile:1.2
|
||||||
|
|
||||||
FROM golang:1.18-stretch as build
|
FROM golang:1.18-stretch as build
|
||||||
RUN apt-get update && apt-get install -y sqlite3
|
RUN apt-get update && apt-get install -y sqlite3
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
@ -8,14 +10,12 @@ RUN mkdir /dendrite
|
||||||
|
|
||||||
# Utilise Docker caching when downloading dependencies, this stops us needlessly
|
# Utilise Docker caching when downloading dependencies, this stops us needlessly
|
||||||
# downloading dependencies every time.
|
# downloading dependencies every time.
|
||||||
COPY go.mod .
|
RUN --mount=target=. \
|
||||||
COPY go.sum .
|
--mount=type=cache,target=/go/pkg/mod \
|
||||||
RUN go mod download
|
--mount=type=cache,target=/root/.cache/go-build \
|
||||||
|
go build -o /dendrite ./cmd/generate-config && \
|
||||||
COPY . .
|
go build -o /dendrite ./cmd/generate-keys && \
|
||||||
RUN go build -o /dendrite ./cmd/dendrite-monolith-server
|
go build -o /dendrite ./cmd/dendrite-monolith-server
|
||||||
RUN go build -o /dendrite ./cmd/generate-keys
|
|
||||||
RUN go build -o /dendrite ./cmd/generate-config
|
|
||||||
|
|
||||||
WORKDIR /dendrite
|
WORKDIR /dendrite
|
||||||
RUN ./generate-keys --private-key matrix_key.pem
|
RUN ./generate-keys --private-key matrix_key.pem
|
||||||
|
|
@ -26,7 +26,7 @@ EXPOSE 8008 8448
|
||||||
|
|
||||||
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
||||||
# At runtime, replace the SERVER_NAME with what we are told
|
# At runtime, replace the SERVER_NAME with what we are told
|
||||||
CMD ./generate-keys --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 && \
|
CMD ./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 > dendrite.yaml && \
|
./generate-config -server $SERVER_NAME --ci > dendrite.yaml && \
|
||||||
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
||||||
./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml -api=${API:-0}
|
exec ./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml -api=${API:-0}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#syntax=docker/dockerfile:1.2
|
||||||
|
|
||||||
# A local development Complement dockerfile, to be used with host mounts
|
# A local development Complement dockerfile, to be used with host mounts
|
||||||
# /cache -> Contains the entire dendrite code at Dockerfile build time. Builds binaries but only keeps the generate-* ones. Pre-compilation saves time.
|
# /cache -> Contains the entire dendrite code at Dockerfile build time. Builds binaries but only keeps the generate-* ones. Pre-compilation saves time.
|
||||||
# /dendrite -> Host-mounted sources
|
# /dendrite -> Host-mounted sources
|
||||||
|
|
@ -9,11 +11,10 @@
|
||||||
FROM golang:1.18-stretch
|
FROM golang:1.18-stretch
|
||||||
RUN apt-get update && apt-get install -y sqlite3
|
RUN apt-get update && apt-get install -y sqlite3
|
||||||
|
|
||||||
WORKDIR /runtime
|
|
||||||
|
|
||||||
ENV SERVER_NAME=localhost
|
ENV SERVER_NAME=localhost
|
||||||
EXPOSE 8008 8448
|
EXPOSE 8008 8448
|
||||||
|
|
||||||
|
WORKDIR /runtime
|
||||||
# This script compiles Dendrite for us.
|
# This script compiles Dendrite for us.
|
||||||
RUN echo '\
|
RUN echo '\
|
||||||
#!/bin/bash -eux \n\
|
#!/bin/bash -eux \n\
|
||||||
|
|
@ -29,25 +30,23 @@ RUN echo '\
|
||||||
RUN echo '\
|
RUN echo '\
|
||||||
#!/bin/bash -eu \n\
|
#!/bin/bash -eu \n\
|
||||||
./generate-keys --private-key matrix_key.pem \n\
|
./generate-keys --private-key matrix_key.pem \n\
|
||||||
./generate-keys --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 \n\
|
./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 \n\
|
||||||
./generate-config -server $SERVER_NAME --ci > dendrite.yaml \n\
|
./generate-config -server $SERVER_NAME --ci > dendrite.yaml \n\
|
||||||
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates \n\
|
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates \n\
|
||||||
./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml \n\
|
exec ./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml \n\
|
||||||
' > run.sh && chmod +x run.sh
|
' > run.sh && chmod +x run.sh
|
||||||
|
|
||||||
|
|
||||||
WORKDIR /cache
|
WORKDIR /cache
|
||||||
# Pre-download deps; we don't need to do this if the GOPATH is mounted.
|
|
||||||
COPY go.mod .
|
|
||||||
COPY go.sum .
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
# Build the monolith in /cache - we won't actually use this but will rely on build artifacts to speed
|
# Build the monolith in /cache - we won't actually use this but will rely on build artifacts to speed
|
||||||
# up the real compilation. Build the generate-* binaries in the true /runtime locations.
|
# up the real compilation. Build the generate-* binaries in the true /runtime locations.
|
||||||
# If the generate-* source is changed, this dockerfile needs re-running.
|
# If the generate-* source is changed, this dockerfile needs re-running.
|
||||||
COPY . .
|
RUN --mount=target=. \
|
||||||
RUN go build ./cmd/dendrite-monolith-server && go build -o /runtime ./cmd/generate-keys && go build -o /runtime ./cmd/generate-config
|
--mount=type=cache,target=/go/pkg/mod \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build \
|
||||||
|
go build -o /runtime ./cmd/generate-config && \
|
||||||
|
go build -o /runtime ./cmd/generate-keys
|
||||||
|
|
||||||
|
|
||||||
WORKDIR /runtime
|
WORKDIR /runtime
|
||||||
CMD /runtime/compile.sh && /runtime/run.sh
|
CMD /runtime/compile.sh && exec /runtime/run.sh
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#syntax=docker/dockerfile:1.2
|
||||||
|
|
||||||
FROM golang:1.18-stretch as build
|
FROM golang:1.18-stretch as build
|
||||||
RUN apt-get update && apt-get install -y postgresql
|
RUN apt-get update && apt-get install -y postgresql
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
@ -26,14 +28,12 @@ RUN mkdir /dendrite
|
||||||
|
|
||||||
# Utilise Docker caching when downloading dependencies, this stops us needlessly
|
# Utilise Docker caching when downloading dependencies, this stops us needlessly
|
||||||
# downloading dependencies every time.
|
# downloading dependencies every time.
|
||||||
COPY go.mod .
|
RUN --mount=target=. \
|
||||||
COPY go.sum .
|
--mount=type=cache,target=/go/pkg/mod \
|
||||||
RUN go mod download
|
--mount=type=cache,target=/root/.cache/go-build \
|
||||||
|
go build -o /dendrite ./cmd/generate-config && \
|
||||||
COPY . .
|
go build -o /dendrite ./cmd/generate-keys && \
|
||||||
RUN go build -o /dendrite ./cmd/dendrite-monolith-server
|
go build -o /dendrite ./cmd/dendrite-monolith-server
|
||||||
RUN go build -o /dendrite ./cmd/generate-keys
|
|
||||||
RUN go build -o /dendrite ./cmd/generate-config
|
|
||||||
|
|
||||||
WORKDIR /dendrite
|
WORKDIR /dendrite
|
||||||
RUN ./generate-keys --private-key matrix_key.pem
|
RUN ./generate-keys --private-key matrix_key.pem
|
||||||
|
|
@ -45,10 +45,10 @@ EXPOSE 8008 8448
|
||||||
|
|
||||||
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
||||||
# At runtime, replace the SERVER_NAME with what we are told
|
# At runtime, replace the SERVER_NAME with what we are told
|
||||||
CMD /build/run_postgres.sh && ./generate-keys --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 && \
|
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 > dendrite.yaml && \
|
./generate-config -server $SERVER_NAME --ci > dendrite.yaml && \
|
||||||
# Replace the connection string with a single postgres DB, using user/db = 'postgres' and no password, bump max_conns
|
# Replace the connection string with a single postgres DB, using user/db = 'postgres' and no password, bump max_conns
|
||||||
sed -i "s%connection_string:.*$%connection_string: postgresql://postgres@localhost/postgres?sslmode=disable%g" dendrite.yaml && \
|
sed -i "s%connection_string:.*$%connection_string: postgresql://postgres@localhost/postgres?sslmode=disable%g" dendrite.yaml && \
|
||||||
sed -i 's/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml && \
|
sed -i 's/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml && \
|
||||||
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
||||||
./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml -api=${API:-0}
|
exec ./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml -api=${API:-0}
|
||||||
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
|
@ -34,7 +33,7 @@ import (
|
||||||
// If the final return value is non-nil, an error occurred and the cleanup function
|
// If the final return value is non-nil, an error occurred and the cleanup function
|
||||||
// is nil.
|
// is nil.
|
||||||
func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.UserLoginAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.UserLoginAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||||
reqBytes, err := ioutil.ReadAll(r)
|
reqBytes, err := io.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := &util.JSONResponse{
|
err := &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
|
@ -102,6 +103,7 @@ type userInteractiveFlow struct {
|
||||||
// the user already has a valid access token, but we want to double-check
|
// the user already has a valid access token, but we want to double-check
|
||||||
// that it isn't stolen by re-authenticating them.
|
// that it isn't stolen by re-authenticating them.
|
||||||
type UserInteractive struct {
|
type UserInteractive struct {
|
||||||
|
sync.RWMutex
|
||||||
Flows []userInteractiveFlow
|
Flows []userInteractiveFlow
|
||||||
// Map of login type to implementation
|
// Map of login type to implementation
|
||||||
Types map[string]Type
|
Types map[string]Type
|
||||||
|
|
@ -128,6 +130,8 @@ func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInteractive) IsSingleStageFlow(authType string) bool {
|
func (u *UserInteractive) IsSingleStageFlow(authType string) bool {
|
||||||
|
u.RLock()
|
||||||
|
defer u.RUnlock()
|
||||||
for _, f := range u.Flows {
|
for _, f := range u.Flows {
|
||||||
if len(f.Stages) == 1 && f.Stages[0] == authType {
|
if len(f.Stages) == 1 && f.Stages[0] == authType {
|
||||||
return true
|
return true
|
||||||
|
|
@ -137,8 +141,10 @@ func (u *UserInteractive) IsSingleStageFlow(authType string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInteractive) AddCompletedStage(sessionID, authType string) {
|
func (u *UserInteractive) AddCompletedStage(sessionID, authType string) {
|
||||||
|
u.Lock()
|
||||||
// TODO: Handle multi-stage flows
|
// TODO: Handle multi-stage flows
|
||||||
delete(u.Sessions, sessionID)
|
delete(u.Sessions, sessionID)
|
||||||
|
u.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
type Challenge struct {
|
type Challenge struct {
|
||||||
|
|
@ -150,12 +156,17 @@ type Challenge struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Challenge returns an HTTP 401 with the supported flows for authenticating
|
// Challenge returns an HTTP 401 with the supported flows for authenticating
|
||||||
func (u *UserInteractive) Challenge(sessionID string) *util.JSONResponse {
|
func (u *UserInteractive) challenge(sessionID string) *util.JSONResponse {
|
||||||
|
u.RLock()
|
||||||
|
completed := u.Sessions[sessionID]
|
||||||
|
flows := u.Flows
|
||||||
|
u.RUnlock()
|
||||||
|
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: 401,
|
Code: 401,
|
||||||
JSON: Challenge{
|
JSON: Challenge{
|
||||||
Completed: u.Sessions[sessionID],
|
Completed: completed,
|
||||||
Flows: u.Flows,
|
Flows: flows,
|
||||||
Session: sessionID,
|
Session: sessionID,
|
||||||
Params: make(map[string]interface{}),
|
Params: make(map[string]interface{}),
|
||||||
},
|
},
|
||||||
|
|
@ -170,8 +181,10 @@ func (u *UserInteractive) NewSession() *util.JSONResponse {
|
||||||
res := jsonerror.InternalServerError()
|
res := jsonerror.InternalServerError()
|
||||||
return &res
|
return &res
|
||||||
}
|
}
|
||||||
|
u.Lock()
|
||||||
u.Sessions[sessionID] = []string{}
|
u.Sessions[sessionID] = []string{}
|
||||||
return u.Challenge(sessionID)
|
u.Unlock()
|
||||||
|
return u.challenge(sessionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResponseWithChallenge mixes together a JSON body (e.g an error with errcode/message) with the
|
// ResponseWithChallenge mixes together a JSON body (e.g an error with errcode/message) with the
|
||||||
|
|
@ -184,7 +197,7 @@ func (u *UserInteractive) ResponseWithChallenge(sessionID string, response inter
|
||||||
return &ise
|
return &ise
|
||||||
}
|
}
|
||||||
_ = json.Unmarshal(b, &mixedObjects)
|
_ = json.Unmarshal(b, &mixedObjects)
|
||||||
challenge := u.Challenge(sessionID)
|
challenge := u.challenge(sessionID)
|
||||||
b, err = json.Marshal(challenge.JSON)
|
b, err = json.Marshal(challenge.JSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ise := jsonerror.InternalServerError()
|
ise := jsonerror.InternalServerError()
|
||||||
|
|
@ -213,7 +226,11 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
|
||||||
|
|
||||||
// extract the type so we know which login type to use
|
// extract the type so we know which login type to use
|
||||||
authType := gjson.GetBytes(bodyBytes, "auth.type").Str
|
authType := gjson.GetBytes(bodyBytes, "auth.type").Str
|
||||||
|
|
||||||
|
u.RLock()
|
||||||
loginType, ok := u.Types[authType]
|
loginType, ok := u.Types[authType]
|
||||||
|
u.RUnlock()
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, &util.JSONResponse{
|
return nil, &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
|
@ -223,7 +240,12 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
|
||||||
|
|
||||||
// retrieve the session
|
// retrieve the session
|
||||||
sessionID := gjson.GetBytes(bodyBytes, "auth.session").Str
|
sessionID := gjson.GetBytes(bodyBytes, "auth.session").Str
|
||||||
if _, ok = u.Sessions[sessionID]; !ok {
|
|
||||||
|
u.RLock()
|
||||||
|
_, ok = u.Sessions[sessionID]
|
||||||
|
u.RUnlock()
|
||||||
|
|
||||||
|
if !ok {
|
||||||
// if the login type is part of a single stage flow then allow them to omit the session ID
|
// if the login type is part of a single stage flow then allow them to omit the session ID
|
||||||
if !u.IsSingleStageFlow(authType) {
|
if !u.IsSingleStageFlow(authType) {
|
||||||
return nil, &util.JSONResponse{
|
return nil, &util.JSONResponse{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ package httputil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
|
@ -29,9 +29,9 @@ import (
|
||||||
func UnmarshalJSONRequest(req *http.Request, iface interface{}) *util.JSONResponse {
|
func UnmarshalJSONRequest(req *http.Request, iface interface{}) *util.JSONResponse {
|
||||||
// encoding/json allows invalid utf-8, matrix does not
|
// encoding/json allows invalid utf-8, matrix does not
|
||||||
// https://matrix.org/docs/spec/client_server/r0.6.1#api-standards
|
// https://matrix.org/docs/spec/client_server/r0.6.1#api-standards
|
||||||
body, err := ioutil.ReadAll(req.Body)
|
body, err := io.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("ioutil.ReadAll failed")
|
util.GetLogger(req.Context()).WithError(err).Error("io.ReadAll failed")
|
||||||
resp := jsonerror.InternalServerError()
|
resp := jsonerror.InternalServerError()
|
||||||
return &resp
|
return &resp
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,13 @@
|
||||||
package jsonerror
|
package jsonerror
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MatrixError represents the "standard error response" in Matrix.
|
// MatrixError represents the "standard error response" in Matrix.
|
||||||
|
|
@ -213,3 +215,15 @@ func NotTrusted(serverName string) *MatrixError {
|
||||||
Err: fmt.Sprintf("Untrusted server '%s'", serverName),
|
Err: fmt.Sprintf("Untrusted server '%s'", serverName),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InternalAPIError is returned when Dendrite failed to reach an internal API.
|
||||||
|
func InternalAPIError(ctx context.Context, err error) util.JSONResponse {
|
||||||
|
logrus.WithContext(ctx).WithError(err).Error("Error reaching an internal API")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: &MatrixError{
|
||||||
|
ErrCode: "M_INTERNAL_SERVER_ERROR",
|
||||||
|
Err: "Dendrite encountered an error reaching an internal API.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ package routing
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
|
@ -101,9 +101,9 @@ func SaveAccountData(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(req.Body)
|
body, err := io.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("ioutil.ReadAll failed")
|
util.GetLogger(req.Context()).WithError(err).Error("io.ReadAll failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,20 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AdminEvacuateRoom(req *http.Request, device *userapi.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
|
func AdminEvacuateRoom(req *http.Request, cfg *config.ClientAPI, device *userapi.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
|
||||||
if device.AccountType != userapi.AccountTypeAdmin {
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
JSON: jsonerror.Forbidden("This API can only be used by admin users."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
|
|
@ -30,13 +27,15 @@ func AdminEvacuateRoom(req *http.Request, device *userapi.Device, rsAPI roomserv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res := &roomserverAPI.PerformAdminEvacuateRoomResponse{}
|
res := &roomserverAPI.PerformAdminEvacuateRoomResponse{}
|
||||||
rsAPI.PerformAdminEvacuateRoom(
|
if err := rsAPI.PerformAdminEvacuateRoom(
|
||||||
req.Context(),
|
req.Context(),
|
||||||
&roomserverAPI.PerformAdminEvacuateRoomRequest{
|
&roomserverAPI.PerformAdminEvacuateRoomRequest{
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
},
|
},
|
||||||
res,
|
res,
|
||||||
)
|
); err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
if err := res.Error; err != nil {
|
if err := res.Error; err != nil {
|
||||||
return err.JSONResponse()
|
return err.JSONResponse()
|
||||||
}
|
}
|
||||||
|
|
@ -48,13 +47,7 @@ func AdminEvacuateRoom(req *http.Request, device *userapi.Device, rsAPI roomserv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func AdminEvacuateUser(req *http.Request, device *userapi.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
|
func AdminEvacuateUser(req *http.Request, cfg *config.ClientAPI, device *userapi.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
|
||||||
if device.AccountType != userapi.AccountTypeAdmin {
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
JSON: jsonerror.Forbidden("This API can only be used by admin users."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
|
|
@ -66,14 +59,26 @@ func AdminEvacuateUser(req *http.Request, device *userapi.Device, rsAPI roomserv
|
||||||
JSON: jsonerror.MissingArgument("Expecting user ID."),
|
JSON: jsonerror.MissingArgument("Expecting user ID."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_, domain, err := gomatrixserverlib.SplitID('@', userID)
|
||||||
|
if err != nil {
|
||||||
|
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
if domain != cfg.Matrix.ServerName {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.MissingArgument("User ID must belong to this server."),
|
||||||
|
}
|
||||||
|
}
|
||||||
res := &roomserverAPI.PerformAdminEvacuateUserResponse{}
|
res := &roomserverAPI.PerformAdminEvacuateUserResponse{}
|
||||||
rsAPI.PerformAdminEvacuateUser(
|
if err := rsAPI.PerformAdminEvacuateUser(
|
||||||
req.Context(),
|
req.Context(),
|
||||||
&roomserverAPI.PerformAdminEvacuateUserRequest{
|
&roomserverAPI.PerformAdminEvacuateUserRequest{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
},
|
},
|
||||||
res,
|
res,
|
||||||
)
|
); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
if err := res.Error; err != nil {
|
if err := res.Error; err != nil {
|
||||||
return err.JSONResponse()
|
return err.JSONResponse()
|
||||||
}
|
}
|
||||||
|
|
@ -84,3 +89,52 @@ func AdminEvacuateUser(req *http.Request, device *userapi.Device, rsAPI roomserv
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
|
||||||
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
localpart, ok := vars["localpart"]
|
||||||
|
if !ok {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.MissingArgument("Expecting user localpart."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request := struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
}{}
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.Unknown("Failed to decode request body: " + err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if request.Password == "" {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.MissingArgument("Expecting non-empty password."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateReq := &userapi.PerformPasswordUpdateRequest{
|
||||||
|
Localpart: localpart,
|
||||||
|
Password: request.Password,
|
||||||
|
LogoutDevices: true,
|
||||||
|
}
|
||||||
|
updateRes := &userapi.PerformPasswordUpdateResponse{}
|
||||||
|
if err := userAPI.PerformPasswordUpdate(req.Context(), updateReq, updateRes); err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.Unknown("Failed to perform password update: " + err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: struct {
|
||||||
|
Updated bool `json:"password_updated"`
|
||||||
|
}{
|
||||||
|
Updated: updateRes.PasswordUpdated,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ type createRoomRequest struct {
|
||||||
GuestCanJoin bool `json:"guest_can_join"`
|
GuestCanJoin bool `json:"guest_can_join"`
|
||||||
RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
|
RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
|
||||||
PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"`
|
PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"`
|
||||||
|
IsDirect bool `json:"is_direct"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -499,9 +500,17 @@ func createRoom(
|
||||||
// Build some stripped state for the invite.
|
// Build some stripped state for the invite.
|
||||||
var globalStrippedState []gomatrixserverlib.InviteV2StrippedState
|
var globalStrippedState []gomatrixserverlib.InviteV2StrippedState
|
||||||
for _, event := range builtEvents {
|
for _, event := range builtEvents {
|
||||||
|
// Chosen events from the spec:
|
||||||
|
// https://spec.matrix.org/v1.3/client-server-api/#stripped-state
|
||||||
switch event.Type() {
|
switch event.Type() {
|
||||||
|
case gomatrixserverlib.MRoomCreate:
|
||||||
|
fallthrough
|
||||||
case gomatrixserverlib.MRoomName:
|
case gomatrixserverlib.MRoomName:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
case gomatrixserverlib.MRoomAvatar:
|
||||||
|
fallthrough
|
||||||
|
case gomatrixserverlib.MRoomTopic:
|
||||||
|
fallthrough
|
||||||
case gomatrixserverlib.MRoomCanonicalAlias:
|
case gomatrixserverlib.MRoomCanonicalAlias:
|
||||||
fallthrough
|
fallthrough
|
||||||
case gomatrixserverlib.MRoomEncryption:
|
case gomatrixserverlib.MRoomEncryption:
|
||||||
|
|
@ -522,7 +531,7 @@ func createRoom(
|
||||||
// Build the invite event.
|
// Build the invite event.
|
||||||
inviteEvent, err := buildMembershipEvent(
|
inviteEvent, err := buildMembershipEvent(
|
||||||
ctx, invitee, "", profileAPI, device, gomatrixserverlib.Invite,
|
ctx, invitee, "", profileAPI, device, gomatrixserverlib.Invite,
|
||||||
roomID, true, cfg, evTime, rsAPI, asAPI,
|
roomID, r.IsDirect, cfg, evTime, rsAPI, asAPI,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed")
|
util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed")
|
||||||
|
|
@ -556,10 +565,12 @@ func createRoom(
|
||||||
if r.Visibility == "public" {
|
if r.Visibility == "public" {
|
||||||
// expose this room in the published room list
|
// expose this room in the published room list
|
||||||
var pubRes roomserverAPI.PerformPublishResponse
|
var pubRes roomserverAPI.PerformPublishResponse
|
||||||
rsAPI.PerformPublish(ctx, &roomserverAPI.PerformPublishRequest{
|
if err := rsAPI.PerformPublish(ctx, &roomserverAPI.PerformPublishRequest{
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
Visibility: "public",
|
Visibility: "public",
|
||||||
}, &pubRes)
|
}, &pubRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(ctx, err)
|
||||||
|
}
|
||||||
if pubRes.Error != nil {
|
if pubRes.Error != nil {
|
||||||
// treat as non-fatal since the room is already made by this point
|
// treat as non-fatal since the room is already made by this point
|
||||||
util.GetLogger(ctx).WithError(pubRes.Error).Error("failed to visibility:public")
|
util.GetLogger(ctx).WithError(pubRes.Error).Error("failed to visibility:public")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||||
|
|
@ -20,7 +20,7 @@ func Deactivate(
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
ctx := req.Context()
|
ctx := req.Context()
|
||||||
defer req.Body.Close() // nolint:errcheck
|
defer req.Body.Close() // nolint:errcheck
|
||||||
bodyBytes, err := ioutil.ReadAll(req.Body)
|
bodyBytes, err := io.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
|
@ -175,7 +175,7 @@ func DeleteDeviceById(
|
||||||
}()
|
}()
|
||||||
ctx := req.Context()
|
ctx := req.Context()
|
||||||
defer req.Body.Close() // nolint:errcheck
|
defer req.Body.Close() // nolint:errcheck
|
||||||
bodyBytes, err := ioutil.ReadAll(req.Body)
|
bodyBytes, err := io.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
|
|
||||||
|
|
@ -302,10 +302,12 @@ func SetVisibility(
|
||||||
}
|
}
|
||||||
|
|
||||||
var publishRes roomserverAPI.PerformPublishResponse
|
var publishRes roomserverAPI.PerformPublishResponse
|
||||||
rsAPI.PerformPublish(req.Context(), &roomserverAPI.PerformPublishRequest{
|
if err := rsAPI.PerformPublish(req.Context(), &roomserverAPI.PerformPublishRequest{
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
Visibility: v.Visibility,
|
Visibility: v.Visibility,
|
||||||
}, &publishRes)
|
}, &publishRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
if publishRes.Error != nil {
|
if publishRes.Error != nil {
|
||||||
util.GetLogger(req.Context()).WithError(publishRes.Error).Error("PerformPublish failed")
|
util.GetLogger(req.Context()).WithError(publishRes.Error).Error("PerformPublish failed")
|
||||||
return publishRes.Error.JSONResponse()
|
return publishRes.Error.JSONResponse()
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,14 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/api"
|
"github.com/matrix-org/dendrite/clientapi/api"
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
"github.com/matrix-org/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,9 @@ func JoinRoomByIDOrAlias(
|
||||||
done := make(chan util.JSONResponse, 1)
|
done := make(chan util.JSONResponse, 1)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(done)
|
defer close(done)
|
||||||
rsAPI.PerformJoin(req.Context(), &joinReq, &joinRes)
|
if err := rsAPI.PerformJoin(req.Context(), &joinReq, &joinRes); err != nil {
|
||||||
if joinRes.Error != nil {
|
done <- jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
} else if joinRes.Error != nil {
|
||||||
done <- joinRes.Error.JSONResponse()
|
done <- joinRes.Error.JSONResponse()
|
||||||
} else {
|
} else {
|
||||||
done <- util.JSONResponse{
|
done <- util.JSONResponse{
|
||||||
|
|
|
||||||
|
|
@ -91,10 +91,12 @@ func CreateKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, de
|
||||||
// Implements GET /_matrix/client/r0/room_keys/version and GET /_matrix/client/r0/room_keys/version/{version}
|
// Implements GET /_matrix/client/r0/room_keys/version and GET /_matrix/client/r0/room_keys/version/{version}
|
||||||
func KeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse {
|
func KeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse {
|
||||||
var queryResp userapi.QueryKeyBackupResponse
|
var queryResp userapi.QueryKeyBackupResponse
|
||||||
userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
|
if err := userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
|
||||||
UserID: device.UserID,
|
UserID: device.UserID,
|
||||||
Version: version,
|
Version: version,
|
||||||
}, &queryResp)
|
}, &queryResp); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
if queryResp.Error != "" {
|
if queryResp.Error != "" {
|
||||||
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", queryResp.Error))
|
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", queryResp.Error))
|
||||||
}
|
}
|
||||||
|
|
@ -233,13 +235,15 @@ func GetBackupKeys(
|
||||||
req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version, roomID, sessionID string,
|
req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version, roomID, sessionID string,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
var queryResp userapi.QueryKeyBackupResponse
|
var queryResp userapi.QueryKeyBackupResponse
|
||||||
userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
|
if err := userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
|
||||||
UserID: device.UserID,
|
UserID: device.UserID,
|
||||||
Version: version,
|
Version: version,
|
||||||
ReturnKeys: true,
|
ReturnKeys: true,
|
||||||
KeysForRoomID: roomID,
|
KeysForRoomID: roomID,
|
||||||
KeysForSessionID: sessionID,
|
KeysForSessionID: sessionID,
|
||||||
}, &queryResp)
|
}, &queryResp); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
if queryResp.Error != "" {
|
if queryResp.Error != "" {
|
||||||
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", queryResp.Error))
|
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", queryResp.Error))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ func UploadCrossSigningDeviceKeys(
|
||||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
|
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
|
||||||
|
|
||||||
uploadReq.UserID = device.UserID
|
uploadReq.UserID = device.UserID
|
||||||
keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes)
|
if err := keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := uploadRes.Error; err != nil {
|
if err := uploadRes.Error; err != nil {
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -114,7 +116,9 @@ func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.Clie
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadReq.UserID = device.UserID
|
uploadReq.UserID = device.UserID
|
||||||
keyserverAPI.PerformUploadDeviceSignatures(req.Context(), uploadReq, uploadRes)
|
if err := keyserverAPI.PerformUploadDeviceSignatures(req.Context(), uploadReq, uploadRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := uploadRes.Error; err != nil {
|
if err := uploadRes.Error; err != nil {
|
||||||
switch {
|
switch {
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,9 @@ func UploadKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *userapi.Devi
|
||||||
}
|
}
|
||||||
|
|
||||||
var uploadRes api.PerformUploadKeysResponse
|
var uploadRes api.PerformUploadKeysResponse
|
||||||
keyAPI.PerformUploadKeys(req.Context(), uploadReq, &uploadRes)
|
if err := keyAPI.PerformUploadKeys(req.Context(), uploadReq, &uploadRes); err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
if uploadRes.Error != nil {
|
if uploadRes.Error != nil {
|
||||||
util.GetLogger(req.Context()).WithError(uploadRes.Error).Error("Failed to PerformUploadKeys")
|
util.GetLogger(req.Context()).WithError(uploadRes.Error).Error("Failed to PerformUploadKeys")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
|
@ -107,12 +109,14 @@ func QueryKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *userapi.Devic
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
queryRes := api.QueryKeysResponse{}
|
queryRes := api.QueryKeysResponse{}
|
||||||
keyAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
|
if err := keyAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
|
||||||
UserID: device.UserID,
|
UserID: device.UserID,
|
||||||
UserToDevices: r.DeviceKeys,
|
UserToDevices: r.DeviceKeys,
|
||||||
Timeout: r.GetTimeout(),
|
Timeout: r.GetTimeout(),
|
||||||
// TODO: Token?
|
// TODO: Token?
|
||||||
}, &queryRes)
|
}, &queryRes); err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
JSON: map[string]interface{}{
|
JSON: map[string]interface{}{
|
||||||
|
|
@ -145,10 +149,12 @@ func ClaimKeys(req *http.Request, keyAPI api.ClientKeyAPI) util.JSONResponse {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
claimRes := api.PerformClaimKeysResponse{}
|
claimRes := api.PerformClaimKeysResponse{}
|
||||||
keyAPI.PerformClaimKeys(req.Context(), &api.PerformClaimKeysRequest{
|
if err := keyAPI.PerformClaimKeys(req.Context(), &api.PerformClaimKeysRequest{
|
||||||
OneTimeKeys: r.OneTimeKeys,
|
OneTimeKeys: r.OneTimeKeys,
|
||||||
Timeout: r.GetTimeout(),
|
Timeout: r.GetTimeout(),
|
||||||
}, &claimRes)
|
}, &claimRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
if claimRes.Error != nil {
|
if claimRes.Error != nil {
|
||||||
util.GetLogger(req.Context()).WithError(claimRes.Error).Error("failed to PerformClaimKeys")
|
util.GetLogger(req.Context()).WithError(claimRes.Error).Error("failed to PerformClaimKeys")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package routing
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
@ -54,7 +55,9 @@ func PeekRoomByIDOrAlias(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the roomserver to perform the peek.
|
// Ask the roomserver to perform the peek.
|
||||||
rsAPI.PerformPeek(req.Context(), &peekReq, &peekRes)
|
if err := rsAPI.PerformPeek(req.Context(), &peekReq, &peekRes); err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
if peekRes.Error != nil {
|
if peekRes.Error != nil {
|
||||||
return peekRes.Error.JSONResponse()
|
return peekRes.Error.JSONResponse()
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +92,9 @@ func UnpeekRoomByID(
|
||||||
}
|
}
|
||||||
unpeekRes := roomserverAPI.PerformUnpeekResponse{}
|
unpeekRes := roomserverAPI.PerformUnpeekResponse{}
|
||||||
|
|
||||||
rsAPI.PerformUnpeek(req.Context(), &unpeekReq, &unpeekRes)
|
if err := rsAPI.PerformUnpeek(req.Context(), &unpeekReq, &unpeekRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
if unpeekRes.Error != nil {
|
if unpeekRes.Error != nil {
|
||||||
return unpeekRes.Error.JSONResponse()
|
return unpeekRes.Error.JSONResponse()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
@ -276,19 +276,19 @@ type recaptchaResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateUsername returns an error response if the username is invalid
|
// validateUsername returns an error response if the username is invalid
|
||||||
func validateUsername(username string) *util.JSONResponse {
|
func validateUsername(localpart string, domain gomatrixserverlib.ServerName) *util.JSONResponse {
|
||||||
// https://github.com/matrix-org/synapse/blob/v0.20.0/synapse/rest/client/v2_alpha/register.py#L161
|
// https://github.com/matrix-org/synapse/blob/v0.20.0/synapse/rest/client/v2_alpha/register.py#L161
|
||||||
if len(username) > maxUsernameLength {
|
if id := fmt.Sprintf("@%s:%s", localpart, domain); len(id) > maxUsernameLength {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.BadJSON(fmt.Sprintf("'username' >%d characters", maxUsernameLength)),
|
JSON: jsonerror.BadJSON(fmt.Sprintf("%q exceeds the maximum length of %d characters", id, maxUsernameLength)),
|
||||||
}
|
}
|
||||||
} else if !validUsernameRegex.MatchString(username) {
|
} else if !validUsernameRegex.MatchString(localpart) {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.InvalidUsername("Username can only contain characters a-z, 0-9, or '_-./='"),
|
JSON: jsonerror.InvalidUsername("Username can only contain characters a-z, 0-9, or '_-./='"),
|
||||||
}
|
}
|
||||||
} else if username[0] == '_' { // Regex checks its not a zero length string
|
} else if localpart[0] == '_' { // Regex checks its not a zero length string
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.InvalidUsername("Username cannot start with a '_'"),
|
JSON: jsonerror.InvalidUsername("Username cannot start with a '_'"),
|
||||||
|
|
@ -298,13 +298,13 @@ func validateUsername(username string) *util.JSONResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateApplicationServiceUsername returns an error response if the username is invalid for an application service
|
// validateApplicationServiceUsername returns an error response if the username is invalid for an application service
|
||||||
func validateApplicationServiceUsername(username string) *util.JSONResponse {
|
func validateApplicationServiceUsername(localpart string, domain gomatrixserverlib.ServerName) *util.JSONResponse {
|
||||||
if len(username) > maxUsernameLength {
|
if id := fmt.Sprintf("@%s:%s", localpart, domain); len(id) > maxUsernameLength {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.BadJSON(fmt.Sprintf("'username' >%d characters", maxUsernameLength)),
|
JSON: jsonerror.BadJSON(fmt.Sprintf("%q exceeds the maximum length of %d characters", id, maxUsernameLength)),
|
||||||
}
|
}
|
||||||
} else if !validUsernameRegex.MatchString(username) {
|
} else if !validUsernameRegex.MatchString(localpart) {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.InvalidUsername("Username can only contain characters a-z, 0-9, or '_-./='"),
|
JSON: jsonerror.InvalidUsername("Username can only contain characters a-z, 0-9, or '_-./='"),
|
||||||
|
|
@ -371,7 +371,7 @@ func validateRecaptcha(
|
||||||
|
|
||||||
// Grab the body of the response from the captcha server
|
// Grab the body of the response from the captcha server
|
||||||
var r recaptchaResponse
|
var r recaptchaResponse
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusGatewayTimeout,
|
Code: http.StatusGatewayTimeout,
|
||||||
|
|
@ -523,7 +523,7 @@ func validateApplicationService(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check username application service is trying to register is valid
|
// Check username application service is trying to register is valid
|
||||||
if err := validateApplicationServiceUsername(username); err != nil {
|
if err := validateApplicationServiceUsername(username, cfg.Matrix.ServerName); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -539,7 +539,7 @@ func Register(
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
defer req.Body.Close() // nolint: errcheck
|
defer req.Body.Close() // nolint: errcheck
|
||||||
reqBody, err := ioutil.ReadAll(req.Body)
|
reqBody, err := io.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
|
@ -604,7 +604,7 @@ func Register(
|
||||||
case r.Type == authtypes.LoginTypeApplicationService && accessTokenErr == nil:
|
case r.Type == authtypes.LoginTypeApplicationService && accessTokenErr == nil:
|
||||||
// Spec-compliant case (the access_token is specified and the login type
|
// Spec-compliant case (the access_token is specified and the login type
|
||||||
// is correctly set, so it's an appservice registration)
|
// is correctly set, so it's an appservice registration)
|
||||||
if resErr := validateApplicationServiceUsername(r.Username); resErr != nil {
|
if resErr := validateApplicationServiceUsername(r.Username, cfg.Matrix.ServerName); resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
case accessTokenErr == nil:
|
case accessTokenErr == nil:
|
||||||
|
|
@ -617,7 +617,7 @@ func Register(
|
||||||
default:
|
default:
|
||||||
// Spec-compliant case (neither the access_token nor the login type are
|
// Spec-compliant case (neither the access_token nor the login type are
|
||||||
// specified, so it's a normal user registration)
|
// specified, so it's a normal user registration)
|
||||||
if resErr := validateUsername(r.Username); resErr != nil {
|
if resErr := validateUsername(r.Username, cfg.Matrix.ServerName); resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1018,7 +1018,7 @@ func RegisterAvailable(
|
||||||
// Squash username to all lowercase letters
|
// Squash username to all lowercase letters
|
||||||
username = strings.ToLower(username)
|
username = strings.ToLower(username)
|
||||||
|
|
||||||
if err := validateUsername(username); err != nil {
|
if err := validateUsername(username, cfg.Matrix.ServerName); err != nil {
|
||||||
return *err
|
return *err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1059,7 +1059,7 @@ func RegisterAvailable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSharedSecretRegistration(userAPI userapi.ClientUserAPI, sr *SharedSecretRegistration, req *http.Request) util.JSONResponse {
|
func handleSharedSecretRegistration(cfg *config.ClientAPI, userAPI userapi.ClientUserAPI, sr *SharedSecretRegistration, req *http.Request) util.JSONResponse {
|
||||||
ssrr, err := NewSharedSecretRegistrationRequest(req.Body)
|
ssrr, err := NewSharedSecretRegistrationRequest(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
|
@ -1080,7 +1080,7 @@ func handleSharedSecretRegistration(userAPI userapi.ClientUserAPI, sr *SharedSec
|
||||||
// downcase capitals
|
// downcase capitals
|
||||||
ssrr.User = strings.ToLower(ssrr.User)
|
ssrr.User = strings.ToLower(ssrr.User)
|
||||||
|
|
||||||
if resErr := validateUsername(ssrr.User); resErr != nil {
|
if resErr := validateUsername(ssrr.User, cfg.Matrix.ServerName); resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
if resErr := validatePassword(ssrr.Password); resErr != nil {
|
if resErr := validatePassword(ssrr.Password); resErr != nil {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/patrickmn/go-cache"
|
"github.com/patrickmn/go-cache"
|
||||||
|
|
@ -13,7 +13,7 @@ func TestSharedSecretRegister(t *testing.T) {
|
||||||
jsonStr := []byte(`{"admin":false,"mac":"f1ba8d37123866fd659b40de4bad9b0f8965c565","nonce":"759f047f312b99ff428b21d581256f8592b8976e58bc1b543972dc6147e529a79657605b52d7becd160ff5137f3de11975684319187e06901955f79e5a6c5a79","password":"wonderland","username":"alice"}`)
|
jsonStr := []byte(`{"admin":false,"mac":"f1ba8d37123866fd659b40de4bad9b0f8965c565","nonce":"759f047f312b99ff428b21d581256f8592b8976e58bc1b543972dc6147e529a79657605b52d7becd160ff5137f3de11975684319187e06901955f79e5a6c5a79","password":"wonderland","username":"alice"}`)
|
||||||
sharedSecret := "dendritetest"
|
sharedSecret := "dendritetest"
|
||||||
|
|
||||||
req, err := NewSharedSecretRegistrationRequest(ioutil.NopCloser(bytes.NewBuffer(jsonStr)))
|
req, err := NewSharedSecretRegistrationRequest(io.NopCloser(bytes.NewBuffer(jsonStr)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to read request: %s", err)
|
t.Fatalf("failed to read request: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ func Setup(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.Method == http.MethodPost {
|
if req.Method == http.MethodPost {
|
||||||
return handleSharedSecretRegistration(userAPI, sr, req)
|
return handleSharedSecretRegistration(cfg, userAPI, sr, req)
|
||||||
}
|
}
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusMethodNotAllowed,
|
Code: http.StatusMethodNotAllowed,
|
||||||
|
|
@ -144,17 +144,23 @@ func Setup(
|
||||||
}
|
}
|
||||||
|
|
||||||
dendriteAdminRouter.Handle("/admin/evacuateRoom/{roomID}",
|
dendriteAdminRouter.Handle("/admin/evacuateRoom/{roomID}",
|
||||||
httputil.MakeAuthAPI("admin_evacuate_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAdminAPI("admin_evacuate_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return AdminEvacuateRoom(req, device, rsAPI)
|
return AdminEvacuateRoom(req, cfg, device, rsAPI)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
dendriteAdminRouter.Handle("/admin/evacuateUser/{userID}",
|
dendriteAdminRouter.Handle("/admin/evacuateUser/{userID}",
|
||||||
httputil.MakeAuthAPI("admin_evacuate_user", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAdminAPI("admin_evacuate_user", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return AdminEvacuateUser(req, device, rsAPI)
|
return AdminEvacuateUser(req, cfg, device, rsAPI)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
dendriteAdminRouter.Handle("/admin/resetPassword/{localpart}",
|
||||||
|
httputil.MakeAdminAPI("admin_reset_password", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
return AdminResetPassword(req, cfg, device, userAPI)
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
// server notifications
|
// server notifications
|
||||||
if cfg.Matrix.ServerNotices.Enabled {
|
if cfg.Matrix.ServerNotices.Enabled {
|
||||||
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
|
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
|
||||||
|
|
@ -929,12 +935,12 @@ func Setup(
|
||||||
return SearchUserDirectory(
|
return SearchUserDirectory(
|
||||||
req.Context(),
|
req.Context(),
|
||||||
device,
|
device,
|
||||||
userAPI,
|
|
||||||
rsAPI,
|
rsAPI,
|
||||||
userDirectoryProvider,
|
userDirectoryProvider,
|
||||||
cfg.Matrix.ServerName,
|
|
||||||
postContent.SearchString,
|
postContent.SearchString,
|
||||||
postContent.Limit,
|
postContent.Limit,
|
||||||
|
federation,
|
||||||
|
cfg.Matrix.ServerName,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ var sendEventDuration = prometheus.NewHistogramVec(
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendEvent implements:
|
// SendEvent implements:
|
||||||
|
//
|
||||||
// /rooms/{roomID}/send/{eventType}
|
// /rooms/{roomID}/send/{eventType}
|
||||||
// /rooms/{roomID}/send/{eventType}/{txnID}
|
// /rooms/{roomID}/send/{eventType}/{txnID}
|
||||||
// /rooms/{roomID}/state/{eventType}/{stateKey}
|
// /rooms/{roomID}/state/{eventType}/{stateKey}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ type threePIDsResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestEmailToken implements:
|
// RequestEmailToken implements:
|
||||||
|
//
|
||||||
// POST /account/3pid/email/requestToken
|
// POST /account/3pid/email/requestToken
|
||||||
// POST /register/email/requestToken
|
// POST /register/email/requestToken
|
||||||
func RequestEmailToken(req *http.Request, threePIDAPI api.ClientUserAPI, cfg *config.ClientAPI) util.JSONResponse {
|
func RequestEmailToken(req *http.Request, threePIDAPI api.ClientUserAPI, cfg *config.ClientAPI) util.JSONResponse {
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,9 @@ func UpgradeRoom(
|
||||||
}
|
}
|
||||||
upgradeResp := roomserverAPI.PerformRoomUpgradeResponse{}
|
upgradeResp := roomserverAPI.PerformRoomUpgradeResponse{}
|
||||||
|
|
||||||
rsAPI.PerformRoomUpgrade(req.Context(), &upgradeReq, &upgradeResp)
|
if err := rsAPI.PerformRoomUpgrade(req.Context(), &upgradeReq, &upgradeResp); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if upgradeResp.Error != nil {
|
if upgradeResp.Error != nil {
|
||||||
if upgradeResp.Error.Code == roomserverAPI.PerformErrorNoRoom {
|
if upgradeResp.Error.Code == roomserverAPI.PerformErrorNoRoom {
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrix"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
@ -34,12 +37,12 @@ type UserDirectoryResponse struct {
|
||||||
func SearchUserDirectory(
|
func SearchUserDirectory(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
device *userapi.Device,
|
device *userapi.Device,
|
||||||
userAPI userapi.ClientUserAPI,
|
|
||||||
rsAPI api.ClientRoomserverAPI,
|
rsAPI api.ClientRoomserverAPI,
|
||||||
provider userapi.QuerySearchProfilesAPI,
|
provider userapi.QuerySearchProfilesAPI,
|
||||||
serverName gomatrixserverlib.ServerName,
|
|
||||||
searchString string,
|
searchString string,
|
||||||
limit int,
|
limit int,
|
||||||
|
federation *gomatrixserverlib.FederationClient,
|
||||||
|
localServerName gomatrixserverlib.ServerName,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if limit < 10 {
|
if limit < 10 {
|
||||||
limit = 10
|
limit = 10
|
||||||
|
|
@ -51,59 +54,74 @@ func SearchUserDirectory(
|
||||||
Limited: false,
|
Limited: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// First start searching local users.
|
// Get users we share a room with
|
||||||
|
knownUsersReq := &api.QueryKnownUsersRequest{
|
||||||
|
UserID: device.UserID,
|
||||||
|
Limit: limit,
|
||||||
|
}
|
||||||
|
knownUsersRes := &api.QueryKnownUsersResponse{}
|
||||||
|
if err := rsAPI.QueryKnownUsers(ctx, knownUsersReq, knownUsersRes); err != nil && err != sql.ErrNoRows {
|
||||||
|
return util.ErrorResponse(fmt.Errorf("rsAPI.QueryKnownUsers: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
knownUsersLoop:
|
||||||
|
for _, profile := range knownUsersRes.Users {
|
||||||
|
if len(results) == limit {
|
||||||
|
response.Limited = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
userID := profile.UserID
|
||||||
|
// get the full profile of the local user
|
||||||
|
localpart, serverName, _ := gomatrixserverlib.SplitID('@', userID)
|
||||||
|
if serverName == localServerName {
|
||||||
userReq := &userapi.QuerySearchProfilesRequest{
|
userReq := &userapi.QuerySearchProfilesRequest{
|
||||||
SearchString: searchString,
|
SearchString: localpart,
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
}
|
}
|
||||||
userRes := &userapi.QuerySearchProfilesResponse{}
|
userRes := &userapi.QuerySearchProfilesResponse{}
|
||||||
if err := provider.QuerySearchProfiles(ctx, userReq, userRes); err != nil {
|
if err := provider.QuerySearchProfiles(ctx, userReq, userRes); err != nil {
|
||||||
return util.ErrorResponse(fmt.Errorf("userAPI.QuerySearchProfiles: %w", err))
|
return util.ErrorResponse(fmt.Errorf("userAPI.QuerySearchProfiles: %w", err))
|
||||||
}
|
}
|
||||||
|
for _, p := range userRes.Profiles {
|
||||||
for _, user := range userRes.Profiles {
|
if strings.Contains(p.DisplayName, searchString) ||
|
||||||
|
strings.Contains(p.Localpart, searchString) {
|
||||||
|
profile.DisplayName = p.DisplayName
|
||||||
|
profile.AvatarURL = p.AvatarURL
|
||||||
|
results[userID] = profile
|
||||||
if len(results) == limit {
|
if len(results) == limit {
|
||||||
response.Limited = true
|
response.Limited = true
|
||||||
break
|
break knownUsersLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var userID string
|
|
||||||
if user.ServerName != "" {
|
|
||||||
userID = fmt.Sprintf("@%s:%s", user.Localpart, user.ServerName)
|
|
||||||
} else {
|
} else {
|
||||||
userID = fmt.Sprintf("@%s:%s", user.Localpart, serverName)
|
// If the username already contains the search string, don't bother hitting federation.
|
||||||
}
|
// This will result in missing avatars and displaynames, but saves the federation roundtrip.
|
||||||
if _, ok := results[userID]; !ok {
|
if strings.Contains(localpart, searchString) {
|
||||||
results[userID] = authtypes.FullyQualifiedProfile{
|
results[userID] = profile
|
||||||
UserID: userID,
|
|
||||||
DisplayName: user.DisplayName,
|
|
||||||
AvatarURL: user.AvatarURL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then, if we have enough room left in the response,
|
|
||||||
// start searching for known users from joined rooms.
|
|
||||||
|
|
||||||
if len(results) <= limit {
|
|
||||||
stateReq := &api.QueryKnownUsersRequest{
|
|
||||||
UserID: device.UserID,
|
|
||||||
SearchString: searchString,
|
|
||||||
Limit: limit - len(results),
|
|
||||||
}
|
|
||||||
stateRes := &api.QueryKnownUsersResponse{}
|
|
||||||
if err := rsAPI.QueryKnownUsers(ctx, stateReq, stateRes); err != nil && err != sql.ErrNoRows {
|
|
||||||
return util.ErrorResponse(fmt.Errorf("rsAPI.QueryKnownUsers: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, user := range stateRes.Users {
|
|
||||||
if len(results) == limit {
|
if len(results) == limit {
|
||||||
response.Limited = true
|
response.Limited = true
|
||||||
break
|
break knownUsersLoop
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO: We should probably cache/store this
|
||||||
|
fedProfile, fedErr := federation.LookupProfile(ctx, serverName, userID, "")
|
||||||
|
if fedErr != nil {
|
||||||
|
if x, ok := fedErr.(gomatrix.HTTPError); ok {
|
||||||
|
if x.Code == http.StatusNotFound {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Contains(fedProfile.DisplayName, searchString) {
|
||||||
|
profile.DisplayName = fedProfile.DisplayName
|
||||||
|
profile.AvatarURL = fedProfile.AvatarURL
|
||||||
|
results[userID] = profile
|
||||||
|
if len(results) == limit {
|
||||||
|
response.Limited = true
|
||||||
|
break knownUsersLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := results[user.UserID]; !ok {
|
|
||||||
results[user.UserID] = user
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,16 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/gomatrix"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/gomatrix"
|
|
||||||
"github.com/matrix-org/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RequestTurnServer implements:
|
// RequestTurnServer implements:
|
||||||
|
//
|
||||||
// GET /voip/turnServer
|
// GET /voip/turnServer
|
||||||
func RequestTurnServer(req *http.Request, device *api.Device, cfg *config.ClientAPI) util.JSONResponse {
|
func RequestTurnServer(req *http.Request, device *api.Device, cfg *config.ClientAPI) util.JSONResponse {
|
||||||
turnConfig := cfg.TURN
|
turnConfig := cfg.TURN
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,26 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/setup"
|
|
||||||
"github.com/matrix-org/dendrite/setup/base"
|
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
|
||||||
"github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/setup"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `Usage: %s
|
const usage = `Usage: %s
|
||||||
|
|
@ -47,8 +52,6 @@ Example:
|
||||||
# read password from stdin
|
# read password from stdin
|
||||||
%s --config dendrite.yaml -username alice -passwordstdin < my.pass
|
%s --config dendrite.yaml -username alice -passwordstdin < my.pass
|
||||||
cat my.pass | %s --config dendrite.yaml -username alice -passwordstdin
|
cat my.pass | %s --config dendrite.yaml -username alice -passwordstdin
|
||||||
# reset password for a user, can be used with a combination above to read the password
|
|
||||||
%s --config dendrite.yaml -reset-password -username alice -password foobarbaz
|
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
|
|
@ -59,29 +62,39 @@ var (
|
||||||
password = flag.String("password", "", "The password to associate with the account")
|
password = flag.String("password", "", "The password to associate with the account")
|
||||||
pwdFile = flag.String("passwordfile", "", "The file to use for the password (e.g. for automated account creation)")
|
pwdFile = flag.String("passwordfile", "", "The file to use for the password (e.g. for automated account creation)")
|
||||||
pwdStdin = flag.Bool("passwordstdin", false, "Reads the password from stdin")
|
pwdStdin = flag.Bool("passwordstdin", false, "Reads the password from stdin")
|
||||||
pwdLess = flag.Bool("passwordless", false, "Create a passwordless account, e.g. if only an accesstoken is required")
|
|
||||||
isAdmin = flag.Bool("admin", false, "Create an admin account")
|
isAdmin = flag.Bool("admin", false, "Create an admin account")
|
||||||
resetPassword = flag.Bool("reset-password", false, "Resets the password for the given username")
|
resetPassword = flag.Bool("reset-password", false, "Deprecated")
|
||||||
|
serverURL = flag.String("url", "https://localhost:8448", "The URL to connect to.")
|
||||||
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-=./]+$`)
|
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-=./]+$`)
|
||||||
|
timeout = flag.Duration("timeout", time.Second*30, "Timeout for the http client when connecting to the server")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var cl = http.Client{
|
||||||
|
Timeout: time.Second * 30,
|
||||||
|
Transport: http.DefaultTransport,
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
name := os.Args[0]
|
name := os.Args[0]
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, usage, name, name, name, name, name, name, name)
|
_, _ = fmt.Fprintf(os.Stderr, usage, name, name, name, name, name, name)
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
cfg := setup.ParseFlags(true)
|
cfg := setup.ParseFlags(true)
|
||||||
|
|
||||||
|
if *resetPassword {
|
||||||
|
logrus.Fatalf("The reset-password flag has been replaced by the POST /_dendrite/admin/resetPassword/{localpart} admin API.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ClientAPI.RegistrationSharedSecret == "" {
|
||||||
|
logrus.Fatalln("Shared secret registration is not enabled, enable it by setting a shared secret in the config: 'client_api.registration_shared_secret'")
|
||||||
|
}
|
||||||
|
|
||||||
if *username == "" {
|
if *username == "" {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *pwdLess && *resetPassword {
|
|
||||||
logrus.Fatalf("Can not reset to an empty password, unable to login afterwards.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !validUsernameRegex.MatchString(*username) {
|
if !validUsernameRegex.MatchString(*username) {
|
||||||
logrus.Warn("Username can only contain characters a-z, 0-9, or '_-./='")
|
logrus.Warn("Username can only contain characters a-z, 0-9, or '_-./='")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
@ -91,73 +104,102 @@ func main() {
|
||||||
logrus.Fatalf("Username can not be longer than 255 characters: %s", fmt.Sprintf("@%s:%s", *username, cfg.Global.ServerName))
|
logrus.Fatalf("Username can not be longer than 255 characters: %s", fmt.Sprintf("@%s:%s", *username, cfg.Global.ServerName))
|
||||||
}
|
}
|
||||||
|
|
||||||
var pass string
|
pass, err := getPassword(*password, *pwdFile, *pwdStdin, os.Stdin)
|
||||||
var err error
|
|
||||||
if !*pwdLess {
|
|
||||||
pass, err = getPassword(*password, *pwdFile, *pwdStdin, os.Stdin)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalln(err)
|
logrus.Fatalln(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// avoid warning about open registration
|
cl.Timeout = *timeout
|
||||||
cfg.ClientAPI.RegistrationDisabled = true
|
|
||||||
|
|
||||||
b := base.NewBaseDendrite(cfg, "")
|
accessToken, err := sharedSecretRegister(cfg.ClientAPI.RegistrationSharedSecret, *serverURL, *username, pass, *isAdmin)
|
||||||
defer b.Close() // nolint: errcheck
|
|
||||||
|
|
||||||
accountDB, err := storage.NewUserAPIDatabase(
|
|
||||||
b,
|
|
||||||
&cfg.UserAPI.AccountDatabase,
|
|
||||||
cfg.Global.ServerName,
|
|
||||||
cfg.UserAPI.BCryptCost,
|
|
||||||
cfg.UserAPI.OpenIDTokenLifetimeMS,
|
|
||||||
0, // TODO
|
|
||||||
cfg.Global.ServerNotices.LocalPart,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Fatalln("Failed to connect to the database")
|
|
||||||
}
|
|
||||||
|
|
||||||
accType := api.AccountTypeUser
|
|
||||||
if *isAdmin {
|
|
||||||
accType = api.AccountTypeAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
available, err := accountDB.CheckAccountAvailability(context.Background(), *username)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatalln("Unable check username existence.")
|
|
||||||
}
|
|
||||||
if *resetPassword {
|
|
||||||
if available {
|
|
||||||
logrus.Fatalln("Username could not be found.")
|
|
||||||
}
|
|
||||||
err = accountDB.SetPassword(context.Background(), *username, pass)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatalf("Failed to update password for user %s: %s", *username, err.Error())
|
|
||||||
}
|
|
||||||
if _, err = accountDB.RemoveAllDevices(context.Background(), *username, ""); err != nil {
|
|
||||||
logrus.Fatalf("Failed to remove all devices: %s", err.Error())
|
|
||||||
}
|
|
||||||
logrus.Infof("Updated password for user %s and invalidated all logins\n", *username)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !available {
|
|
||||||
logrus.Fatalln("Username is already in use.")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = accountDB.CreateAccount(context.Background(), *username, pass, "", accType)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalln("Failed to create the account:", err.Error())
|
logrus.Fatalln("Failed to create the account:", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infoln("Created account", *username)
|
logrus.Infof("Created account: %s (AccessToken: %s)", *username, accessToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sharedSecretRegistrationRequest struct {
|
||||||
|
User string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
MacStr string `json:"mac"`
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharedSecretRegister(sharedSecret, serverURL, localpart, password string, admin bool) (accessToken string, err error) {
|
||||||
|
registerURL := fmt.Sprintf("%s/_synapse/admin/v1/register", strings.Trim(serverURL, "/"))
|
||||||
|
nonceReq, err := http.NewRequest(http.MethodGet, registerURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to create http request: %w", err)
|
||||||
|
}
|
||||||
|
nonceResp, err := cl.Do(nonceReq)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to get nonce: %w", err)
|
||||||
|
}
|
||||||
|
body, err := io.ReadAll(nonceResp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read response body: %w", err)
|
||||||
|
}
|
||||||
|
defer nonceResp.Body.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
nonce := gjson.GetBytes(body, "nonce").Str
|
||||||
|
|
||||||
|
adminStr := "notadmin"
|
||||||
|
if admin {
|
||||||
|
adminStr = "admin"
|
||||||
|
}
|
||||||
|
reg := sharedSecretRegistrationRequest{
|
||||||
|
User: localpart,
|
||||||
|
Password: password,
|
||||||
|
Nonce: nonce,
|
||||||
|
Admin: admin,
|
||||||
|
}
|
||||||
|
macStr, err := getRegisterMac(sharedSecret, nonce, localpart, password, adminStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
reg.MacStr = macStr
|
||||||
|
|
||||||
|
js, err := json.Marshal(reg)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to marshal json: %w", err)
|
||||||
|
}
|
||||||
|
registerReq, err := http.NewRequest(http.MethodPost, registerURL, bytes.NewBuffer(js))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to create http request: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
regResp, err := cl.Do(registerReq)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to create account: %w", err)
|
||||||
|
}
|
||||||
|
defer regResp.Body.Close() // nolint: errcheck
|
||||||
|
if regResp.StatusCode < 200 || regResp.StatusCode >= 300 {
|
||||||
|
body, _ = io.ReadAll(regResp.Body)
|
||||||
|
return "", fmt.Errorf(gjson.GetBytes(body, "error").Str)
|
||||||
|
}
|
||||||
|
r, _ := io.ReadAll(regResp.Body)
|
||||||
|
|
||||||
|
return gjson.GetBytes(r, "access_token").Str, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRegisterMac(sharedSecret, nonce, localpart, password, adminStr string) (string, error) {
|
||||||
|
joined := strings.Join([]string{nonce, localpart, password, adminStr}, "\x00")
|
||||||
|
mac := hmac.New(sha1.New, []byte(sharedSecret))
|
||||||
|
_, err := mac.Write([]byte(joined))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to construct mac: %w", err)
|
||||||
|
}
|
||||||
|
regMac := mac.Sum(nil)
|
||||||
|
|
||||||
|
return hex.EncodeToString(regMac), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPassword(password, pwdFile string, pwdStdin bool, r io.Reader) (string, error) {
|
func getPassword(password, pwdFile string, pwdStdin bool, r io.Reader) (string, error) {
|
||||||
// read password from file
|
// read password from file
|
||||||
if pwdFile != "" {
|
if pwdFile != "" {
|
||||||
pw, err := ioutil.ReadFile(pwdFile)
|
pw, err := os.ReadFile(pwdFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Unable to read password from file: %v", err)
|
return "", fmt.Errorf("Unable to read password from file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +208,7 @@ func getPassword(password, pwdFile string, pwdStdin bool, r io.Reader) (string,
|
||||||
|
|
||||||
// read password from stdin
|
// read password from stdin
|
||||||
if pwdStdin {
|
if pwdStdin {
|
||||||
data, err := ioutil.ReadAll(r)
|
data, err := io.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Unable to read password from stdin: %v", err)
|
return "", fmt.Errorf("Unable to read password from stdin: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -76,11 +75,11 @@ func main() {
|
||||||
if pk, sk, err = ed25519.GenerateKey(nil); err != nil {
|
if pk, sk, err = ed25519.GenerateKey(nil); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if err = ioutil.WriteFile(keyfile, sk, 0644); err != nil {
|
if err = os.WriteFile(keyfile, sk, 0644); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
if sk, err = ioutil.ReadFile(keyfile); err != nil {
|
if sk, err = os.ReadFile(keyfile); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if len(sk) != ed25519.PrivateKeySize {
|
if len(sk) != ed25519.PrivateKeySize {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -69,7 +68,7 @@ func Setup(instanceName, storageDirectory, peerURI string) (*Node, error) {
|
||||||
|
|
||||||
yggfile := fmt.Sprintf("%s/%s-yggdrasil.conf", storageDirectory, instanceName)
|
yggfile := fmt.Sprintf("%s/%s-yggdrasil.conf", storageDirectory, instanceName)
|
||||||
if _, err := os.Stat(yggfile); !os.IsNotExist(err) {
|
if _, err := os.Stat(yggfile); !os.IsNotExist(err) {
|
||||||
yggconf, e := ioutil.ReadFile(yggfile)
|
yggconf, e := os.ReadFile(yggfile)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +87,7 @@ func Setup(instanceName, storageDirectory, peerURI string) (*Node, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if e := ioutil.WriteFile(yggfile, j, 0600); e != nil {
|
if e := os.WriteFile(yggfile, j, 0600); e != nil {
|
||||||
n.log.Printf("Couldn't write private key to file '%s': %s\n", yggfile, e)
|
n.log.Printf("Couldn't write private key to file '%s': %s\n", yggfile, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -95,7 +95,9 @@ CMD /build/run_dendrite.sh `
|
||||||
const dendriteUpgradeTestLabel = "dendrite_upgrade_test"
|
const dendriteUpgradeTestLabel = "dendrite_upgrade_test"
|
||||||
|
|
||||||
// downloadArchive downloads an arbitrary github archive of the form:
|
// downloadArchive downloads an arbitrary github archive of the form:
|
||||||
|
//
|
||||||
// https://github.com/matrix-org/dendrite/archive/v0.3.11.tar.gz
|
// https://github.com/matrix-org/dendrite/archive/v0.3.11.tar.gz
|
||||||
|
//
|
||||||
// and re-tarballs it without the top-level directory which contains branch information. It inserts
|
// and re-tarballs it without the top-level directory which contains branch information. It inserts
|
||||||
// the contents of `dockerfile` as a root file `Dockerfile` in the re-tarballed directory such that
|
// the contents of `dockerfile` as a root file `Dockerfile` in the re-tarballed directory such that
|
||||||
// you can directly feed the retarballed archive to `ImageBuild` to have it run said dockerfile.
|
// you can directly feed the retarballed archive to `ImageBuild` to have it run said dockerfile.
|
||||||
|
|
@ -126,7 +128,7 @@ func downloadArchive(cli *http.Client, tmpDir, archiveURL string, dockerfile []b
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// add top level Dockerfile
|
// add top level Dockerfile
|
||||||
err = ioutil.WriteFile(path.Join(tmpDir, "Dockerfile"), dockerfile, os.ModePerm)
|
err = os.WriteFile(path.Join(tmpDir, "Dockerfile"), dockerfile, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to inject /Dockerfile: %w", err)
|
return nil, fmt.Errorf("failed to inject /Dockerfile: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +150,7 @@ func buildDendrite(httpClient *http.Client, dockerClient *client.Client, tmpDir,
|
||||||
if branchOrTagName == HEAD && *flagHead != "" {
|
if branchOrTagName == HEAD && *flagHead != "" {
|
||||||
log.Printf("%s: Using %s as HEAD", branchOrTagName, *flagHead)
|
log.Printf("%s: Using %s as HEAD", branchOrTagName, *flagHead)
|
||||||
// add top level Dockerfile
|
// add top level Dockerfile
|
||||||
err = ioutil.WriteFile(path.Join(*flagHead, "Dockerfile"), []byte(Dockerfile), os.ModePerm)
|
err = os.WriteFile(path.Join(*flagHead, "Dockerfile"), []byte(Dockerfile), os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("custom HEAD: failed to inject /Dockerfile: %w", err)
|
return "", fmt.Errorf("custom HEAD: failed to inject /Dockerfile: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -386,7 +388,7 @@ func runImage(dockerClient *client.Client, volumeName, version, imageID string)
|
||||||
})
|
})
|
||||||
// ignore errors when cannot get logs, it's just for debugging anyways
|
// ignore errors when cannot get logs, it's just for debugging anyways
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logbody, err := ioutil.ReadAll(logs)
|
logbody, err := io.ReadAll(logs)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Printf("Container logs:\n\n%s\n\n", string(logbody))
|
log.Printf("Container logs:\n\n%s\n\n", string(logbody))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
|
@ -30,7 +29,7 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(*requestKey)
|
data, err := os.ReadFile(*requestKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ var (
|
||||||
authorityCertFile = flag.String("tls-authority-cert", "", "Optional: Create TLS certificate/keys based on this CA authority. Useful for integration testing.")
|
authorityCertFile = flag.String("tls-authority-cert", "", "Optional: Create TLS certificate/keys based on this CA authority. Useful for integration testing.")
|
||||||
authorityKeyFile = flag.String("tls-authority-key", "", "Optional: Create TLS certificate/keys based on this CA authority. Useful for integration testing.")
|
authorityKeyFile = flag.String("tls-authority-key", "", "Optional: Create TLS certificate/keys based on this CA authority. Useful for integration testing.")
|
||||||
serverName = flag.String("server", "", "Optional: Create TLS certificate/keys with this domain name set. Useful for integration testing.")
|
serverName = flag.String("server", "", "Optional: Create TLS certificate/keys with this domain name set. Useful for integration testing.")
|
||||||
|
keySize = flag.Int("keysize", 4096, "Optional: Create TLS RSA private key with the given key size")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -58,12 +59,12 @@ func main() {
|
||||||
log.Fatal("Zero or both of --tls-key and --tls-cert must be supplied")
|
log.Fatal("Zero or both of --tls-key and --tls-cert must be supplied")
|
||||||
}
|
}
|
||||||
if *authorityCertFile == "" && *authorityKeyFile == "" {
|
if *authorityCertFile == "" && *authorityKeyFile == "" {
|
||||||
if err := test.NewTLSKey(*tlsKeyFile, *tlsCertFile); err != nil {
|
if err := test.NewTLSKey(*tlsKeyFile, *tlsCertFile, *keySize); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// generate the TLS cert/key based on the authority given.
|
// generate the TLS cert/key based on the authority given.
|
||||||
if err := test.NewTLSKeyWithAuthority(*serverName, *tlsKeyFile, *tlsCertFile, *authorityKeyFile, *authorityCertFile); err != nil {
|
if err := test.NewTLSKeyWithAuthority(*serverName, *tlsKeyFile, *tlsCertFile, *authorityKeyFile, *authorityCertFile, *keySize); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,13 +178,16 @@ client_api:
|
||||||
|
|
||||||
# TURN server information that this homeserver should send to clients.
|
# TURN server information that this homeserver should send to clients.
|
||||||
turn:
|
turn:
|
||||||
turn_user_lifetime: ""
|
turn_user_lifetime: "5m"
|
||||||
turn_uris:
|
turn_uris:
|
||||||
# - turn:turn.server.org?transport=udp
|
# - turn:turn.server.org?transport=udp
|
||||||
# - turn:turn.server.org?transport=tcp
|
# - turn:turn.server.org?transport=tcp
|
||||||
turn_shared_secret: ""
|
turn_shared_secret: ""
|
||||||
turn_username: ""
|
# If your TURN server requires static credentials, then you will need to enter
|
||||||
turn_password: ""
|
# them here instead of supplying a shared secret. Note that these credentials
|
||||||
|
# will be visible to clients!
|
||||||
|
# turn_username: ""
|
||||||
|
# turn_password: ""
|
||||||
|
|
||||||
# Settings for rate-limited endpoints. Rate limiting kicks in after the threshold
|
# Settings for rate-limited endpoints. Rate limiting kicks in after the threshold
|
||||||
# number of "slots" have been taken by requests from a specific host. Each "slot"
|
# number of "slots" have been taken by requests from a specific host. Each "slot"
|
||||||
|
|
|
||||||
|
|
@ -181,13 +181,16 @@ client_api:
|
||||||
|
|
||||||
# TURN server information that this homeserver should send to clients.
|
# TURN server information that this homeserver should send to clients.
|
||||||
turn:
|
turn:
|
||||||
turn_user_lifetime: ""
|
turn_user_lifetime: "5m"
|
||||||
turn_uris:
|
turn_uris:
|
||||||
# - turn:turn.server.org?transport=udp
|
# - turn:turn.server.org?transport=udp
|
||||||
# - turn:turn.server.org?transport=tcp
|
# - turn:turn.server.org?transport=tcp
|
||||||
turn_shared_secret: ""
|
turn_shared_secret: ""
|
||||||
turn_username: ""
|
# If your TURN server requires static credentials, then you will need to enter
|
||||||
turn_password: ""
|
# them here instead of supplying a shared secret. Note that these credentials
|
||||||
|
# will be visible to clients!
|
||||||
|
# turn_username: ""
|
||||||
|
# turn_password: ""
|
||||||
|
|
||||||
# Settings for rate-limited endpoints. Rate limiting kicks in after the threshold
|
# Settings for rate-limited endpoints. Rate limiting kicks in after the threshold
|
||||||
# number of "slots" have been taken by requests from a specific host. Each "slot"
|
# number of "slots" have been taken by requests from a specific host. Each "slot"
|
||||||
|
|
|
||||||
26
docs/FAQ.md
26
docs/FAQ.md
|
|
@ -12,7 +12,13 @@ Mostly, although there are still bugs and missing features. If you are a confide
|
||||||
|
|
||||||
## Is Dendrite feature-complete?
|
## Is Dendrite feature-complete?
|
||||||
|
|
||||||
No, although a good portion of the Matrix specification has been implemented. Mostly missing are client features - see the readme at the root of the repository for more information.
|
No, although a good portion of the Matrix specification has been implemented. Mostly missing are client features - see the [readme](../README.md) at the root of the repository for more information.
|
||||||
|
|
||||||
|
## Why doesn't Dendrite have "x" yet?
|
||||||
|
|
||||||
|
Dendrite development is currently supported by a small team of developers and due to those limited resources, the majority of the effort is focused on getting Dendrite to be
|
||||||
|
specification complete. If there are major features you're requesting (e.g. new administration endpoints), we'd like to strongly encourage you to join the community in supporting
|
||||||
|
the development efforts through [contributing](https://matrix-org.github.io/dendrite/development/contributing).
|
||||||
|
|
||||||
## Is there a migration path from Synapse to Dendrite?
|
## Is there a migration path from Synapse to Dendrite?
|
||||||
|
|
||||||
|
|
@ -43,6 +49,20 @@ It should do, although we are aware of some minor issues:
|
||||||
* **Element Android**: registration does not work, but logging in with an existing account does
|
* **Element Android**: registration does not work, but logging in with an existing account does
|
||||||
* **Hydrogen**: occasionally sync can fail due to gaps in the `since` parameter, but clearing the cache fixes this
|
* **Hydrogen**: occasionally sync can fail due to gaps in the `since` parameter, but clearing the cache fixes this
|
||||||
|
|
||||||
|
## Does Dendrite support Space Summaries?
|
||||||
|
|
||||||
|
Yes, [Space Summaries](https://github.com/matrix-org/matrix-spec-proposals/pull/2946) were merged into the Matrix Spec as of 2022-01-17 however, they are still treated as an MSC (Matrix Specification Change) in Dendrite. In order to enable Space Summaries in Dendrite, you must add the MSC to the MSC configuration section in the configuration YAML. If the MSC is not enabled, a user will typically see a perpetual loading icon on the summary page. See below for a demonstration of how to add to the Dendrite configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
mscs:
|
||||||
|
mscs:
|
||||||
|
- msc2946
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly, [msc2836](https://github.com/matrix-org/matrix-spec-proposals/pull/2836) would need to be added to mscs configuration in order to support Threading. Other MSCs are not currently supported.
|
||||||
|
|
||||||
|
Please note that MSCs should be considered experimental and can result in significant usability issues when enabled. If you'd like more details on how MSCs are ratified or the current status of MSCs, please see the [Matrix specification documentation](https://spec.matrix.org/proposals/) on the subject.
|
||||||
|
|
||||||
## Does Dendrite support push notifications?
|
## Does Dendrite support push notifications?
|
||||||
|
|
||||||
Yes, we have experimental support for push notifications. Configure them in the usual way in your Matrix client.
|
Yes, we have experimental support for push notifications. Configure them in the usual way in your Matrix client.
|
||||||
|
|
@ -86,6 +106,10 @@ would be a huge help too, as that will help us to understand where the memory us
|
||||||
|
|
||||||
You may need to revisit the connection limit of your PostgreSQL server and/or make changes to the `max_connections` lines in your Dendrite configuration. Be aware that each Dendrite component opens its own database connections and has its own connection limit, even in monolith mode!
|
You may need to revisit the connection limit of your PostgreSQL server and/or make changes to the `max_connections` lines in your Dendrite configuration. Be aware that each Dendrite component opens its own database connections and has its own connection limit, even in monolith mode!
|
||||||
|
|
||||||
|
## VOIP and Video Calls don't appear to work on Dendrite
|
||||||
|
|
||||||
|
There is likely an issue with your STUN/TURN configuration on the server. If you believe your configuration to be correct, please see the [troubleshooting](administration/5_troubleshooting.md) for troubleshooting recommendations.
|
||||||
|
|
||||||
## What is being reported when enabling phone-home statistics?
|
## What is being reported when enabling phone-home statistics?
|
||||||
|
|
||||||
Phone-home statistics contain your server's domain name, some configuration information about
|
Phone-home statistics contain your server's domain name, some configuration information about
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,8 @@ User accounts can be created on a Dendrite instance in a number of ways.
|
||||||
The `create-account` tool is built in the `bin` folder when building Dendrite with
|
The `create-account` tool is built in the `bin` folder when building Dendrite with
|
||||||
the `build.sh` script.
|
the `build.sh` script.
|
||||||
|
|
||||||
It uses the `dendrite.yaml` configuration file to connect to the Dendrite user database
|
It uses the `dendrite.yaml` configuration file to connect to a running Dendrite instance and requires
|
||||||
and create the account entries directly. It can therefore be used even if Dendrite is not
|
shared secret registration to be enabled as explained below.
|
||||||
running yet, as long as the database is up.
|
|
||||||
|
|
||||||
An example of using `create-account` to create a **normal account**:
|
An example of using `create-account` to create a **normal account**:
|
||||||
|
|
||||||
|
|
@ -32,6 +31,13 @@ To create a new **admin account**, add the `-admin` flag:
|
||||||
./bin/create-account -config /path/to/dendrite.yaml -username USERNAME -admin
|
./bin/create-account -config /path/to/dendrite.yaml -username USERNAME -admin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
By default `create-account` uses `https://localhost:8448` to connect to Dendrite, this can be overwritten using
|
||||||
|
the `-url` flag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./bin/create-account -config /path/to/dendrite.yaml -username USERNAME -url http://localhost:8008
|
||||||
|
```
|
||||||
|
|
||||||
An example of using `create-account` when running in **Docker**, having found the `CONTAINERNAME` from `docker ps`:
|
An example of using `create-account` when running in **Docker**, having found the `CONTAINERNAME` from `docker ps`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,78 @@ without warning.
|
||||||
|
|
||||||
More endpoints will be added in the future.
|
More endpoints will be added in the future.
|
||||||
|
|
||||||
## `/_dendrite/admin/evacuateRoom/{roomID}`
|
Endpoints may be used directly through curl:
|
||||||
|
|
||||||
|
```
|
||||||
|
curl --header "Authorization: Bearer <access_token>" -X <POST|GET|PUT> <Endpoint URI> -d '<Request Body Contents>'
|
||||||
|
```
|
||||||
|
|
||||||
|
An `access_token` can be obtained through most Element-based matrix clients by going to `Settings` -> `Help & About` -> `Advanced` -> `Access Token`.
|
||||||
|
Be aware that an `access_token` allows a client to perform actions as an user and should be kept **secret**.
|
||||||
|
|
||||||
|
The user must be an administrator in the `account_accounts` table in order to use these endpoints.
|
||||||
|
|
||||||
|
Existing user accounts can be set to administrative accounts by changing `account_type` to `3` in `account_accounts`
|
||||||
|
|
||||||
|
```
|
||||||
|
UPDATE account_accounts SET account_type = 3 WHERE localpart = '$localpart';
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `$localpart` is the username only (e.g. `alice`).
|
||||||
|
|
||||||
|
## GET `/_dendrite/admin/evacuateRoom/{roomID}`
|
||||||
|
|
||||||
This endpoint will instruct Dendrite to part all local users from the given `roomID`
|
This endpoint will instruct Dendrite to part all local users from the given `roomID`
|
||||||
in the URL. It may take some time to complete. A JSON body will be returned containing
|
in the URL. It may take some time to complete. A JSON body will be returned containing
|
||||||
the user IDs of all affected users.
|
the user IDs of all affected users.
|
||||||
|
|
||||||
## `/_dendrite/admin/evacuateUser/{userID}`
|
## GET `/_dendrite/admin/evacuateUser/{userID}`
|
||||||
|
|
||||||
This endpoint will instruct Dendrite to part the given local `userID` in the URL from
|
This endpoint will instruct Dendrite to part the given local `userID` in the URL from
|
||||||
all rooms which they are currently joined. A JSON body will be returned containing
|
all rooms which they are currently joined. A JSON body will be returned containing
|
||||||
the room IDs of all affected rooms.
|
the room IDs of all affected rooms.
|
||||||
|
|
||||||
## `/_synapse/admin/v1/register`
|
## POST `/_dendrite/admin/resetPassword/{localpart}`
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"password": "new_password_here"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reset the password of a local user. The `localpart` is the username only, i.e. if
|
||||||
|
the full user ID is `@alice:domain.com` then the local part is `alice`.
|
||||||
|
|
||||||
|
## POST `/_synapse/admin/v1/send_server_notice`
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"user_id": "@target_user:server_name",
|
||||||
|
"content": {
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": "This is my message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Send a server notice to a specific user. See the [Matrix Spec](https://spec.matrix.org/v1.3/client-server-api/#server-notices) for additional details on server notice behaviour.
|
||||||
|
If successfully sent, the API will return the following response:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"event_id": "<event_id>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## GET `/_synapse/admin/v1/register`
|
||||||
|
|
||||||
Shared secret registration — please see the [user creation page](createusers) for
|
Shared secret registration — please see the [user creation page](createusers) for
|
||||||
guidance on configuring and using this endpoint.
|
guidance on configuring and using this endpoint.
|
||||||
|
|
||||||
|
## GET `/_matrix/client/v3/admin/whois/{userId}`
|
||||||
|
|
||||||
|
From the [Matrix Spec](https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3adminwhoisuserid).
|
||||||
|
Gets information about a particular user. `userId` is the full user ID (e.g. `@alice:domain.com`)
|
||||||
|
|
|
||||||
|
|
@ -77,5 +77,12 @@ If there aren't, you will see a log lines like this:
|
||||||
level=warning msg="IMPORTANT: Process file descriptor limit is currently 65535, it is recommended to raise the limit for Dendrite to at least 65535 to avoid issues"
|
level=warning msg="IMPORTANT: Process file descriptor limit is currently 65535, it is recommended to raise the limit for Dendrite to at least 65535 to avoid issues"
|
||||||
```
|
```
|
||||||
|
|
||||||
Follow the [Optimisation](../installation/10_optimisation.md) instructions to correct the
|
Follow the [Optimisation](../installation/11_optimisation.md) instructions to correct the
|
||||||
available number of file descriptors.
|
available number of file descriptors.
|
||||||
|
|
||||||
|
## 6. STUN/TURN Server tester
|
||||||
|
|
||||||
|
If you are experiencing problems with VoIP or video calls, you should check that Dendrite
|
||||||
|
is able to successfully connect your TURN server using
|
||||||
|
[Matrix VoIP Tester](https://test.voip.librepush.net/). This can highlight any issues
|
||||||
|
that the server may encounter so that you can begin the troubleshooting process.
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ type FederationClientError struct {
|
||||||
Blacklisted bool
|
Blacklisted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *FederationClientError) Error() string {
|
func (e FederationClientError) Error() string {
|
||||||
return fmt.Sprintf("%s - (retry_after=%s, blacklisted=%v)", e.Err, e.RetryAfter.String(), e.Blacklisted)
|
return fmt.Sprintf("%s - (retry_after=%s, blacklisted=%v)", e.Err, e.RetryAfter.String(), e.Blacklisted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -208,9 +208,11 @@ func (s *OutputRoomEventConsumer) processMessage(ore api.OutputNewRoomEvent, rew
|
||||||
// joinedHostsAtEvent works out a list of matrix servers that were joined to
|
// joinedHostsAtEvent works out a list of matrix servers that were joined to
|
||||||
// the room at the event (including peeking ones)
|
// the room at the event (including peeking ones)
|
||||||
// It is important to use the state at the event for sending messages because:
|
// It is important to use the state at the event for sending messages because:
|
||||||
// 1) We shouldn't send messages to servers that weren't in the room.
|
//
|
||||||
// 2) If a server is kicked from the rooms it should still be told about the
|
// 1. We shouldn't send messages to servers that weren't in the room.
|
||||||
// kick event,
|
// 2. If a server is kicked from the rooms it should still be told about the
|
||||||
|
// kick event.
|
||||||
|
//
|
||||||
// Usually the list can be calculated locally, but sometimes it will need fetch
|
// Usually the list can be calculated locally, but sometimes it will need fetch
|
||||||
// events from the room server.
|
// events from the room server.
|
||||||
// Returns an error if there was a problem talking to the room server.
|
// Returns an error if there was a problem talking to the room server.
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
package federationapi
|
package federationapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/federationapi/api"
|
"github.com/matrix-org/dendrite/federationapi/api"
|
||||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
|
|
@ -167,5 +169,16 @@ func NewInternalAPI(
|
||||||
if err = presenceConsumer.Start(); err != nil {
|
if err = presenceConsumer.Start(); err != nil {
|
||||||
logrus.WithError(err).Panic("failed to start presence consumer")
|
logrus.WithError(err).Panic("failed to start presence consumer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cleanExpiredEDUs func()
|
||||||
|
cleanExpiredEDUs = func() {
|
||||||
|
logrus.Infof("Cleaning expired EDUs")
|
||||||
|
if err := federationDB.DeleteExpiredEDUs(base.Context()); err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to clean expired EDUs")
|
||||||
|
}
|
||||||
|
time.AfterFunc(time.Hour, cleanExpiredEDUs)
|
||||||
|
}
|
||||||
|
time.AfterFunc(time.Minute, cleanExpiredEDUs)
|
||||||
|
|
||||||
return internal.NewFederationInternalAPI(federationDB, cfg, rsAPI, federation, stats, caches, queues, keyRing)
|
return internal.NewFederationInternalAPI(federationDB, cfg, rsAPI, federation, stats, caches, queues, keyRing)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -66,7 +66,7 @@ func TestMain(m *testing.M) {
|
||||||
s.cache = caching.NewRistrettoCache(8*1024*1024, time.Hour, false)
|
s.cache = caching.NewRistrettoCache(8*1024*1024, time.Hour, false)
|
||||||
|
|
||||||
// Create a temporary directory for JetStream.
|
// Create a temporary directory for JetStream.
|
||||||
d, err := ioutil.TempDir("./", "jetstream*")
|
d, err := os.MkdirTemp("./", "jetstream*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +136,7 @@ func (m *MockRoundTripper) RoundTrip(req *http.Request) (res *http.Response, err
|
||||||
// And respond.
|
// And respond.
|
||||||
res = &http.Response{
|
res = &http.Response{
|
||||||
StatusCode: 200,
|
StatusCode: 200,
|
||||||
Body: ioutil.NopCloser(bytes.NewReader(body)),
|
Body: io.NopCloser(bytes.NewReader(body)),
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,12 @@ type fedRoomserverAPI struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PerformJoin will call this function
|
// PerformJoin will call this function
|
||||||
func (f *fedRoomserverAPI) InputRoomEvents(ctx context.Context, req *rsapi.InputRoomEventsRequest, res *rsapi.InputRoomEventsResponse) {
|
func (f *fedRoomserverAPI) InputRoomEvents(ctx context.Context, req *rsapi.InputRoomEventsRequest, res *rsapi.InputRoomEventsResponse) error {
|
||||||
if f.inputRoomEvents == nil {
|
if f.inputRoomEvents == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
f.inputRoomEvents(ctx, req, res)
|
f.inputRoomEvents(ctx, req, res)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// keychange consumer calls this
|
// keychange consumer calls this
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/gomatrix"
|
"github.com/matrix-org/gomatrix"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/opentracing/opentracing-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP paths for the internal HTTP API
|
// HTTP paths for the internal HTTP API
|
||||||
|
|
@ -48,7 +47,11 @@ func NewFederationAPIClient(federationSenderURL string, httpClient *http.Client,
|
||||||
if httpClient == nil {
|
if httpClient == nil {
|
||||||
return nil, errors.New("NewFederationInternalAPIHTTP: httpClient is <nil>")
|
return nil, errors.New("NewFederationInternalAPIHTTP: httpClient is <nil>")
|
||||||
}
|
}
|
||||||
return &httpFederationInternalAPI{federationSenderURL, httpClient, cache}, nil
|
return &httpFederationInternalAPI{
|
||||||
|
federationAPIURL: federationSenderURL,
|
||||||
|
httpClient: httpClient,
|
||||||
|
cache: cache,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpFederationInternalAPI struct {
|
type httpFederationInternalAPI struct {
|
||||||
|
|
@ -63,11 +66,10 @@ func (h *httpFederationInternalAPI) PerformLeave(
|
||||||
request *api.PerformLeaveRequest,
|
request *api.PerformLeaveRequest,
|
||||||
response *api.PerformLeaveResponse,
|
response *api.PerformLeaveResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformLeaveRequest")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformLeave", h.federationAPIURL+FederationAPIPerformLeaveRequestPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIPerformLeaveRequestPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle sending an invite to a remote server.
|
// Handle sending an invite to a remote server.
|
||||||
|
|
@ -76,11 +78,10 @@ func (h *httpFederationInternalAPI) PerformInvite(
|
||||||
request *api.PerformInviteRequest,
|
request *api.PerformInviteRequest,
|
||||||
response *api.PerformInviteResponse,
|
response *api.PerformInviteResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformInviteRequest")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformInvite", h.federationAPIURL+FederationAPIPerformInviteRequestPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIPerformInviteRequestPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle starting a peek on a remote server.
|
// Handle starting a peek on a remote server.
|
||||||
|
|
@ -89,11 +90,10 @@ func (h *httpFederationInternalAPI) PerformOutboundPeek(
|
||||||
request *api.PerformOutboundPeekRequest,
|
request *api.PerformOutboundPeekRequest,
|
||||||
response *api.PerformOutboundPeekResponse,
|
response *api.PerformOutboundPeekResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformOutboundPeekRequest")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformOutboundPeek", h.federationAPIURL+FederationAPIPerformOutboundPeekRequestPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIPerformOutboundPeekRequestPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryJoinedHostServerNamesInRoom implements FederationInternalAPI
|
// QueryJoinedHostServerNamesInRoom implements FederationInternalAPI
|
||||||
|
|
@ -102,11 +102,10 @@ func (h *httpFederationInternalAPI) QueryJoinedHostServerNamesInRoom(
|
||||||
request *api.QueryJoinedHostServerNamesInRoomRequest,
|
request *api.QueryJoinedHostServerNamesInRoomRequest,
|
||||||
response *api.QueryJoinedHostServerNamesInRoomResponse,
|
response *api.QueryJoinedHostServerNamesInRoomResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryJoinedHostServerNamesInRoom")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"QueryJoinedHostServerNamesInRoom", h.federationAPIURL+FederationAPIQueryJoinedHostServerNamesInRoomPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIQueryJoinedHostServerNamesInRoomPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle an instruction to make_join & send_join with a remote server.
|
// Handle an instruction to make_join & send_join with a remote server.
|
||||||
|
|
@ -115,12 +114,10 @@ func (h *httpFederationInternalAPI) PerformJoin(
|
||||||
request *api.PerformJoinRequest,
|
request *api.PerformJoinRequest,
|
||||||
response *api.PerformJoinResponse,
|
response *api.PerformJoinResponse,
|
||||||
) {
|
) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformJoinRequest")
|
if err := httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformJoinRequest", h.federationAPIURL+FederationAPIPerformJoinRequestPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIPerformJoinRequestPath
|
); err != nil {
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.LastError = &gomatrix.HTTPError{
|
response.LastError = &gomatrix.HTTPError{
|
||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
Code: 0,
|
Code: 0,
|
||||||
|
|
@ -135,11 +132,10 @@ func (h *httpFederationInternalAPI) PerformDirectoryLookup(
|
||||||
request *api.PerformDirectoryLookupRequest,
|
request *api.PerformDirectoryLookupRequest,
|
||||||
response *api.PerformDirectoryLookupResponse,
|
response *api.PerformDirectoryLookupResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformDirectoryLookup")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformDirectoryLookup", h.federationAPIURL+FederationAPIPerformDirectoryLookupRequestPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIPerformDirectoryLookupRequestPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle an instruction to broadcast an EDU to all servers in rooms we are joined to.
|
// Handle an instruction to broadcast an EDU to all servers in rooms we are joined to.
|
||||||
|
|
@ -148,101 +144,61 @@ func (h *httpFederationInternalAPI) PerformBroadcastEDU(
|
||||||
request *api.PerformBroadcastEDURequest,
|
request *api.PerformBroadcastEDURequest,
|
||||||
response *api.PerformBroadcastEDUResponse,
|
response *api.PerformBroadcastEDUResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformBroadcastEDU")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformBroadcastEDU", h.federationAPIURL+FederationAPIPerformBroadcastEDUPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIPerformBroadcastEDUPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type getUserDevices struct {
|
type getUserDevices struct {
|
||||||
S gomatrixserverlib.ServerName
|
S gomatrixserverlib.ServerName
|
||||||
UserID string
|
UserID string
|
||||||
Res *gomatrixserverlib.RespUserDevices
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) GetUserDevices(
|
func (h *httpFederationInternalAPI) GetUserDevices(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, userID string,
|
ctx context.Context, s gomatrixserverlib.ServerName, userID string,
|
||||||
) (gomatrixserverlib.RespUserDevices, error) {
|
) (gomatrixserverlib.RespUserDevices, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "GetUserDevices")
|
return httputil.CallInternalProxyAPI[getUserDevices, gomatrixserverlib.RespUserDevices, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"GetUserDevices", h.federationAPIURL+FederationAPIGetUserDevicesPath, h.httpClient,
|
||||||
|
ctx, &getUserDevices{
|
||||||
var result gomatrixserverlib.RespUserDevices
|
|
||||||
request := getUserDevices{
|
|
||||||
S: s,
|
S: s,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
}
|
},
|
||||||
var response getUserDevices
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPIGetUserDevicesPath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return result, response.Err
|
|
||||||
}
|
|
||||||
return *response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type claimKeys struct {
|
type claimKeys struct {
|
||||||
S gomatrixserverlib.ServerName
|
S gomatrixserverlib.ServerName
|
||||||
OneTimeKeys map[string]map[string]string
|
OneTimeKeys map[string]map[string]string
|
||||||
Res *gomatrixserverlib.RespClaimKeys
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) ClaimKeys(
|
func (h *httpFederationInternalAPI) ClaimKeys(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string,
|
ctx context.Context, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string,
|
||||||
) (gomatrixserverlib.RespClaimKeys, error) {
|
) (gomatrixserverlib.RespClaimKeys, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "ClaimKeys")
|
return httputil.CallInternalProxyAPI[claimKeys, gomatrixserverlib.RespClaimKeys, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"ClaimKeys", h.federationAPIURL+FederationAPIClaimKeysPath, h.httpClient,
|
||||||
|
ctx, &claimKeys{
|
||||||
var result gomatrixserverlib.RespClaimKeys
|
|
||||||
request := claimKeys{
|
|
||||||
S: s,
|
S: s,
|
||||||
OneTimeKeys: oneTimeKeys,
|
OneTimeKeys: oneTimeKeys,
|
||||||
}
|
},
|
||||||
var response claimKeys
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPIClaimKeysPath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return result, response.Err
|
|
||||||
}
|
|
||||||
return *response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type queryKeys struct {
|
type queryKeys struct {
|
||||||
S gomatrixserverlib.ServerName
|
S gomatrixserverlib.ServerName
|
||||||
Keys map[string][]string
|
Keys map[string][]string
|
||||||
Res *gomatrixserverlib.RespQueryKeys
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) QueryKeys(
|
func (h *httpFederationInternalAPI) QueryKeys(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, keys map[string][]string,
|
ctx context.Context, s gomatrixserverlib.ServerName, keys map[string][]string,
|
||||||
) (gomatrixserverlib.RespQueryKeys, error) {
|
) (gomatrixserverlib.RespQueryKeys, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryKeys")
|
return httputil.CallInternalProxyAPI[queryKeys, gomatrixserverlib.RespQueryKeys, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"QueryKeys", h.federationAPIURL+FederationAPIQueryKeysPath, h.httpClient,
|
||||||
|
ctx, &queryKeys{
|
||||||
var result gomatrixserverlib.RespQueryKeys
|
|
||||||
request := queryKeys{
|
|
||||||
S: s,
|
S: s,
|
||||||
Keys: keys,
|
Keys: keys,
|
||||||
}
|
},
|
||||||
var response queryKeys
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPIQueryKeysPath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return result, response.Err
|
|
||||||
}
|
|
||||||
return *response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type backfill struct {
|
type backfill struct {
|
||||||
|
|
@ -250,32 +206,20 @@ type backfill struct {
|
||||||
RoomID string
|
RoomID string
|
||||||
Limit int
|
Limit int
|
||||||
EventIDs []string
|
EventIDs []string
|
||||||
Res *gomatrixserverlib.Transaction
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) Backfill(
|
func (h *httpFederationInternalAPI) Backfill(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, roomID string, limit int, eventIDs []string,
|
ctx context.Context, s gomatrixserverlib.ServerName, roomID string, limit int, eventIDs []string,
|
||||||
) (gomatrixserverlib.Transaction, error) {
|
) (gomatrixserverlib.Transaction, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "Backfill")
|
return httputil.CallInternalProxyAPI[backfill, gomatrixserverlib.Transaction, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"Backfill", h.federationAPIURL+FederationAPIBackfillPath, h.httpClient,
|
||||||
|
ctx, &backfill{
|
||||||
request := backfill{
|
|
||||||
S: s,
|
S: s,
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
EventIDs: eventIDs,
|
EventIDs: eventIDs,
|
||||||
}
|
},
|
||||||
var response backfill
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPIBackfillPath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return gomatrixserverlib.Transaction{}, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return gomatrixserverlib.Transaction{}, response.Err
|
|
||||||
}
|
|
||||||
return *response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type lookupState struct {
|
type lookupState struct {
|
||||||
|
|
@ -283,63 +227,39 @@ type lookupState struct {
|
||||||
RoomID string
|
RoomID string
|
||||||
EventID string
|
EventID string
|
||||||
RoomVersion gomatrixserverlib.RoomVersion
|
RoomVersion gomatrixserverlib.RoomVersion
|
||||||
Res *gomatrixserverlib.RespState
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) LookupState(
|
func (h *httpFederationInternalAPI) LookupState(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion,
|
ctx context.Context, s gomatrixserverlib.ServerName, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion,
|
||||||
) (gomatrixserverlib.RespState, error) {
|
) (gomatrixserverlib.RespState, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "LookupState")
|
return httputil.CallInternalProxyAPI[lookupState, gomatrixserverlib.RespState, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"LookupState", h.federationAPIURL+FederationAPILookupStatePath, h.httpClient,
|
||||||
|
ctx, &lookupState{
|
||||||
request := lookupState{
|
|
||||||
S: s,
|
S: s,
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
EventID: eventID,
|
EventID: eventID,
|
||||||
RoomVersion: roomVersion,
|
RoomVersion: roomVersion,
|
||||||
}
|
},
|
||||||
var response lookupState
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPILookupStatePath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return gomatrixserverlib.RespState{}, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return gomatrixserverlib.RespState{}, response.Err
|
|
||||||
}
|
|
||||||
return *response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type lookupStateIDs struct {
|
type lookupStateIDs struct {
|
||||||
S gomatrixserverlib.ServerName
|
S gomatrixserverlib.ServerName
|
||||||
RoomID string
|
RoomID string
|
||||||
EventID string
|
EventID string
|
||||||
Res *gomatrixserverlib.RespStateIDs
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) LookupStateIDs(
|
func (h *httpFederationInternalAPI) LookupStateIDs(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, roomID, eventID string,
|
ctx context.Context, s gomatrixserverlib.ServerName, roomID, eventID string,
|
||||||
) (gomatrixserverlib.RespStateIDs, error) {
|
) (gomatrixserverlib.RespStateIDs, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "LookupStateIDs")
|
return httputil.CallInternalProxyAPI[lookupStateIDs, gomatrixserverlib.RespStateIDs, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"LookupStateIDs", h.federationAPIURL+FederationAPILookupStateIDsPath, h.httpClient,
|
||||||
|
ctx, &lookupStateIDs{
|
||||||
request := lookupStateIDs{
|
|
||||||
S: s,
|
S: s,
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
EventID: eventID,
|
EventID: eventID,
|
||||||
}
|
},
|
||||||
var response lookupStateIDs
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPILookupStateIDsPath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return gomatrixserverlib.RespStateIDs{}, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return gomatrixserverlib.RespStateIDs{}, response.Err
|
|
||||||
}
|
|
||||||
return *response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type lookupMissingEvents struct {
|
type lookupMissingEvents struct {
|
||||||
|
|
@ -347,64 +267,38 @@ type lookupMissingEvents struct {
|
||||||
RoomID string
|
RoomID string
|
||||||
Missing gomatrixserverlib.MissingEvents
|
Missing gomatrixserverlib.MissingEvents
|
||||||
RoomVersion gomatrixserverlib.RoomVersion
|
RoomVersion gomatrixserverlib.RoomVersion
|
||||||
Res struct {
|
|
||||||
Events []gomatrixserverlib.RawJSON `json:"events"`
|
|
||||||
}
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) LookupMissingEvents(
|
func (h *httpFederationInternalAPI) LookupMissingEvents(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, roomID string,
|
ctx context.Context, s gomatrixserverlib.ServerName, roomID string,
|
||||||
missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion,
|
missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion,
|
||||||
) (res gomatrixserverlib.RespMissingEvents, err error) {
|
) (res gomatrixserverlib.RespMissingEvents, err error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "LookupMissingEvents")
|
return httputil.CallInternalProxyAPI[lookupMissingEvents, gomatrixserverlib.RespMissingEvents, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"LookupMissingEvents", h.federationAPIURL+FederationAPILookupMissingEventsPath, h.httpClient,
|
||||||
|
ctx, &lookupMissingEvents{
|
||||||
request := lookupMissingEvents{
|
|
||||||
S: s,
|
S: s,
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
Missing: missing,
|
Missing: missing,
|
||||||
RoomVersion: roomVersion,
|
RoomVersion: roomVersion,
|
||||||
}
|
},
|
||||||
apiURL := h.federationAPIURL + FederationAPILookupMissingEventsPath
|
)
|
||||||
err = httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &request)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if request.Err != nil {
|
|
||||||
return res, request.Err
|
|
||||||
}
|
|
||||||
res.Events = request.Res.Events
|
|
||||||
return res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type getEvent struct {
|
type getEvent struct {
|
||||||
S gomatrixserverlib.ServerName
|
S gomatrixserverlib.ServerName
|
||||||
EventID string
|
EventID string
|
||||||
Res *gomatrixserverlib.Transaction
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) GetEvent(
|
func (h *httpFederationInternalAPI) GetEvent(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, eventID string,
|
ctx context.Context, s gomatrixserverlib.ServerName, eventID string,
|
||||||
) (gomatrixserverlib.Transaction, error) {
|
) (gomatrixserverlib.Transaction, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "GetEvent")
|
return httputil.CallInternalProxyAPI[getEvent, gomatrixserverlib.Transaction, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"GetEvent", h.federationAPIURL+FederationAPIGetEventPath, h.httpClient,
|
||||||
|
ctx, &getEvent{
|
||||||
request := getEvent{
|
|
||||||
S: s,
|
S: s,
|
||||||
EventID: eventID,
|
EventID: eventID,
|
||||||
}
|
},
|
||||||
var response getEvent
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPIGetEventPath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return gomatrixserverlib.Transaction{}, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return gomatrixserverlib.Transaction{}, response.Err
|
|
||||||
}
|
|
||||||
return *response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type getEventAuth struct {
|
type getEventAuth struct {
|
||||||
|
|
@ -412,135 +306,86 @@ type getEventAuth struct {
|
||||||
RoomVersion gomatrixserverlib.RoomVersion
|
RoomVersion gomatrixserverlib.RoomVersion
|
||||||
RoomID string
|
RoomID string
|
||||||
EventID string
|
EventID string
|
||||||
Res *gomatrixserverlib.RespEventAuth
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) GetEventAuth(
|
func (h *httpFederationInternalAPI) GetEventAuth(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName,
|
ctx context.Context, s gomatrixserverlib.ServerName,
|
||||||
roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string,
|
roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string,
|
||||||
) (gomatrixserverlib.RespEventAuth, error) {
|
) (gomatrixserverlib.RespEventAuth, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "GetEventAuth")
|
return httputil.CallInternalProxyAPI[getEventAuth, gomatrixserverlib.RespEventAuth, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"GetEventAuth", h.federationAPIURL+FederationAPIGetEventAuthPath, h.httpClient,
|
||||||
|
ctx, &getEventAuth{
|
||||||
request := getEventAuth{
|
|
||||||
S: s,
|
S: s,
|
||||||
RoomVersion: roomVersion,
|
RoomVersion: roomVersion,
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
EventID: eventID,
|
EventID: eventID,
|
||||||
}
|
},
|
||||||
var response getEventAuth
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPIGetEventAuthPath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return gomatrixserverlib.RespEventAuth{}, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return gomatrixserverlib.RespEventAuth{}, response.Err
|
|
||||||
}
|
|
||||||
return *response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) QueryServerKeys(
|
func (h *httpFederationInternalAPI) QueryServerKeys(
|
||||||
ctx context.Context, req *api.QueryServerKeysRequest, res *api.QueryServerKeysResponse,
|
ctx context.Context, req *api.QueryServerKeysRequest, res *api.QueryServerKeysResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryServerKeys")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"QueryServerKeys", h.federationAPIURL+FederationAPIQueryServerKeysPath,
|
||||||
|
h.httpClient, ctx, req, res,
|
||||||
apiURL := h.federationAPIURL + FederationAPIQueryServerKeysPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, req, res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type lookupServerKeys struct {
|
type lookupServerKeys struct {
|
||||||
S gomatrixserverlib.ServerName
|
S gomatrixserverlib.ServerName
|
||||||
KeyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp
|
KeyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp
|
||||||
ServerKeys []gomatrixserverlib.ServerKeys
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) LookupServerKeys(
|
func (h *httpFederationInternalAPI) LookupServerKeys(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||||
) ([]gomatrixserverlib.ServerKeys, error) {
|
) ([]gomatrixserverlib.ServerKeys, error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "LookupServerKeys")
|
return httputil.CallInternalProxyAPI[lookupServerKeys, []gomatrixserverlib.ServerKeys, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"LookupServerKeys", h.federationAPIURL+FederationAPILookupServerKeysPath, h.httpClient,
|
||||||
|
ctx, &lookupServerKeys{
|
||||||
request := lookupServerKeys{
|
|
||||||
S: s,
|
S: s,
|
||||||
KeyRequests: keyRequests,
|
KeyRequests: keyRequests,
|
||||||
}
|
},
|
||||||
var response lookupServerKeys
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPILookupServerKeysPath
|
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return []gomatrixserverlib.ServerKeys{}, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return []gomatrixserverlib.ServerKeys{}, response.Err
|
|
||||||
}
|
|
||||||
return response.ServerKeys, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type eventRelationships struct {
|
type eventRelationships struct {
|
||||||
S gomatrixserverlib.ServerName
|
S gomatrixserverlib.ServerName
|
||||||
Req gomatrixserverlib.MSC2836EventRelationshipsRequest
|
Req gomatrixserverlib.MSC2836EventRelationshipsRequest
|
||||||
RoomVer gomatrixserverlib.RoomVersion
|
RoomVer gomatrixserverlib.RoomVersion
|
||||||
Res gomatrixserverlib.MSC2836EventRelationshipsResponse
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) MSC2836EventRelationships(
|
func (h *httpFederationInternalAPI) MSC2836EventRelationships(
|
||||||
ctx context.Context, s gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest,
|
ctx context.Context, s gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest,
|
||||||
roomVersion gomatrixserverlib.RoomVersion,
|
roomVersion gomatrixserverlib.RoomVersion,
|
||||||
) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error) {
|
) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "MSC2836EventRelationships")
|
return httputil.CallInternalProxyAPI[eventRelationships, gomatrixserverlib.MSC2836EventRelationshipsResponse, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"MSC2836EventRelationships", h.federationAPIURL+FederationAPIEventRelationshipsPath, h.httpClient,
|
||||||
|
ctx, &eventRelationships{
|
||||||
request := eventRelationships{
|
|
||||||
S: s,
|
S: s,
|
||||||
Req: r,
|
Req: r,
|
||||||
RoomVer: roomVersion,
|
RoomVer: roomVersion,
|
||||||
}
|
},
|
||||||
var response eventRelationships
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPIEventRelationshipsPath
|
|
||||||
err = httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return res, response.Err
|
|
||||||
}
|
|
||||||
return response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type spacesReq struct {
|
type spacesReq struct {
|
||||||
S gomatrixserverlib.ServerName
|
S gomatrixserverlib.ServerName
|
||||||
SuggestedOnly bool
|
SuggestedOnly bool
|
||||||
RoomID string
|
RoomID string
|
||||||
Res gomatrixserverlib.MSC2946SpacesResponse
|
|
||||||
Err *api.FederationClientError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) MSC2946Spaces(
|
func (h *httpFederationInternalAPI) MSC2946Spaces(
|
||||||
ctx context.Context, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool,
|
ctx context.Context, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool,
|
||||||
) (res gomatrixserverlib.MSC2946SpacesResponse, err error) {
|
) (res gomatrixserverlib.MSC2946SpacesResponse, err error) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "MSC2946Spaces")
|
return httputil.CallInternalProxyAPI[spacesReq, gomatrixserverlib.MSC2946SpacesResponse, *api.FederationClientError](
|
||||||
defer span.Finish()
|
"MSC2836EventRelationships", h.federationAPIURL+FederationAPISpacesSummaryPath, h.httpClient,
|
||||||
|
ctx, &spacesReq{
|
||||||
request := spacesReq{
|
|
||||||
S: dst,
|
S: dst,
|
||||||
SuggestedOnly: suggestedOnly,
|
SuggestedOnly: suggestedOnly,
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
}
|
},
|
||||||
var response spacesReq
|
)
|
||||||
apiURL := h.federationAPIURL + FederationAPISpacesSummaryPath
|
|
||||||
err = httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if response.Err != nil {
|
|
||||||
return res, response.Err
|
|
||||||
}
|
|
||||||
return response.Res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *httpFederationInternalAPI) KeyRing() *gomatrixserverlib.KeyRing {
|
func (s *httpFederationInternalAPI) KeyRing() *gomatrixserverlib.KeyRing {
|
||||||
|
|
@ -614,11 +459,10 @@ func (h *httpFederationInternalAPI) InputPublicKeys(
|
||||||
request *api.InputPublicKeysRequest,
|
request *api.InputPublicKeysRequest,
|
||||||
response *api.InputPublicKeysResponse,
|
response *api.InputPublicKeysResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "InputPublicKey")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"InputPublicKey", h.federationAPIURL+FederationAPIInputPublicKeyPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIInputPublicKeyPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpFederationInternalAPI) QueryPublicKeys(
|
func (h *httpFederationInternalAPI) QueryPublicKeys(
|
||||||
|
|
@ -626,9 +470,8 @@ func (h *httpFederationInternalAPI) QueryPublicKeys(
|
||||||
request *api.QueryPublicKeysRequest,
|
request *api.QueryPublicKeysRequest,
|
||||||
response *api.QueryPublicKeysResponse,
|
response *api.QueryPublicKeysResponse,
|
||||||
) error {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryPublicKey")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"QueryPublicKeys", h.federationAPIURL+FederationAPIQueryPublicKeyPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.federationAPIURL + FederationAPIQueryPublicKeyPath
|
)
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
package inthttp
|
package inthttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/federationapi/api"
|
"github.com/matrix-org/dendrite/federationapi/api"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -15,372 +17,180 @@ import (
|
||||||
func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
|
func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIQueryJoinedHostServerNamesInRoomPath,
|
FederationAPIQueryJoinedHostServerNamesInRoomPath,
|
||||||
httputil.MakeInternalAPI("QueryJoinedHostServerNamesInRoom", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalRPCAPI("FederationAPIQueryJoinedHostServerNamesInRoom", intAPI.QueryJoinedHostServerNamesInRoom),
|
||||||
var request api.QueryJoinedHostServerNamesInRoomRequest
|
|
||||||
var response api.QueryJoinedHostServerNamesInRoomResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
if err := intAPI.QueryJoinedHostServerNamesInRoom(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
internalAPIMux.Handle(
|
|
||||||
FederationAPIPerformJoinRequestPath,
|
|
||||||
httputil.MakeInternalAPI("PerformJoinRequest", func(req *http.Request) util.JSONResponse {
|
|
||||||
var request api.PerformJoinRequest
|
|
||||||
var response api.PerformJoinResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
intAPI.PerformJoin(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
internalAPIMux.Handle(
|
|
||||||
FederationAPIPerformLeaveRequestPath,
|
|
||||||
httputil.MakeInternalAPI("PerformLeaveRequest", func(req *http.Request) util.JSONResponse {
|
|
||||||
var request api.PerformLeaveRequest
|
|
||||||
var response api.PerformLeaveResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
if err := intAPI.PerformLeave(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIPerformInviteRequestPath,
|
FederationAPIPerformInviteRequestPath,
|
||||||
httputil.MakeInternalAPI("PerformInviteRequest", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalRPCAPI("FederationAPIPerformInvite", intAPI.PerformInvite),
|
||||||
var request api.PerformInviteRequest
|
|
||||||
var response api.PerformInviteResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
if err := intAPI.PerformInvite(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internalAPIMux.Handle(
|
||||||
|
FederationAPIPerformLeaveRequestPath,
|
||||||
|
httputil.MakeInternalRPCAPI("FederationAPIPerformLeave", intAPI.PerformLeave),
|
||||||
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIPerformDirectoryLookupRequestPath,
|
FederationAPIPerformDirectoryLookupRequestPath,
|
||||||
httputil.MakeInternalAPI("PerformDirectoryLookupRequest", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalRPCAPI("FederationAPIPerformDirectoryLookupRequest", intAPI.PerformDirectoryLookup),
|
||||||
var request api.PerformDirectoryLookupRequest
|
|
||||||
var response api.PerformDirectoryLookupResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
if err := intAPI.PerformDirectoryLookup(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIPerformBroadcastEDUPath,
|
FederationAPIPerformBroadcastEDUPath,
|
||||||
httputil.MakeInternalAPI("PerformBroadcastEDU", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalRPCAPI("FederationAPIPerformBroadcastEDU", intAPI.PerformBroadcastEDU),
|
||||||
var request api.PerformBroadcastEDURequest
|
|
||||||
var response api.PerformBroadcastEDUResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
if err := intAPI.PerformBroadcastEDU(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internalAPIMux.Handle(
|
||||||
|
FederationAPIPerformJoinRequestPath,
|
||||||
|
httputil.MakeInternalRPCAPI(
|
||||||
|
"FederationAPIPerformJoinRequest",
|
||||||
|
func(ctx context.Context, req *api.PerformJoinRequest, res *api.PerformJoinResponse) error {
|
||||||
|
intAPI.PerformJoin(ctx, req, res)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIGetUserDevicesPath,
|
FederationAPIGetUserDevicesPath,
|
||||||
httputil.MakeInternalAPI("GetUserDevices", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request getUserDevices
|
"FederationAPIGetUserDevices",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *getUserDevices) (*gomatrixserverlib.RespUserDevices, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.GetUserDevices(ctx, req.S, req.UserID)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.GetUserDevices(req.Context(), request.S, request.UserID)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = &res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIClaimKeysPath,
|
FederationAPIClaimKeysPath,
|
||||||
httputil.MakeInternalAPI("ClaimKeys", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request claimKeys
|
"FederationAPIClaimKeys",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *claimKeys) (*gomatrixserverlib.RespClaimKeys, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.ClaimKeys(ctx, req.S, req.OneTimeKeys)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.ClaimKeys(req.Context(), request.S, request.OneTimeKeys)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = &res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIQueryKeysPath,
|
FederationAPIQueryKeysPath,
|
||||||
httputil.MakeInternalAPI("QueryKeys", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request queryKeys
|
"FederationAPIQueryKeys",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *queryKeys) (*gomatrixserverlib.RespQueryKeys, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.QueryKeys(ctx, req.S, req.Keys)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.QueryKeys(req.Context(), request.S, request.Keys)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = &res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIBackfillPath,
|
FederationAPIBackfillPath,
|
||||||
httputil.MakeInternalAPI("Backfill", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request backfill
|
"FederationAPIBackfill",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *backfill) (*gomatrixserverlib.Transaction, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.Backfill(ctx, req.S, req.RoomID, req.Limit, req.EventIDs)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.Backfill(req.Context(), request.S, request.RoomID, request.Limit, request.EventIDs)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = &res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPILookupStatePath,
|
FederationAPILookupStatePath,
|
||||||
httputil.MakeInternalAPI("LookupState", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request lookupState
|
"FederationAPILookupState",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *lookupState) (*gomatrixserverlib.RespState, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.LookupState(ctx, req.S, req.RoomID, req.EventID, req.RoomVersion)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.LookupState(req.Context(), request.S, request.RoomID, request.EventID, request.RoomVersion)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = &res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPILookupStateIDsPath,
|
FederationAPILookupStateIDsPath,
|
||||||
httputil.MakeInternalAPI("LookupStateIDs", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request lookupStateIDs
|
"FederationAPILookupStateIDs",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *lookupStateIDs) (*gomatrixserverlib.RespStateIDs, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.LookupStateIDs(ctx, req.S, req.RoomID, req.EventID)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.LookupStateIDs(req.Context(), request.S, request.RoomID, request.EventID)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = &res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPILookupMissingEventsPath,
|
FederationAPILookupMissingEventsPath,
|
||||||
httputil.MakeInternalAPI("LookupMissingEvents", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request lookupMissingEvents
|
"FederationAPILookupMissingEvents",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *lookupMissingEvents) (*gomatrixserverlib.RespMissingEvents, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.LookupMissingEvents(ctx, req.S, req.RoomID, req.Missing, req.RoomVersion)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.LookupMissingEvents(req.Context(), request.S, request.RoomID, request.Missing, request.RoomVersion)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, event := range res.Events {
|
|
||||||
js, err := json.Marshal(event)
|
|
||||||
if err != nil {
|
|
||||||
return util.MessageResponse(http.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
request.Res.Events = append(request.Res.Events, js)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIGetEventPath,
|
FederationAPIGetEventPath,
|
||||||
httputil.MakeInternalAPI("GetEvent", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request getEvent
|
"FederationAPIGetEvent",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *getEvent) (*gomatrixserverlib.Transaction, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.GetEvent(ctx, req.S, req.EventID)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.GetEvent(req.Context(), request.S, request.EventID)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = &res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIGetEventAuthPath,
|
FederationAPIGetEventAuthPath,
|
||||||
httputil.MakeInternalAPI("GetEventAuth", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request getEventAuth
|
"FederationAPIGetEventAuth",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *getEventAuth) (*gomatrixserverlib.RespEventAuth, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.GetEventAuth(ctx, req.S, req.RoomVersion, req.RoomID, req.EventID)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.GetEventAuth(req.Context(), request.S, request.RoomVersion, request.RoomID, request.EventID)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = &res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIQueryServerKeysPath,
|
FederationAPIQueryServerKeysPath,
|
||||||
httputil.MakeInternalAPI("QueryServerKeys", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalRPCAPI("FederationAPIQueryServerKeys", intAPI.QueryServerKeys),
|
||||||
var request api.QueryServerKeysRequest
|
|
||||||
var response api.QueryServerKeysResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
if err := intAPI.QueryServerKeys(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPILookupServerKeysPath,
|
FederationAPILookupServerKeysPath,
|
||||||
httputil.MakeInternalAPI("LookupServerKeys", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request lookupServerKeys
|
"FederationAPILookupServerKeys",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *lookupServerKeys) (*[]gomatrixserverlib.ServerKeys, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.LookupServerKeys(ctx, req.S, req.KeyRequests)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.LookupServerKeys(req.Context(), request.S, request.KeyRequests)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.ServerKeys = res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPIEventRelationshipsPath,
|
FederationAPIEventRelationshipsPath,
|
||||||
httputil.MakeInternalAPI("MSC2836EventRelationships", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request eventRelationships
|
"FederationAPIMSC2836EventRelationships",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *eventRelationships) (*gomatrixserverlib.MSC2836EventRelationshipsResponse, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.MSC2836EventRelationships(ctx, req.S, req.Req, req.RoomVer)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.MSC2836EventRelationships(req.Context(), request.S, request.Req, request.RoomVer)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
internalAPIMux.Handle(
|
internalAPIMux.Handle(
|
||||||
FederationAPISpacesSummaryPath,
|
FederationAPISpacesSummaryPath,
|
||||||
httputil.MakeInternalAPI("MSC2946SpacesSummary", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalProxyAPI(
|
||||||
var request spacesReq
|
"FederationAPIMSC2946SpacesSummary",
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
func(ctx context.Context, req *spacesReq) (*gomatrixserverlib.MSC2946SpacesResponse, error) {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
res, err := intAPI.MSC2946Spaces(ctx, req.S, req.RoomID, req.SuggestedOnly)
|
||||||
}
|
return &res, federationClientError(err)
|
||||||
res, err := intAPI.MSC2946Spaces(req.Context(), request.S, request.RoomID, request.SuggestedOnly)
|
},
|
||||||
if err != nil {
|
),
|
||||||
ferr, ok := err.(*api.FederationClientError)
|
|
||||||
if ok {
|
|
||||||
request.Err = ferr
|
|
||||||
} else {
|
|
||||||
request.Err = &api.FederationClientError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.Res = res
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: request}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Look at this shape
|
||||||
internalAPIMux.Handle(FederationAPIQueryPublicKeyPath,
|
internalAPIMux.Handle(FederationAPIQueryPublicKeyPath,
|
||||||
httputil.MakeInternalAPI("queryPublicKeys", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalAPI("FederationAPIQueryPublicKeys", func(req *http.Request) util.JSONResponse {
|
||||||
request := api.QueryPublicKeysRequest{}
|
request := api.QueryPublicKeysRequest{}
|
||||||
response := api.QueryPublicKeysResponse{}
|
response := api.QueryPublicKeysResponse{}
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
|
|
@ -394,8 +204,10 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Look at this shape
|
||||||
internalAPIMux.Handle(FederationAPIInputPublicKeyPath,
|
internalAPIMux.Handle(FederationAPIInputPublicKeyPath,
|
||||||
httputil.MakeInternalAPI("inputPublicKeys", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalAPI("FederationAPIInputPublicKeys", func(req *http.Request) util.JSONResponse {
|
||||||
request := api.InputPublicKeysRequest{}
|
request := api.InputPublicKeysRequest{}
|
||||||
response := api.InputPublicKeysResponse{}
|
response := api.InputPublicKeysResponse{}
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
|
|
@ -408,3 +220,18 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func federationClientError(err error) error {
|
||||||
|
switch ferr := err.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case api.FederationClientError:
|
||||||
|
return &ferr
|
||||||
|
case *api.FederationClientError:
|
||||||
|
return ferr
|
||||||
|
default:
|
||||||
|
return &api.FederationClientError{
|
||||||
|
Err: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ func (oq *destinationQueue) sendEDU(event *gomatrixserverlib.EDU, receipt *share
|
||||||
oq.destination, // the destination server name
|
oq.destination, // the destination server name
|
||||||
receipt, // NIDs from federationapi_queue_json table
|
receipt, // NIDs from federationapi_queue_json table
|
||||||
event.Type,
|
event.Type,
|
||||||
|
nil, // this will use the default expireEDUTypes map
|
||||||
); err != nil {
|
); err != nil {
|
||||||
logrus.WithError(err).Errorf("failed to associate EDU with destination %q", oq.destination)
|
logrus.WithError(err).Errorf("failed to associate EDU with destination %q", oq.destination)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ func (oqs *OutgoingQueues) getQueue(destination gomatrixserverlib.ServerName) *d
|
||||||
oqs.queuesMutex.Lock()
|
oqs.queuesMutex.Lock()
|
||||||
defer oqs.queuesMutex.Unlock()
|
defer oqs.queuesMutex.Unlock()
|
||||||
oq, ok := oqs.queues[destination]
|
oq, ok := oqs.queues[destination]
|
||||||
if !ok || oq != nil {
|
if !ok || oq == nil {
|
||||||
destinationQueueTotal.Inc()
|
destinationQueueTotal.Inc()
|
||||||
oq = &destinationQueue{
|
oq = &destinationQueue{
|
||||||
queues: oqs,
|
queues: oqs,
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,11 @@ func GetUserDevices(
|
||||||
userID string,
|
userID string,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
var res keyapi.QueryDeviceMessagesResponse
|
var res keyapi.QueryDeviceMessagesResponse
|
||||||
keyAPI.QueryDeviceMessages(req.Context(), &keyapi.QueryDeviceMessagesRequest{
|
if err := keyAPI.QueryDeviceMessages(req.Context(), &keyapi.QueryDeviceMessagesRequest{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
}, &res)
|
}, &res); err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
util.GetLogger(req.Context()).WithError(res.Error).Error("keyAPI.QueryDeviceMessages failed")
|
util.GetLogger(req.Context()).WithError(res.Error).Error("keyAPI.QueryDeviceMessages failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
|
@ -47,7 +49,9 @@ func GetUserDevices(
|
||||||
for _, dev := range res.Devices {
|
for _, dev := range res.Devices {
|
||||||
sigReq.TargetIDs[userID] = append(sigReq.TargetIDs[userID], gomatrixserverlib.KeyID(dev.DeviceID))
|
sigReq.TargetIDs[userID] = append(sigReq.TargetIDs[userID], gomatrixserverlib.KeyID(dev.DeviceID))
|
||||||
}
|
}
|
||||||
keyAPI.QuerySignatures(req.Context(), sigReq, sigRes)
|
if err := keyAPI.QuerySignatures(req.Context(), sigReq, sigRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(req.Context(), err)
|
||||||
|
}
|
||||||
|
|
||||||
response := gomatrixserverlib.RespUserDevices{
|
response := gomatrixserverlib.RespUserDevices{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,14 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
"github.com/matrix-org/util"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MakeJoin implements the /make_join API
|
// MakeJoin implements the /make_join API
|
||||||
|
|
@ -328,6 +329,12 @@ func SendJoin(
|
||||||
JSON: jsonerror.NotFound("Room does not exist"),
|
JSON: jsonerror.NotFound("Room does not exist"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !stateAndAuthChainResponse.StateKnown {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: jsonerror.Forbidden("State not known"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user is already in the room. If they're already in then
|
// Check if the user is already in the room. If they're already in then
|
||||||
// there isn't much point in sending another join event into the room.
|
// there isn't much point in sending another join event into the room.
|
||||||
|
|
@ -391,7 +398,7 @@ func SendJoin(
|
||||||
// the room, so set SendAsServer to cfg.Matrix.ServerName
|
// the room, so set SendAsServer to cfg.Matrix.ServerName
|
||||||
if !alreadyJoined {
|
if !alreadyJoined {
|
||||||
var response api.InputRoomEventsResponse
|
var response api.InputRoomEventsResponse
|
||||||
rsAPI.InputRoomEvents(httpReq.Context(), &api.InputRoomEventsRequest{
|
if err := rsAPI.InputRoomEvents(httpReq.Context(), &api.InputRoomEventsRequest{
|
||||||
InputRoomEvents: []api.InputRoomEvent{
|
InputRoomEvents: []api.InputRoomEvent{
|
||||||
{
|
{
|
||||||
Kind: api.KindNew,
|
Kind: api.KindNew,
|
||||||
|
|
@ -400,7 +407,9 @@ func SendJoin(
|
||||||
TransactionID: nil,
|
TransactionID: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, &response)
|
}, &response); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(httpReq.Context(), err)
|
||||||
|
}
|
||||||
if response.ErrMsg != "" {
|
if response.ErrMsg != "" {
|
||||||
util.GetLogger(httpReq.Context()).WithField(logrus.ErrorKey, response.ErrMsg).Error("SendEvents failed")
|
util.GetLogger(httpReq.Context()).WithField(logrus.ErrorKey, response.ErrMsg).Error("SendEvents failed")
|
||||||
if response.NotAllowed {
|
if response.NotAllowed {
|
||||||
|
|
@ -435,11 +444,11 @@ func SendJoin(
|
||||||
// a restricted room join. If the room version does not support restricted
|
// a restricted room join. If the room version does not support restricted
|
||||||
// joins then this function returns with no side effects. This returns three
|
// joins then this function returns with no side effects. This returns three
|
||||||
// values:
|
// values:
|
||||||
// * an optional JSON response body (i.e. M_UNABLE_TO_AUTHORISE_JOIN) which
|
// - an optional JSON response body (i.e. M_UNABLE_TO_AUTHORISE_JOIN) which
|
||||||
// should always be sent back to the client if one is specified
|
// should always be sent back to the client if one is specified
|
||||||
// * a user ID of an authorising user, typically a user that has power to
|
// - a user ID of an authorising user, typically a user that has power to
|
||||||
// issue invites in the room, if one has been found
|
// issue invites in the room, if one has been found
|
||||||
// * an error if there was a problem finding out if this was allowable,
|
// - an error if there was a problem finding out if this was allowable,
|
||||||
// like if the room version isn't known or a problem happened talking to
|
// like if the room version isn't known or a problem happened talking to
|
||||||
// the roomserver
|
// the roomserver
|
||||||
func checkRestrictedJoin(
|
func checkRestrictedJoin(
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
clienthttputil "github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
|
@ -61,9 +61,11 @@ func QueryDeviceKeys(
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryRes api.QueryKeysResponse
|
var queryRes api.QueryKeysResponse
|
||||||
keyAPI.QueryKeys(httpReq.Context(), &api.QueryKeysRequest{
|
if err := keyAPI.QueryKeys(httpReq.Context(), &api.QueryKeysRequest{
|
||||||
UserToDevices: qkr.DeviceKeys,
|
UserToDevices: qkr.DeviceKeys,
|
||||||
}, &queryRes)
|
}, &queryRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(httpReq.Context(), err)
|
||||||
|
}
|
||||||
if queryRes.Error != nil {
|
if queryRes.Error != nil {
|
||||||
util.GetLogger(httpReq.Context()).WithError(queryRes.Error).Error("Failed to QueryKeys")
|
util.GetLogger(httpReq.Context()).WithError(queryRes.Error).Error("Failed to QueryKeys")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
|
@ -113,9 +115,11 @@ func ClaimOneTimeKeys(
|
||||||
}
|
}
|
||||||
|
|
||||||
var claimRes api.PerformClaimKeysResponse
|
var claimRes api.PerformClaimKeysResponse
|
||||||
keyAPI.PerformClaimKeys(httpReq.Context(), &api.PerformClaimKeysRequest{
|
if err := keyAPI.PerformClaimKeys(httpReq.Context(), &api.PerformClaimKeysRequest{
|
||||||
OneTimeKeys: cor.OneTimeKeys,
|
OneTimeKeys: cor.OneTimeKeys,
|
||||||
}, &claimRes)
|
}, &claimRes); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(httpReq.Context(), err)
|
||||||
|
}
|
||||||
if claimRes.Error != nil {
|
if claimRes.Error != nil {
|
||||||
util.GetLogger(httpReq.Context()).WithError(claimRes.Error).Error("Failed to PerformClaimKeys")
|
util.GetLogger(httpReq.Context()).WithError(claimRes.Error).Error("Failed to PerformClaimKeys")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
|
@ -184,7 +188,7 @@ func NotaryKeys(
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if req == nil {
|
if req == nil {
|
||||||
req = &gomatrixserverlib.PublicKeyNotaryLookupRequest{}
|
req = &gomatrixserverlib.PublicKeyNotaryLookupRequest{}
|
||||||
if reqErr := httputil.UnmarshalJSONRequest(httpReq, &req); reqErr != nil {
|
if reqErr := clienthttputil.UnmarshalJSONRequest(httpReq, &req); reqErr != nil {
|
||||||
return *reqErr
|
return *reqErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ func SendLeave(
|
||||||
// We are responsible for notifying other servers that the user has left
|
// We are responsible for notifying other servers that the user has left
|
||||||
// the room, so set SendAsServer to cfg.Matrix.ServerName
|
// the room, so set SendAsServer to cfg.Matrix.ServerName
|
||||||
var response api.InputRoomEventsResponse
|
var response api.InputRoomEventsResponse
|
||||||
rsAPI.InputRoomEvents(httpReq.Context(), &api.InputRoomEventsRequest{
|
if err := rsAPI.InputRoomEvents(httpReq.Context(), &api.InputRoomEventsRequest{
|
||||||
InputRoomEvents: []api.InputRoomEvent{
|
InputRoomEvents: []api.InputRoomEvent{
|
||||||
{
|
{
|
||||||
Kind: api.KindNew,
|
Kind: api.KindNew,
|
||||||
|
|
@ -286,7 +286,9 @@ func SendLeave(
|
||||||
TransactionID: nil,
|
TransactionID: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, &response)
|
}, &response); err != nil {
|
||||||
|
return jsonerror.InternalAPIError(httpReq.Context(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if response.ErrMsg != "" {
|
if response.ErrMsg != "" {
|
||||||
util.GetLogger(httpReq.Context()).WithField(logrus.ErrorKey, response.ErrMsg).WithField("not_allowed", response.NotAllowed).Error("producer.SendEvents failed")
|
util.GetLogger(httpReq.Context()).WithField(logrus.ErrorKey, response.ErrMsg).WithField("not_allowed", response.NotAllowed).Error("producer.SendEvents failed")
|
||||||
|
|
|
||||||
|
|
@ -458,7 +458,9 @@ func (t *txnReq) processSigningKeyUpdate(ctx context.Context, e gomatrixserverli
|
||||||
UserID: updatePayload.UserID,
|
UserID: updatePayload.UserID,
|
||||||
}
|
}
|
||||||
uploadRes := &keyapi.PerformUploadDeviceKeysResponse{}
|
uploadRes := &keyapi.PerformUploadDeviceKeysResponse{}
|
||||||
t.keyAPI.PerformUploadDeviceKeys(ctx, uploadReq, uploadRes)
|
if err := t.keyAPI.PerformUploadDeviceKeys(ctx, uploadReq, uploadRes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if uploadRes.Error != nil {
|
if uploadRes.Error != nil {
|
||||||
return uploadRes.Error
|
return uploadRes.Error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,12 @@ func (t *testRoomserverAPI) InputRoomEvents(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.InputRoomEventsRequest,
|
request *api.InputRoomEventsRequest,
|
||||||
response *api.InputRoomEventsResponse,
|
response *api.InputRoomEventsResponse,
|
||||||
) {
|
) error {
|
||||||
t.inputRoomEvents = append(t.inputRoomEvents, request.InputRoomEvents...)
|
t.inputRoomEvents = append(t.inputRoomEvents, request.InputRoomEvents...)
|
||||||
for _, ire := range request.InputRoomEvents {
|
for _, ire := range request.InputRoomEvents {
|
||||||
fmt.Println("InputRoomEvents: ", ire.Event.EventID())
|
fmt.Println("InputRoomEvents: ", ire.Event.EventID())
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query the latest events and state for a room from the room server.
|
// Query the latest events and state for a room from the room server.
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,12 @@ func getState(
|
||||||
return nil, nil, &resErr
|
return nil, nil, &resErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !response.StateKnown {
|
||||||
|
return nil, nil, &util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("State not known"),
|
||||||
|
}
|
||||||
|
}
|
||||||
if response.IsRejected {
|
if response.IsRejected {
|
||||||
return nil, nil, &util.JSONResponse{
|
return nil, nil, &util.JSONResponse{
|
||||||
Code: http.StatusNotFound,
|
Code: http.StatusNotFound,
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,11 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Statistics contains information about all of the remote federated
|
// Statistics contains information about all of the remote federated
|
||||||
|
|
@ -126,13 +127,13 @@ func (s *ServerStatistics) Failure() (time.Time, bool) {
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
until, ok := s.backoffUntil.Load().(time.Time)
|
until, ok := s.backoffUntil.Load().(time.Time)
|
||||||
if ok {
|
if ok && !until.IsZero() {
|
||||||
select {
|
select {
|
||||||
case <-time.After(time.Until(until)):
|
case <-time.After(time.Until(until)):
|
||||||
case <-s.interrupt:
|
case <-s.interrupt:
|
||||||
}
|
}
|
||||||
}
|
|
||||||
s.backoffStarted.Store(false)
|
s.backoffStarted.Store(false)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/storage/shared"
|
"github.com/matrix-org/dendrite/federationapi/storage/shared"
|
||||||
"github.com/matrix-org/dendrite/federationapi/types"
|
"github.com/matrix-org/dendrite/federationapi/types"
|
||||||
|
|
@ -38,7 +39,7 @@ type Database interface {
|
||||||
GetPendingEDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, limit int) (edus map[*shared.Receipt]*gomatrixserverlib.EDU, err error)
|
GetPendingEDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, limit int) (edus map[*shared.Receipt]*gomatrixserverlib.EDU, err error)
|
||||||
|
|
||||||
AssociatePDUWithDestination(ctx context.Context, transactionID gomatrixserverlib.TransactionID, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt) error
|
AssociatePDUWithDestination(ctx context.Context, transactionID gomatrixserverlib.TransactionID, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt) error
|
||||||
AssociateEDUWithDestination(ctx context.Context, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt, eduType string) error
|
AssociateEDUWithDestination(ctx context.Context, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt, eduType string, expireEDUTypes map[string]time.Duration) error
|
||||||
|
|
||||||
CleanPDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, receipts []*shared.Receipt) error
|
CleanPDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, receipts []*shared.Receipt) error
|
||||||
CleanEDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, receipts []*shared.Receipt) error
|
CleanEDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, receipts []*shared.Receipt) error
|
||||||
|
|
@ -70,4 +71,6 @@ type Database interface {
|
||||||
// Query the notary for the server keys for the given server. If `optKeyIDs` is not empty, multiple server keys may be returned (between 1 - len(optKeyIDs))
|
// Query the notary for the server keys for the given server. If `optKeyIDs` is not empty, multiple server keys may be returned (between 1 - len(optKeyIDs))
|
||||||
// such that the combination of all server keys will include all the `optKeyIDs`.
|
// such that the combination of all server keys will include all the `optKeyIDs`.
|
||||||
GetNotaryKeys(ctx context.Context, serverName gomatrixserverlib.ServerName, optKeyIDs []gomatrixserverlib.KeyID) ([]gomatrixserverlib.ServerKeys, error)
|
GetNotaryKeys(ctx context.Context, serverName gomatrixserverlib.ServerName, optKeyIDs []gomatrixserverlib.KeyID) ([]gomatrixserverlib.ServerKeys, error)
|
||||||
|
// DeleteExpiredEDUs cleans up expired EDUs
|
||||||
|
DeleteExpiredEDUs(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2022 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 deltas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpAddexpiresat(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE federationsender_queue_edus ADD COLUMN IF NOT EXISTS expires_at BIGINT NOT NULL DEFAULT 0;")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute upgrade: %w", err)
|
||||||
|
}
|
||||||
|
_, err = tx.ExecContext(ctx, "UPDATE federationsender_queue_edus SET expires_at = $1 WHERE edu_type != 'm.direct_to_device'", gomatrixserverlib.AsTimestamp(time.Now().Add(time.Hour*24)))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update queue_edus: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DownAddexpiresat(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE federationsender_queue_edus DROP COLUMN expires_at;")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute downgrade: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -19,9 +19,11 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/storage/postgres/deltas"
|
||||||
"github.com/matrix-org/dendrite/internal"
|
"github.com/matrix-org/dendrite/internal"
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const queueEDUsSchema = `
|
const queueEDUsSchema = `
|
||||||
|
|
@ -31,7 +33,9 @@ CREATE TABLE IF NOT EXISTS federationsender_queue_edus (
|
||||||
-- The domain part of the user ID the EDU event is for.
|
-- The domain part of the user ID the EDU event is for.
|
||||||
server_name TEXT NOT NULL,
|
server_name TEXT NOT NULL,
|
||||||
-- The JSON NID from the federationsender_queue_edus_json table.
|
-- The JSON NID from the federationsender_queue_edus_json table.
|
||||||
json_nid BIGINT NOT NULL
|
json_nid BIGINT NOT NULL,
|
||||||
|
-- The expiry time of this edu, if any.
|
||||||
|
expires_at BIGINT NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS federationsender_queue_edus_json_nid_idx
|
CREATE UNIQUE INDEX IF NOT EXISTS federationsender_queue_edus_json_nid_idx
|
||||||
|
|
@ -43,8 +47,8 @@ CREATE INDEX IF NOT EXISTS federationsender_queue_edus_server_name_idx
|
||||||
`
|
`
|
||||||
|
|
||||||
const insertQueueEDUSQL = "" +
|
const insertQueueEDUSQL = "" +
|
||||||
"INSERT INTO federationsender_queue_edus (edu_type, server_name, json_nid)" +
|
"INSERT INTO federationsender_queue_edus (edu_type, server_name, json_nid, expires_at)" +
|
||||||
" VALUES ($1, $2, $3)"
|
" VALUES ($1, $2, $3, $4)"
|
||||||
|
|
||||||
const deleteQueueEDUSQL = "" +
|
const deleteQueueEDUSQL = "" +
|
||||||
"DELETE FROM federationsender_queue_edus WHERE server_name = $1 AND json_nid = ANY($2)"
|
"DELETE FROM federationsender_queue_edus WHERE server_name = $1 AND json_nid = ANY($2)"
|
||||||
|
|
@ -65,6 +69,12 @@ const selectQueueEDUCountSQL = "" +
|
||||||
const selectQueueServerNamesSQL = "" +
|
const selectQueueServerNamesSQL = "" +
|
||||||
"SELECT DISTINCT server_name FROM federationsender_queue_edus"
|
"SELECT DISTINCT server_name FROM federationsender_queue_edus"
|
||||||
|
|
||||||
|
const selectExpiredEDUsSQL = "" +
|
||||||
|
"SELECT DISTINCT json_nid FROM federationsender_queue_edus WHERE expires_at > 0 AND expires_at <= $1"
|
||||||
|
|
||||||
|
const deleteExpiredEDUsSQL = "" +
|
||||||
|
"DELETE FROM federationsender_queue_edus WHERE expires_at > 0 AND expires_at <= $1"
|
||||||
|
|
||||||
type queueEDUsStatements struct {
|
type queueEDUsStatements struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
insertQueueEDUStmt *sql.Stmt
|
insertQueueEDUStmt *sql.Stmt
|
||||||
|
|
@ -73,6 +83,8 @@ type queueEDUsStatements struct {
|
||||||
selectQueueEDUReferenceJSONCountStmt *sql.Stmt
|
selectQueueEDUReferenceJSONCountStmt *sql.Stmt
|
||||||
selectQueueEDUCountStmt *sql.Stmt
|
selectQueueEDUCountStmt *sql.Stmt
|
||||||
selectQueueEDUServerNamesStmt *sql.Stmt
|
selectQueueEDUServerNamesStmt *sql.Stmt
|
||||||
|
selectExpiredEDUsStmt *sql.Stmt
|
||||||
|
deleteExpiredEDUsStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostgresQueueEDUsTable(db *sql.DB) (s *queueEDUsStatements, err error) {
|
func NewPostgresQueueEDUsTable(db *sql.DB) (s *queueEDUsStatements, err error) {
|
||||||
|
|
@ -81,27 +93,34 @@ func NewPostgresQueueEDUsTable(db *sql.DB) (s *queueEDUsStatements, err error) {
|
||||||
}
|
}
|
||||||
_, err = s.db.Exec(queueEDUsSchema)
|
_, err = s.db.Exec(queueEDUsSchema)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return s, err
|
||||||
}
|
}
|
||||||
if s.insertQueueEDUStmt, err = s.db.Prepare(insertQueueEDUSQL); err != nil {
|
|
||||||
return
|
m := sqlutil.NewMigrator(db)
|
||||||
|
m.AddMigrations(
|
||||||
|
sqlutil.Migration{
|
||||||
|
Version: "federationapi: add expiresat column",
|
||||||
|
Up: deltas.UpAddexpiresat,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err := m.Up(context.Background()); err != nil {
|
||||||
|
return s, err
|
||||||
}
|
}
|
||||||
if s.deleteQueueEDUStmt, err = s.db.Prepare(deleteQueueEDUSQL); err != nil {
|
|
||||||
return
|
return s, nil
|
||||||
}
|
}
|
||||||
if s.selectQueueEDUStmt, err = s.db.Prepare(selectQueueEDUSQL); err != nil {
|
|
||||||
return
|
func (s *queueEDUsStatements) Prepare() error {
|
||||||
}
|
return sqlutil.StatementList{
|
||||||
if s.selectQueueEDUReferenceJSONCountStmt, err = s.db.Prepare(selectQueueEDUReferenceJSONCountSQL); err != nil {
|
{&s.insertQueueEDUStmt, insertQueueEDUSQL},
|
||||||
return
|
{&s.deleteQueueEDUStmt, deleteQueueEDUSQL},
|
||||||
}
|
{&s.selectQueueEDUStmt, selectQueueEDUSQL},
|
||||||
if s.selectQueueEDUCountStmt, err = s.db.Prepare(selectQueueEDUCountSQL); err != nil {
|
{&s.selectQueueEDUReferenceJSONCountStmt, selectQueueEDUReferenceJSONCountSQL},
|
||||||
return
|
{&s.selectQueueEDUCountStmt, selectQueueEDUCountSQL},
|
||||||
}
|
{&s.selectQueueEDUServerNamesStmt, selectQueueServerNamesSQL},
|
||||||
if s.selectQueueEDUServerNamesStmt, err = s.db.Prepare(selectQueueServerNamesSQL); err != nil {
|
{&s.selectExpiredEDUsStmt, selectExpiredEDUsSQL},
|
||||||
return
|
{&s.deleteExpiredEDUsStmt, deleteExpiredEDUsSQL},
|
||||||
}
|
}.Prepare(s.db)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *queueEDUsStatements) InsertQueueEDU(
|
func (s *queueEDUsStatements) InsertQueueEDU(
|
||||||
|
|
@ -110,6 +129,7 @@ func (s *queueEDUsStatements) InsertQueueEDU(
|
||||||
eduType string,
|
eduType string,
|
||||||
serverName gomatrixserverlib.ServerName,
|
serverName gomatrixserverlib.ServerName,
|
||||||
nid int64,
|
nid int64,
|
||||||
|
expiresAt gomatrixserverlib.Timestamp,
|
||||||
) error {
|
) error {
|
||||||
stmt := sqlutil.TxStmt(txn, s.insertQueueEDUStmt)
|
stmt := sqlutil.TxStmt(txn, s.insertQueueEDUStmt)
|
||||||
_, err := stmt.ExecContext(
|
_, err := stmt.ExecContext(
|
||||||
|
|
@ -117,6 +137,7 @@ func (s *queueEDUsStatements) InsertQueueEDU(
|
||||||
eduType, // the EDU type
|
eduType, // the EDU type
|
||||||
serverName, // destination server name
|
serverName, // destination server name
|
||||||
nid, // JSON blob NID
|
nid, // JSON blob NID
|
||||||
|
expiresAt, // timestamp of expiry
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +171,7 @@ func (s *queueEDUsStatements) SelectQueueEDUs(
|
||||||
}
|
}
|
||||||
result = append(result, nid)
|
result = append(result, nid)
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *queueEDUsStatements) SelectQueueEDUReferenceJSONCount(
|
func (s *queueEDUsStatements) SelectQueueEDUReferenceJSONCount(
|
||||||
|
|
@ -200,3 +221,33 @@ func (s *queueEDUsStatements) SelectQueueEDUServerNames(
|
||||||
|
|
||||||
return result, rows.Err()
|
return result, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *queueEDUsStatements) SelectExpiredEDUs(
|
||||||
|
ctx context.Context, txn *sql.Tx,
|
||||||
|
expiredBefore gomatrixserverlib.Timestamp,
|
||||||
|
) ([]int64, error) {
|
||||||
|
stmt := sqlutil.TxStmt(txn, s.selectExpiredEDUsStmt)
|
||||||
|
rows, err := stmt.QueryContext(ctx, expiredBefore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "SelectExpiredEDUs: rows.close() failed")
|
||||||
|
var result []int64
|
||||||
|
var nid int64
|
||||||
|
for rows.Next() {
|
||||||
|
if err = rows.Scan(&nid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, nid)
|
||||||
|
}
|
||||||
|
return result, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *queueEDUsStatements) DeleteExpiredEDUs(
|
||||||
|
ctx context.Context, txn *sql.Tx,
|
||||||
|
expiredBefore gomatrixserverlib.Timestamp,
|
||||||
|
) error {
|
||||||
|
stmt := sqlutil.TxStmt(txn, s.deleteExpiredEDUsStmt)
|
||||||
|
_, err := stmt.ExecContext(ctx, expiredBefore)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,9 @@ func NewDatabase(base *base.BaseDendrite, dbProperties *config.DatabaseOptions,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err = queueEDUs.Prepare(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
d.Database = shared.Database{
|
d.Database = shared.Database{
|
||||||
DB: d.db,
|
DB: d.db,
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,21 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// defaultExpiry for EDUs if not listed below
|
||||||
|
var defaultExpiry = time.Hour * 24
|
||||||
|
|
||||||
|
// defaultExpireEDUTypes contains EDUs which can/should be expired after a given time
|
||||||
|
// if the target server isn't reachable for some reason.
|
||||||
|
var defaultExpireEDUTypes = map[string]time.Duration{
|
||||||
|
gomatrixserverlib.MTyping: time.Minute,
|
||||||
|
gomatrixserverlib.MPresence: time.Minute * 10,
|
||||||
|
}
|
||||||
|
|
||||||
// AssociateEDUWithDestination creates an association that the
|
// AssociateEDUWithDestination creates an association that the
|
||||||
// destination queues will use to determine which JSON blobs to send
|
// destination queues will use to determine which JSON blobs to send
|
||||||
// to which servers.
|
// to which servers.
|
||||||
|
|
@ -32,7 +43,21 @@ func (d *Database) AssociateEDUWithDestination(
|
||||||
serverName gomatrixserverlib.ServerName,
|
serverName gomatrixserverlib.ServerName,
|
||||||
receipt *Receipt,
|
receipt *Receipt,
|
||||||
eduType string,
|
eduType string,
|
||||||
|
expireEDUTypes map[string]time.Duration,
|
||||||
) error {
|
) error {
|
||||||
|
if expireEDUTypes == nil {
|
||||||
|
expireEDUTypes = defaultExpireEDUTypes
|
||||||
|
}
|
||||||
|
expiresAt := gomatrixserverlib.AsTimestamp(time.Now().Add(defaultExpiry))
|
||||||
|
if duration, ok := expireEDUTypes[eduType]; ok {
|
||||||
|
// Keep EDUs for at least x minutes before deleting them
|
||||||
|
expiresAt = gomatrixserverlib.AsTimestamp(time.Now().Add(duration))
|
||||||
|
}
|
||||||
|
// We forcibly set m.direct_to_device events to 0, as we always want them
|
||||||
|
// to be delivered. (required for E2EE)
|
||||||
|
if eduType == gomatrixserverlib.MDirectToDevice {
|
||||||
|
expiresAt = 0
|
||||||
|
}
|
||||||
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||||
if err := d.FederationQueueEDUs.InsertQueueEDU(
|
if err := d.FederationQueueEDUs.InsertQueueEDU(
|
||||||
ctx, // context
|
ctx, // context
|
||||||
|
|
@ -40,6 +65,7 @@ func (d *Database) AssociateEDUWithDestination(
|
||||||
eduType, // EDU type for coalescing
|
eduType, // EDU type for coalescing
|
||||||
serverName, // destination server name
|
serverName, // destination server name
|
||||||
receipt.nid, // NID from the federationapi_queue_json table
|
receipt.nid, // NID from the federationapi_queue_json table
|
||||||
|
expiresAt, // The timestamp this EDU will expire
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return fmt.Errorf("InsertQueueEDU: %w", err)
|
return fmt.Errorf("InsertQueueEDU: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -84,6 +110,7 @@ func (d *Database) GetPendingEDUs(
|
||||||
return fmt.Errorf("json.Unmarshal: %w", err)
|
return fmt.Errorf("json.Unmarshal: %w", err)
|
||||||
}
|
}
|
||||||
edus[&Receipt{nid}] = &event
|
edus[&Receipt{nid}] = &event
|
||||||
|
d.Cache.StoreFederationQueuedEDU(nid, &event)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -150,3 +177,34 @@ func (d *Database) GetPendingEDUServerNames(
|
||||||
) ([]gomatrixserverlib.ServerName, error) {
|
) ([]gomatrixserverlib.ServerName, error) {
|
||||||
return d.FederationQueueEDUs.SelectQueueEDUServerNames(ctx, nil)
|
return d.FederationQueueEDUs.SelectQueueEDUServerNames(ctx, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteExpiredEDUs deletes expired EDUs and evicts them from the cache.
|
||||||
|
func (d *Database) DeleteExpiredEDUs(ctx context.Context) error {
|
||||||
|
var jsonNIDs []int64
|
||||||
|
err := d.Writer.Do(d.DB, nil, func(txn *sql.Tx) (err error) {
|
||||||
|
expiredBefore := gomatrixserverlib.AsTimestamp(time.Now())
|
||||||
|
jsonNIDs, err = d.FederationQueueEDUs.SelectExpiredEDUs(ctx, txn, expiredBefore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(jsonNIDs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = d.FederationQueueJSON.DeleteQueueJSON(ctx, txn, jsonNIDs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.FederationQueueEDUs.DeleteExpiredEDUs(ctx, txn, expiredBefore)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range jsonNIDs {
|
||||||
|
d.Cache.EvictFederationQueuedEDU(jsonNIDs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2022 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 deltas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpAddexpiresat(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE federationsender_queue_edus RENAME TO federationsender_queue_edus_old;")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to rename table: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.ExecContext(ctx, `
|
||||||
|
CREATE TABLE IF NOT EXISTS federationsender_queue_edus (
|
||||||
|
edu_type TEXT NOT NULL,
|
||||||
|
server_name TEXT NOT NULL,
|
||||||
|
json_nid BIGINT NOT NULL,
|
||||||
|
expires_at BIGINT NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS federationsender_queue_edus_json_nid_idx
|
||||||
|
ON federationsender_queue_edus (json_nid, server_name);
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create new table: %w", err)
|
||||||
|
}
|
||||||
|
_, err = tx.ExecContext(ctx, `
|
||||||
|
INSERT
|
||||||
|
INTO federationsender_queue_edus (
|
||||||
|
edu_type, server_name, json_nid, expires_at
|
||||||
|
) SELECT edu_type, server_name, json_nid, 0 FROM federationsender_queue_edus_old;
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update queue_edus: %w", err)
|
||||||
|
}
|
||||||
|
_, err = tx.ExecContext(ctx, "UPDATE federationsender_queue_edus SET expires_at = $1 WHERE edu_type != 'm.direct_to_device'", gomatrixserverlib.AsTimestamp(time.Now().Add(time.Hour*24)))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update queue_edus: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DownAddexpiresat(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE federationsender_queue_edus DROP COLUMN expires_at;")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to rename table: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -20,9 +20,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/storage/sqlite3/deltas"
|
||||||
"github.com/matrix-org/dendrite/internal"
|
"github.com/matrix-org/dendrite/internal"
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const queueEDUsSchema = `
|
const queueEDUsSchema = `
|
||||||
|
|
@ -32,7 +34,9 @@ CREATE TABLE IF NOT EXISTS federationsender_queue_edus (
|
||||||
-- The domain part of the user ID the EDU event is for.
|
-- The domain part of the user ID the EDU event is for.
|
||||||
server_name TEXT NOT NULL,
|
server_name TEXT NOT NULL,
|
||||||
-- The JSON NID from the federationsender_queue_edus_json table.
|
-- The JSON NID from the federationsender_queue_edus_json table.
|
||||||
json_nid BIGINT NOT NULL
|
json_nid BIGINT NOT NULL,
|
||||||
|
-- The expiry time of this edu, if any.
|
||||||
|
expires_at BIGINT NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS federationsender_queue_edus_json_nid_idx
|
CREATE UNIQUE INDEX IF NOT EXISTS federationsender_queue_edus_json_nid_idx
|
||||||
|
|
@ -44,8 +48,8 @@ CREATE INDEX IF NOT EXISTS federationsender_queue_edus_server_name_idx
|
||||||
`
|
`
|
||||||
|
|
||||||
const insertQueueEDUSQL = "" +
|
const insertQueueEDUSQL = "" +
|
||||||
"INSERT INTO federationsender_queue_edus (edu_type, server_name, json_nid)" +
|
"INSERT INTO federationsender_queue_edus (edu_type, server_name, json_nid, expires_at)" +
|
||||||
" VALUES ($1, $2, $3)"
|
" VALUES ($1, $2, $3, $4)"
|
||||||
|
|
||||||
const deleteQueueEDUsSQL = "" +
|
const deleteQueueEDUsSQL = "" +
|
||||||
"DELETE FROM federationsender_queue_edus WHERE server_name = $1 AND json_nid IN ($2)"
|
"DELETE FROM federationsender_queue_edus WHERE server_name = $1 AND json_nid IN ($2)"
|
||||||
|
|
@ -66,13 +70,22 @@ const selectQueueEDUCountSQL = "" +
|
||||||
const selectQueueServerNamesSQL = "" +
|
const selectQueueServerNamesSQL = "" +
|
||||||
"SELECT DISTINCT server_name FROM federationsender_queue_edus"
|
"SELECT DISTINCT server_name FROM federationsender_queue_edus"
|
||||||
|
|
||||||
|
const selectExpiredEDUsSQL = "" +
|
||||||
|
"SELECT DISTINCT json_nid FROM federationsender_queue_edus WHERE expires_at > 0 AND expires_at <= $1"
|
||||||
|
|
||||||
|
const deleteExpiredEDUsSQL = "" +
|
||||||
|
"DELETE FROM federationsender_queue_edus WHERE expires_at > 0 AND expires_at <= $1"
|
||||||
|
|
||||||
type queueEDUsStatements struct {
|
type queueEDUsStatements struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
insertQueueEDUStmt *sql.Stmt
|
insertQueueEDUStmt *sql.Stmt
|
||||||
|
// deleteQueueEDUStmt *sql.Stmt - prepared at runtime due to variadic
|
||||||
selectQueueEDUStmt *sql.Stmt
|
selectQueueEDUStmt *sql.Stmt
|
||||||
selectQueueEDUReferenceJSONCountStmt *sql.Stmt
|
selectQueueEDUReferenceJSONCountStmt *sql.Stmt
|
||||||
selectQueueEDUCountStmt *sql.Stmt
|
selectQueueEDUCountStmt *sql.Stmt
|
||||||
selectQueueEDUServerNamesStmt *sql.Stmt
|
selectQueueEDUServerNamesStmt *sql.Stmt
|
||||||
|
selectExpiredEDUsStmt *sql.Stmt
|
||||||
|
deleteExpiredEDUsStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLiteQueueEDUsTable(db *sql.DB) (s *queueEDUsStatements, err error) {
|
func NewSQLiteQueueEDUsTable(db *sql.DB) (s *queueEDUsStatements, err error) {
|
||||||
|
|
@ -81,24 +94,33 @@ func NewSQLiteQueueEDUsTable(db *sql.DB) (s *queueEDUsStatements, err error) {
|
||||||
}
|
}
|
||||||
_, err = db.Exec(queueEDUsSchema)
|
_, err = db.Exec(queueEDUsSchema)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return s, err
|
||||||
}
|
}
|
||||||
if s.insertQueueEDUStmt, err = db.Prepare(insertQueueEDUSQL); err != nil {
|
|
||||||
return
|
m := sqlutil.NewMigrator(db)
|
||||||
|
m.AddMigrations(
|
||||||
|
sqlutil.Migration{
|
||||||
|
Version: "federationapi: add expiresat column",
|
||||||
|
Up: deltas.UpAddexpiresat,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err := m.Up(context.Background()); err != nil {
|
||||||
|
return s, err
|
||||||
}
|
}
|
||||||
if s.selectQueueEDUStmt, err = db.Prepare(selectQueueEDUSQL); err != nil {
|
|
||||||
return
|
return s, nil
|
||||||
}
|
}
|
||||||
if s.selectQueueEDUReferenceJSONCountStmt, err = db.Prepare(selectQueueEDUReferenceJSONCountSQL); err != nil {
|
|
||||||
return
|
func (s *queueEDUsStatements) Prepare() error {
|
||||||
}
|
return sqlutil.StatementList{
|
||||||
if s.selectQueueEDUCountStmt, err = db.Prepare(selectQueueEDUCountSQL); err != nil {
|
{&s.insertQueueEDUStmt, insertQueueEDUSQL},
|
||||||
return
|
{&s.selectQueueEDUStmt, selectQueueEDUSQL},
|
||||||
}
|
{&s.selectQueueEDUReferenceJSONCountStmt, selectQueueEDUReferenceJSONCountSQL},
|
||||||
if s.selectQueueEDUServerNamesStmt, err = db.Prepare(selectQueueServerNamesSQL); err != nil {
|
{&s.selectQueueEDUCountStmt, selectQueueEDUCountSQL},
|
||||||
return
|
{&s.selectQueueEDUServerNamesStmt, selectQueueServerNamesSQL},
|
||||||
}
|
{&s.selectExpiredEDUsStmt, selectExpiredEDUsSQL},
|
||||||
return
|
{&s.deleteExpiredEDUsStmt, deleteExpiredEDUsSQL},
|
||||||
|
}.Prepare(s.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *queueEDUsStatements) InsertQueueEDU(
|
func (s *queueEDUsStatements) InsertQueueEDU(
|
||||||
|
|
@ -107,6 +129,7 @@ func (s *queueEDUsStatements) InsertQueueEDU(
|
||||||
eduType string,
|
eduType string,
|
||||||
serverName gomatrixserverlib.ServerName,
|
serverName gomatrixserverlib.ServerName,
|
||||||
nid int64,
|
nid int64,
|
||||||
|
expiresAt gomatrixserverlib.Timestamp,
|
||||||
) error {
|
) error {
|
||||||
stmt := sqlutil.TxStmt(txn, s.insertQueueEDUStmt)
|
stmt := sqlutil.TxStmt(txn, s.insertQueueEDUStmt)
|
||||||
_, err := stmt.ExecContext(
|
_, err := stmt.ExecContext(
|
||||||
|
|
@ -114,6 +137,7 @@ func (s *queueEDUsStatements) InsertQueueEDU(
|
||||||
eduType, // the EDU type
|
eduType, // the EDU type
|
||||||
serverName, // destination server name
|
serverName, // destination server name
|
||||||
nid, // JSON blob NID
|
nid, // JSON blob NID
|
||||||
|
expiresAt, // timestamp of expiry
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +183,7 @@ func (s *queueEDUsStatements) SelectQueueEDUs(
|
||||||
}
|
}
|
||||||
result = append(result, nid)
|
result = append(result, nid)
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *queueEDUsStatements) SelectQueueEDUReferenceJSONCount(
|
func (s *queueEDUsStatements) SelectQueueEDUReferenceJSONCount(
|
||||||
|
|
@ -209,3 +233,33 @@ func (s *queueEDUsStatements) SelectQueueEDUServerNames(
|
||||||
|
|
||||||
return result, rows.Err()
|
return result, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *queueEDUsStatements) SelectExpiredEDUs(
|
||||||
|
ctx context.Context, txn *sql.Tx,
|
||||||
|
expiredBefore gomatrixserverlib.Timestamp,
|
||||||
|
) ([]int64, error) {
|
||||||
|
stmt := sqlutil.TxStmt(txn, s.selectExpiredEDUsStmt)
|
||||||
|
rows, err := stmt.QueryContext(ctx, expiredBefore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "SelectExpiredEDUs: rows.close() failed")
|
||||||
|
var result []int64
|
||||||
|
var nid int64
|
||||||
|
for rows.Next() {
|
||||||
|
if err = rows.Scan(&nid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, nid)
|
||||||
|
}
|
||||||
|
return result, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *queueEDUsStatements) DeleteExpiredEDUs(
|
||||||
|
ctx context.Context, txn *sql.Tx,
|
||||||
|
expiredBefore gomatrixserverlib.Timestamp,
|
||||||
|
) error {
|
||||||
|
stmt := sqlutil.TxStmt(txn, s.deleteExpiredEDUsStmt)
|
||||||
|
_, err := stmt.ExecContext(ctx, expiredBefore)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,9 @@ func NewDatabase(base *base.BaseDendrite, dbProperties *config.DatabaseOptions,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err = queueEDUs.Prepare(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
d.Database = shared.Database{
|
d.Database = shared.Database{
|
||||||
DB: d.db,
|
DB: d.db,
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
|
|
|
||||||
81
federationapi/storage/storage_test.go
Normal file
81
federationapi/storage/storage_test.go
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
package storage_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/test"
|
||||||
|
"github.com/matrix-org/dendrite/test/testrig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mustCreateFederationDatabase(t *testing.T, dbType test.DBType) (storage.Database, func()) {
|
||||||
|
b, baseClose := testrig.CreateBaseDendrite(t, dbType)
|
||||||
|
connStr, dbClose := test.PrepareDBConnectionString(t, dbType)
|
||||||
|
db, err := storage.NewDatabase(b, &config.DatabaseOptions{
|
||||||
|
ConnectionString: config.DataSource(connStr),
|
||||||
|
}, b.Caches, b.Cfg.Global.ServerName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewDatabase returned %s", err)
|
||||||
|
}
|
||||||
|
return db, func() {
|
||||||
|
dbClose()
|
||||||
|
baseClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExpireEDUs(t *testing.T) {
|
||||||
|
var expireEDUTypes = map[string]time.Duration{
|
||||||
|
gomatrixserverlib.MReceipt: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||||
|
db, close := mustCreateFederationDatabase(t, dbType)
|
||||||
|
defer close()
|
||||||
|
// insert some data
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
receipt, err := db.StoreJSON(ctx, "{}")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.AssociateEDUWithDestination(ctx, "localhost", receipt, gomatrixserverlib.MReceipt, expireEDUTypes)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
// add data without expiry
|
||||||
|
receipt, err := db.StoreJSON(ctx, "{}")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// m.read_marker gets the default expiry of 24h, so won't be deleted further down in this test
|
||||||
|
err = db.AssociateEDUWithDestination(ctx, "localhost", receipt, "m.read_marker", expireEDUTypes)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Delete expired EDUs
|
||||||
|
err = db.DeleteExpiredEDUs(ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// verify the data is gone
|
||||||
|
data, err := db.GetPendingEDUs(ctx, "localhost", 100)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 1, len(data))
|
||||||
|
|
||||||
|
// check that m.direct_to_device is never expired
|
||||||
|
receipt, err = db.StoreJSON(ctx, "{}")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.AssociateEDUWithDestination(ctx, "localhost", receipt, gomatrixserverlib.MDirectToDevice, expireEDUTypes)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.DeleteExpiredEDUs(ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// We should get two EDUs, the m.read_marker and the m.direct_to_device
|
||||||
|
data, err = db.GetPendingEDUs(ctx, "localhost", 100)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -34,12 +34,15 @@ type FederationQueuePDUs interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type FederationQueueEDUs interface {
|
type FederationQueueEDUs interface {
|
||||||
InsertQueueEDU(ctx context.Context, txn *sql.Tx, eduType string, serverName gomatrixserverlib.ServerName, nid int64) error
|
InsertQueueEDU(ctx context.Context, txn *sql.Tx, eduType string, serverName gomatrixserverlib.ServerName, nid int64, expiresAt gomatrixserverlib.Timestamp) error
|
||||||
DeleteQueueEDUs(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, jsonNIDs []int64) error
|
DeleteQueueEDUs(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, jsonNIDs []int64) error
|
||||||
SelectQueueEDUs(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, limit int) ([]int64, error)
|
SelectQueueEDUs(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, limit int) ([]int64, error)
|
||||||
SelectQueueEDUReferenceJSONCount(ctx context.Context, txn *sql.Tx, jsonNID int64) (int64, error)
|
SelectQueueEDUReferenceJSONCount(ctx context.Context, txn *sql.Tx, jsonNID int64) (int64, error)
|
||||||
SelectQueueEDUCount(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName) (int64, error)
|
SelectQueueEDUCount(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName) (int64, error)
|
||||||
SelectQueueEDUServerNames(ctx context.Context, txn *sql.Tx) ([]gomatrixserverlib.ServerName, error)
|
SelectQueueEDUServerNames(ctx context.Context, txn *sql.Tx) ([]gomatrixserverlib.ServerName, error)
|
||||||
|
SelectExpiredEDUs(ctx context.Context, txn *sql.Tx, expiredBefore gomatrixserverlib.Timestamp) ([]int64, error)
|
||||||
|
DeleteExpiredEDUs(ctx context.Context, txn *sql.Tx, expiredBefore gomatrixserverlib.Timestamp) error
|
||||||
|
Prepare() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type FederationQueueJSON interface {
|
type FederationQueueJSON interface {
|
||||||
|
|
|
||||||
12
go.mod
12
go.mod
|
|
@ -21,12 +21,12 @@ require (
|
||||||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
|
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/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220725104114-b6003e522771
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20220824082345-662dca17bf94
|
||||||
github.com/matrix-org/pinecone v0.0.0-20220803093810-b7a830c08fb9
|
github.com/matrix-org/pinecone v0.0.0-20220803093810-b7a830c08fb9
|
||||||
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
|
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
|
||||||
github.com/mattn/go-sqlite3 v1.14.13
|
github.com/mattn/go-sqlite3 v1.14.13
|
||||||
github.com/nats-io/nats-server/v2 v2.8.5-0.20220731184415-903a06a5b4ee
|
github.com/nats-io/nats-server/v2 v2.8.5-0.20220811224153-d8d25d9b0b1c
|
||||||
github.com/nats-io/nats.go v1.16.1-0.20220731182438-87bbea85922b
|
github.com/nats-io/nats.go v1.16.1-0.20220810192301-fb5ca2cbc995
|
||||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9
|
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79
|
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79
|
||||||
|
|
@ -34,7 +34,7 @@ require (
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.12.2
|
github.com/prometheus/client_golang v1.12.2
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/stretchr/testify v1.7.1
|
github.com/stretchr/testify v1.7.1
|
||||||
github.com/tidwall/gjson v1.14.1
|
github.com/tidwall/gjson v1.14.1
|
||||||
github.com/tidwall/sjson v1.2.4
|
github.com/tidwall/sjson v1.2.4
|
||||||
|
|
@ -42,7 +42,7 @@ require (
|
||||||
github.com/uber/jaeger-lib v2.4.1+incompatible
|
github.com/uber/jaeger-lib v2.4.1+incompatible
|
||||||
github.com/yggdrasil-network/yggdrasil-go v0.4.3
|
github.com/yggdrasil-network/yggdrasil-go v0.4.3
|
||||||
go.uber.org/atomic v1.9.0
|
go.uber.org/atomic v1.9.0
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||||
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9
|
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9
|
||||||
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd
|
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd
|
||||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
|
||||||
|
|
@ -99,7 +99,7 @@ require (
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
golang.org/x/sys v0.0.0-20220731174439-a90be440212d // indirect
|
||||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b // indirect
|
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b // indirect
|
||||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
|
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
|
||||||
golang.org/x/tools v0.1.10 // indirect
|
golang.org/x/tools v0.1.10 // indirect
|
||||||
|
|
|
||||||
24
go.sum
24
go.sum
|
|
@ -343,8 +343,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0=
|
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0=
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4=
|
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4=
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220725104114-b6003e522771 h1:ZIPHFIPNDS9dmEbPEiJbNmyCGJtn9exfpLC7JOcn/bE=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20220824082345-662dca17bf94 h1:zoTv/qxg7C/O995JBPvp+Z8KMR69HhB+M+P22A8Hmm0=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220725104114-b6003e522771/go.mod h1:jX38yp3SSLJNftBg3PXU1ayd0PCLIiDHQ4xAc9DIixk=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20220824082345-662dca17bf94/go.mod h1:jX38yp3SSLJNftBg3PXU1ayd0PCLIiDHQ4xAc9DIixk=
|
||||||
github.com/matrix-org/pinecone v0.0.0-20220803093810-b7a830c08fb9 h1:ed8yvWhTLk7+sNeK/eOZRTvESFTOHDRevoRoyeqPtvY=
|
github.com/matrix-org/pinecone v0.0.0-20220803093810-b7a830c08fb9 h1:ed8yvWhTLk7+sNeK/eOZRTvESFTOHDRevoRoyeqPtvY=
|
||||||
github.com/matrix-org/pinecone v0.0.0-20220803093810-b7a830c08fb9/go.mod h1:P4MqPf+u83OPulPJ+XTbSDbbWrdFYNY4LZ/B1PIduFE=
|
github.com/matrix-org/pinecone v0.0.0-20220803093810-b7a830c08fb9/go.mod h1:P4MqPf+u83OPulPJ+XTbSDbbWrdFYNY4LZ/B1PIduFE=
|
||||||
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=
|
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=
|
||||||
|
|
@ -385,10 +385,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI=
|
github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI=
|
||||||
github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
|
github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
|
||||||
github.com/nats-io/nats-server/v2 v2.8.5-0.20220731184415-903a06a5b4ee h1:vAtoZ+LW6eIUjkCWWwO1DZ6o16UGrVOG+ot/AkwejO8=
|
github.com/nats-io/nats-server/v2 v2.8.5-0.20220811224153-d8d25d9b0b1c h1:U5qngWGZ7E/nQxz0544IpIEdKFUUaOJxQN2LHCYLGhg=
|
||||||
github.com/nats-io/nats-server/v2 v2.8.5-0.20220731184415-903a06a5b4ee/go.mod h1:3Yg3ApyQxPlAs1KKHKV5pobV5VtZk+TtOiUJx/iqkkg=
|
github.com/nats-io/nats-server/v2 v2.8.5-0.20220811224153-d8d25d9b0b1c/go.mod h1:+f++B/5jpr71JATt7b5KCX+G7bt43iWx1OYWGkpE/Kk=
|
||||||
github.com/nats-io/nats.go v1.16.1-0.20220731182438-87bbea85922b h1:CE9wSYLvwq8aC/0+6zH8lhhtZYvJ9p8PzwvZeYgdBc0=
|
github.com/nats-io/nats.go v1.16.1-0.20220810192301-fb5ca2cbc995 h1:CUcSQR8jwa9//qNgN/t3tW53DObnTPQ/G/K+qnS7yRc=
|
||||||
github.com/nats-io/nats.go v1.16.1-0.20220731182438-87bbea85922b/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
github.com/nats-io/nats.go v1.16.1-0.20220810192301-fb5ca2cbc995/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
||||||
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
|
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
|
||||||
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
|
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
|
|
@ -493,8 +493,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||||
|
|
@ -569,8 +569,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
|
@ -748,8 +748,10 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80=
|
||||||
|
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM=
|
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM=
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ type lazyLoadingCacheKey struct {
|
||||||
type LazyLoadCache interface {
|
type LazyLoadCache interface {
|
||||||
StoreLazyLoadedUser(device *userapi.Device, roomID, userID, eventID string)
|
StoreLazyLoadedUser(device *userapi.Device, roomID, userID, eventID string)
|
||||||
IsLazyLoadedUserCached(device *userapi.Device, roomID, userID string) (string, bool)
|
IsLazyLoadedUserCached(device *userapi.Device, roomID, userID string) (string, bool)
|
||||||
|
InvalidateLazyLoadedUser(device *userapi.Device, roomID, userID string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Caches) StoreLazyLoadedUser(device *userapi.Device, roomID, userID, eventID string) {
|
func (c Caches) StoreLazyLoadedUser(device *userapi.Device, roomID, userID, eventID string) {
|
||||||
|
|
@ -33,3 +34,12 @@ func (c Caches) IsLazyLoadedUserCached(device *userapi.Device, roomID, userID st
|
||||||
TargetUserID: userID,
|
TargetUserID: userID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Caches) InvalidateLazyLoadedUser(device *userapi.Device, roomID, userID string) {
|
||||||
|
c.LazyLoading.Unset(lazyLoadingCacheKey{
|
||||||
|
UserID: device.UserID,
|
||||||
|
DeviceID: device.ID,
|
||||||
|
RoomID: roomID,
|
||||||
|
TargetUserID: userID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ func (c *RistrettoCostedCachePartition[K, V]) Set(key K, value V) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RistrettoCachePartition[K keyable, V any] struct {
|
type RistrettoCachePartition[K keyable, V any] struct {
|
||||||
cache *ristretto.Cache
|
cache *ristretto.Cache //nolint:all,unused
|
||||||
Prefix byte
|
Prefix byte
|
||||||
Mutable bool
|
Mutable bool
|
||||||
MaxAge time.Duration
|
MaxAge time.Duration
|
||||||
|
|
|
||||||
|
|
@ -19,19 +19,21 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
"github.com/opentracing/opentracing-go/ext"
|
"github.com/opentracing/opentracing-go/ext"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PostJSON performs a POST request with JSON on an internal HTTP API
|
// PostJSON performs a POST request with JSON on an internal HTTP API.
|
||||||
func PostJSON(
|
// The error will match the errtype if returned from the remote API, or
|
||||||
|
// will be a different type if there was a problem reaching the API.
|
||||||
|
func PostJSON[reqtype, restype any, errtype error](
|
||||||
ctx context.Context, span opentracing.Span, httpClient *http.Client,
|
ctx context.Context, span opentracing.Span, httpClient *http.Client,
|
||||||
apiURL string, request, response interface{},
|
apiURL string, request *reqtype, response *restype,
|
||||||
) error {
|
) error {
|
||||||
jsonBytes, err := json.Marshal(request)
|
jsonBytes, err := json.Marshal(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -69,17 +71,23 @@ func PostJSON(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
var body []byte
|
||||||
|
body, err = io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if res.StatusCode != http.StatusOK {
|
if res.StatusCode != http.StatusOK {
|
||||||
var errorBody struct {
|
if len(body) == 0 {
|
||||||
Message string `json:"message"`
|
return fmt.Errorf("HTTP %d from %s (no response body)", res.StatusCode, apiURL)
|
||||||
|
}
|
||||||
|
var reserr errtype
|
||||||
|
if err = json.Unmarshal(body, reserr); err != nil {
|
||||||
|
return fmt.Errorf("HTTP %d from %s", res.StatusCode, apiURL)
|
||||||
|
}
|
||||||
|
return reserr
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(body, response); err != nil {
|
||||||
|
return fmt.Errorf("json.Unmarshal: %w", err)
|
||||||
}
|
}
|
||||||
if _, ok := response.(*api.PerformKeyBackupResponse); ok { // TODO: remove this, once cross-boundary errors are a thing
|
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
if msgerr := json.NewDecoder(res.Body).Decode(&errorBody); msgerr == nil {
|
|
||||||
return fmt.Errorf("internal API: %d from %s: %s", res.StatusCode, apiURL, errorBody.Message)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("internal API: %d from %s", res.StatusCode, apiURL)
|
|
||||||
}
|
|
||||||
return json.NewDecoder(res.Body).Decode(response)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
|
|
@ -83,6 +84,23 @@ func MakeAuthAPI(
|
||||||
return MakeExternalAPI(metricsName, h)
|
return MakeExternalAPI(metricsName, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeAdminAPI is a wrapper around MakeAuthAPI which enforces that the request can only be
|
||||||
|
// completed by a user that is a server administrator.
|
||||||
|
func MakeAdminAPI(
|
||||||
|
metricsName string, userAPI userapi.QueryAcccessTokenAPI,
|
||||||
|
f func(*http.Request, *userapi.Device) util.JSONResponse,
|
||||||
|
) http.Handler {
|
||||||
|
return MakeAuthAPI(metricsName, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
if device.AccountType != userapi.AccountTypeAdmin {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: jsonerror.Forbidden("This API can only be used by admin users."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f(req, device)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// MakeExternalAPI turns a util.JSONRequestHandler function into an http.Handler.
|
// MakeExternalAPI turns a util.JSONRequestHandler function into an http.Handler.
|
||||||
// This is used for APIs that are called from the internet.
|
// This is used for APIs that are called from the internet.
|
||||||
func MakeExternalAPI(metricsName string, f func(*http.Request) util.JSONResponse) http.Handler {
|
func MakeExternalAPI(metricsName string, f func(*http.Request) util.JSONResponse) http.Handler {
|
||||||
|
|
|
||||||
93
internal/httputil/internalapi.go
Normal file
93
internal/httputil/internalapi.go
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
// Copyright 2022 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 httputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InternalAPIError struct {
|
||||||
|
Type string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e InternalAPIError) Error() string {
|
||||||
|
return fmt.Sprintf("internal API returned %q error: %s", e.Type, e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeInternalRPCAPI[reqtype, restype any](metricsName string, f func(context.Context, *reqtype, *restype) error) http.Handler {
|
||||||
|
return MakeInternalAPI(metricsName, func(req *http.Request) util.JSONResponse {
|
||||||
|
var request reqtype
|
||||||
|
var response restype
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
|
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
if err := f(req.Context(), &request, &response); err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: &InternalAPIError{
|
||||||
|
Type: reflect.TypeOf(err).String(),
|
||||||
|
Message: fmt.Sprintf("%s", err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: &response,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeInternalProxyAPI[reqtype, restype any](metricsName string, f func(context.Context, *reqtype) (*restype, error)) http.Handler {
|
||||||
|
return MakeInternalAPI(metricsName, func(req *http.Request) util.JSONResponse {
|
||||||
|
var request reqtype
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
|
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
response, err := f(req.Context(), &request)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: response,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallInternalRPCAPI[reqtype, restype any](name, url string, client *http.Client, ctx context.Context, request *reqtype, response *restype) error {
|
||||||
|
span, ctx := opentracing.StartSpanFromContext(ctx, name)
|
||||||
|
defer span.Finish()
|
||||||
|
|
||||||
|
return PostJSON[reqtype, restype, InternalAPIError](ctx, span, client, url, request, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallInternalProxyAPI[reqtype, restype any, errtype error](name, url string, client *http.Client, ctx context.Context, request *reqtype) (restype, error) {
|
||||||
|
span, ctx := opentracing.StartSpanFromContext(ctx, name)
|
||||||
|
defer span.Finish()
|
||||||
|
|
||||||
|
var response restype
|
||||||
|
return response, PostJSON[reqtype, restype, errtype](ctx, span, client, url, request, &response)
|
||||||
|
}
|
||||||
|
|
@ -27,9 +27,10 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
|
||||||
"github.com/matrix-org/dugong"
|
"github.com/matrix-org/dugong"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type utcFormatter struct {
|
type utcFormatter struct {
|
||||||
|
|
@ -145,7 +146,7 @@ func setupFileHook(hook config.LogrusHook, level logrus.Level, componentName str
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//CloseAndLogIfError Closes io.Closer and logs the error if any
|
// CloseAndLogIfError Closes io.Closer and logs the error if any
|
||||||
func CloseAndLogIfError(ctx context.Context, closer io.Closer, message string) {
|
func CloseAndLogIfError(ctx context.Context, closer io.Closer, message string) {
|
||||||
if closer == nil {
|
if closer == nil {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io"
|
||||||
"log/syslog"
|
"log/syslog"
|
||||||
|
|
||||||
"github.com/MFAshby/stdemuxerhook"
|
"github.com/MFAshby/stdemuxerhook"
|
||||||
|
|
@ -63,7 +63,7 @@ func SetupHookLogging(hooks []config.LogrusHook, componentName string) {
|
||||||
setupStdLogHook(logrus.InfoLevel)
|
setupStdLogHook(logrus.InfoLevel)
|
||||||
}
|
}
|
||||||
// Hooks are now configured for stdout/err, so throw away the default logger output
|
// Hooks are now configured for stdout/err, so throw away the default logger output
|
||||||
logrus.SetOutput(ioutil.Discard)
|
logrus.SetOutput(io.Discard)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSyslogHookParams(params map[string]interface{}) {
|
func checkSyslogHookParams(params map[string]interface{}) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ var build string
|
||||||
const (
|
const (
|
||||||
VersionMajor = 0
|
VersionMajor = 0
|
||||||
VersionMinor = 9
|
VersionMinor = 9
|
||||||
VersionPatch = 1
|
VersionPatch = 5
|
||||||
VersionTag = "" // example: "rc1"
|
VersionTag = "" // example: "rc1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,32 +38,32 @@ type KeyInternalAPI interface {
|
||||||
|
|
||||||
// API functions required by the clientapi
|
// API functions required by the clientapi
|
||||||
type ClientKeyAPI interface {
|
type ClientKeyAPI interface {
|
||||||
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse) error
|
||||||
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse)
|
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) error
|
||||||
PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse)
|
PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse) error
|
||||||
PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse)
|
PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse) error
|
||||||
// PerformClaimKeys claims one-time keys for use in pre-key messages
|
// PerformClaimKeys claims one-time keys for use in pre-key messages
|
||||||
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
|
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// API functions required by the userapi
|
// API functions required by the userapi
|
||||||
type UserKeyAPI interface {
|
type UserKeyAPI interface {
|
||||||
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse)
|
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) error
|
||||||
PerformDeleteKeys(ctx context.Context, req *PerformDeleteKeysRequest, res *PerformDeleteKeysResponse)
|
PerformDeleteKeys(ctx context.Context, req *PerformDeleteKeysRequest, res *PerformDeleteKeysResponse) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// API functions required by the syncapi
|
// API functions required by the syncapi
|
||||||
type SyncKeyAPI interface {
|
type SyncKeyAPI interface {
|
||||||
QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse)
|
QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse) error
|
||||||
QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse)
|
QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type FederationKeyAPI interface {
|
type FederationKeyAPI interface {
|
||||||
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse) error
|
||||||
QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse)
|
QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse) error
|
||||||
QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse)
|
QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse) error
|
||||||
PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse)
|
PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse) error
|
||||||
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
|
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyError is returned if there was a problem performing/querying the server
|
// KeyError is returned if there was a problem performing/querying the server
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ func sanityCheckKey(key gomatrixserverlib.CrossSigningKey, userID string, purpos
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:gocyclo
|
// nolint:gocyclo
|
||||||
func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) {
|
func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) error {
|
||||||
// Find the keys to store.
|
// Find the keys to store.
|
||||||
byPurpose := map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey{}
|
byPurpose := map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey{}
|
||||||
toStore := types.CrossSigningKeyMap{}
|
toStore := types.CrossSigningKeyMap{}
|
||||||
|
|
@ -115,7 +115,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
Err: "Master key sanity check failed: " + err.Error(),
|
Err: "Master key sanity check failed: " + err.Error(),
|
||||||
IsInvalidParam: true,
|
IsInvalidParam: true,
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
byPurpose[gomatrixserverlib.CrossSigningKeyPurposeMaster] = req.MasterKey
|
byPurpose[gomatrixserverlib.CrossSigningKeyPurposeMaster] = req.MasterKey
|
||||||
|
|
@ -131,7 +131,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
Err: "Self-signing key sanity check failed: " + err.Error(),
|
Err: "Self-signing key sanity check failed: " + err.Error(),
|
||||||
IsInvalidParam: true,
|
IsInvalidParam: true,
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
byPurpose[gomatrixserverlib.CrossSigningKeyPurposeSelfSigning] = req.SelfSigningKey
|
byPurpose[gomatrixserverlib.CrossSigningKeyPurposeSelfSigning] = req.SelfSigningKey
|
||||||
|
|
@ -146,7 +146,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
Err: "User-signing key sanity check failed: " + err.Error(),
|
Err: "User-signing key sanity check failed: " + err.Error(),
|
||||||
IsInvalidParam: true,
|
IsInvalidParam: true,
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
byPurpose[gomatrixserverlib.CrossSigningKeyPurposeUserSigning] = req.UserSigningKey
|
byPurpose[gomatrixserverlib.CrossSigningKeyPurposeUserSigning] = req.UserSigningKey
|
||||||
|
|
@ -161,7 +161,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
Err: "No keys were supplied in the request",
|
Err: "No keys were supplied in the request",
|
||||||
IsMissingParam: true,
|
IsMissingParam: true,
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't have a self-signing or user-signing key without a master
|
// We can't have a self-signing or user-signing key without a master
|
||||||
|
|
@ -174,7 +174,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: "Retrieving cross-signing keys from database failed: " + err.Error(),
|
Err: "Retrieving cross-signing keys from database failed: " + err.Error(),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we still can't find a master key for the user then stop the upload.
|
// If we still can't find a master key for the user then stop the upload.
|
||||||
|
|
@ -185,7 +185,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
Err: "No master key was found",
|
Err: "No master key was found",
|
||||||
IsMissingParam: true,
|
IsMissingParam: true,
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,7 +212,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !changed {
|
if !changed {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the keys.
|
// Store the keys.
|
||||||
|
|
@ -220,7 +220,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("a.DB.StoreCrossSigningKeysForUser: %s", err),
|
Err: fmt.Sprintf("a.DB.StoreCrossSigningKeysForUser: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now upload any signatures that were included with the keys.
|
// Now upload any signatures that were included with the keys.
|
||||||
|
|
@ -238,7 +238,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("a.DB.StoreCrossSigningSigsForTarget: %s", err),
|
Err: fmt.Sprintf("a.DB.StoreCrossSigningSigsForTarget: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -255,17 +255,18 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
|
||||||
update.SelfSigningKey = &ssk
|
update.SelfSigningKey = &ssk
|
||||||
}
|
}
|
||||||
if update.MasterKey == nil && update.SelfSigningKey == nil {
|
if update.MasterKey == nil && update.SelfSigningKey == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
if err := a.Producer.ProduceSigningKeyUpdate(update); err != nil {
|
if err := a.Producer.ProduceSigningKeyUpdate(update); err != nil {
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("a.Producer.ProduceSigningKeyUpdate: %s", err),
|
Err: fmt.Sprintf("a.Producer.ProduceSigningKeyUpdate: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req *api.PerformUploadDeviceSignaturesRequest, res *api.PerformUploadDeviceSignaturesResponse) {
|
func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req *api.PerformUploadDeviceSignaturesRequest, res *api.PerformUploadDeviceSignaturesResponse) error {
|
||||||
// Before we do anything, we need the master and self-signing keys for this user.
|
// Before we do anything, we need the master and self-signing keys for this user.
|
||||||
// Then we can verify the signatures make sense.
|
// Then we can verify the signatures make sense.
|
||||||
queryReq := &api.QueryKeysRequest{
|
queryReq := &api.QueryKeysRequest{
|
||||||
|
|
@ -276,7 +277,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req
|
||||||
for userID := range req.Signatures {
|
for userID := range req.Signatures {
|
||||||
queryReq.UserToDevices[userID] = []string{}
|
queryReq.UserToDevices[userID] = []string{}
|
||||||
}
|
}
|
||||||
a.QueryKeys(ctx, queryReq, queryRes)
|
_ = a.QueryKeys(ctx, queryReq, queryRes)
|
||||||
|
|
||||||
selfSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
selfSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
||||||
otherSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
otherSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
||||||
|
|
@ -322,14 +323,14 @@ func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("a.processSelfSignatures: %s", err),
|
Err: fmt.Sprintf("a.processSelfSignatures: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.processOtherSignatures(ctx, req.UserID, queryRes, otherSignatures); err != nil {
|
if err := a.processOtherSignatures(ctx, req.UserID, queryRes, otherSignatures); err != nil {
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("a.processOtherSignatures: %s", err),
|
Err: fmt.Sprintf("a.processOtherSignatures: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, generate a notification that we updated the signatures.
|
// Finally, generate a notification that we updated the signatures.
|
||||||
|
|
@ -345,9 +346,10 @@ func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("a.Producer.ProduceSigningKeyUpdate: %s", err),
|
Err: fmt.Sprintf("a.Producer.ProduceSigningKeyUpdate: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) processSelfSignatures(
|
func (a *KeyInternalAPI) processSelfSignatures(
|
||||||
|
|
@ -520,7 +522,7 @@ func (a *KeyInternalAPI) crossSigningKeysFromDatabase(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) QuerySignatures(ctx context.Context, req *api.QuerySignaturesRequest, res *api.QuerySignaturesResponse) {
|
func (a *KeyInternalAPI) QuerySignatures(ctx context.Context, req *api.QuerySignaturesRequest, res *api.QuerySignaturesResponse) error {
|
||||||
for targetUserID, forTargetUser := range req.TargetIDs {
|
for targetUserID, forTargetUser := range req.TargetIDs {
|
||||||
keyMap, err := a.DB.CrossSigningKeysForUser(ctx, targetUserID)
|
keyMap, err := a.DB.CrossSigningKeysForUser(ctx, targetUserID)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
|
@ -559,7 +561,7 @@ func (a *KeyInternalAPI) QuerySignatures(ctx context.Context, req *api.QuerySign
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("a.DB.CrossSigningSigsForTarget: %s", err),
|
Err: fmt.Sprintf("a.DB.CrossSigningSigsForTarget: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for sourceUserID, forSourceUser := range sigMap {
|
for sourceUserID, forSourceUser := range sigMap {
|
||||||
|
|
@ -581,4 +583,5 @@ func (a *KeyInternalAPI) QuerySignatures(ctx context.Context, req *api.QuerySign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,13 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
fedsenderapi "github.com/matrix-org/dendrite/federationapi/api"
|
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
fedsenderapi "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -66,12 +67,14 @@ func init() {
|
||||||
// - We don't have unbounded growth in proportion to the number of servers (this is more important in a P2P world where
|
// - We don't have unbounded growth in proportion to the number of servers (this is more important in a P2P world where
|
||||||
// we have many many servers)
|
// we have many many servers)
|
||||||
// - We can adjust concurrency (at the cost of memory usage) by tuning N, to accommodate mobile devices vs servers.
|
// - We can adjust concurrency (at the cost of memory usage) by tuning N, to accommodate mobile devices vs servers.
|
||||||
|
//
|
||||||
// The downsides are that:
|
// The downsides are that:
|
||||||
// - Query requests can get queued behind other servers if they hash to the same worker, even if there are other free
|
// - Query requests can get queued behind other servers if they hash to the same worker, even if there are other free
|
||||||
// workers elsewhere. Whilst suboptimal, provided we cap how long a single request can last (e.g using context timeouts)
|
// workers elsewhere. Whilst suboptimal, provided we cap how long a single request can last (e.g using context timeouts)
|
||||||
// we guarantee we will get around to it. Also, more users on a given server does not increase the number of requests
|
// we guarantee we will get around to it. Also, more users on a given server does not increase the number of requests
|
||||||
// (as /keys/query allows multiple users to be specified) so being stuck behind matrix.org won't materially be any worse
|
// (as /keys/query allows multiple users to be specified) so being stuck behind matrix.org won't materially be any worse
|
||||||
// than being stuck behind foo.bar
|
// than being stuck behind foo.bar
|
||||||
|
//
|
||||||
// In the event that the query fails, a lock is acquired and the server name along with the time to wait before retrying is
|
// In the event that the query fails, a lock is acquired and the server name along with the time to wait before retrying is
|
||||||
// set in a map. A restarter goroutine periodically probes this map and injects servers which are ready to be retried.
|
// set in a map. A restarter goroutine periodically probes this map and injects servers which are ready to be retried.
|
||||||
type DeviceListUpdater struct {
|
type DeviceListUpdater struct {
|
||||||
|
|
@ -116,7 +119,7 @@ type DeviceListUpdaterDatabase interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeviceListUpdaterAPI interface {
|
type DeviceListUpdaterAPI interface {
|
||||||
PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse)
|
PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyChangeProducer is the interface for producers.KeyChange useful for testing.
|
// KeyChangeProducer is the interface for producers.KeyChange useful for testing.
|
||||||
|
|
@ -332,8 +335,9 @@ func (u *DeviceListUpdater) worker(ch chan gomatrixserverlib.ServerName) {
|
||||||
retriesMu := &sync.Mutex{}
|
retriesMu := &sync.Mutex{}
|
||||||
// restarter goroutine which will inject failed servers into ch when it is time
|
// restarter goroutine which will inject failed servers into ch when it is time
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
|
||||||
var serversToRetry []gomatrixserverlib.ServerName
|
var serversToRetry []gomatrixserverlib.ServerName
|
||||||
|
for {
|
||||||
|
serversToRetry = serversToRetry[:0] // reuse memory
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
retriesMu.Lock()
|
retriesMu.Lock()
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
@ -352,11 +356,17 @@ func (u *DeviceListUpdater) worker(ch chan gomatrixserverlib.ServerName) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
for serverName := range ch {
|
for serverName := range ch {
|
||||||
|
retriesMu.Lock()
|
||||||
|
_, exists := retries[serverName]
|
||||||
|
retriesMu.Unlock()
|
||||||
|
if exists {
|
||||||
|
// Don't retry a server that we're already waiting for.
|
||||||
|
continue
|
||||||
|
}
|
||||||
waitTime, shouldRetry := u.processServer(serverName)
|
waitTime, shouldRetry := u.processServer(serverName)
|
||||||
if shouldRetry {
|
if shouldRetry {
|
||||||
retriesMu.Lock()
|
retriesMu.Lock()
|
||||||
_, exists := retries[serverName]
|
if _, exists = retries[serverName]; !exists {
|
||||||
if !exists {
|
|
||||||
retries[serverName] = time.Now().Add(waitTime)
|
retries[serverName] = time.Now().Add(waitTime)
|
||||||
}
|
}
|
||||||
retriesMu.Unlock()
|
retriesMu.Unlock()
|
||||||
|
|
@ -418,7 +428,7 @@ func (u *DeviceListUpdater) processServer(serverName gomatrixserverlib.ServerNam
|
||||||
uploadReq.SelfSigningKey = *res.SelfSigningKey
|
uploadReq.SelfSigningKey = *res.SelfSigningKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u.api.PerformUploadDeviceKeys(ctx, uploadReq, uploadRes)
|
_ = u.api.PerformUploadDeviceKeys(ctx, uploadReq, uploadRes)
|
||||||
}
|
}
|
||||||
err = u.updateDeviceList(&res)
|
err = u.updateDeviceList(&res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -27,8 +27,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -112,8 +113,8 @@ func (d *mockDeviceListUpdaterDatabase) DeviceKeysJSON(ctx context.Context, keys
|
||||||
type mockDeviceListUpdaterAPI struct {
|
type mockDeviceListUpdaterAPI struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *mockDeviceListUpdaterAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) {
|
func (d *mockDeviceListUpdaterAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type roundTripper struct {
|
type roundTripper struct {
|
||||||
|
|
@ -202,7 +203,7 @@ func TestUpdateNoPrevID(t *testing.T) {
|
||||||
}
|
}
|
||||||
return &http.Response{
|
return &http.Response{
|
||||||
StatusCode: 200,
|
StatusCode: 200,
|
||||||
Body: ioutil.NopCloser(strings.NewReader(`
|
Body: io.NopCloser(strings.NewReader(`
|
||||||
{
|
{
|
||||||
"user_id": "` + remoteUserID + `",
|
"user_id": "` + remoteUserID + `",
|
||||||
"stream_id": 5,
|
"stream_id": 5,
|
||||||
|
|
@ -317,7 +318,7 @@ func TestDebounce(t *testing.T) {
|
||||||
// now send the response over federation
|
// now send the response over federation
|
||||||
fedCh <- &http.Response{
|
fedCh <- &http.Response{
|
||||||
StatusCode: 200,
|
StatusCode: 200,
|
||||||
Body: ioutil.NopCloser(strings.NewReader(`
|
Body: io.NopCloser(strings.NewReader(`
|
||||||
{
|
{
|
||||||
"user_id": "` + userID + `",
|
"user_id": "` + userID + `",
|
||||||
"stream_id": 5,
|
"stream_id": 5,
|
||||||
|
|
|
||||||
|
|
@ -48,18 +48,20 @@ func (a *KeyInternalAPI) SetUserAPI(i userapi.KeyserverUserAPI) {
|
||||||
a.UserAPI = i
|
a.UserAPI = i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) QueryKeyChanges(ctx context.Context, req *api.QueryKeyChangesRequest, res *api.QueryKeyChangesResponse) {
|
func (a *KeyInternalAPI) QueryKeyChanges(ctx context.Context, req *api.QueryKeyChangesRequest, res *api.QueryKeyChangesResponse) error {
|
||||||
userIDs, latest, err := a.DB.KeyChanges(ctx, req.Offset, req.ToOffset)
|
userIDs, latest, err := a.DB.KeyChanges(ctx, req.Offset, req.ToOffset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: err.Error(),
|
Err: err.Error(),
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
res.Offset = latest
|
res.Offset = latest
|
||||||
res.UserIDs = userIDs
|
res.UserIDs = userIDs
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) PerformUploadKeys(ctx context.Context, req *api.PerformUploadKeysRequest, res *api.PerformUploadKeysResponse) {
|
func (a *KeyInternalAPI) PerformUploadKeys(ctx context.Context, req *api.PerformUploadKeysRequest, res *api.PerformUploadKeysResponse) error {
|
||||||
res.KeyErrors = make(map[string]map[string]*api.KeyError)
|
res.KeyErrors = make(map[string]map[string]*api.KeyError)
|
||||||
if len(req.DeviceKeys) > 0 {
|
if len(req.DeviceKeys) > 0 {
|
||||||
a.uploadLocalDeviceKeys(ctx, req, res)
|
a.uploadLocalDeviceKeys(ctx, req, res)
|
||||||
|
|
@ -67,9 +69,10 @@ func (a *KeyInternalAPI) PerformUploadKeys(ctx context.Context, req *api.Perform
|
||||||
if len(req.OneTimeKeys) > 0 {
|
if len(req.OneTimeKeys) > 0 {
|
||||||
a.uploadOneTimeKeys(ctx, req, res)
|
a.uploadOneTimeKeys(ctx, req, res)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) PerformClaimKeys(ctx context.Context, req *api.PerformClaimKeysRequest, res *api.PerformClaimKeysResponse) {
|
func (a *KeyInternalAPI) PerformClaimKeys(ctx context.Context, req *api.PerformClaimKeysRequest, res *api.PerformClaimKeysResponse) error {
|
||||||
res.OneTimeKeys = make(map[string]map[string]map[string]json.RawMessage)
|
res.OneTimeKeys = make(map[string]map[string]map[string]json.RawMessage)
|
||||||
res.Failures = make(map[string]interface{})
|
res.Failures = make(map[string]interface{})
|
||||||
// wrap request map in a top-level by-domain map
|
// wrap request map in a top-level by-domain map
|
||||||
|
|
@ -113,6 +116,7 @@ func (a *KeyInternalAPI) PerformClaimKeys(ctx context.Context, req *api.PerformC
|
||||||
if len(domainToDeviceKeys) > 0 {
|
if len(domainToDeviceKeys) > 0 {
|
||||||
a.claimRemoteKeys(ctx, req.Timeout, res, domainToDeviceKeys)
|
a.claimRemoteKeys(ctx, req.Timeout, res, domainToDeviceKeys)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) claimRemoteKeys(
|
func (a *KeyInternalAPI) claimRemoteKeys(
|
||||||
|
|
@ -172,32 +176,34 @@ func (a *KeyInternalAPI) claimRemoteKeys(
|
||||||
util.GetLogger(ctx).WithField("num_keys", keysClaimed).Info("Claimed remote keys")
|
util.GetLogger(ctx).WithField("num_keys", keysClaimed).Info("Claimed remote keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) PerformDeleteKeys(ctx context.Context, req *api.PerformDeleteKeysRequest, res *api.PerformDeleteKeysResponse) {
|
func (a *KeyInternalAPI) PerformDeleteKeys(ctx context.Context, req *api.PerformDeleteKeysRequest, res *api.PerformDeleteKeysResponse) error {
|
||||||
if err := a.DB.DeleteDeviceKeys(ctx, req.UserID, req.KeyIDs); err != nil {
|
if err := a.DB.DeleteDeviceKeys(ctx, req.UserID, req.KeyIDs); err != nil {
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("Failed to delete device keys: %s", err),
|
Err: fmt.Sprintf("Failed to delete device keys: %s", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) QueryOneTimeKeys(ctx context.Context, req *api.QueryOneTimeKeysRequest, res *api.QueryOneTimeKeysResponse) {
|
func (a *KeyInternalAPI) QueryOneTimeKeys(ctx context.Context, req *api.QueryOneTimeKeysRequest, res *api.QueryOneTimeKeysResponse) error {
|
||||||
count, err := a.DB.OneTimeKeysCount(ctx, req.UserID, req.DeviceID)
|
count, err := a.DB.OneTimeKeysCount(ctx, req.UserID, req.DeviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("Failed to query OTK counts: %s", err),
|
Err: fmt.Sprintf("Failed to query OTK counts: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
res.Count = *count
|
res.Count = *count
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) QueryDeviceMessages(ctx context.Context, req *api.QueryDeviceMessagesRequest, res *api.QueryDeviceMessagesResponse) {
|
func (a *KeyInternalAPI) QueryDeviceMessages(ctx context.Context, req *api.QueryDeviceMessagesRequest, res *api.QueryDeviceMessagesResponse) error {
|
||||||
msgs, err := a.DB.DeviceKeysForUser(ctx, req.UserID, nil, false)
|
msgs, err := a.DB.DeviceKeysForUser(ctx, req.UserID, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("failed to query DB for device keys: %s", err),
|
Err: fmt.Sprintf("failed to query DB for device keys: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
maxStreamID := int64(0)
|
maxStreamID := int64(0)
|
||||||
for _, m := range msgs {
|
for _, m := range msgs {
|
||||||
|
|
@ -215,10 +221,11 @@ func (a *KeyInternalAPI) QueryDeviceMessages(ctx context.Context, req *api.Query
|
||||||
}
|
}
|
||||||
res.Devices = result
|
res.Devices = result
|
||||||
res.StreamID = maxStreamID
|
res.StreamID = maxStreamID
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:gocyclo
|
// nolint:gocyclo
|
||||||
func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) {
|
func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) error {
|
||||||
res.DeviceKeys = make(map[string]map[string]json.RawMessage)
|
res.DeviceKeys = make(map[string]map[string]json.RawMessage)
|
||||||
res.MasterKeys = make(map[string]gomatrixserverlib.CrossSigningKey)
|
res.MasterKeys = make(map[string]gomatrixserverlib.CrossSigningKey)
|
||||||
res.SelfSigningKeys = make(map[string]gomatrixserverlib.CrossSigningKey)
|
res.SelfSigningKeys = make(map[string]gomatrixserverlib.CrossSigningKey)
|
||||||
|
|
@ -244,7 +251,7 @@ func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysReques
|
||||||
res.Error = &api.KeyError{
|
res.Error = &api.KeyError{
|
||||||
Err: fmt.Sprintf("failed to query local device keys: %s", err),
|
Err: fmt.Sprintf("failed to query local device keys: %s", err),
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// pull out display names after we have the keys so we handle wildcards correctly
|
// pull out display names after we have the keys so we handle wildcards correctly
|
||||||
|
|
@ -318,7 +325,7 @@ func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysReques
|
||||||
// Stop executing the function if the context was canceled/the deadline was exceeded,
|
// Stop executing the function if the context was canceled/the deadline was exceeded,
|
||||||
// as we can't continue without a valid context.
|
// as we can't continue without a valid context.
|
||||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
logrus.WithError(err).Errorf("a.DB.CrossSigningSigsForTarget failed")
|
logrus.WithError(err).Errorf("a.DB.CrossSigningSigsForTarget failed")
|
||||||
continue
|
continue
|
||||||
|
|
@ -344,7 +351,7 @@ func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysReques
|
||||||
// Stop executing the function if the context was canceled/the deadline was exceeded,
|
// Stop executing the function if the context was canceled/the deadline was exceeded,
|
||||||
// as we can't continue without a valid context.
|
// as we can't continue without a valid context.
|
||||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
logrus.WithError(err).Errorf("a.DB.CrossSigningSigsForTarget failed")
|
logrus.WithError(err).Errorf("a.DB.CrossSigningSigsForTarget failed")
|
||||||
continue
|
continue
|
||||||
|
|
@ -372,6 +379,7 @@ func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysReques
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) remoteKeysFromDatabase(
|
func (a *KeyInternalAPI) remoteKeysFromDatabase(
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/opentracing/opentracing-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP paths for the internal HTTP APIs
|
// HTTP paths for the internal HTTP APIs
|
||||||
|
|
@ -68,168 +67,108 @@ func (h *httpKeyInternalAPI) PerformClaimKeys(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.PerformClaimKeysRequest,
|
request *api.PerformClaimKeysRequest,
|
||||||
response *api.PerformClaimKeysResponse,
|
response *api.PerformClaimKeysResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformClaimKeys")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformClaimKeys", h.apiURL+PerformClaimKeysPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + PerformClaimKeysPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) PerformDeleteKeys(
|
func (h *httpKeyInternalAPI) PerformDeleteKeys(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.PerformDeleteKeysRequest,
|
request *api.PerformDeleteKeysRequest,
|
||||||
response *api.PerformDeleteKeysResponse,
|
response *api.PerformDeleteKeysResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformClaimKeys")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformDeleteKeys", h.apiURL+PerformDeleteKeysPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + PerformClaimKeysPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) PerformUploadKeys(
|
func (h *httpKeyInternalAPI) PerformUploadKeys(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.PerformUploadKeysRequest,
|
request *api.PerformUploadKeysRequest,
|
||||||
response *api.PerformUploadKeysResponse,
|
response *api.PerformUploadKeysResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformUploadKeys")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformUploadKeys", h.apiURL+PerformUploadKeysPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + PerformUploadKeysPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) QueryKeys(
|
func (h *httpKeyInternalAPI) QueryKeys(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.QueryKeysRequest,
|
request *api.QueryKeysRequest,
|
||||||
response *api.QueryKeysResponse,
|
response *api.QueryKeysResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryKeys")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"QueryKeys", h.apiURL+QueryKeysPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + QueryKeysPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) QueryOneTimeKeys(
|
func (h *httpKeyInternalAPI) QueryOneTimeKeys(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.QueryOneTimeKeysRequest,
|
request *api.QueryOneTimeKeysRequest,
|
||||||
response *api.QueryOneTimeKeysResponse,
|
response *api.QueryOneTimeKeysResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryOneTimeKeys")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"QueryOneTimeKeys", h.apiURL+QueryOneTimeKeysPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + QueryOneTimeKeysPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) QueryDeviceMessages(
|
func (h *httpKeyInternalAPI) QueryDeviceMessages(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.QueryDeviceMessagesRequest,
|
request *api.QueryDeviceMessagesRequest,
|
||||||
response *api.QueryDeviceMessagesResponse,
|
response *api.QueryDeviceMessagesResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryDeviceMessages")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"QueryDeviceMessages", h.apiURL+QueryDeviceMessagesPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + QueryDeviceMessagesPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) QueryKeyChanges(
|
func (h *httpKeyInternalAPI) QueryKeyChanges(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.QueryKeyChangesRequest,
|
request *api.QueryKeyChangesRequest,
|
||||||
response *api.QueryKeyChangesResponse,
|
response *api.QueryKeyChangesResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryKeyChanges")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"QueryKeyChanges", h.apiURL+QueryKeyChangesPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + QueryKeyChangesPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) PerformUploadDeviceKeys(
|
func (h *httpKeyInternalAPI) PerformUploadDeviceKeys(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.PerformUploadDeviceKeysRequest,
|
request *api.PerformUploadDeviceKeysRequest,
|
||||||
response *api.PerformUploadDeviceKeysResponse,
|
response *api.PerformUploadDeviceKeysResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformUploadDeviceKeys")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformUploadDeviceKeys", h.apiURL+PerformUploadDeviceKeysPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + PerformUploadDeviceKeysPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) PerformUploadDeviceSignatures(
|
func (h *httpKeyInternalAPI) PerformUploadDeviceSignatures(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.PerformUploadDeviceSignaturesRequest,
|
request *api.PerformUploadDeviceSignaturesRequest,
|
||||||
response *api.PerformUploadDeviceSignaturesResponse,
|
response *api.PerformUploadDeviceSignaturesResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformUploadDeviceSignatures")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"PerformUploadDeviceSignatures", h.apiURL+PerformUploadDeviceSignaturesPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + PerformUploadDeviceSignaturesPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpKeyInternalAPI) QuerySignatures(
|
func (h *httpKeyInternalAPI) QuerySignatures(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.QuerySignaturesRequest,
|
request *api.QuerySignaturesRequest,
|
||||||
response *api.QuerySignaturesResponse,
|
response *api.QuerySignaturesResponse,
|
||||||
) {
|
) error {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QuerySignatures")
|
return httputil.CallInternalRPCAPI(
|
||||||
defer span.Finish()
|
"QuerySignatures", h.apiURL+QuerySignaturesPath,
|
||||||
|
h.httpClient, ctx, request, response,
|
||||||
apiURL := h.apiURL + QuerySignaturesPath
|
)
|
||||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
if err != nil {
|
|
||||||
response.Error = &api.KeyError{
|
|
||||||
Err: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,124 +15,59 @@
|
||||||
package inthttp
|
package inthttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
"github.com/matrix-org/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddRoutes(internalAPIMux *mux.Router, s api.KeyInternalAPI) {
|
func AddRoutes(internalAPIMux *mux.Router, s api.KeyInternalAPI) {
|
||||||
internalAPIMux.Handle(PerformClaimKeysPath,
|
internalAPIMux.Handle(
|
||||||
httputil.MakeInternalAPI("performClaimKeys", func(req *http.Request) util.JSONResponse {
|
PerformClaimKeysPath,
|
||||||
request := api.PerformClaimKeysRequest{}
|
httputil.MakeInternalRPCAPI("KeyserverPerformClaimKeys", s.PerformClaimKeys),
|
||||||
response := api.PerformClaimKeysResponse{}
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.PerformClaimKeys(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(PerformDeleteKeysPath,
|
|
||||||
httputil.MakeInternalAPI("performDeleteKeys", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.PerformDeleteKeysRequest{}
|
PerformDeleteKeysPath,
|
||||||
response := api.PerformDeleteKeysResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverPerformDeleteKeys", s.PerformDeleteKeys),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.PerformDeleteKeys(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(PerformUploadKeysPath,
|
|
||||||
httputil.MakeInternalAPI("performUploadKeys", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.PerformUploadKeysRequest{}
|
PerformUploadKeysPath,
|
||||||
response := api.PerformUploadKeysResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverPerformUploadKeys", s.PerformUploadKeys),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.PerformUploadKeys(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(PerformUploadDeviceKeysPath,
|
|
||||||
httputil.MakeInternalAPI("performUploadDeviceKeys", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.PerformUploadDeviceKeysRequest{}
|
PerformUploadDeviceKeysPath,
|
||||||
response := api.PerformUploadDeviceKeysResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverPerformUploadDeviceKeys", s.PerformUploadDeviceKeys),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.PerformUploadDeviceKeys(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(PerformUploadDeviceSignaturesPath,
|
|
||||||
httputil.MakeInternalAPI("performUploadDeviceSignatures", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.PerformUploadDeviceSignaturesRequest{}
|
PerformUploadDeviceSignaturesPath,
|
||||||
response := api.PerformUploadDeviceSignaturesResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverPerformUploadDeviceSignatures", s.PerformUploadDeviceSignatures),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.PerformUploadDeviceSignatures(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(QueryKeysPath,
|
|
||||||
httputil.MakeInternalAPI("queryKeys", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.QueryKeysRequest{}
|
QueryKeysPath,
|
||||||
response := api.QueryKeysResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverQueryKeys", s.QueryKeys),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.QueryKeys(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(QueryOneTimeKeysPath,
|
|
||||||
httputil.MakeInternalAPI("queryOneTimeKeys", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.QueryOneTimeKeysRequest{}
|
QueryOneTimeKeysPath,
|
||||||
response := api.QueryOneTimeKeysResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverQueryOneTimeKeys", s.QueryOneTimeKeys),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.QueryOneTimeKeys(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(QueryDeviceMessagesPath,
|
|
||||||
httputil.MakeInternalAPI("queryDeviceMessages", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.QueryDeviceMessagesRequest{}
|
QueryDeviceMessagesPath,
|
||||||
response := api.QueryDeviceMessagesResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverQueryDeviceMessages", s.QueryDeviceMessages),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.QueryDeviceMessages(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(QueryKeyChangesPath,
|
|
||||||
httputil.MakeInternalAPI("queryKeyChanges", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.QueryKeyChangesRequest{}
|
QueryKeyChangesPath,
|
||||||
response := api.QueryKeyChangesResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverQueryKeyChanges", s.QueryKeyChanges),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.QueryKeyChanges(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
internalAPIMux.Handle(QuerySignaturesPath,
|
|
||||||
httputil.MakeInternalAPI("querySignatures", func(req *http.Request) util.JSONResponse {
|
internalAPIMux.Handle(
|
||||||
request := api.QuerySignaturesRequest{}
|
QuerySignaturesPath,
|
||||||
response := api.QuerySignaturesResponse{}
|
httputil.MakeInternalRPCAPI("KeyserverQuerySignatures", s.QuerySignatures),
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
s.QuerySignatures(req.Context(), &request, &response)
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal"
|
"github.com/matrix-org/dendrite/internal"
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/dendrite/keyserver/storage/postgres/deltas"
|
"github.com/matrix-org/dendrite/keyserver/storage/postgres/deltas"
|
||||||
|
|
@ -64,7 +66,8 @@ func NewPostgresKeyChangesTable(db *sql.DB) (tables.KeyChanges, error) {
|
||||||
// TODO: Remove when we are sure we are not having goose artefacts in the db
|
// TODO: Remove when we are sure we are not having goose artefacts in the db
|
||||||
// This forces an error, which indicates the migration is already applied, since the
|
// This forces an error, which indicates the migration is already applied, since the
|
||||||
// column partition was removed from the table
|
// column partition was removed from the table
|
||||||
err = db.QueryRow("SELECT partition FROM keyserver_key_changes LIMIT 1;").Scan()
|
var count int
|
||||||
|
err = db.QueryRow("SELECT partition FROM keyserver_key_changes LIMIT 1;").Scan(&count)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m := sqlutil.NewMigrator(db)
|
m := sqlutil.NewMigrator(db)
|
||||||
m.AddMigrations(sqlutil.Migration{
|
m.AddMigrations(sqlutil.Migration{
|
||||||
|
|
@ -72,6 +75,16 @@ func NewPostgresKeyChangesTable(db *sql.DB) (tables.KeyChanges, error) {
|
||||||
Up: deltas.UpRefactorKeyChanges,
|
Up: deltas.UpRefactorKeyChanges,
|
||||||
})
|
})
|
||||||
return s, m.Up(context.Background())
|
return s, m.Up(context.Background())
|
||||||
|
} else {
|
||||||
|
switch e := err.(type) {
|
||||||
|
case *pq.Error:
|
||||||
|
// ignore undefined_column (42703) errors, as this is expected at this point
|
||||||
|
if e.Code != "42703" {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,8 @@ func NewSqliteKeyChangesTable(db *sql.DB) (tables.KeyChanges, error) {
|
||||||
// TODO: Remove when we are sure we are not having goose artefacts in the db
|
// TODO: Remove when we are sure we are not having goose artefacts in the db
|
||||||
// This forces an error, which indicates the migration is already applied, since the
|
// This forces an error, which indicates the migration is already applied, since the
|
||||||
// column partition was removed from the table
|
// column partition was removed from the table
|
||||||
err = db.QueryRow("SELECT partition FROM keyserver_key_changes LIMIT 1;").Scan()
|
var count int
|
||||||
|
err = db.QueryRow("SELECT partition FROM keyserver_key_changes LIMIT 1;").Scan(&count)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m := sqlutil.NewMigrator(db)
|
m := sqlutil.NewMigrator(db)
|
||||||
m.AddMigrations(sqlutil.Migration{
|
m.AddMigrations(sqlutil.Migration{
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -180,7 +179,7 @@ func createTempDir(baseDirectory config.Path) (types.Path, error) {
|
||||||
if err := os.MkdirAll(baseTmpDir, 0770); err != nil {
|
if err := os.MkdirAll(baseTmpDir, 0770); err != nil {
|
||||||
return "", fmt.Errorf("failed to create base temp dir: %w", err)
|
return "", fmt.Errorf("failed to create base temp dir: %w", err)
|
||||||
}
|
}
|
||||||
tmpDir, err := ioutil.TempDir(baseTmpDir, "")
|
tmpDir, err := os.MkdirTemp(baseTmpDir, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to create temp dir: %w", err)
|
return "", fmt.Errorf("failed to create temp dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
@ -695,7 +694,7 @@ func (r *downloadRequest) GetContentLengthAndReader(contentLengthHeader string,
|
||||||
|
|
||||||
// We successfully parsed the Content-Length, so we'll return a limited
|
// We successfully parsed the Content-Length, so we'll return a limited
|
||||||
// reader that restricts us to reading only up to this size.
|
// reader that restricts us to reading only up to this size.
|
||||||
reader = ioutil.NopCloser(io.LimitReader(*body, parsedLength))
|
reader = io.NopCloser(io.LimitReader(*body, parsedLength))
|
||||||
contentLength = parsedLength
|
contentLength = parsedLength
|
||||||
} else {
|
} else {
|
||||||
// Content-Length header is missing. If we have a maximum file size
|
// Content-Length header is missing. If we have a maximum file size
|
||||||
|
|
@ -704,7 +703,7 @@ func (r *downloadRequest) GetContentLengthAndReader(contentLengthHeader string,
|
||||||
// ultimately it will get rewritten later when the temp file is written
|
// ultimately it will get rewritten later when the temp file is written
|
||||||
// to disk.
|
// to disk.
|
||||||
if maxFileSizeBytes > 0 {
|
if maxFileSizeBytes > 0 {
|
||||||
reader = ioutil.NopCloser(io.LimitReader(*body, int64(maxFileSizeBytes)))
|
reader = io.NopCloser(io.LimitReader(*body, int64(maxFileSizeBytes)))
|
||||||
}
|
}
|
||||||
contentLength = 0
|
contentLength = 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ type InputRoomEventsAPI interface {
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *InputRoomEventsRequest,
|
req *InputRoomEventsRequest,
|
||||||
res *InputRoomEventsResponse,
|
res *InputRoomEventsResponse,
|
||||||
)
|
) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query the latest events and state for a room from the room server.
|
// Query the latest events and state for a room from the room server.
|
||||||
|
|
@ -97,6 +97,14 @@ type SyncRoomserverAPI interface {
|
||||||
req *PerformBackfillRequest,
|
req *PerformBackfillRequest,
|
||||||
res *PerformBackfillResponse,
|
res *PerformBackfillResponse,
|
||||||
) error
|
) error
|
||||||
|
|
||||||
|
// QueryMembershipAtEvent queries the memberships at the given events.
|
||||||
|
// Returns a map from eventID to a slice of gomatrixserverlib.HeaderedEvent.
|
||||||
|
QueryMembershipAtEvent(
|
||||||
|
ctx context.Context,
|
||||||
|
request *QueryMembershipAtEventRequest,
|
||||||
|
response *QueryMembershipAtEventResponse,
|
||||||
|
) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppserviceRoomserverAPI interface {
|
type AppserviceRoomserverAPI interface {
|
||||||
|
|
@ -139,15 +147,15 @@ type ClientRoomserverAPI interface {
|
||||||
GetAliasesForRoomID(ctx context.Context, req *GetAliasesForRoomIDRequest, res *GetAliasesForRoomIDResponse) error
|
GetAliasesForRoomID(ctx context.Context, req *GetAliasesForRoomIDRequest, res *GetAliasesForRoomIDResponse) error
|
||||||
|
|
||||||
// PerformRoomUpgrade upgrades a room to a newer version
|
// PerformRoomUpgrade upgrades a room to a newer version
|
||||||
PerformRoomUpgrade(ctx context.Context, req *PerformRoomUpgradeRequest, resp *PerformRoomUpgradeResponse)
|
PerformRoomUpgrade(ctx context.Context, req *PerformRoomUpgradeRequest, resp *PerformRoomUpgradeResponse) error
|
||||||
PerformAdminEvacuateRoom(ctx context.Context, req *PerformAdminEvacuateRoomRequest, res *PerformAdminEvacuateRoomResponse)
|
PerformAdminEvacuateRoom(ctx context.Context, req *PerformAdminEvacuateRoomRequest, res *PerformAdminEvacuateRoomResponse) error
|
||||||
PerformAdminEvacuateUser(ctx context.Context, req *PerformAdminEvacuateUserRequest, res *PerformAdminEvacuateUserResponse)
|
PerformAdminEvacuateUser(ctx context.Context, req *PerformAdminEvacuateUserRequest, res *PerformAdminEvacuateUserResponse) error
|
||||||
PerformPeek(ctx context.Context, req *PerformPeekRequest, res *PerformPeekResponse)
|
PerformPeek(ctx context.Context, req *PerformPeekRequest, res *PerformPeekResponse) error
|
||||||
PerformUnpeek(ctx context.Context, req *PerformUnpeekRequest, res *PerformUnpeekResponse)
|
PerformUnpeek(ctx context.Context, req *PerformUnpeekRequest, res *PerformUnpeekResponse) error
|
||||||
PerformInvite(ctx context.Context, req *PerformInviteRequest, res *PerformInviteResponse) error
|
PerformInvite(ctx context.Context, req *PerformInviteRequest, res *PerformInviteResponse) error
|
||||||
PerformJoin(ctx context.Context, req *PerformJoinRequest, res *PerformJoinResponse)
|
PerformJoin(ctx context.Context, req *PerformJoinRequest, res *PerformJoinResponse) error
|
||||||
PerformLeave(ctx context.Context, req *PerformLeaveRequest, res *PerformLeaveResponse) error
|
PerformLeave(ctx context.Context, req *PerformLeaveRequest, res *PerformLeaveResponse) error
|
||||||
PerformPublish(ctx context.Context, req *PerformPublishRequest, res *PerformPublishResponse)
|
PerformPublish(ctx context.Context, req *PerformPublishRequest, res *PerformPublishResponse) error
|
||||||
// PerformForget forgets a rooms history for a specific user
|
// PerformForget forgets a rooms history for a specific user
|
||||||
PerformForget(ctx context.Context, req *PerformForgetRequest, resp *PerformForgetResponse) error
|
PerformForget(ctx context.Context, req *PerformForgetRequest, resp *PerformForgetResponse) error
|
||||||
SetRoomAlias(ctx context.Context, req *SetRoomAliasRequest, res *SetRoomAliasResponse) error
|
SetRoomAlias(ctx context.Context, req *SetRoomAliasRequest, res *SetRoomAliasResponse) error
|
||||||
|
|
@ -158,7 +166,7 @@ type UserRoomserverAPI interface {
|
||||||
QueryLatestEventsAndStateAPI
|
QueryLatestEventsAndStateAPI
|
||||||
QueryCurrentState(ctx context.Context, req *QueryCurrentStateRequest, res *QueryCurrentStateResponse) error
|
QueryCurrentState(ctx context.Context, req *QueryCurrentStateRequest, res *QueryCurrentStateResponse) error
|
||||||
QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error
|
QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error
|
||||||
PerformAdminEvacuateUser(ctx context.Context, req *PerformAdminEvacuateUserRequest, res *PerformAdminEvacuateUserResponse)
|
PerformAdminEvacuateUser(ctx context.Context, req *PerformAdminEvacuateUserRequest, res *PerformAdminEvacuateUserResponse) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type FederationRoomserverAPI interface {
|
type FederationRoomserverAPI interface {
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,10 @@ func (t *RoomserverInternalAPITrace) InputRoomEvents(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *InputRoomEventsRequest,
|
req *InputRoomEventsRequest,
|
||||||
res *InputRoomEventsResponse,
|
res *InputRoomEventsResponse,
|
||||||
) {
|
) error {
|
||||||
t.Impl.InputRoomEvents(ctx, req, res)
|
err := t.Impl.InputRoomEvents(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("InputRoomEvents req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("InputRoomEvents req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformInvite(
|
func (t *RoomserverInternalAPITrace) PerformInvite(
|
||||||
|
|
@ -45,44 +46,49 @@ func (t *RoomserverInternalAPITrace) PerformInvite(
|
||||||
req *PerformInviteRequest,
|
req *PerformInviteRequest,
|
||||||
res *PerformInviteResponse,
|
res *PerformInviteResponse,
|
||||||
) error {
|
) error {
|
||||||
util.GetLogger(ctx).Infof("PerformInvite req=%+v res=%+v", js(req), js(res))
|
err := t.Impl.PerformInvite(ctx, req, res)
|
||||||
return t.Impl.PerformInvite(ctx, req, res)
|
util.GetLogger(ctx).WithError(err).Infof("PerformInvite req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformPeek(
|
func (t *RoomserverInternalAPITrace) PerformPeek(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *PerformPeekRequest,
|
req *PerformPeekRequest,
|
||||||
res *PerformPeekResponse,
|
res *PerformPeekResponse,
|
||||||
) {
|
) error {
|
||||||
t.Impl.PerformPeek(ctx, req, res)
|
err := t.Impl.PerformPeek(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("PerformPeek req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("PerformPeek req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformUnpeek(
|
func (t *RoomserverInternalAPITrace) PerformUnpeek(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *PerformUnpeekRequest,
|
req *PerformUnpeekRequest,
|
||||||
res *PerformUnpeekResponse,
|
res *PerformUnpeekResponse,
|
||||||
) {
|
) error {
|
||||||
t.Impl.PerformUnpeek(ctx, req, res)
|
err := t.Impl.PerformUnpeek(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("PerformUnpeek req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("PerformUnpeek req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformRoomUpgrade(
|
func (t *RoomserverInternalAPITrace) PerformRoomUpgrade(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *PerformRoomUpgradeRequest,
|
req *PerformRoomUpgradeRequest,
|
||||||
res *PerformRoomUpgradeResponse,
|
res *PerformRoomUpgradeResponse,
|
||||||
) {
|
) error {
|
||||||
t.Impl.PerformRoomUpgrade(ctx, req, res)
|
err := t.Impl.PerformRoomUpgrade(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("PerformRoomUpgrade req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("PerformRoomUpgrade req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformJoin(
|
func (t *RoomserverInternalAPITrace) PerformJoin(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *PerformJoinRequest,
|
req *PerformJoinRequest,
|
||||||
res *PerformJoinResponse,
|
res *PerformJoinResponse,
|
||||||
) {
|
) error {
|
||||||
t.Impl.PerformJoin(ctx, req, res)
|
err := t.Impl.PerformJoin(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("PerformJoin req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("PerformJoin req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformLeave(
|
func (t *RoomserverInternalAPITrace) PerformLeave(
|
||||||
|
|
@ -99,27 +105,30 @@ func (t *RoomserverInternalAPITrace) PerformPublish(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *PerformPublishRequest,
|
req *PerformPublishRequest,
|
||||||
res *PerformPublishResponse,
|
res *PerformPublishResponse,
|
||||||
) {
|
) error {
|
||||||
t.Impl.PerformPublish(ctx, req, res)
|
err := t.Impl.PerformPublish(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("PerformPublish req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("PerformPublish req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformAdminEvacuateRoom(
|
func (t *RoomserverInternalAPITrace) PerformAdminEvacuateRoom(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *PerformAdminEvacuateRoomRequest,
|
req *PerformAdminEvacuateRoomRequest,
|
||||||
res *PerformAdminEvacuateRoomResponse,
|
res *PerformAdminEvacuateRoomResponse,
|
||||||
) {
|
) error {
|
||||||
t.Impl.PerformAdminEvacuateRoom(ctx, req, res)
|
err := t.Impl.PerformAdminEvacuateRoom(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("PerformAdminEvacuateRoom req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("PerformAdminEvacuateRoom req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformAdminEvacuateUser(
|
func (t *RoomserverInternalAPITrace) PerformAdminEvacuateUser(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *PerformAdminEvacuateUserRequest,
|
req *PerformAdminEvacuateUserRequest,
|
||||||
res *PerformAdminEvacuateUserResponse,
|
res *PerformAdminEvacuateUserResponse,
|
||||||
) {
|
) error {
|
||||||
t.Impl.PerformAdminEvacuateUser(ctx, req, res)
|
err := t.Impl.PerformAdminEvacuateUser(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("PerformAdminEvacuateUser req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("PerformAdminEvacuateUser req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformInboundPeek(
|
func (t *RoomserverInternalAPITrace) PerformInboundPeek(
|
||||||
|
|
@ -128,7 +137,7 @@ func (t *RoomserverInternalAPITrace) PerformInboundPeek(
|
||||||
res *PerformInboundPeekResponse,
|
res *PerformInboundPeekResponse,
|
||||||
) error {
|
) error {
|
||||||
err := t.Impl.PerformInboundPeek(ctx, req, res)
|
err := t.Impl.PerformInboundPeek(ctx, req, res)
|
||||||
util.GetLogger(ctx).Infof("PerformInboundPeek req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).WithError(err).Infof("PerformInboundPeek req=%+v res=%+v", js(req), js(res))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,6 +382,16 @@ func (t *RoomserverInternalAPITrace) QueryRestrictedJoinAllowed(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *RoomserverInternalAPITrace) QueryMembershipAtEvent(
|
||||||
|
ctx context.Context,
|
||||||
|
request *QueryMembershipAtEventRequest,
|
||||||
|
response *QueryMembershipAtEventResponse,
|
||||||
|
) error {
|
||||||
|
err := t.Impl.QueryMembershipAtEvent(ctx, request, response)
|
||||||
|
util.GetLogger(ctx).WithError(err).Infof("QueryMembershipAtEvent req=%+v res=%+v", js(request), js(response))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func js(thing interface{}) string {
|
func js(thing interface{}) string {
|
||||||
b, err := json.Marshal(thing)
|
b, err := json.Marshal(thing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PerformErrorCode int
|
type PerformErrorCode int
|
||||||
|
|
@ -162,6 +163,7 @@ func (r *PerformBackfillRequest) PrevEventIDs() []string {
|
||||||
type PerformBackfillResponse struct {
|
type PerformBackfillResponse struct {
|
||||||
// Missing events, arbritrary order.
|
// Missing events, arbritrary order.
|
||||||
Events []*gomatrixserverlib.HeaderedEvent `json:"events"`
|
Events []*gomatrixserverlib.HeaderedEvent `json:"events"`
|
||||||
|
HistoryVisibility gomatrixserverlib.HistoryVisibility `json:"history_visibility"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PerformPublishRequest struct {
|
type PerformPublishRequest struct {
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,7 @@ type QueryStateAndAuthChainResponse struct {
|
||||||
// Do all the previous events exist on this roomserver?
|
// Do all the previous events exist on this roomserver?
|
||||||
// If some of previous events do not exist this will be false and StateEvents will be empty.
|
// If some of previous events do not exist this will be false and StateEvents will be empty.
|
||||||
PrevEventsExist bool `json:"prev_events_exist"`
|
PrevEventsExist bool `json:"prev_events_exist"`
|
||||||
|
StateKnown bool `json:"state_known"`
|
||||||
// The state and auth chain events that were requested.
|
// The state and auth chain events that were requested.
|
||||||
// The lists will be in an arbitrary order.
|
// The lists will be in an arbitrary order.
|
||||||
StateEvents []*gomatrixserverlib.HeaderedEvent `json:"state_events"`
|
StateEvents []*gomatrixserverlib.HeaderedEvent `json:"state_events"`
|
||||||
|
|
@ -427,3 +428,17 @@ func (r *QueryCurrentStateResponse) UnmarshalJSON(data []byte) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryMembershipAtEventRequest requests the membership events for a user
|
||||||
|
// for a list of eventIDs.
|
||||||
|
type QueryMembershipAtEventRequest struct {
|
||||||
|
RoomID string
|
||||||
|
EventIDs []string
|
||||||
|
UserID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryMembershipAtEventResponse is the response to QueryMembershipAtEventRequest.
|
||||||
|
type QueryMembershipAtEventResponse struct {
|
||||||
|
// Memberships is a map from eventID to a list of events (if any).
|
||||||
|
Memberships map[string][]*gomatrixserverlib.HeaderedEvent `json:"memberships"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendEvents to the roomserver The events are written with KindNew.
|
// SendEvents to the roomserver The events are written with KindNew.
|
||||||
|
|
@ -69,6 +70,13 @@ func SendEventWithState(
|
||||||
stateEventIDs[i] = stateEvents[i].EventID()
|
stateEventIDs[i] = stateEvents[i].EventID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.WithContext(ctx).WithFields(logrus.Fields{
|
||||||
|
"room_id": event.RoomID(),
|
||||||
|
"event_id": event.EventID(),
|
||||||
|
"outliers": len(ires),
|
||||||
|
"state_ids": len(stateEventIDs),
|
||||||
|
}).Infof("Submitting %q event to roomserver with state snapshot", event.Type())
|
||||||
|
|
||||||
ires = append(ires, InputRoomEvent{
|
ires = append(ires, InputRoomEvent{
|
||||||
Kind: kind,
|
Kind: kind,
|
||||||
Event: event,
|
Event: event,
|
||||||
|
|
@ -90,7 +98,9 @@ func SendInputRoomEvents(
|
||||||
Asynchronous: async,
|
Asynchronous: async,
|
||||||
}
|
}
|
||||||
var response InputRoomEventsResponse
|
var response InputRoomEventsResponse
|
||||||
rsAPI.InputRoomEvents(ctx, &request, &response)
|
if err := rsAPI.InputRoomEvents(ctx, &request, &response); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return response.Err()
|
return response.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ func CheckForSoftFail(
|
||||||
var authStateEntries []types.StateEntry
|
var authStateEntries []types.StateEntry
|
||||||
var err error
|
var err error
|
||||||
if rewritesState {
|
if rewritesState {
|
||||||
authStateEntries, err = db.StateEntriesForEventIDs(ctx, stateEventIDs)
|
authStateEntries, err = db.StateEntriesForEventIDs(ctx, stateEventIDs, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, fmt.Errorf("StateEntriesForEventIDs failed: %w", err)
|
return true, fmt.Errorf("StateEntriesForEventIDs failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ func CheckAuthEvents(
|
||||||
authEventIDs []string,
|
authEventIDs []string,
|
||||||
) ([]types.EventNID, error) {
|
) ([]types.EventNID, error) {
|
||||||
// Grab the numeric IDs for the supplied auth state events from the database.
|
// Grab the numeric IDs for the supplied auth state events from the database.
|
||||||
authStateEntries, err := db.StateEntriesForEventIDs(ctx, authEventIDs)
|
authStateEntries, err := db.StateEntriesForEventIDs(ctx, authEventIDs, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("db.StateEntriesForEventIDs: %w", err)
|
return nil, fmt.Errorf("db.StateEntriesForEventIDs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,12 @@ func StateBeforeEvent(ctx context.Context, db storage.Database, info *types.Room
|
||||||
return roomState.LoadCombinedStateAfterEvents(ctx, prevState)
|
return roomState.LoadCombinedStateAfterEvents(ctx, prevState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MembershipAtEvent(ctx context.Context, db storage.Database, info *types.RoomInfo, eventIDs []string, stateKeyNID types.EventStateKeyNID) (map[string][]types.StateEntry, error) {
|
||||||
|
roomState := state.NewStateResolution(db, info)
|
||||||
|
// Fetch the state as it was when this event was fired
|
||||||
|
return roomState.LoadMembershipAtEvent(ctx, eventIDs, stateKeyNID)
|
||||||
|
}
|
||||||
|
|
||||||
func LoadEvents(
|
func LoadEvents(
|
||||||
ctx context.Context, db storage.Database, eventNIDs []types.EventNID,
|
ctx context.Context, db storage.Database, eventNIDs []types.EventNID,
|
||||||
) ([]*gomatrixserverlib.Event, error) {
|
) ([]*gomatrixserverlib.Event, error) {
|
||||||
|
|
@ -247,10 +253,17 @@ func CheckServerAllowedToSeeEvent(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
switch err.(type) {
|
||||||
|
case types.MissingStateError:
|
||||||
|
// If there's no state then we assume it's open visibility, as Synapse does:
|
||||||
|
// https://github.com/matrix-org/synapse/blob/aec87a0f9369a3015b2a53469f88d1de274e8b71/synapse/visibility.py#L654-L655
|
||||||
|
return true, nil
|
||||||
default:
|
default:
|
||||||
// Something else went wrong
|
// Something else went wrong
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return auth.IsServerAllowed(serverName, isServerInRoom, stateAtEvent), nil
|
return auth.IsServerAllowed(serverName, isServerInRoom, stateAtEvent), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,20 +25,22 @@ import (
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
"github.com/Arceliar/phony"
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
fedapi "github.com/matrix-org/dendrite/federationapi/api"
|
fedapi "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
"github.com/matrix-org/dendrite/roomserver/acls"
|
"github.com/matrix-org/dendrite/roomserver/acls"
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/roomserver/internal/query"
|
"github.com/matrix-org/dendrite/roomserver/internal/query"
|
||||||
"github.com/matrix-org/dendrite/roomserver/producers"
|
"github.com/matrix-org/dendrite/roomserver/producers"
|
||||||
"github.com/matrix-org/dendrite/roomserver/storage"
|
"github.com/matrix-org/dendrite/roomserver/storage"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/types"
|
||||||
"github.com/matrix-org/dendrite/setup/base"
|
"github.com/matrix-org/dendrite/setup/base"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
"github.com/matrix-org/dendrite/setup/process"
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
"github.com/nats-io/nats.go"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Inputer is responsible for consuming from the roomserver input
|
// Inputer is responsible for consuming from the roomserver input
|
||||||
|
|
@ -246,6 +248,15 @@ func (w *worker) _next() {
|
||||||
// it was a synchronous request.
|
// it was a synchronous request.
|
||||||
var errString string
|
var errString string
|
||||||
if err = w.r.processRoomEvent(w.r.ProcessContext.Context(), &inputRoomEvent); err != nil {
|
if err = w.r.processRoomEvent(w.r.ProcessContext.Context(), &inputRoomEvent); err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case types.RejectedError:
|
||||||
|
// Don't send events that were rejected to Sentry
|
||||||
|
logrus.WithError(err).WithFields(logrus.Fields{
|
||||||
|
"room_id": w.roomID,
|
||||||
|
"event_id": inputRoomEvent.Event.EventID(),
|
||||||
|
"type": inputRoomEvent.Event.Type(),
|
||||||
|
}).Warn("Roomserver rejected event")
|
||||||
|
default:
|
||||||
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) {
|
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) {
|
||||||
sentry.CaptureException(err)
|
sentry.CaptureException(err)
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +264,8 @@ func (w *worker) _next() {
|
||||||
"room_id": w.roomID,
|
"room_id": w.roomID,
|
||||||
"event_id": inputRoomEvent.Event.EventID(),
|
"event_id": inputRoomEvent.Event.EventID(),
|
||||||
"type": inputRoomEvent.Event.Type(),
|
"type": inputRoomEvent.Event.Type(),
|
||||||
}).Warn("Roomserver failed to process async event")
|
}).Warn("Roomserver failed to process event")
|
||||||
|
}
|
||||||
_ = msg.Term()
|
_ = msg.Term()
|
||||||
errString = err.Error()
|
errString = err.Error()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -336,18 +348,18 @@ func (r *Inputer) InputRoomEvents(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.InputRoomEventsRequest,
|
request *api.InputRoomEventsRequest,
|
||||||
response *api.InputRoomEventsResponse,
|
response *api.InputRoomEventsResponse,
|
||||||
) {
|
) error {
|
||||||
// Queue up the event into the roomserver.
|
// Queue up the event into the roomserver.
|
||||||
replySub, err := r.queueInputRoomEvents(ctx, request)
|
replySub, err := r.queueInputRoomEvents(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.ErrMsg = err.Error()
|
response.ErrMsg = err.Error()
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we aren't waiting for synchronous responses then we can
|
// If we aren't waiting for synchronous responses then we can
|
||||||
// give up here, there is nothing further to do.
|
// give up here, there is nothing further to do.
|
||||||
if replySub == nil {
|
if replySub == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we'll want to sit and wait for the responses
|
// Otherwise, we'll want to sit and wait for the responses
|
||||||
|
|
@ -359,12 +371,14 @@ func (r *Inputer) InputRoomEvents(
|
||||||
msg, err := replySub.NextMsgWithContext(ctx)
|
msg, err := replySub.NextMsgWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.ErrMsg = err.Error()
|
response.ErrMsg = err.Error()
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
if len(msg.Data) > 0 {
|
if len(msg.Data) > 0 {
|
||||||
response.ErrMsg = string(msg.Data)
|
response.ErrMsg = string(msg.Data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var roomserverInputBackpressure = prometheus.NewGaugeVec(
|
var roomserverInputBackpressure = prometheus.NewGaugeVec(
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue