mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-09 07:03:10 -06:00
Merge branch 'main' of https://github.com/HereNotThere/dendrite
This commit is contained in:
commit
bed1f9ab66
11
.github/workflows/dendrite.yml
vendored
11
.github/workflows/dendrite.yml
vendored
|
|
@ -17,13 +17,14 @@ jobs:
|
|||
name: WASM build test
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ false }} # disable for now
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.18
|
||||
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
|
|
@ -96,7 +97,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go: ["1.16", "1.17", "1.18"]
|
||||
go: ["1.18"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup go
|
||||
|
|
@ -126,7 +127,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go: ["1.16", "1.17", "1.18"]
|
||||
go: ["1.18"]
|
||||
goos: ["linux"]
|
||||
goarch: ["amd64", "386"]
|
||||
steps:
|
||||
|
|
@ -159,7 +160,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.16", "1.17", "1.18"]
|
||||
go: ["1.18"]
|
||||
goos: ["windows"]
|
||||
goarch: ["amd64"]
|
||||
steps:
|
||||
|
|
@ -208,7 +209,7 @@ jobs:
|
|||
- name: Setup go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.16"
|
||||
go-version: "1.18"
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
|
|
|
|||
34
CHANGES.md
34
CHANGES.md
|
|
@ -1,5 +1,39 @@
|
|||
# Changelog
|
||||
|
||||
## Dendrite 0.8.9 (2022-07-01)
|
||||
|
||||
### Features
|
||||
|
||||
* Incoming device list updates over federation are now queued in JetStream for processing so that they will no longer block incoming federation transactions and should never end up dropped, which will hopefully help E2EE reliability
|
||||
* The `/context` endpoint now returns `"start"` and `"end"` parameters to allow pagination from a context call
|
||||
* The `/messages` endpoint will no longer return `"end"` when there are no more messages remaining
|
||||
* Deactivated user accounts will now leave all rooms automatically
|
||||
* New admin endpoint `/_dendrite/admin/evacuateUser/{userID}` has been added for forcing a local user to leave all joined rooms
|
||||
* Dendrite will now automatically attempt to raise the file descriptor limit at startup if it is too low
|
||||
|
||||
### Fixes
|
||||
|
||||
* A rare crash when retrieving remote device lists has been fixed
|
||||
* Fixes a bug where events were not redacted properly over federation
|
||||
* The `/invite` endpoints will now return an error instead of silently proceeding if the user ID is obviously malformed
|
||||
|
||||
## Dendrite 0.8.8 (2022-06-09)
|
||||
|
||||
### Features
|
||||
|
||||
* The performance of state resolution has been increased significantly for larger rooms
|
||||
* A number of changes have been made to rate limiting:
|
||||
* Logged in users will now be rate-limited on a per-session basis rather than by remote IP
|
||||
* Rate limiting no longer applies to admin or appservice users
|
||||
* It is now possible to configure additional users that are exempt from rate limiting using the `exempt_user_ids` option in the `rate_limiting` section of the Dendrite config
|
||||
* Setting state is now idempotent via the client API state endpoints
|
||||
|
||||
### Fixes
|
||||
|
||||
* Room upgrades now properly propagate tombstone events to remote servers
|
||||
* Room upgrades will no longer send tombstone events if creating the upgraded room fails
|
||||
* A crash has been fixed when evaluating restricted room joins
|
||||
|
||||
## Dendrite 0.8.7 (2022-06-01)
|
||||
|
||||
### Features
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ If you have further questions, please take a look at [our FAQ](docs/FAQ.md) or j
|
|||
|
||||
## Requirements
|
||||
|
||||
To build Dendrite, you will need Go 1.16 or later.
|
||||
To build Dendrite, you will need Go 1.18 or later.
|
||||
|
||||
For a usable federating Dendrite deployment, you will also need:
|
||||
|
||||
|
|
|
|||
2
build.sh
2
build.sh
|
|
@ -21,4 +21,4 @@ mkdir -p bin
|
|||
|
||||
CGO_ENABLED=1 go build -trimpath -ldflags "$FLAGS" -v -o "bin/" ./cmd/...
|
||||
|
||||
CGO_ENABLED=0 GOOS=js GOARCH=wasm go build -trimpath -ldflags "$FLAGS" -o bin/main.wasm ./cmd/dendritejs-pinecone
|
||||
# CGO_ENABLED=0 GOOS=js GOARCH=wasm go build -trimpath -ldflags "$FLAGS" -o bin/main.wasm ./cmd/dendritejs-pinecone
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ func (m *DendriteMonolith) Start() {
|
|||
m.PineconeRouter = pineconeRouter.NewRouter(logrus.WithField("pinecone", "router"), sk, false)
|
||||
m.PineconeQUIC = pineconeSessions.NewSessions(logrus.WithField("pinecone", "sessions"), m.PineconeRouter, []string{"matrix"})
|
||||
m.PineconeMulticast = pineconeMulticast.NewMulticast(logrus.WithField("pinecone", "multicast"), m.PineconeRouter)
|
||||
m.PineconeManager = pineconeConnections.NewConnectionManager(m.PineconeRouter)
|
||||
m.PineconeManager = pineconeConnections.NewConnectionManager(m.PineconeRouter, nil)
|
||||
|
||||
prefix := hex.EncodeToString(pk)
|
||||
cfg := &config.Dendrite{}
|
||||
|
|
@ -261,7 +261,7 @@ func (m *DendriteMonolith) Start() {
|
|||
cfg.MSCs.MSCs = []string{"msc2836", "msc2946"}
|
||||
cfg.ClientAPI.RegistrationDisabled = false
|
||||
cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled = true
|
||||
if err := cfg.Derive(); err != nil {
|
||||
if err = cfg.Derive(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
|
@ -342,11 +342,23 @@ func (m *DendriteMonolith) Start() {
|
|||
|
||||
go func() {
|
||||
m.logger.Info("Listening on ", cfg.Global.ServerName)
|
||||
m.logger.Fatal(m.httpServer.Serve(m.PineconeQUIC.Protocol("matrix")))
|
||||
|
||||
switch m.httpServer.Serve(m.PineconeQUIC.Protocol("matrix")) {
|
||||
case net.ErrClosed, http.ErrServerClosed:
|
||||
m.logger.Info("Stopped listening on ", cfg.Global.ServerName)
|
||||
default:
|
||||
m.logger.Fatal(err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
logrus.Info("Listening on ", m.listener.Addr())
|
||||
logrus.Fatal(http.Serve(m.listener, httpRouter))
|
||||
|
||||
switch http.Serve(m.listener, httpRouter) {
|
||||
case net.ErrClosed, http.ErrServerClosed:
|
||||
m.logger.Info("Stopped listening on ", cfg.Global.ServerName)
|
||||
default:
|
||||
m.logger.Fatal(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,11 +170,11 @@ func (m *DendriteMonolith) Start() {
|
|||
|
||||
go func() {
|
||||
m.logger.Info("Listening on ", ygg.DerivedServerName())
|
||||
m.logger.Fatal(m.httpServer.Serve(ygg))
|
||||
m.logger.Error(m.httpServer.Serve(ygg))
|
||||
}()
|
||||
go func() {
|
||||
logrus.Info("Listening on ", m.listener.Addr())
|
||||
logrus.Fatal(http.Serve(m.listener, httpRouter))
|
||||
logrus.Error(http.Serve(m.listener, httpRouter))
|
||||
}()
|
||||
go func() {
|
||||
logrus.Info("Sending wake-up message to known nodes")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.16-stretch as build
|
||||
FROM golang:1.18-stretch as build
|
||||
RUN apt-get update && apt-get install -y sqlite3
|
||||
WORKDIR /build
|
||||
|
||||
|
|
@ -27,6 +27,6 @@ EXPOSE 8008 8448
|
|||
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
||||
# At runtime, replace the SERVER_NAME with what we are told
|
||||
CMD ./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 && \
|
||||
./generate-config -server $SERVER_NAME --ci > dendrite.yaml && \
|
||||
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}
|
||||
./generate-config -server $SERVER_NAME --ci > dendrite.yaml && \
|
||||
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}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#
|
||||
# Use these mounts to make use of this dockerfile:
|
||||
# COMPLEMENT_HOST_MOUNTS='/your/local/dendrite:/dendrite:ro;/your/go/path:/go:ro'
|
||||
FROM golang:1.16-stretch
|
||||
FROM golang:1.18-stretch
|
||||
RUN apt-get update && apt-get install -y sqlite3
|
||||
|
||||
WORKDIR /runtime
|
||||
|
|
@ -16,24 +16,24 @@ EXPOSE 8008 8448
|
|||
|
||||
# This script compiles Dendrite for us.
|
||||
RUN echo '\
|
||||
#!/bin/bash -eux \n\
|
||||
if test -f "/runtime/dendrite-monolith-server"; then \n\
|
||||
#!/bin/bash -eux \n\
|
||||
if test -f "/runtime/dendrite-monolith-server"; then \n\
|
||||
echo "Skipping compilation; binaries exist" \n\
|
||||
exit 0 \n\
|
||||
fi \n\
|
||||
cd /dendrite \n\
|
||||
go build -v -o /runtime /dendrite/cmd/dendrite-monolith-server \n\
|
||||
' > compile.sh && chmod +x compile.sh
|
||||
fi \n\
|
||||
cd /dendrite \n\
|
||||
go build -v -o /runtime /dendrite/cmd/dendrite-monolith-server \n\
|
||||
' > compile.sh && chmod +x compile.sh
|
||||
|
||||
# This script runs Dendrite for us. Must be run in the /runtime directory.
|
||||
RUN echo '\
|
||||
#!/bin/bash -eu \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-config -server $SERVER_NAME --ci > dendrite.yaml \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\
|
||||
' > run.sh && chmod +x run.sh
|
||||
#!/bin/bash -eu \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-config -server $SERVER_NAME --ci > dendrite.yaml \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\
|
||||
' > run.sh && chmod +x run.sh
|
||||
|
||||
|
||||
WORKDIR /cache
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.16-stretch as build
|
||||
FROM golang:1.18-stretch as build
|
||||
RUN apt-get update && apt-get install -y postgresql
|
||||
WORKDIR /build
|
||||
|
||||
|
|
@ -9,16 +9,16 @@ RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/
|
|||
|
||||
# This entry script starts postgres, waits for it to be up then starts dendrite
|
||||
RUN echo '\
|
||||
#!/bin/bash -eu \n\
|
||||
pg_lsclusters \n\
|
||||
pg_ctlcluster 9.6 main start \n\
|
||||
\n\
|
||||
until pg_isready \n\
|
||||
do \n\
|
||||
echo "Waiting for postgres"; \n\
|
||||
sleep 1; \n\
|
||||
done \n\
|
||||
' > run_postgres.sh && chmod +x run_postgres.sh
|
||||
#!/bin/bash -eu \n\
|
||||
pg_lsclusters \n\
|
||||
pg_ctlcluster 9.6 main start \n\
|
||||
\n\
|
||||
until pg_isready \n\
|
||||
do \n\
|
||||
echo "Waiting for postgres"; \n\
|
||||
sleep 1; \n\
|
||||
done \n\
|
||||
' > run_postgres.sh && chmod +x run_postgres.sh
|
||||
|
||||
# we will dump the binaries and config file to this location to ensure any local untracked files
|
||||
# that come from the COPY . . file don't contaminate the build
|
||||
|
|
@ -46,9 +46,9 @@ EXPOSE 8008 8448
|
|||
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
||||
# At runtime, replace the SERVER_NAME with what we are told
|
||||
CMD /build/run_postgres.sh && ./generate-keys --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 && \
|
||||
# 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/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml && \
|
||||
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}
|
||||
./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
|
||||
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 && \
|
||||
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}
|
||||
19
clientapi/auth/authtypes/stages.go
Normal file
19
clientapi/auth/authtypes/stages.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2021 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 authtypes
|
||||
|
||||
const (
|
||||
LoginStagePublicKeyNewRegistration = "m.login.publickey.newregistration"
|
||||
)
|
||||
|
|
@ -33,6 +33,7 @@ type LoginPublicKeyHandler interface {
|
|||
CreateLogin() *Login
|
||||
GetSession() string
|
||||
GetType() string
|
||||
IsValidUserId(userId string) bool
|
||||
ValidateLoginResponse() (bool, *jsonerror.MatrixError)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,51 +16,30 @@ package auth
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/spruceid/siwe-go"
|
||||
)
|
||||
|
||||
type LoginPublicKeyEthereum struct {
|
||||
// https://github.com/tak-hntlabs/matrix-spec-proposals/blob/main/proposals/3782-matrix-publickey-login-spec.md#client-sends-login-request-with-authentication-data
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
Session string `json:"session"`
|
||||
Message string `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
HashFields publicKeyEthereumHashFields `json:"hashFields"`
|
||||
HashFieldsRaw string // Raw base64 encoded string of MessageFields for hash verification
|
||||
Type string `json:"type"`
|
||||
UserId string `json:"user_id"`
|
||||
Session string `json:"session"`
|
||||
Message string `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
|
||||
userAPI userapi.ClientUserAPI
|
||||
config *config.ClientAPI
|
||||
}
|
||||
|
||||
type publicKeyEthereumHashFields struct {
|
||||
// Todo: See https://...
|
||||
Domain string `json:"domain"` // home server domain
|
||||
Address string `json:"address"` // Ethereum address. 0x...
|
||||
Nonce string `json:"nonce"` // session ID
|
||||
Version string `json:"version"` // version of the Matrix public key spec that the client is complying with
|
||||
ChainId string `json:"chainId"` // blockchain network ID.
|
||||
}
|
||||
|
||||
type publicKeyEthereumRequiredFields struct {
|
||||
From string // Sender
|
||||
To string // Recipient
|
||||
Hash string // Hash of JSON representation of the message fields
|
||||
}
|
||||
|
||||
func CreatePublicKeyEthereumHandler(
|
||||
reqBytes []byte,
|
||||
userAPI userapi.ClientUserAPI,
|
||||
|
|
@ -71,17 +50,10 @@ func CreatePublicKeyEthereumHandler(
|
|||
return nil, jsonerror.BadJSON("auth")
|
||||
}
|
||||
|
||||
hashFields := gjson.GetBytes(reqBytes, "hashFields")
|
||||
if !hashFields.Exists() {
|
||||
return nil, jsonerror.BadJSON("auth.hashFields")
|
||||
}
|
||||
|
||||
pk.config = config
|
||||
pk.userAPI = userAPI
|
||||
// Save raw bytes for hash verification later.
|
||||
pk.HashFieldsRaw = hashFields.Raw
|
||||
// Case-insensitive
|
||||
pk.Address = strings.ToLower(pk.Address)
|
||||
pk.UserId = strings.ToLower(pk.UserId)
|
||||
|
||||
return &pk, nil
|
||||
}
|
||||
|
|
@ -95,12 +67,16 @@ func (pk LoginPublicKeyEthereum) GetType() string {
|
|||
}
|
||||
|
||||
func (pk LoginPublicKeyEthereum) AccountExists(ctx context.Context) (string, *jsonerror.MatrixError) {
|
||||
localPart, err := userutil.ParseUsernameParam(pk.Address, &pk.config.Matrix.ServerName)
|
||||
localPart, err := userutil.ParseUsernameParam(pk.UserId, &pk.config.Matrix.ServerName)
|
||||
if err != nil {
|
||||
// userId does not exist
|
||||
return "", jsonerror.Forbidden("the address is incorrect, or the account does not exist.")
|
||||
}
|
||||
|
||||
if !pk.IsValidUserId(localPart) {
|
||||
return "", jsonerror.InvalidUsername("the username is not valid.")
|
||||
}
|
||||
|
||||
res := userapi.QueryAccountAvailabilityResponse{}
|
||||
if err := pk.userAPI.QueryAccountAvailability(ctx, &userapi.QueryAccountAvailabilityRequest{
|
||||
Localpart: localPart,
|
||||
|
|
@ -108,49 +84,52 @@ func (pk LoginPublicKeyEthereum) AccountExists(ctx context.Context) (string, *js
|
|||
return "", jsonerror.Unknown("failed to check availability: " + err.Error())
|
||||
}
|
||||
|
||||
if res.Available {
|
||||
if localPart == "" || res.Available {
|
||||
return "", jsonerror.Forbidden("the address is incorrect, account does not exist")
|
||||
}
|
||||
|
||||
return localPart, nil
|
||||
}
|
||||
|
||||
var validChainAgnosticIdRegex = regexp.MustCompile("^eip155=3a[0-9]+=3a0x[0-9a-fA-F]+$")
|
||||
|
||||
func (pk LoginPublicKeyEthereum) IsValidUserId(userId string) bool {
|
||||
// Verify that the user ID is a valid one according to spec.
|
||||
// https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md
|
||||
|
||||
// Matrix ID has additional grammar requirements for user ID.
|
||||
// https://spec.matrix.org/v1.1/appendices/#user-identifiers
|
||||
// Make sure disallowed characters are escaped.
|
||||
// E.g. ":" is replaced with "=3a".
|
||||
|
||||
isValid := validChainAgnosticIdRegex.MatchString(userId)
|
||||
|
||||
// In addition, double check that the user ID
|
||||
// matches the authentication data in the request.
|
||||
return isValid && strings.ToLower(userId) == pk.UserId
|
||||
}
|
||||
|
||||
func (pk LoginPublicKeyEthereum) ValidateLoginResponse() (bool, *jsonerror.MatrixError) {
|
||||
// Check signature to verify message was not tempered
|
||||
isVerified := verifySignature(pk.Address, []byte(pk.Message), pk.Signature)
|
||||
if !isVerified {
|
||||
return false, jsonerror.InvalidSignature("")
|
||||
}
|
||||
|
||||
// Extract the required message fields for validation
|
||||
requiredFields, err := extractRequiredMessageFields(pk.Message)
|
||||
// Parse the message to extract all the fields.
|
||||
message, err := siwe.ParseMessage(pk.Message)
|
||||
if err != nil {
|
||||
return false, jsonerror.MissingParam("message does not contain domain, address, or hash")
|
||||
return false, jsonerror.InvalidParam("auth.message")
|
||||
}
|
||||
|
||||
// Verify that the hash is valid for the message fields.
|
||||
if !verifyHash(pk.HashFieldsRaw, requiredFields.Hash) {
|
||||
return false, jsonerror.Forbidden("error verifying message hash")
|
||||
// Check signature to verify message was not tempered
|
||||
_, err = message.Verify(pk.Signature, (*string)(&pk.config.Matrix.ServerName), nil, nil)
|
||||
if err != nil {
|
||||
return false, jsonerror.InvalidSignature(err.Error())
|
||||
}
|
||||
|
||||
// Unmarshal the hashFields for further validation
|
||||
var authData publicKeyEthereumHashFields
|
||||
if err := json.Unmarshal([]byte(pk.HashFieldsRaw), &authData); err != nil {
|
||||
return false, jsonerror.BadJSON("auth.hashFields")
|
||||
}
|
||||
|
||||
// Error if the message is not from the expected public address
|
||||
if pk.Address != requiredFields.From || requiredFields.From != pk.HashFields.Address {
|
||||
return false, jsonerror.Forbidden("address")
|
||||
}
|
||||
|
||||
// Error if the message is not for the home server
|
||||
if requiredFields.To != pk.HashFields.Domain {
|
||||
return false, jsonerror.Forbidden("domain")
|
||||
// Error if the user ID does not match the signed message.
|
||||
isVerifiedUserId := pk.verifyMessageUserId(message)
|
||||
if !isVerifiedUserId {
|
||||
return false, jsonerror.InvalidUsername(pk.UserId)
|
||||
}
|
||||
|
||||
// Error if the chainId is not supported by the server.
|
||||
if !contains(pk.config.PublicKeyAuthentication.Ethereum.ChainIDs, authData.ChainId) {
|
||||
if !contains(pk.config.PublicKeyAuthentication.Ethereum.ChainIDs, message.GetChainID()) {
|
||||
return false, jsonerror.Forbidden("chainId")
|
||||
}
|
||||
|
||||
|
|
@ -160,8 +139,8 @@ func (pk LoginPublicKeyEthereum) ValidateLoginResponse() (bool, *jsonerror.Matri
|
|||
|
||||
func (pk LoginPublicKeyEthereum) CreateLogin() *Login {
|
||||
identifier := LoginIdentifier{
|
||||
Type: "m.id.publickey",
|
||||
User: pk.Address,
|
||||
Type: "m.id.decentralizedid",
|
||||
User: pk.UserId,
|
||||
}
|
||||
login := Login{
|
||||
Identifier: identifier,
|
||||
|
|
@ -169,75 +148,16 @@ func (pk LoginPublicKeyEthereum) CreateLogin() *Login {
|
|||
return &login
|
||||
}
|
||||
|
||||
// The required fields in the signed message are:
|
||||
// 1. Domain -- home server. First non-whitespace characters in the first line.
|
||||
// 2. Address -- public address of the user. Starts with 0x... in the second line on its own.
|
||||
// 3. Hash -- Base64-encoded hash string of the metadata that represents the message.
|
||||
// The rest of the fields are informational, and will be used in the future.
|
||||
var regexpAuthority = regexp.MustCompile(`^\S+`)
|
||||
var regexpAddress = regexp.MustCompile(`\n(?P<address>0x\w+)\n`)
|
||||
var regexpHash = regexp.MustCompile(`\nHash: (?P<hash>.*)\n`)
|
||||
func (pk LoginPublicKeyEthereum) verifyMessageUserId(message *siwe.Message) bool {
|
||||
// Use info in the signed message to derive the expected user ID.
|
||||
expectedUserId := fmt.Sprintf("eip155=3a%d=3a%s", message.GetChainID(), message.GetAddress())
|
||||
|
||||
func extractRequiredMessageFields(message string) (*publicKeyEthereumRequiredFields, error) {
|
||||
var requiredFields publicKeyEthereumRequiredFields
|
||||
/*
|
||||
service.org wants you to sign in with your account:
|
||||
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
|
||||
|
||||
I accept the ServiceOrg Terms of Service: https://service.org/tos
|
||||
|
||||
Hash: yfSIwarByPfKFxeYSCWN3XoIgNgeEFJffbwFA+JxYbA=
|
||||
*/
|
||||
|
||||
requiredFields.To = regexpAuthority.FindString(message)
|
||||
|
||||
from := regexpAddress.FindStringSubmatch(message)
|
||||
if len(from) == 2 {
|
||||
requiredFields.From = from[1]
|
||||
}
|
||||
|
||||
hash := regexpHash.FindStringSubmatch(message)
|
||||
if len(hash) == 2 {
|
||||
requiredFields.Hash = hash[1]
|
||||
}
|
||||
|
||||
if len(requiredFields.To) == 0 || len(requiredFields.From) == 0 || len(requiredFields.Hash) == 0 {
|
||||
return nil, errors.New("required message fields are missing")
|
||||
}
|
||||
|
||||
// Make these fields case-insensitive
|
||||
requiredFields.From = strings.ToLower(requiredFields.From)
|
||||
requiredFields.To = strings.ToLower(requiredFields.To)
|
||||
|
||||
return &requiredFields, nil
|
||||
// Case-insensitive comparison to make sure the user ID matches the expected
|
||||
// one derived from the signed message.
|
||||
return pk.UserId == strings.ToLower(expectedUserId)
|
||||
}
|
||||
|
||||
func verifySignature(from string, message []byte, signature string) bool {
|
||||
decodedSig := hexutil.MustDecode(signature)
|
||||
|
||||
message = accounts.TextHash(message)
|
||||
// Issue: https://stackoverflow.com/questions/49085737/geth-ecrecover-invalid-signature-recovery-id
|
||||
// Fix: https://gist.github.com/dcb9/385631846097e1f59e3cba3b1d42f3ed#file-eth_sign_verify-go
|
||||
decodedSig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1
|
||||
|
||||
recovered, err := crypto.SigToPub(message, decodedSig)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
recoveredAddr := crypto.PubkeyToAddress(*recovered)
|
||||
|
||||
addressStr := strings.ToLower(recoveredAddr.Hex())
|
||||
return from == addressStr
|
||||
}
|
||||
|
||||
func verifyHash(rawStr string, expectedHash string) bool {
|
||||
hash := crypto.Keccak256([]byte(rawStr))
|
||||
hashStr := base64.StdEncoding.EncodeToString(hash)
|
||||
return expectedHash == hashStr
|
||||
}
|
||||
|
||||
func contains(list []string, element string) bool {
|
||||
func contains(list []int, element int) bool {
|
||||
for _, i := range list {
|
||||
if i == element {
|
||||
return true
|
||||
|
|
|
|||
472
clientapi/auth/login_publickey_ethereum_test.go
Normal file
472
clientapi/auth/login_publickey_ethereum_test.go
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
// 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 auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type loginContext struct {
|
||||
config *config.ClientAPI
|
||||
userInteractive *UserInteractive
|
||||
}
|
||||
|
||||
func createLoginContext(_ *testing.T) *loginContext {
|
||||
chainIds := []int{4}
|
||||
|
||||
cfg := &config.ClientAPI{
|
||||
Matrix: &config.Global{
|
||||
ServerName: test.TestServerName,
|
||||
},
|
||||
Derived: &config.Derived{},
|
||||
PasswordAuthenticationDisabled: true,
|
||||
PublicKeyAuthentication: config.PublicKeyAuthentication{
|
||||
Ethereum: config.EthereumAuthConfig{
|
||||
Enabled: true,
|
||||
Version: 1,
|
||||
ChainIDs: chainIds,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pkFlows := cfg.PublicKeyAuthentication.GetPublicKeyRegistrationFlows()
|
||||
cfg.Derived.Registration.Flows = append(cfg.Derived.Registration.Flows, pkFlows...)
|
||||
pkParams := cfg.PublicKeyAuthentication.GetPublicKeyRegistrationParams()
|
||||
cfg.Derived.Registration.Params = mapsutil.MapsUnion(cfg.Derived.Registration.Params, pkParams)
|
||||
|
||||
var userAPI fakePublicKeyUserApi
|
||||
var loginApi uapi.UserLoginAPI
|
||||
|
||||
userInteractive := NewUserInteractive(
|
||||
loginApi,
|
||||
&userAPI,
|
||||
cfg)
|
||||
|
||||
return &loginContext{
|
||||
config: cfg,
|
||||
userInteractive: userInteractive,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type fakePublicKeyUserApi struct {
|
||||
UserInternalAPIForLogin
|
||||
uapi.UserLoginAPI
|
||||
uapi.ClientUserAPI
|
||||
DeletedTokens []string
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) QueryAccountAvailability(ctx context.Context, req *uapi.QueryAccountAvailabilityRequest, res *uapi.QueryAccountAvailabilityResponse) error {
|
||||
if req.Localpart == "does_not_exist" {
|
||||
res.Available = true
|
||||
return nil
|
||||
}
|
||||
|
||||
res.Available = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) QueryAccountByPassword(ctx context.Context, req *uapi.QueryAccountByPasswordRequest, res *uapi.QueryAccountByPasswordResponse) error {
|
||||
if req.PlaintextPassword == "invalidpassword" {
|
||||
res.Account = nil
|
||||
return nil
|
||||
}
|
||||
res.Exists = true
|
||||
res.Account = &uapi.Account{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformLoginTokenDeletion(ctx context.Context, req *uapi.PerformLoginTokenDeletionRequest, res *uapi.PerformLoginTokenDeletionResponse) error {
|
||||
ua.DeletedTokens = append(ua.DeletedTokens, req.Token)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformLoginTokenCreation(ctx context.Context, req *uapi.PerformLoginTokenCreationRequest, res *uapi.PerformLoginTokenCreationResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakePublicKeyUserApi) QueryLoginToken(ctx context.Context, req *uapi.QueryLoginTokenRequest, res *uapi.QueryLoginTokenResponse) error {
|
||||
if req.Token == "invalidtoken" {
|
||||
return nil
|
||||
}
|
||||
|
||||
res.Data = &uapi.LoginTokenData{UserID: "@auser:example.com"}
|
||||
return nil
|
||||
}
|
||||
|
||||
func publicKeyTestSession(
|
||||
ctx *context.Context,
|
||||
cfg *config.ClientAPI,
|
||||
userInteractive *UserInteractive,
|
||||
userAPI *fakePublicKeyUserApi,
|
||||
|
||||
) string {
|
||||
emptyAuth := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: `{
|
||||
"type": "m.login.publickey"
|
||||
}`,
|
||||
}
|
||||
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
*ctx,
|
||||
strings.NewReader(emptyAuth.Body),
|
||||
userAPI,
|
||||
userAPI,
|
||||
userAPI,
|
||||
userInteractive,
|
||||
cfg)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(*ctx, nil)
|
||||
}
|
||||
|
||||
json := err.JSON.(Challenge)
|
||||
return json.Session
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereum(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
wallet, _ := test.CreateTestAccount()
|
||||
message, _ := test.CreateEip4361TestMessage(wallet.PublicAddress)
|
||||
signature, _ := test.SignMessage(message.String(), wallet.PrivateKey)
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
// Escape \t and \n. Work around for marshalling and unmarshalling message.
|
||||
msgStr := test.FromEip4361MessageToString(message)
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v",
|
||||
"message": "%v",
|
||||
"signature": "%v"
|
||||
}
|
||||
}`,
|
||||
sessionId,
|
||||
wallet.Eip155UserId,
|
||||
msgStr,
|
||||
signature,
|
||||
)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
login, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Nilf(err, "err actual: %v, expected: nil", err)
|
||||
assert.NotNil(login, "login: actual: nil, expected: not nil")
|
||||
assert.Truef(
|
||||
login.Identifier.Type == "m.id.decentralizedid",
|
||||
"login.Identifier.Type actual: %v, expected: %v", login.Identifier.Type, "m.id.decentralizedid")
|
||||
walletAddress := strings.ToLower(wallet.Eip155UserId)
|
||||
assert.Truef(
|
||||
login.Identifier.User == walletAddress,
|
||||
"login.Identifier.User actual: %v, expected: %v", login.Identifier.User, walletAddress)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumMissingSignature(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
wallet, _ := test.CreateTestAccount()
|
||||
message, _ := test.CreateEip4361TestMessage(wallet.PublicAddress)
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
// Escape \t and \n. Work around for marshalling and unmarshalling message.
|
||||
msgStr := test.FromEip4361MessageToString(message)
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v",
|
||||
"message": "%v"
|
||||
}
|
||||
}`,
|
||||
sessionId,
|
||||
wallet.Eip155UserId,
|
||||
msgStr,
|
||||
)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusUnauthorized)
|
||||
json := err.JSON.(*jsonerror.MatrixError)
|
||||
expectedErr := jsonerror.InvalidSignature("")
|
||||
assert.Truef(
|
||||
json.ErrCode == expectedErr.ErrCode,
|
||||
"err.JSON.ErrCode actual: %v, expected: %v", json.ErrCode, expectedErr.ErrCode)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumEmptyMessage(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
wallet, _ := test.CreateTestAccount()
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v"
|
||||
}
|
||||
}`, sessionId, wallet.Eip155UserId)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusUnauthorized)
|
||||
json := err.JSON.(*jsonerror.MatrixError)
|
||||
expectedErr := jsonerror.InvalidParam("")
|
||||
assert.Truef(
|
||||
json.ErrCode == expectedErr.ErrCode,
|
||||
"err.JSON.ErrCode actual: %v, expected: %v", json.ErrCode, expectedErr.ErrCode)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumWrongUserId(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
wallet, _ := test.CreateTestAccount()
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v"
|
||||
}
|
||||
}`,
|
||||
sessionId,
|
||||
wallet.PublicAddress)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusForbidden,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusForbidden)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumMissingUserId(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v"
|
||||
}
|
||||
}`, sessionId)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusForbidden,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusForbidden)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyEthereumAccountNotAvailable(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
sessionId := publicKeyTestSession(
|
||||
&ctx,
|
||||
loginContext.config,
|
||||
loginContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "does_not_exist"
|
||||
}
|
||||
}`, sessionId)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusForbidden,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusForbidden)
|
||||
}
|
||||
161
clientapi/auth/login_publickey_test.go
Normal file
161
clientapi/auth/login_publickey_test.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// 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 auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLoginPublicKeyNewSession(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: `{ "type": "m.login.publickey" }`,
|
||||
}
|
||||
|
||||
// Test
|
||||
login, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNilf(
|
||||
err,
|
||||
"err actual: not nil returned %+v, expected: nil", login)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual: %v, expected: %v", err.Code, http.StatusUnauthorized)
|
||||
challenge := err.JSON.(Challenge)
|
||||
assert.NotEmptyf(challenge.Session, "challenge.Session")
|
||||
assert.NotEmptyf(challenge.Completed, "challenge.Completed")
|
||||
assert.Truef(
|
||||
authtypes.LoginTypePublicKeyEthereum == challenge.Flows[0].Stages[0],
|
||||
"challenge.Flows[0].Stages[0] actual: %v, expected: %v", challenge.Flows[0].Stages[0], authtypes.LoginTypePublicKeyEthereum)
|
||||
params := challenge.Params[authtypes.LoginTypePublicKeyEthereum]
|
||||
assert.NotEmptyf(
|
||||
params,
|
||||
"challenge.Params[\"%v\"] actual %v, expected %v",
|
||||
authtypes.LoginTypePublicKeyEthereum,
|
||||
params,
|
||||
"[object]")
|
||||
ethParams := params.(config.EthereumAuthParams)
|
||||
assert.NotEmptyf(ethParams.ChainIDs, "ChainIDs actual: empty, expected not empty")
|
||||
assert.NotEmptyf(ethParams.Nonce, "Nonce actual: \"\", expected: not empty")
|
||||
assert.NotEmptyf(ethParams.Version, "Version actual: \"\", expected: not empty")
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyInvalidSessionId(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: `{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "invalid_session_id"
|
||||
}
|
||||
}`,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual %v, expected %v", err.Code, http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
func TestLoginPublicKeyInvalidAuthType(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
ctx := context.Background()
|
||||
loginContext := createLoginContext(t)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: `{
|
||||
"type": "m.login.publickey",
|
||||
"auth": {
|
||||
"type": "m.login.publickey.someAlgo"
|
||||
}
|
||||
}`,
|
||||
}
|
||||
|
||||
// Test
|
||||
_, cleanup, err := LoginFromJSONReader(
|
||||
ctx,
|
||||
strings.NewReader(test.Body),
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
&userAPI,
|
||||
loginContext.userInteractive,
|
||||
loginContext.config)
|
||||
|
||||
if cleanup != nil {
|
||||
cleanup(ctx, nil)
|
||||
}
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNil(err, "Expected an err response.actual: nil")
|
||||
assert.Truef(
|
||||
err.Code == http.StatusUnauthorized,
|
||||
"err.Code actual %v, expected %v", err.Code, http.StatusUnauthorized)
|
||||
_, ok := err.JSON.(Challenge)
|
||||
assert.False(
|
||||
ok,
|
||||
"should not return a Challenge response")
|
||||
}
|
||||
|
|
@ -62,11 +62,10 @@ func TestLoginFromJSONReader(t *testing.T) {
|
|||
},
|
||||
}
|
||||
userInteractive := UserInteractive{
|
||||
Completed: []string{},
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
for _, tst := range tsts {
|
||||
|
|
@ -148,11 +147,10 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
|||
},
|
||||
}
|
||||
userInteractive := UserInteractive{
|
||||
Completed: []string{},
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
for _, tst := range tsts {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
|
|
@ -74,7 +75,7 @@ type Login struct {
|
|||
|
||||
// Username returns the user localpart/user_id in this request, if it exists.
|
||||
func (r *Login) Username() string {
|
||||
if r.Identifier.Type == "m.id.user" || r.Identifier.Type == "m.id.publickey" {
|
||||
if r.Identifier.Type == "m.id.user" || r.Identifier.Type == "m.id.decentralizedid" {
|
||||
return r.Identifier.User
|
||||
}
|
||||
// deprecated but without it Element iOS won't log in
|
||||
|
|
@ -102,8 +103,6 @@ type userInteractiveFlow struct {
|
|||
// the user already has a valid access token, but we want to double-check
|
||||
// that it isn't stolen by re-authenticating them.
|
||||
type UserInteractive struct {
|
||||
Completed []string
|
||||
|
||||
Flows []userInteractiveFlow
|
||||
// Map of login type to implementation
|
||||
Types map[string]Type
|
||||
|
|
@ -118,11 +117,10 @@ func NewUserInteractive(
|
|||
cfg *config.ClientAPI,
|
||||
) *UserInteractive {
|
||||
userInteractive := UserInteractive{
|
||||
Completed: []string{},
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
Flows: []userInteractiveFlow{},
|
||||
Types: make(map[string]Type),
|
||||
Sessions: make(map[string][]string),
|
||||
Params: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
if !cfg.PasswordAuthenticationDisabled {
|
||||
|
|
@ -130,12 +128,6 @@ func NewUserInteractive(
|
|||
GetAccountByPassword: userAccountAPI.QueryAccountByPassword,
|
||||
Config: cfg,
|
||||
}
|
||||
|
||||
userInteractive.Flows = append(userInteractive.Flows, userInteractiveFlow{
|
||||
Stages: []string{typePassword.Name()},
|
||||
},
|
||||
)
|
||||
userInteractive.Types[typePassword.Name()] = typePassword
|
||||
typePassword.AddFLows(&userInteractive)
|
||||
}
|
||||
|
||||
|
|
@ -179,13 +171,29 @@ type Challenge struct {
|
|||
|
||||
// Challenge returns an HTTP 401 with the supported flows for authenticating
|
||||
func (u *UserInteractive) Challenge(sessionID string) *util.JSONResponse {
|
||||
paramsCopy := mapsutil.MapCopy(u.Params)
|
||||
for key, element := range paramsCopy {
|
||||
p := GetAuthParams(element)
|
||||
if p != nil {
|
||||
// If an auth flow has params,
|
||||
// send it as part of the challenge.
|
||||
paramsCopy[key] = p
|
||||
|
||||
// If an auth flow generated a nonce, add it to the session.
|
||||
nonce := getAuthParamNonce(p)
|
||||
if nonce != "" {
|
||||
u.Sessions[sessionID] = append(u.Sessions[sessionID], nonce)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &util.JSONResponse{
|
||||
Code: 401,
|
||||
JSON: Challenge{
|
||||
Completed: u.Sessions[sessionID],
|
||||
Flows: u.Flows,
|
||||
Session: sessionID,
|
||||
Params: u.Params,
|
||||
Params: paramsCopy,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -229,7 +237,7 @@ func (u *UserInteractive) ResponseWithChallenge(sessionID string, response inter
|
|||
// Verify returns an error/challenge response to send to the client, or nil if the user is authenticated.
|
||||
// `bodyBytes` is the HTTP request body which must contain an `auth` key.
|
||||
// Returns the login that was verified for additional checks if required.
|
||||
func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte) (*Login, *util.JSONResponse) {
|
||||
func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *api.Device) (*Login, *util.JSONResponse) {
|
||||
// TODO: rate limit
|
||||
|
||||
// "A client should first make a request with no auth parameter. The homeserver returns an HTTP 401 response, with a JSON body"
|
||||
|
|
@ -271,3 +279,20 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte) (*Login,
|
|||
// TODO: Check if there's more stages to go and return an error
|
||||
return login, nil
|
||||
}
|
||||
|
||||
func GetAuthParams(params interface{}) interface{} {
|
||||
v, ok := params.(config.AuthParams)
|
||||
if ok {
|
||||
p := v.GetParams()
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAuthParamNonce(p interface{}) string {
|
||||
v, ok := p.(config.AuthParams)
|
||||
if ok {
|
||||
return v.GetNonce()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ var (
|
|||
serverName = gomatrixserverlib.ServerName("example.com")
|
||||
// space separated localpart+password -> account
|
||||
lookup = make(map[string]*api.Account)
|
||||
device = &api.Device{
|
||||
AccessToken: "flibble",
|
||||
DisplayName: "My Device",
|
||||
ID: "device_id_goes_here",
|
||||
}
|
||||
)
|
||||
|
||||
type fakeAccountDatabase struct {
|
||||
|
|
@ -55,7 +60,7 @@ func setup() *UserInteractive {
|
|||
func TestUserInteractiveChallenge(t *testing.T) {
|
||||
uia := setup()
|
||||
// no auth key results in a challenge
|
||||
_, errRes := uia.Verify(ctx, []byte(`{}`))
|
||||
_, errRes := uia.Verify(ctx, []byte(`{}`), device)
|
||||
if errRes == nil {
|
||||
t.Fatalf("Verify succeeded with {} but expected failure")
|
||||
}
|
||||
|
|
@ -95,7 +100,7 @@ func TestUserInteractivePasswordLogin(t *testing.T) {
|
|||
}`),
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
_, errRes := uia.Verify(ctx, tc)
|
||||
_, errRes := uia.Verify(ctx, tc, device)
|
||||
if errRes != nil {
|
||||
t.Errorf("Verify failed but expected success for request: %s - got %+v", string(tc), errRes)
|
||||
}
|
||||
|
|
@ -176,7 +181,7 @@ func TestUserInteractivePasswordBadLogin(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
_, errRes := uia.Verify(ctx, tc.body)
|
||||
_, errRes := uia.Verify(ctx, tc.body, device)
|
||||
if errRes == nil {
|
||||
t.Errorf("Verify succeeded but expected failure for request: %s", string(tc.body))
|
||||
continue
|
||||
|
|
@ -209,7 +214,7 @@ func TestUserInteractive_AddCompletedStage(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, resp := u.Verify(ctx, []byte("{}"))
|
||||
_, resp := u.Verify(ctx, []byte("{}"), nil)
|
||||
challenge, ok := resp.JSON.(Challenge)
|
||||
if !ok {
|
||||
t.Fatalf("expected a Challenge, got %T", resp.JSON)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package producers
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ func (p *SyncAPIProducer) SendReceipt(
|
|||
m.Header.Set(jetstream.RoomID, roomID)
|
||||
m.Header.Set(jetstream.EventID, eventID)
|
||||
m.Header.Set("type", receiptType)
|
||||
m.Header.Set("timestamp", strconv.Itoa(int(timestamp)))
|
||||
m.Header.Set("timestamp", fmt.Sprintf("%d", timestamp))
|
||||
|
||||
log.WithFields(log.Fields{}).Tracef("Producing to topic '%s'", p.TopicReceiptEvent)
|
||||
_, err := p.JetStream.PublishMsg(m, nats.Context(ctx))
|
||||
|
|
|
|||
|
|
@ -47,3 +47,40 @@ func AdminEvacuateRoom(req *http.Request, device *userapi.Device, rsAPI roomserv
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func AdminEvacuateUser(req *http.Request, 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))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
userID, ok := vars["userID"]
|
||||
if !ok {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.MissingArgument("Expecting user ID."),
|
||||
}
|
||||
}
|
||||
res := &roomserverAPI.PerformAdminEvacuateUserResponse{}
|
||||
rsAPI.PerformAdminEvacuateUser(
|
||||
req.Context(),
|
||||
&roomserverAPI.PerformAdminEvacuateUserRequest{
|
||||
UserID: userID,
|
||||
},
|
||||
res,
|
||||
)
|
||||
if err := res.Error; err != nil {
|
||||
return err.JSONResponse()
|
||||
}
|
||||
return util.JSONResponse{
|
||||
Code: 200,
|
||||
JSON: map[string]interface{}{
|
||||
"affected": res.Affected,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ func GetAliases(
|
|||
return util.ErrorResponse(fmt.Errorf("rsAPI.QueryCurrentState: %w", err))
|
||||
}
|
||||
|
||||
visibility := "invite"
|
||||
visibility := gomatrixserverlib.HistoryVisibilityInvited
|
||||
if historyVisEvent, ok := stateRes.StateEvents[stateTuple]; ok {
|
||||
var err error
|
||||
visibility, err = historyVisEvent.HistoryVisibility()
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func Deactivate(
|
|||
}
|
||||
}
|
||||
|
||||
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes)
|
||||
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes, deviceAPI)
|
||||
if errRes != nil {
|
||||
return *errRes
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ func DeleteDeviceById(
|
|||
sessionID = s
|
||||
}
|
||||
|
||||
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes)
|
||||
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes, device)
|
||||
if errRes != nil {
|
||||
switch data := errRes.JSON.(type) {
|
||||
case auth.Challenge:
|
||||
|
|
|
|||
|
|
@ -29,10 +29,12 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/tokens"
|
||||
"github.com/matrix-org/util"
|
||||
|
|
@ -68,9 +70,10 @@ const (
|
|||
// It shouldn't be passed by value because it contains a mutex.
|
||||
type sessionsDict struct {
|
||||
sync.RWMutex
|
||||
sessions map[string][]authtypes.LoginType
|
||||
params map[string]registerRequest
|
||||
timer map[string]*time.Timer
|
||||
sessions map[string][]authtypes.LoginType
|
||||
sessionCompletedResult map[string]registerResponse
|
||||
params map[string]registerRequest
|
||||
timer map[string]*time.Timer
|
||||
// deleteSessionToDeviceID protects requests to DELETE /devices/{deviceID} from being abused.
|
||||
// If a UIA session is started by trying to delete device1, and then UIA is completed by deleting device2,
|
||||
// the delete request will fail for device2 since the UIA was initiated by trying to delete device1.
|
||||
|
|
@ -115,6 +118,7 @@ func (d *sessionsDict) deleteSession(sessionID string) {
|
|||
delete(d.params, sessionID)
|
||||
delete(d.sessions, sessionID)
|
||||
delete(d.deleteSessionToDeviceID, sessionID)
|
||||
delete(d.sessionCompletedResult, sessionID)
|
||||
// stop the timer, e.g. because the registration was completed
|
||||
if t, ok := d.timer[sessionID]; ok {
|
||||
if !t.Stop() {
|
||||
|
|
@ -130,6 +134,7 @@ func (d *sessionsDict) deleteSession(sessionID string) {
|
|||
func newSessionsDict() *sessionsDict {
|
||||
return &sessionsDict{
|
||||
sessions: make(map[string][]authtypes.LoginType),
|
||||
sessionCompletedResult: make(map[string]registerResponse),
|
||||
params: make(map[string]registerRequest),
|
||||
timer: make(map[string]*time.Timer),
|
||||
deleteSessionToDeviceID: make(map[string]string),
|
||||
|
|
@ -152,6 +157,13 @@ func (d *sessionsDict) startTimer(duration time.Duration, sessionID string) {
|
|||
})
|
||||
}
|
||||
|
||||
func (d *sessionsDict) hasSession(sessionID string) bool {
|
||||
d.RLock()
|
||||
defer d.RUnlock()
|
||||
_, ok := d.sessions[sessionID]
|
||||
return ok
|
||||
}
|
||||
|
||||
// addCompletedSessionStage records that a session has completed an auth stage
|
||||
// also starts a timer to delete the session once done.
|
||||
func (d *sessionsDict) addCompletedSessionStage(sessionID string, stage authtypes.LoginType) {
|
||||
|
|
@ -173,6 +185,19 @@ func (d *sessionsDict) addDeviceToDelete(sessionID, deviceID string) {
|
|||
d.deleteSessionToDeviceID[sessionID] = deviceID
|
||||
}
|
||||
|
||||
func (d *sessionsDict) addCompletedRegistration(sessionID string, response registerResponse) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
d.sessionCompletedResult[sessionID] = response
|
||||
}
|
||||
|
||||
func (d *sessionsDict) getCompletedRegistration(sessionID string) (registerResponse, bool) {
|
||||
d.RLock()
|
||||
defer d.RUnlock()
|
||||
result, ok := d.sessionCompletedResult[sessionID]
|
||||
return result, ok
|
||||
}
|
||||
|
||||
func (d *sessionsDict) getDeviceToDelete(sessionID string) (string, bool) {
|
||||
d.RLock()
|
||||
defer d.RUnlock()
|
||||
|
|
@ -223,7 +248,7 @@ type authDict struct {
|
|||
}
|
||||
|
||||
// http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#user-interactive-authentication-api
|
||||
type userInteractiveResponse struct {
|
||||
type UserInteractiveResponse struct {
|
||||
Flows []authtypes.Flow `json:"flows"`
|
||||
Completed []authtypes.LoginType `json:"completed"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
|
|
@ -236,9 +261,18 @@ func newUserInteractiveResponse(
|
|||
sessionID string,
|
||||
fs []authtypes.Flow,
|
||||
params map[string]interface{},
|
||||
) userInteractiveResponse {
|
||||
return userInteractiveResponse{
|
||||
fs, sessions.getCompletedStages(sessionID), params, sessionID,
|
||||
) UserInteractiveResponse {
|
||||
paramsCopy := mapsutil.MapCopy(params)
|
||||
for key, element := range paramsCopy {
|
||||
p := auth.GetAuthParams(element)
|
||||
if p != nil {
|
||||
// If an auth flow has params, make a new copy
|
||||
// and send it as part of the response.
|
||||
paramsCopy[key] = p
|
||||
}
|
||||
}
|
||||
return UserInteractiveResponse{
|
||||
fs, sessions.getCompletedStages(sessionID), paramsCopy, sessionID,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -544,6 +578,14 @@ func Register(
|
|||
r.DeviceID = data.DeviceID
|
||||
r.InitialDisplayName = data.InitialDisplayName
|
||||
r.InhibitLogin = data.InhibitLogin
|
||||
// Check if the user already registered using this session, if so, return that result
|
||||
if response, ok := sessions.getCompletedRegistration(sessionID); ok {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: response,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if resErr := httputil.UnmarshalJSON(reqBody, &r); resErr != nil {
|
||||
return *resErr
|
||||
|
|
@ -739,19 +781,17 @@ func handleRegistrationFlow(
|
|||
case authtypes.LoginTypeDummy:
|
||||
// there is nothing to do
|
||||
// Add Dummy to the list of completed registration stages
|
||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeDummy)
|
||||
if !cfg.PasswordAuthenticationDisabled {
|
||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeDummy)
|
||||
}
|
||||
|
||||
case authtypes.LoginTypePublicKey:
|
||||
isCompleted, authType, err := handlePublicKeyRegistration(cfg, reqBody, userAPI)
|
||||
_, authType, err := handlePublicKeyRegistration(cfg, reqBody, &r, userAPI)
|
||||
if err != nil {
|
||||
return *err
|
||||
}
|
||||
|
||||
if isCompleted {
|
||||
sessions.addCompletedSessionStage(sessionID, authType)
|
||||
} else {
|
||||
newPublicKeyAuthSession(&r)
|
||||
}
|
||||
sessions.addCompletedSessionStage(sessionID, authType)
|
||||
|
||||
case "":
|
||||
// An empty auth type means that we want to fetch the available
|
||||
|
|
@ -856,13 +896,6 @@ func completeRegistration(
|
|||
displayName, deviceID *string,
|
||||
accType userapi.AccountType,
|
||||
) util.JSONResponse {
|
||||
var registrationOK bool
|
||||
defer func() {
|
||||
if registrationOK {
|
||||
sessions.deleteSession(sessionID)
|
||||
}
|
||||
}()
|
||||
|
||||
if username == "" {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
|
|
@ -903,7 +936,6 @@ func completeRegistration(
|
|||
// Check whether inhibit_login option is set. If so, don't create an access
|
||||
// token or a device for this user
|
||||
if inhibitLogin {
|
||||
registrationOK = true
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: registerResponse{
|
||||
|
|
@ -937,15 +969,17 @@ func completeRegistration(
|
|||
}
|
||||
}
|
||||
|
||||
registrationOK = true
|
||||
result := registerResponse{
|
||||
UserID: devRes.Device.UserID,
|
||||
AccessToken: devRes.Device.AccessToken,
|
||||
HomeServer: accRes.Account.ServerName,
|
||||
DeviceID: devRes.Device.ID,
|
||||
}
|
||||
sessions.addCompletedRegistration(sessionID, result)
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: registerResponse{
|
||||
UserID: devRes.Device.UserID,
|
||||
AccessToken: devRes.Device.AccessToken,
|
||||
HomeServer: accRes.Account.ServerName,
|
||||
DeviceID: devRes.Device.ID,
|
||||
},
|
||||
JSON: result,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,16 +26,10 @@ import (
|
|||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func newPublicKeyAuthSession(request *registerRequest) {
|
||||
// Public key auth does not use password. But the registration flow
|
||||
// requires setting a password in order to create the account.
|
||||
// Create a random password to satisfy the requirement.
|
||||
request.Password = util.RandomString(sessionIDLength)
|
||||
}
|
||||
|
||||
func handlePublicKeyRegistration(
|
||||
cfg *config.ClientAPI,
|
||||
reqBytes []byte,
|
||||
r *registerRequest,
|
||||
userAPI userapi.ClientUserAPI,
|
||||
) (bool, authtypes.LoginType, *util.JSONResponse) {
|
||||
if !cfg.PublicKeyAuthentication.Enabled() {
|
||||
|
|
@ -65,10 +59,25 @@ func handlePublicKeyRegistration(
|
|||
authHandler = pkEthHandler
|
||||
default:
|
||||
// No response. Client is asking for a new registration session
|
||||
return false, "", nil
|
||||
return false, authtypes.LoginStagePublicKeyNewRegistration, nil
|
||||
}
|
||||
|
||||
isCompleted, jerr := authHandler.ValidateLoginResponse()
|
||||
if !sessions.hasSession(authHandler.GetSession()) {
|
||||
return false, "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.Unknown("the session ID is missing or unknown."),
|
||||
}
|
||||
}
|
||||
|
||||
isValidUserId := authHandler.IsValidUserId(r.Username)
|
||||
if !isValidUserId {
|
||||
return false, "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.InvalidUsername(r.Username),
|
||||
}
|
||||
}
|
||||
|
||||
isValidated, jerr := authHandler.ValidateLoginResponse()
|
||||
if jerr != nil {
|
||||
return false, "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
|
|
@ -76,5 +85,19 @@ func handlePublicKeyRegistration(
|
|||
}
|
||||
}
|
||||
|
||||
return isCompleted, authtypes.LoginType(authHandler.GetType()), nil
|
||||
// Registration flow requires a password to
|
||||
// create a user account. Create a random one
|
||||
// to satisfy the requirement. This is not used
|
||||
// for public key cryptography.
|
||||
createPassword(r)
|
||||
|
||||
return isValidated, authtypes.LoginType(authHandler.GetType()), nil
|
||||
}
|
||||
|
||||
func createPassword(request *registerRequest) {
|
||||
// Public key auth does not use password.
|
||||
// Create a random one that is never used.
|
||||
// Login validation will be done using public / private
|
||||
// key cryptography.
|
||||
request.Password = util.RandomString(sessionIDLength)
|
||||
}
|
||||
|
|
|
|||
386
clientapi/routing/register_publickey_test.go
Normal file
386
clientapi/routing/register_publickey_test.go
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
// 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 routing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/dendrite/userapi/api"
|
||||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const testCaip10UserId = "eip155=3a1=3a0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"
|
||||
|
||||
type registerContext struct {
|
||||
config *config.ClientAPI
|
||||
userInteractive *auth.UserInteractive
|
||||
}
|
||||
|
||||
func createRegisterContext(_ *testing.T) *registerContext {
|
||||
chainIds := []int{4}
|
||||
|
||||
cfg := &config.ClientAPI{
|
||||
Matrix: &config.Global{
|
||||
ServerName: test.TestServerName,
|
||||
},
|
||||
Derived: &config.Derived{},
|
||||
PasswordAuthenticationDisabled: true,
|
||||
PublicKeyAuthentication: config.PublicKeyAuthentication{
|
||||
Ethereum: config.EthereumAuthConfig{
|
||||
Enabled: true,
|
||||
Version: 1,
|
||||
ChainIDs: chainIds,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pkFlows := cfg.PublicKeyAuthentication.GetPublicKeyRegistrationFlows()
|
||||
cfg.Derived.Registration.Flows = append(cfg.Derived.Registration.Flows, pkFlows...)
|
||||
pkParams := cfg.PublicKeyAuthentication.GetPublicKeyRegistrationParams()
|
||||
cfg.Derived.Registration.Params = mapsutil.MapsUnion(cfg.Derived.Registration.Params, pkParams)
|
||||
|
||||
var userAPI fakePublicKeyUserApi
|
||||
var loginApi uapi.UserLoginAPI
|
||||
|
||||
userInteractive := auth.NewUserInteractive(
|
||||
loginApi,
|
||||
&userAPI,
|
||||
cfg)
|
||||
|
||||
return ®isterContext{
|
||||
config: cfg,
|
||||
userInteractive: userInteractive,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type fakeHttpRequest struct {
|
||||
request *http.Request
|
||||
body []byte
|
||||
registerRequest registerRequest
|
||||
}
|
||||
|
||||
func createFakeHttpRequest(body string) *fakeHttpRequest {
|
||||
var r registerRequest
|
||||
req, _ := http.NewRequest(http.MethodPost, "", strings.NewReader(body))
|
||||
reqBody := []byte(body)
|
||||
json.Unmarshal([]byte(body), &r)
|
||||
|
||||
return &fakeHttpRequest{
|
||||
request: req,
|
||||
body: reqBody,
|
||||
registerRequest: r,
|
||||
}
|
||||
}
|
||||
|
||||
type fakePublicKeyUserApi struct {
|
||||
auth.UserInternalAPIForLogin
|
||||
uapi.UserLoginAPI
|
||||
uapi.ClientUserAPI
|
||||
DeletedTokens []string
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) QueryAccountAvailability(ctx context.Context, req *uapi.QueryAccountAvailabilityRequest, res *uapi.QueryAccountAvailabilityResponse) error {
|
||||
if req.Localpart == "does_not_exist" {
|
||||
res.Available = true
|
||||
return nil
|
||||
}
|
||||
|
||||
res.Available = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) QueryAccountByPassword(ctx context.Context, req *uapi.QueryAccountByPasswordRequest, res *uapi.QueryAccountByPasswordResponse) error {
|
||||
if req.PlaintextPassword == "invalidpassword" {
|
||||
res.Account = nil
|
||||
return nil
|
||||
}
|
||||
res.Exists = true
|
||||
res.Account = &uapi.Account{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformDeviceCreation(
|
||||
ctx context.Context,
|
||||
req *uapi.PerformDeviceCreationRequest,
|
||||
res *uapi.PerformDeviceCreationResponse) error {
|
||||
res.DeviceCreated = true
|
||||
res.Device = &api.Device{
|
||||
ID: "device_id",
|
||||
UserID: req.Localpart,
|
||||
AccessToken: req.AccessToken,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformAccountCreation(
|
||||
ctx context.Context,
|
||||
req *uapi.PerformAccountCreationRequest,
|
||||
res *uapi.PerformAccountCreationResponse) error {
|
||||
res.AccountCreated = true
|
||||
res.Account = &api.Account{
|
||||
AppServiceID: req.AppServiceID,
|
||||
Localpart: req.Localpart,
|
||||
ServerName: test.TestServerName,
|
||||
UserID: fmt.Sprintf("@%s:%s", req.Localpart, test.TestServerName),
|
||||
AccountType: req.AccountType,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformLoginTokenDeletion(ctx context.Context, req *uapi.PerformLoginTokenDeletionRequest, res *uapi.PerformLoginTokenDeletionResponse) error {
|
||||
ua.DeletedTokens = append(ua.DeletedTokens, req.Token)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ua *fakePublicKeyUserApi) PerformLoginTokenCreation(ctx context.Context, req *uapi.PerformLoginTokenCreationRequest, res *uapi.PerformLoginTokenCreationResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakePublicKeyUserApi) QueryLoginToken(ctx context.Context, req *uapi.QueryLoginTokenRequest, res *uapi.QueryLoginTokenResponse) error {
|
||||
if req.Token == "invalidtoken" {
|
||||
return nil
|
||||
}
|
||||
|
||||
res.Data = &uapi.LoginTokenData{UserID: "@auser:example.com"}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newRegistrationSession(
|
||||
t *testing.T,
|
||||
userId string,
|
||||
_ *config.ClientAPI,
|
||||
_ *auth.UserInteractive,
|
||||
userAPI *fakePublicKeyUserApi,
|
||||
) string {
|
||||
body := fmt.Sprintf(`{
|
||||
"auth": {
|
||||
"type": "m.login.publickey",
|
||||
"username": "%v"
|
||||
}
|
||||
}`,
|
||||
userId)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
fakeReq := createFakeHttpRequest(test.Body)
|
||||
sessionID := util.RandomString(sessionIDLength)
|
||||
registerContext := createRegisterContext(t)
|
||||
|
||||
// Test
|
||||
response := handleRegistrationFlow(
|
||||
fakeReq.request,
|
||||
fakeReq.body,
|
||||
fakeReq.registerRequest,
|
||||
sessionID,
|
||||
registerContext.config,
|
||||
userAPI,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
json := response.JSON.(UserInteractiveResponse)
|
||||
return json.Session
|
||||
}
|
||||
|
||||
func TestRegisterEthereum(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
wallet, _ := test.CreateTestAccount()
|
||||
message, _ := test.CreateEip4361TestMessage(wallet.PublicAddress)
|
||||
signature, _ := test.SignMessage(message.String(), wallet.PrivateKey)
|
||||
registerContext := createRegisterContext(t)
|
||||
sessionId := newRegistrationSession(
|
||||
t,
|
||||
wallet.Eip155UserId,
|
||||
registerContext.config,
|
||||
registerContext.userInteractive,
|
||||
&userAPI,
|
||||
)
|
||||
|
||||
// Escape \t and \n. Work around for marshalling and unmarshalling message.
|
||||
msgStr := test.FromEip4361MessageToString(message)
|
||||
body := fmt.Sprintf(`{
|
||||
"username": "%v",
|
||||
"auth": {
|
||||
"type": "m.login.publickey",
|
||||
"session": "%v",
|
||||
"public_key_response": {
|
||||
"type": "m.login.publickey.ethereum",
|
||||
"session": "%v",
|
||||
"user_id": "%v",
|
||||
"message": "%v",
|
||||
"signature": "%v"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
wallet.Eip155UserId,
|
||||
sessionId,
|
||||
sessionId,
|
||||
wallet.Eip155UserId,
|
||||
msgStr,
|
||||
signature,
|
||||
)
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
fakeReq := createFakeHttpRequest(test.Body)
|
||||
|
||||
// Test
|
||||
response := handleRegistrationFlow(
|
||||
fakeReq.request,
|
||||
fakeReq.body,
|
||||
fakeReq.registerRequest,
|
||||
sessionId,
|
||||
registerContext.config,
|
||||
&userAPI,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNil(response, "response actual: nil, expected: not nil")
|
||||
registerRes := response.JSON.(registerResponse)
|
||||
assert.Truef(
|
||||
registerRes.UserID == wallet.Eip155UserId,
|
||||
"registerRes.UserID actual: %v, expected: %v", registerRes.UserID, wallet.Eip155UserId)
|
||||
assert.NotEmptyf(
|
||||
registerRes.AccessToken,
|
||||
"registerRes.AccessToken actual: empty, expected: not empty")
|
||||
}
|
||||
|
||||
func TestNewRegistrationSession(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
|
||||
body := fmt.Sprintf(`{
|
||||
"auth": {
|
||||
"type": "m.login.publickey",
|
||||
"username": "%v"
|
||||
}
|
||||
}`,
|
||||
testCaip10UserId)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
fakeReq := createFakeHttpRequest(test.Body)
|
||||
sessionID := util.RandomString(sessionIDLength)
|
||||
registerContext := createRegisterContext(t)
|
||||
|
||||
// Test
|
||||
response := handleRegistrationFlow(
|
||||
fakeReq.request,
|
||||
fakeReq.body,
|
||||
fakeReq.registerRequest,
|
||||
sessionID,
|
||||
registerContext.config,
|
||||
&userAPI,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNilf(response, "response not nil")
|
||||
assert.Truef(
|
||||
response.Code == http.StatusUnauthorized,
|
||||
"response.Code actual %v, expected %v", response.Code, http.StatusUnauthorized)
|
||||
json := response.JSON.(UserInteractiveResponse)
|
||||
assert.NotEmptyf(json.Session, "response.Session")
|
||||
assert.NotEmptyf(json.Completed, "response.Completed")
|
||||
assert.Truef(
|
||||
json.Completed[0] == authtypes.LoginStagePublicKeyNewRegistration,
|
||||
"response.Completed[0] actual %v, expected %v", json.Completed[0], authtypes.LoginStagePublicKeyNewRegistration)
|
||||
assert.Truef(
|
||||
authtypes.LoginTypePublicKeyEthereum == json.Flows[0].Stages[0],
|
||||
"response.Flows[0].Stages[0] actual: %v, expected: %v", json.Flows[0].Stages[0], authtypes.LoginTypePublicKeyEthereum)
|
||||
|
||||
params := json.Params[authtypes.LoginTypePublicKeyEthereum]
|
||||
assert.NotEmptyf(
|
||||
params,
|
||||
"response.Params[\"%v\"] actual %v, expected %v",
|
||||
authtypes.LoginTypePublicKeyEthereum,
|
||||
params,
|
||||
"[object]")
|
||||
ethParams := params.(config.EthereumAuthParams)
|
||||
assert.NotEmptyf(ethParams.ChainIDs, "ChainIDs actual: empty, expected not empty")
|
||||
assert.NotEmptyf(ethParams.Nonce, "Nonce actual: \"\", expected: not empty")
|
||||
assert.NotEmptyf(ethParams.Version, "Version actual: \"\", expected: not empty")
|
||||
}
|
||||
|
||||
func TestRegistrationUnimplementedAlgo(t *testing.T) {
|
||||
// Setup
|
||||
var userAPI fakePublicKeyUserApi
|
||||
body := fmt.Sprintf(`{
|
||||
"auth": {
|
||||
"type": "m.login.publickey.someAlgo",
|
||||
"username": "%v"
|
||||
}
|
||||
}`,
|
||||
testCaip10UserId)
|
||||
|
||||
test := struct {
|
||||
Body string
|
||||
}{
|
||||
Body: body,
|
||||
}
|
||||
|
||||
fakeReq := createFakeHttpRequest(test.Body)
|
||||
sessionID := util.RandomString(sessionIDLength)
|
||||
registerContext := createRegisterContext(t)
|
||||
|
||||
// Test
|
||||
response := handleRegistrationFlow(
|
||||
fakeReq.request,
|
||||
fakeReq.body,
|
||||
fakeReq.registerRequest,
|
||||
sessionID,
|
||||
registerContext.config,
|
||||
&userAPI,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
// Asserts
|
||||
assert := assert.New(t)
|
||||
assert.NotNilf(response, "response not nil")
|
||||
assert.Truef(
|
||||
response.Code == http.StatusNotImplemented,
|
||||
"response.Code actual %v, expected %v", response.Code, http.StatusNotImplemented)
|
||||
}
|
||||
|
|
@ -129,6 +129,12 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/evacuateUser/{userID}",
|
||||
httputil.MakeAuthAPI("admin_evacuate_user", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminEvacuateUser(req, device, rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
// server notifications
|
||||
if cfg.Matrix.ServerNotices.Enabled {
|
||||
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
|
||||
|
|
@ -140,7 +146,7 @@ func Setup(
|
|||
synapseAdminRouter.Handle("/admin/v1/send_server_notice/{txnID}",
|
||||
httputil.MakeAuthAPI("send_server_notice", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
// not specced, but ensure we're rate limiting requests to this endpoint
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -160,7 +166,7 @@ func Setup(
|
|||
synapseAdminRouter.Handle("/admin/v1/send_server_notice",
|
||||
httputil.MakeAuthAPI("send_server_notice", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
// not specced, but ensure we're rate limiting requests to this endpoint
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
return SendServerNotice(
|
||||
|
|
@ -190,7 +196,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/join/{roomIDOrAlias}",
|
||||
httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -206,7 +212,7 @@ func Setup(
|
|||
if mscCfg.Enabled("msc2753") {
|
||||
v3mux.Handle("/peek/{roomIDOrAlias}",
|
||||
httputil.MakeAuthAPI(gomatrixserverlib.Peek, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -226,7 +232,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/join",
|
||||
httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -240,7 +246,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/leave",
|
||||
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -274,7 +280,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/invite",
|
||||
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -392,14 +398,14 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/register", httputil.MakeExternalAPI("register", func(req *http.Request) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, nil); r != nil {
|
||||
return *r
|
||||
}
|
||||
return Register(req, userAPI, cfg)
|
||||
})).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/register/available", httputil.MakeExternalAPI("registerAvailable", func(req *http.Request) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, nil); r != nil {
|
||||
return *r
|
||||
}
|
||||
return RegisterAvailable(req, cfg, userAPI)
|
||||
|
|
@ -473,7 +479,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/rooms/{roomID}/typing/{userID}",
|
||||
httputil.MakeAuthAPI("rooms_typing", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -530,7 +536,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/account/whoami",
|
||||
httputil.MakeAuthAPI("whoami", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
return Whoami(req, device)
|
||||
|
|
@ -539,7 +545,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/account/password",
|
||||
httputil.MakeAuthAPI("password", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
return Password(req, userAPI, device, cfg)
|
||||
|
|
@ -548,7 +554,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/account/deactivate",
|
||||
httputil.MakeAuthAPI("deactivate", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
return Deactivate(req, userInteractiveAuth, userAPI, device)
|
||||
|
|
@ -559,7 +565,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/login",
|
||||
httputil.MakeExternalAPI("login", func(req *http.Request) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, nil); r != nil {
|
||||
return *r
|
||||
}
|
||||
return Login(req, userAPI, userInteractiveAuth, cfg)
|
||||
|
|
@ -667,7 +673,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind}/{ruleID}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -733,7 +739,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/profile/{userID}/avatar_url",
|
||||
httputil.MakeAuthAPI("profile_avatar_url", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -758,7 +764,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/profile/{userID}/displayname",
|
||||
httputil.MakeAuthAPI("profile_displayname", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -797,7 +803,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/voip/turnServer",
|
||||
httputil.MakeAuthAPI("turn_server", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
return RequestTurnServer(req, device, cfg)
|
||||
|
|
@ -876,7 +882,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/user/{userID}/openid/request_token",
|
||||
httputil.MakeAuthAPI("openid_request_token", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -889,7 +895,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/user_directory/search",
|
||||
httputil.MakeAuthAPI("userdirectory_search", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
postContent := struct {
|
||||
|
|
@ -935,7 +941,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/rooms/{roomID}/read_markers",
|
||||
httputil.MakeAuthAPI("rooms_read_markers", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -948,7 +954,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/rooms/{roomID}/forget",
|
||||
httputil.MakeAuthAPI("rooms_forget", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -1025,7 +1031,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/pushers/set",
|
||||
httputil.MakeAuthAPI("set_pushers", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
return SetPusher(req, device, userAPI)
|
||||
|
|
@ -1083,7 +1089,7 @@ func Setup(
|
|||
|
||||
v3mux.Handle("/capabilities",
|
||||
httputil.MakeAuthAPI("capabilities", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
return GetCapabilities(req, rsAPI)
|
||||
|
|
@ -1299,7 +1305,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomId}/receipt/{receiptType}/{eventId}",
|
||||
httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -96,14 +97,21 @@ func SendEvent(
|
|||
mutex.(*sync.Mutex).Lock()
|
||||
defer mutex.(*sync.Mutex).Unlock()
|
||||
|
||||
startedGeneratingEvent := time.Now()
|
||||
|
||||
var r map[string]interface{} // must be a JSON object
|
||||
resErr := httputil.UnmarshalJSONRequest(req, &r)
|
||||
if resErr != nil {
|
||||
return *resErr
|
||||
}
|
||||
|
||||
if stateKey != nil {
|
||||
// If the existing/new state content are equal, return the existing event_id, making the request idempotent.
|
||||
if resp := stateEqual(req.Context(), rsAPI, eventType, *stateKey, roomID, r); resp != nil {
|
||||
return *resp
|
||||
}
|
||||
}
|
||||
|
||||
startedGeneratingEvent := time.Now()
|
||||
|
||||
// If we're sending a membership update, make sure to strip the authorised
|
||||
// via key if it is present, otherwise other servers won't be able to auth
|
||||
// the event if the room is set to the "restricted" join rule.
|
||||
|
|
@ -208,6 +216,37 @@ func SendEvent(
|
|||
return res
|
||||
}
|
||||
|
||||
// stateEqual compares the new and the existing state event content. If they are equal, returns a *util.JSONResponse
|
||||
// with the existing event_id, making this an idempotent request.
|
||||
func stateEqual(ctx context.Context, rsAPI api.ClientRoomserverAPI, eventType, stateKey, roomID string, newContent map[string]interface{}) *util.JSONResponse {
|
||||
stateRes := api.QueryCurrentStateResponse{}
|
||||
tuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: eventType,
|
||||
StateKey: stateKey,
|
||||
}
|
||||
err := rsAPI.QueryCurrentState(ctx, &api.QueryCurrentStateRequest{
|
||||
RoomID: roomID,
|
||||
StateTuples: []gomatrixserverlib.StateKeyTuple{tuple},
|
||||
}, &stateRes)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if existingEvent, ok := stateRes.StateEvents[tuple]; ok {
|
||||
var existingContent map[string]interface{}
|
||||
if err = json.Unmarshal(existingEvent.Content(), &existingContent); err != nil {
|
||||
return nil
|
||||
}
|
||||
if reflect.DeepEqual(existingContent, newContent) {
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: sendEventResponse{existingEvent.EventID()},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateSendEvent(
|
||||
ctx context.Context,
|
||||
r map[string]interface{},
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func main() {
|
|||
pRouter := pineconeRouter.NewRouter(logrus.WithField("pinecone", "router"), sk, false)
|
||||
pQUIC := pineconeSessions.NewSessions(logrus.WithField("pinecone", "sessions"), pRouter, []string{"matrix"})
|
||||
pMulticast := pineconeMulticast.NewMulticast(logrus.WithField("pinecone", "multicast"), pRouter)
|
||||
pManager := pineconeConnections.NewConnectionManager(pRouter)
|
||||
pManager := pineconeConnections.NewConnectionManager(pRouter, nil)
|
||||
pMulticast.Start()
|
||||
if instancePeer != nil && *instancePeer != "" {
|
||||
pManager.AddPeer(*instancePeer)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Yggdrasil Demo
|
||||
|
||||
This is the Dendrite Yggdrasil demo! It's easy to get started - all you need is Go 1.16 or later.
|
||||
This is the Dendrite Yggdrasil demo! It's easy to get started - all you need is Go 1.18 or later.
|
||||
|
||||
To run the homeserver, start at the root of the Dendrite repository and run:
|
||||
|
||||
|
|
@ -13,10 +13,10 @@ The following command line arguments are accepted:
|
|||
* `-peer tcp://a.b.c.d:e` to specify a static Yggdrasil peer to connect to - you will need to supply this if you do not have another Yggdrasil node on your network
|
||||
* `-port 12345` to specify a port to listen on for client connections
|
||||
|
||||
If you need to find an internet peer, take a look at [this list](https://publicpeers.neilalexander.dev/).
|
||||
If you need to find an internet peer, take a look at [this list](https://publicpeers.neilalexander.dev/).
|
||||
|
||||
Then point your favourite Matrix client to the homeserver URL`http://localhost:8008` (or whichever `-port` you specified), create an account and log in.
|
||||
|
||||
If your peering connection is operational then you should see a `Connected TCP:` line in the log output. If not then try a different peer.
|
||||
|
||||
Once logged in, you should be able to open the room directory or join a room by its ID.
|
||||
Once logged in, you should be able to open the room directory or join a room by its ID.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const HEAD = "HEAD"
|
|||
// due to the error:
|
||||
// When using COPY with more than one source file, the destination must be a directory and end with a /
|
||||
// We need to run a postgres anyway, so use the dockerfile associated with Complement instead.
|
||||
const Dockerfile = `FROM golang:1.16-stretch as build
|
||||
const Dockerfile = `FROM golang:1.18-stretch as build
|
||||
RUN apt-get update && apt-get install -y postgresql
|
||||
WORKDIR /build
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/roomserver/state"
|
||||
|
|
@ -28,7 +29,9 @@ import (
|
|||
|
||||
var roomVersion = flag.String("roomversion", "5", "the room version to parse events as")
|
||||
var filterType = flag.String("filtertype", "", "the event types to filter on")
|
||||
var difference = flag.Bool("difference", false, "whether to calculate the difference between snapshots")
|
||||
|
||||
// nolint:gocyclo
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
cfg := setup.ParseFlags(true)
|
||||
|
|
@ -36,6 +39,7 @@ func main() {
|
|||
Type: "std",
|
||||
Level: "error",
|
||||
})
|
||||
cfg.ClientAPI.RegistrationDisabled = true
|
||||
base := base.NewBaseDendrite(cfg, "ResolveState", base.DisableMetrics)
|
||||
args := flag.Args()
|
||||
|
||||
|
|
@ -50,12 +54,10 @@ func main() {
|
|||
|
||||
fmt.Println("Fetching", len(snapshotNIDs), "snapshot NIDs")
|
||||
|
||||
cache, err := caching.NewInMemoryLRUCache(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
roomserverDB, err := storage.Open(base, &cfg.RoomServer.Database, cache)
|
||||
roomserverDB, err := storage.Open(
|
||||
base, &cfg.RoomServer.Database,
|
||||
caching.NewRistrettoCache(128*1024*1024, time.Hour, true),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -64,6 +66,59 @@ func main() {
|
|||
RoomVersion: gomatrixserverlib.RoomVersion(*roomVersion),
|
||||
})
|
||||
|
||||
if *difference {
|
||||
if len(snapshotNIDs) != 2 {
|
||||
panic("need exactly two state snapshot NIDs to calculate difference")
|
||||
}
|
||||
var removed, added []types.StateEntry
|
||||
removed, added, err = stateres.DifferenceBetweeenStateSnapshots(ctx, snapshotNIDs[0], snapshotNIDs[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var eventNIDs []types.EventNID
|
||||
for _, entry := range append(removed, added...) {
|
||||
eventNIDs = append(eventNIDs, entry.EventNID)
|
||||
}
|
||||
|
||||
var eventEntries []types.Event
|
||||
eventEntries, err = roomserverDB.Events(ctx, eventNIDs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
events := make(map[types.EventNID]*gomatrixserverlib.Event, len(eventEntries))
|
||||
for _, entry := range eventEntries {
|
||||
events[entry.EventNID] = entry.Event
|
||||
}
|
||||
|
||||
if len(removed) > 0 {
|
||||
fmt.Println("Removed:")
|
||||
for _, r := range removed {
|
||||
event := events[r.EventNID]
|
||||
fmt.Println()
|
||||
fmt.Printf("* %s %s %q\n", event.EventID(), event.Type(), *event.StateKey())
|
||||
fmt.Printf(" %s\n", string(event.Content()))
|
||||
}
|
||||
}
|
||||
|
||||
if len(removed) > 0 && len(added) > 0 {
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
if len(added) > 0 {
|
||||
fmt.Println("Added:")
|
||||
for _, a := range added {
|
||||
event := events[a.EventNID]
|
||||
fmt.Println()
|
||||
fmt.Printf("* %s %s %q\n", event.EventID(), event.Type(), *event.StateKey())
|
||||
fmt.Printf(" %s\n", string(event.Content()))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var stateEntries []types.StateEntry
|
||||
for _, snapshotNID := range snapshotNIDs {
|
||||
var entries []types.StateEntry
|
||||
|
|
|
|||
|
|
@ -41,6 +41,25 @@ global:
|
|||
max_idle_conns: 5
|
||||
conn_max_lifetime: -1
|
||||
|
||||
# Configuration for in-memory caches. Caches can often improve performance by
|
||||
# keeping frequently accessed items (like events, identifiers etc.) in memory
|
||||
# rather than having to read them from the database.
|
||||
cache:
|
||||
# The estimated maximum size for the global cache in bytes, or in terabytes,
|
||||
# gigabytes, megabytes or kilobytes when the appropriate 'tb', 'gb', 'mb' or
|
||||
# 'kb' suffix is specified. Note that this is not a hard limit, nor is it a
|
||||
# memory limit for the entire process. A cache that is too small may ultimately
|
||||
# provide little or no benefit.
|
||||
max_size_estimated: 1gb
|
||||
|
||||
# The maximum amount of time that a cache entry can live for in memory before
|
||||
# it will be evicted and/or refreshed from the database. Lower values result in
|
||||
# easier admission of new cache entries but may also increase database load in
|
||||
# comparison to higher values, so adjust conservatively. Higher values may make
|
||||
# it harder for new items to make it into the cache, e.g. if new rooms suddenly
|
||||
# become popular.
|
||||
max_age: 1h
|
||||
|
||||
# The server name to delegate server-server communications to, with optional port
|
||||
# e.g. localhost:443
|
||||
well_known_server_name: ""
|
||||
|
|
@ -137,6 +156,16 @@ client_api:
|
|||
# of whether registration is otherwise disabled.
|
||||
registration_shared_secret: ""
|
||||
|
||||
# Disable password authentication.
|
||||
password_authentication_disabled: false
|
||||
|
||||
# public key authentication
|
||||
public_key_authentication:
|
||||
ethereum:
|
||||
enabled: false
|
||||
version: 1
|
||||
chain_ids: []
|
||||
|
||||
# Whether to require reCAPTCHA for registration. If you have enabled registration
|
||||
# then this is HIGHLY RECOMMENDED to reduce the risk of your homeserver being used
|
||||
# for coordinated spam attacks.
|
||||
|
|
@ -160,11 +189,14 @@ client_api:
|
|||
|
||||
# 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"
|
||||
# will be released after the cooloff time in milliseconds.
|
||||
# will be released after the cooloff time in milliseconds. Server administrators
|
||||
# and appservice users are exempt from rate limiting by default.
|
||||
rate_limiting:
|
||||
enabled: true
|
||||
threshold: 5
|
||||
cooloff_ms: 500
|
||||
exempt_user_ids:
|
||||
# - "@user:domain.com"
|
||||
|
||||
# Configuration for the Federation API.
|
||||
federation_api:
|
||||
|
|
|
|||
|
|
@ -31,6 +31,25 @@ global:
|
|||
# considered valid by other homeservers.
|
||||
key_validity_period: 168h0m0s
|
||||
|
||||
# Configuration for in-memory caches. Caches can often improve performance by
|
||||
# keeping frequently accessed items (like events, identifiers etc.) in memory
|
||||
# rather than having to read them from the database.
|
||||
cache:
|
||||
# The estimated maximum size for the global cache in bytes, or in terabytes,
|
||||
# gigabytes, megabytes or kilobytes when the appropriate 'tb', 'gb', 'mb' or
|
||||
# 'kb' suffix is specified. Note that this is not a hard limit, nor is it a
|
||||
# memory limit for the entire process. A cache that is too small may ultimately
|
||||
# provide little or no benefit.
|
||||
max_size_estimated: 1gb
|
||||
|
||||
# The maximum amount of time that a cache entry can live for in memory before
|
||||
# it will be evicted and/or refreshed from the database. Lower values result in
|
||||
# easier admission of new cache entries but may also increase database load in
|
||||
# comparison to higher values, so adjust conservatively. Higher values may make
|
||||
# it harder for new items to make it into the cache, e.g. if new rooms suddenly
|
||||
# become popular.
|
||||
max_age: 1h
|
||||
|
||||
# The server name to delegate server-server communications to, with optional port
|
||||
# e.g. localhost:443
|
||||
well_known_server_name: ""
|
||||
|
|
@ -173,11 +192,14 @@ client_api:
|
|||
|
||||
# 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"
|
||||
# will be released after the cooloff time in milliseconds.
|
||||
# will be released after the cooloff time in milliseconds. Server administrators
|
||||
# and appservice users are exempt from rate limiting by default.
|
||||
rate_limiting:
|
||||
enabled: true
|
||||
threshold: 5
|
||||
cooloff_ms: 500
|
||||
exempt_user_ids:
|
||||
# - "@user:domain.com"
|
||||
|
||||
# Configuration for the Federation API.
|
||||
federation_api:
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ This endpoint will instruct Dendrite to part all local users from the given `roo
|
|||
in the URL. It may take some time to complete. A JSON body will be returned containing
|
||||
the user IDs of all affected users.
|
||||
|
||||
## `/_dendrite/admin/evacuateUser/{userID}`
|
||||
|
||||
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
|
||||
the room IDs of all affected rooms.
|
||||
|
||||
## `/_synapse/admin/v1/register`
|
||||
|
||||
Shared secret registration — please see the [user creation page](createusers) for
|
||||
|
|
|
|||
81
docs/administration/5_troubleshooting.md
Normal file
81
docs/administration/5_troubleshooting.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
title: Troubleshooting
|
||||
parent: Administration
|
||||
permalink: /administration/troubleshooting
|
||||
---
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
If your Dendrite installation is acting strangely, there are a few things you should
|
||||
check before seeking help.
|
||||
|
||||
## 1. Logs
|
||||
|
||||
Dendrite, by default, will log all warnings and errors to stdout, in addition to any
|
||||
other locations configured in the `dendrite.yaml` configuration file. Often there will
|
||||
be clues in the logs.
|
||||
|
||||
You can increase this log level to the more verbose `debug` level if necessary by adding
|
||||
this to the config and restarting Dendrite:
|
||||
|
||||
```
|
||||
logging:
|
||||
- type: std
|
||||
level: debug
|
||||
```
|
||||
|
||||
Look specifically for lines that contain `level=error` or `level=warning`.
|
||||
|
||||
## 2. Federation tester
|
||||
|
||||
If you are experiencing problems federating with other homeservers, you should check
|
||||
that the [Federation Tester](https://federationtester.matrix.org) is passing for your
|
||||
server.
|
||||
|
||||
Common reasons that it may not pass include:
|
||||
|
||||
1. Incorrect DNS configuration;
|
||||
2. Misconfigured DNS SRV entries or well-known files;
|
||||
3. Invalid TLS/SSL certificates;
|
||||
4. Reverse proxy configuration issues (if applicable).
|
||||
|
||||
Correct any errors if shown and re-run the federation tester to check the results.
|
||||
|
||||
## 3. System time
|
||||
|
||||
Matrix relies heavily on TLS which requires the system time to be correct. If the clock
|
||||
drifts then you may find that federation no works reliably (or at all) and clients may
|
||||
struggle to connect to your Dendrite server.
|
||||
|
||||
Ensure that your system time is correct and consider syncing to a reliable NTP source.
|
||||
|
||||
## 4. Database connections
|
||||
|
||||
If you are using the PostgreSQL database, you should ensure that Dendrite's configured
|
||||
number of database connections does not exceed the maximum allowed by PostgreSQL.
|
||||
|
||||
Open your `postgresql.conf` configuration file and check the value of `max_connections`
|
||||
(which is typically `100` by default). Then open your `dendrite.yaml` configuration file
|
||||
and ensure that:
|
||||
|
||||
1. If you are using the `global.database` section, that `max_open_conns` does not exceed
|
||||
that number;
|
||||
2. If you are **not** using the `global.database` section, that the sum total of all
|
||||
`max_open_conns` across all `database` blocks does not exceed that number.
|
||||
|
||||
## 5. File descriptors
|
||||
|
||||
Dendrite requires a sufficient number of file descriptors for every connection it makes
|
||||
to a remote server, every connection to the database engine and every file it is reading
|
||||
or writing to at a given time (media, logs etc). We recommend ensuring that the limit is
|
||||
no lower than 65535 for Dendrite.
|
||||
|
||||
Dendrite will check at startup if there are a sufficient number of available descriptors.
|
||||
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"
|
||||
```
|
||||
|
||||
Follow the [Optimisation](../installation/10_optimisation.md) instructions to correct the
|
||||
available number of file descriptors.
|
||||
68
docs/caddy/monolith/CaddyFile
Normal file
68
docs/caddy/monolith/CaddyFile
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
# debug
|
||||
admin off
|
||||
email example@example.com
|
||||
default_sni example.com
|
||||
# Debug endpoint
|
||||
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
# Snippets
|
||||
#______________________________________________________________________
|
||||
|
||||
(handle_errors_maintenance) {
|
||||
handle_errors {
|
||||
@maintenance expression {http.error.status_code} == 502
|
||||
rewrite @maintenance maintenance.html
|
||||
root * "/path/to/service/pages"
|
||||
file_server
|
||||
}
|
||||
}
|
||||
|
||||
(matrix-well-known-header) {
|
||||
# Headers
|
||||
header Access-Control-Allow-Origin "*"
|
||||
header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
|
||||
header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"
|
||||
header Content-Type "application/json"
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
example.com {
|
||||
|
||||
# ...
|
||||
|
||||
handle /.well-known/matrix/server {
|
||||
import matrix-well-known-header
|
||||
respond `{ "m.server": "matrix.example.com:443" }` 200
|
||||
}
|
||||
|
||||
handle /.well-known/matrix/client {
|
||||
import matrix-well-known-header
|
||||
respond `{ "m.homeserver": { "base_url": "https://matrix.example.com" } }` 200
|
||||
}
|
||||
|
||||
import handle_errors_maintenance
|
||||
}
|
||||
|
||||
example.com:8448 {
|
||||
# server<->server HTTPS traffic
|
||||
reverse_proxy http://dendrite-host:8008
|
||||
}
|
||||
|
||||
matrix.example.com {
|
||||
|
||||
handle /_matrix/* {
|
||||
# client<->server HTTPS traffic
|
||||
reverse_proxy http://dendrite-host:8008
|
||||
}
|
||||
|
||||
handle_path /* {
|
||||
# Client webapp (Element SPA or ...)
|
||||
file_server {
|
||||
root /path/to/www/example.com/matrix-web-client/
|
||||
}
|
||||
}
|
||||
}
|
||||
71
docs/installation/10_optimisation.md
Normal file
71
docs/installation/10_optimisation.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
title: Optimise your installation
|
||||
parent: Installation
|
||||
has_toc: true
|
||||
nav_order: 10
|
||||
permalink: /installation/start/optimisation
|
||||
---
|
||||
|
||||
# Optimise your installation
|
||||
|
||||
Now that you have Dendrite running, the following tweaks will improve the reliability
|
||||
and performance of your installation.
|
||||
|
||||
## File descriptor limit
|
||||
|
||||
Most platforms have a limit on how many file descriptors a single process can open. All
|
||||
connections made by Dendrite consume file descriptors — this includes database connections
|
||||
and network requests to remote homeservers. When participating in large federated rooms
|
||||
where Dendrite must talk to many remote servers, it is often very easy to exhaust default
|
||||
limits which are quite low.
|
||||
|
||||
We currently recommend setting the file descriptor limit to 65535 to avoid such
|
||||
issues. Dendrite will log immediately after startup if the file descriptor limit is too low:
|
||||
|
||||
```
|
||||
level=warning msg="IMPORTANT: Process file descriptor limit is currently 1024, it is recommended to raise the limit for Dendrite to at least 65535 to avoid issues"
|
||||
```
|
||||
|
||||
UNIX systems have two limits: a hard limit and a soft limit. You can view the soft limit
|
||||
by running `ulimit -Sn` and the hard limit with `ulimit -Hn`:
|
||||
|
||||
```bash
|
||||
$ ulimit -Hn
|
||||
1048576
|
||||
|
||||
$ ulimit -Sn
|
||||
1024
|
||||
```
|
||||
|
||||
Increase the soft limit before starting Dendrite:
|
||||
|
||||
```bash
|
||||
ulimit -Sn 65535
|
||||
```
|
||||
|
||||
The log line at startup should no longer appear if the limit is sufficient.
|
||||
|
||||
If you are running under a systemd service, you can instead add `LimitNOFILE=65535` option
|
||||
to the `[Service]` section of your service unit file.
|
||||
|
||||
## DNS caching
|
||||
|
||||
Dendrite has a built-in DNS cache which significantly reduces the load that Dendrite will
|
||||
place on your DNS resolver. This may also speed up outbound federation.
|
||||
|
||||
Consider enabling the DNS cache by modifying the `global` section of your configuration file:
|
||||
|
||||
```yaml
|
||||
dns_cache:
|
||||
enabled: true
|
||||
cache_size: 4096
|
||||
cache_lifetime: 600s
|
||||
```
|
||||
|
||||
## Time synchronisation
|
||||
|
||||
Matrix relies heavily on TLS which requires the system time to be correct. If the clock
|
||||
drifts then you may find that federation no works reliably (or at all) and clients may
|
||||
struggle to connect to your Dendrite server.
|
||||
|
||||
Ensure that the time is synchronised on your system by enabling NTP sync.
|
||||
|
|
@ -75,7 +75,7 @@ In order to install Dendrite, you will need to satisfy the following dependencie
|
|||
|
||||
### Go
|
||||
|
||||
At this time, Dendrite supports being built with Go 1.16 or later. We do not support building
|
||||
At this time, Dendrite supports being built with Go 1.18 or later. We do not support building
|
||||
Dendrite with older versions of Go than this. If you are installing Go using a package manager,
|
||||
you should check (by running `go version`) that you are using a suitable version before you start.
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ func (t *OutputPresenceConsumer) onMessage(ctx context.Context, msg *nats.Msg) b
|
|||
return true
|
||||
}
|
||||
|
||||
log.Debugf("sending presence EDU to %d servers", len(joined))
|
||||
log.Tracef("sending presence EDU to %d servers", len(joined))
|
||||
if err = t.queues.SendEDU(edu, t.ServerName, joined); err != nil {
|
||||
log.WithError(err).Error("failed to send EDU")
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ func (t *OutputReceiptConsumer) onMessage(ctx context.Context, msg *nats.Msg) bo
|
|||
return true
|
||||
}
|
||||
|
||||
timestamp, err := strconv.Atoi(msg.Header.Get("timestamp"))
|
||||
timestamp, err := strconv.ParseUint(msg.Header.Get("timestamp"), 10, 64)
|
||||
if err != nil {
|
||||
// If the message was invalid, log it and move on to the next message in the stream
|
||||
log.WithError(err).Errorf("EDU output log: message parse failure")
|
||||
|
|
|
|||
|
|
@ -95,6 +95,11 @@ func (t *OutputSendToDeviceConsumer) onMessage(ctx context.Context, msg *nats.Ms
|
|||
return true
|
||||
}
|
||||
|
||||
// The SyncAPI is already handling sendToDevice for the local server
|
||||
if destServerName == t.ServerName {
|
||||
return true
|
||||
}
|
||||
|
||||
// Pack the EDU and marshal it
|
||||
edu := &gomatrixserverlib.EDU{
|
||||
Type: gomatrixserverlib.MDirectToDevice,
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ func AddPublicRoutes(
|
|||
TopicSendToDeviceEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputSendToDeviceEvent),
|
||||
TopicTypingEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputTypingEvent),
|
||||
TopicPresenceEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputPresenceEvent),
|
||||
TopicDeviceListUpdate: cfg.Matrix.JetStream.Prefixed(jetstream.InputDeviceListUpdate),
|
||||
ServerName: cfg.Matrix.ServerName,
|
||||
UserAPI: userAPI,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -64,10 +63,7 @@ func TestMain(m *testing.M) {
|
|||
}
|
||||
|
||||
// Create a new cache but don't enable prometheus!
|
||||
s.cache, err = caching.NewInMemoryLRUCache(false)
|
||||
if err != nil {
|
||||
panic("can't create cache: " + err.Error())
|
||||
}
|
||||
s.cache = caching.NewRistrettoCache(8*1024*1024, time.Hour, false)
|
||||
|
||||
// Create a temporary directory for JetStream.
|
||||
d, err := ioutil.TempDir("./", "jetstream*")
|
||||
|
|
@ -170,72 +166,6 @@ func TestServersRequestOwnKeys(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCachingBehaviour(t *testing.T) {
|
||||
// Server A will request Server B's key, which has a validity
|
||||
// period of an hour from now. We should retrieve the key and
|
||||
// it should make it into the cache automatically.
|
||||
|
||||
req := gomatrixserverlib.PublicKeyLookupRequest{
|
||||
ServerName: serverB.name,
|
||||
KeyID: serverKeyID,
|
||||
}
|
||||
ts := gomatrixserverlib.AsTimestamp(time.Now())
|
||||
|
||||
res, err := serverA.api.FetchKeys(
|
||||
context.Background(),
|
||||
map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp{
|
||||
req: ts,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("server A failed to retrieve server B key: %s", err)
|
||||
}
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("server B should have returned one key but instead returned %d keys", len(res))
|
||||
}
|
||||
if _, ok := res[req]; !ok {
|
||||
t.Fatalf("server B isn't included in the key fetch response")
|
||||
}
|
||||
|
||||
// At this point, if the previous key request was a success,
|
||||
// then the cache should now contain the key. Check if that's
|
||||
// the case - if it isn't then there's something wrong with
|
||||
// the cache implementation or we failed to get the key.
|
||||
|
||||
cres, ok := serverA.cache.GetServerKey(req, ts)
|
||||
if !ok {
|
||||
t.Fatalf("server B key should be in cache but isn't")
|
||||
}
|
||||
if !reflect.DeepEqual(cres, res[req]) {
|
||||
t.Fatalf("the cached result from server B wasn't what server B gave us")
|
||||
}
|
||||
|
||||
// If we ask the cache for the same key but this time for an event
|
||||
// that happened in +30 minutes. Since the validity period is for
|
||||
// another hour, then we should get a response back from the cache.
|
||||
|
||||
_, ok = serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(time.Minute*30)),
|
||||
)
|
||||
if !ok {
|
||||
t.Fatalf("server B key isn't in cache when it should be (+30 minutes)")
|
||||
}
|
||||
|
||||
// If we ask the cache for the same key but this time for an event
|
||||
// that happened in +90 minutes then we should expect to get no
|
||||
// cache result. This is because the cache shouldn't return a result
|
||||
// that is obviously past the validity of the event.
|
||||
|
||||
_, ok = serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(time.Minute*90)),
|
||||
)
|
||||
if ok {
|
||||
t.Fatalf("server B key is in cache when it shouldn't be (+90 minutes)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenewalBehaviour(t *testing.T) {
|
||||
// Server A will request Server C's key but their validity period
|
||||
// is an hour in the past. We'll retrieve the key as, even though it's
|
||||
|
|
@ -262,32 +192,7 @@ func TestRenewalBehaviour(t *testing.T) {
|
|||
t.Fatalf("server C isn't included in the key fetch response")
|
||||
}
|
||||
|
||||
// If we ask the cache for the server key for an event that happened
|
||||
// 90 minutes ago then we should get a cache result, as the key hadn't
|
||||
// passed its validity by that point. The fact that the key is now in
|
||||
// the cache is, in itself, proof that we successfully retrieved the
|
||||
// key before.
|
||||
|
||||
oldcached, ok := serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(-time.Minute*90)),
|
||||
)
|
||||
if !ok {
|
||||
t.Fatalf("server C key isn't in cache when it should be (-90 minutes)")
|
||||
}
|
||||
|
||||
// If we now ask the cache for the same key but this time for an event
|
||||
// that only happened 30 minutes ago then we shouldn't get a cached
|
||||
// result, as the event happened after the key validity expired. This
|
||||
// is really just for sanity checking.
|
||||
|
||||
_, ok = serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(-time.Minute*30)),
|
||||
)
|
||||
if ok {
|
||||
t.Fatalf("server B key is in cache when it shouldn't be (-30 minutes)")
|
||||
}
|
||||
originalValidity := res[req].ValidUntilTS
|
||||
|
||||
// We're now going to kick server C into renewing its key. Since we're
|
||||
// happy at this point that the key that we already have is from the past
|
||||
|
|
@ -308,24 +213,13 @@ func TestRenewalBehaviour(t *testing.T) {
|
|||
if len(res) != 1 {
|
||||
t.Fatalf("server C should have returned one key but instead returned %d keys", len(res))
|
||||
}
|
||||
if _, ok = res[req]; !ok {
|
||||
if _, ok := res[req]; !ok {
|
||||
t.Fatalf("server C isn't included in the key fetch response")
|
||||
}
|
||||
|
||||
// We're now going to ask the cache what the new key validity is. If
|
||||
// it is still the same as the previous validity then we've failed to
|
||||
// retrieve the renewed key. If it's newer then we've successfully got
|
||||
// the renewed key.
|
||||
currentValidity := res[req].ValidUntilTS
|
||||
|
||||
newcached, ok := serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(-time.Minute*30)),
|
||||
)
|
||||
if !ok {
|
||||
t.Fatalf("server B key isn't in cache when it shouldn't be (post-renewal)")
|
||||
if originalValidity == currentValidity {
|
||||
t.Fatalf("server C key should have renewed but didn't")
|
||||
}
|
||||
if oldcached.ValidUntilTS >= newcached.ValidUntilTS {
|
||||
t.Fatalf("the server B key should have been renewed but wasn't")
|
||||
}
|
||||
t.Log(res)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package producers
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
|
@ -34,6 +35,7 @@ type SyncAPIProducer struct {
|
|||
TopicSendToDeviceEvent string
|
||||
TopicTypingEvent string
|
||||
TopicPresenceEvent string
|
||||
TopicDeviceListUpdate string
|
||||
JetStream nats.JetStreamContext
|
||||
ServerName gomatrixserverlib.ServerName
|
||||
UserAPI userapi.UserInternalAPI
|
||||
|
|
@ -51,7 +53,7 @@ func (p *SyncAPIProducer) SendReceipt(
|
|||
m.Header.Set(jetstream.RoomID, roomID)
|
||||
m.Header.Set(jetstream.EventID, eventID)
|
||||
m.Header.Set("type", receiptType)
|
||||
m.Header.Set("timestamp", strconv.Itoa(int(timestamp)))
|
||||
m.Header.Set("timestamp", fmt.Sprintf("%d", timestamp))
|
||||
|
||||
log.WithFields(log.Fields{}).Tracef("Producing to topic '%s'", p.TopicReceiptEvent)
|
||||
_, err := p.JetStream.PublishMsg(m, nats.Context(ctx))
|
||||
|
|
@ -157,7 +159,22 @@ func (p *SyncAPIProducer) SendPresence(
|
|||
lastActiveTS := gomatrixserverlib.AsTimestamp(time.Now().Add(-(time.Duration(lastActiveAgo) * time.Millisecond)))
|
||||
|
||||
m.Header.Set("last_active_ts", strconv.Itoa(int(lastActiveTS)))
|
||||
log.Debugf("Sending presence to syncAPI: %+v", m.Header)
|
||||
log.Tracef("Sending presence to syncAPI: %+v", m.Header)
|
||||
_, err := p.JetStream.PublishMsg(m, nats.Context(ctx))
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *SyncAPIProducer) SendDeviceListUpdate(
|
||||
ctx context.Context, deviceListUpdate *gomatrixserverlib.DeviceListUpdateEvent,
|
||||
) (err error) {
|
||||
m := nats.NewMsg(p.TopicDeviceListUpdate)
|
||||
m.Header.Set(jetstream.UserID, deviceListUpdate.UserID)
|
||||
m.Data, err = json.Marshal(deviceListUpdate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("json.Marshal: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("Sending device list update: %+v", m.Header)
|
||||
_, err = p.JetStream.PublishMsg(m, nats.Context(ctx))
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ func GetUserDevices(
|
|||
if targetKey, ok := targetUser[gomatrixserverlib.KeyID(dev.DeviceID)]; ok {
|
||||
for sourceUserID, forSourceUser := range targetKey {
|
||||
for sourceKeyID, sourceKey := range forSourceUser {
|
||||
if device.Keys.Signatures == nil {
|
||||
device.Keys.Signatures = map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
|
||||
}
|
||||
if _, ok := device.Keys.Signatures[sourceUserID]; !ok {
|
||||
device.Keys.Signatures[sourceUserID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// InviteV2 implements /_matrix/federation/v2/invite/{roomID}/{eventID}
|
||||
|
|
@ -141,10 +142,17 @@ func processInvite(
|
|||
}
|
||||
|
||||
// Check that the event is signed by the server sending the request.
|
||||
redacted := event.Redact()
|
||||
redacted, err := gomatrixserverlib.RedactEventJSON(event.JSON(), event.Version())
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("XXX: invite.go")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.BadJSON("The event JSON could not be redacted"),
|
||||
}
|
||||
}
|
||||
verifyRequests := []gomatrixserverlib.VerifyJSONRequest{{
|
||||
ServerName: event.Origin(),
|
||||
Message: redacted.JSON(),
|
||||
Message: redacted,
|
||||
AtTS: event.OriginServerTS(),
|
||||
StrictValidityChecking: true,
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -266,10 +266,17 @@ func SendJoin(
|
|||
}
|
||||
|
||||
// Check that the event is signed by the server sending the request.
|
||||
redacted := event.Redact()
|
||||
redacted, err := gomatrixserverlib.RedactEventJSON(event.JSON(), event.Version())
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("XXX: join.go")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.BadJSON("The event JSON could not be redacted"),
|
||||
}
|
||||
}
|
||||
verifyRequests := []gomatrixserverlib.VerifyJSONRequest{{
|
||||
ServerName: event.Origin(),
|
||||
Message: redacted.JSON(),
|
||||
Message: redacted,
|
||||
AtTS: event.OriginServerTS(),
|
||||
StrictValidityChecking: true,
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -231,10 +231,17 @@ func SendLeave(
|
|||
}
|
||||
|
||||
// Check that the event is signed by the server sending the request.
|
||||
redacted := event.Redact()
|
||||
redacted, err := gomatrixserverlib.RedactEventJSON(event.JSON(), event.Version())
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("XXX: leave.go")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.BadJSON("The event JSON could not be redacted"),
|
||||
}
|
||||
}
|
||||
verifyRequests := []gomatrixserverlib.VerifyJSONRequest{{
|
||||
ServerName: event.Origin(),
|
||||
Message: redacted.JSON(),
|
||||
Message: redacted,
|
||||
AtTS: event.OriginServerTS(),
|
||||
StrictValidityChecking: true,
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -501,11 +501,7 @@ func (t *txnReq) processDeviceListUpdate(ctx context.Context, e gomatrixserverli
|
|||
} else if serverName != t.Origin {
|
||||
return
|
||||
}
|
||||
var inputRes keyapi.InputDeviceListUpdateResponse
|
||||
t.keyAPI.InputDeviceListUpdate(context.Background(), &keyapi.InputDeviceListUpdateRequest{
|
||||
Event: payload,
|
||||
}, &inputRes)
|
||||
if inputRes.Error != nil {
|
||||
util.GetLogger(ctx).WithError(inputRes.Error).WithField("user_id", payload.UserID).Error("failed to InputDeviceListUpdate")
|
||||
if err := t.producer.SendDeviceListUpdate(ctx, &payload); err != nil {
|
||||
util.GetLogger(ctx).WithError(err).WithField("user_id", payload.UserID).Error("failed to InputDeviceListUpdate")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
62
go.mod
62
go.mod
|
|
@ -2,59 +2,45 @@ module github.com/matrix-org/dendrite
|
|||
|
||||
replace github.com/nats-io/nats-server/v2 => github.com/neilalexander/nats-server/v2 v2.8.3-0.20220513095553-73a9a246d34f
|
||||
|
||||
replace github.com/nats-io/nats.go => github.com/neilalexander/nats.go v1.13.1-0.20220419101051-b262d9f0be1e
|
||||
replace github.com/nats-io/nats.go => github.com/neilalexander/nats.go v1.13.1-0.20220621084451-ac518c356673
|
||||
|
||||
require (
|
||||
github.com/Arceliar/ironwood v0.0.0-20220306165321-319147a02d98
|
||||
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/MFAshby/stdemuxerhook v1.0.0
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/codeclysm/extract v2.2.0+incompatible
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1-0.20220403145359-8e850b710d6d
|
||||
github.com/docker/docker v20.10.16+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/frankban/quicktest v1.14.3 // indirect
|
||||
github.com/getsentry/sentry-go v0.13.0
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/gologme/log v1.3.0
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/h2non/filetype v1.1.3 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
|
||||
github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494 // indirect
|
||||
github.com/kardianos/minwinsvc v1.0.0
|
||||
github.com/lib/pq v1.10.5
|
||||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
|
||||
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91
|
||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220531163017-35e1cabf12ee
|
||||
github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220713083127-fc2ea1e62e46
|
||||
github.com/matrix-org/pinecone v0.0.0-20220708135211-1ce778fcde6a
|
||||
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
|
||||
github.com/mattn/go-sqlite3 v1.14.13
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/miekg/dns v1.1.49 // indirect
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/nats-io/nats-server/v2 v2.7.4-0.20220309205833-773636c1c5bb
|
||||
github.com/nats-io/nats.go v1.14.0
|
||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79
|
||||
github.com/onsi/gomega v1.17.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pressly/goose v2.7.0+incompatible
|
||||
github.com/prometheus/client_golang v1.12.2
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spruceid/siwe-go v0.2.0
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/tidwall/gjson v1.14.1
|
||||
|
|
@ -67,54 +53,72 @@ require (
|
|||
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9
|
||||
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/h2non/bimg.v1 v1.1.9
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||
gotest.tools/v3 v3.0.3 // indirect
|
||||
nhooyr.io/websocket v1.8.7
|
||||
)
|
||||
|
||||
require github.com/ethereum/go-ethereum v1.10.18
|
||||
require github.com/ethereum/go-ethereum v1.10.15
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
|
||||
github.com/btcsuite/btcd v0.20.1-beta // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/frankban/quicktest v1.14.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/h2non/filetype v1.1.3 // indirect
|
||||
github.com/juju/errors v0.0.0-20220203013757-bd733f3c86b9 // indirect
|
||||
github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494 // indirect
|
||||
github.com/klauspost/compress v1.14.4 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.26.0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/miekg/dns v1.1.49 // indirect
|
||||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a // indirect
|
||||
github.com/nats-io/nkeys v0.3.0 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||
github.com/onsi/gomega v1.17.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/relvacode/iso8601 v1.1.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // 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/text v0.3.8-0.20211004125949-5bd84dd9b33b // indirect
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 // indirect
|
||||
golang.org/x/tools v0.1.10 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/macaroon.v2 v2.1.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||
gotest.tools/v3 v3.0.3 // indirect
|
||||
)
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
|
|
|||
266
go.sum
266
go.sum
|
|
@ -42,28 +42,34 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
|
|||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/Arceliar/ironwood v0.0.0-20211125050254-8951369625d0/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk=
|
||||
github.com/Arceliar/ironwood v0.0.0-20220306165321-319147a02d98 h1:PsaZb47k7WB1V+AlGpb+W7SM+ZOhp16vVevg5gl9YkU=
|
||||
github.com/Arceliar/ironwood v0.0.0-20220306165321-319147a02d98/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk=
|
||||
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ=
|
||||
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
|
||||
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/MFAshby/stdemuxerhook v1.0.0 h1:1XFGzakrsHMv76AeanPDL26NOgwjPl/OUxbGhJthwMc=
|
||||
github.com/MFAshby/stdemuxerhook v1.0.0/go.mod h1:nLMI9FUf9Hz98n+yAXsTMUR4RZQy28uCTLG1Fzvj/uY=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
|
|
@ -74,14 +80,11 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
|
|||
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
|
||||
github.com/RyanCarrier/dijkstra v1.0.0/go.mod h1:5agGUBNEtUAGIANmbw09fuO3a2htPEkc1jNH01qxCWA=
|
||||
github.com/RyanCarrier/dijkstra-1 v0.0.0-20170512020943-0e5801a26345/go.mod h1:OK4EvWJ441LQqGzed5NGB6vKBAE34n3z7iayPcEwr30=
|
||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o=
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/albertorestifo/dijkstra v0.0.0-20160910063646-aba76f725f72/go.mod h1:o+JdB7VetTHjLhU0N57x18B9voDBQe0paApdEAEoEfw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
|
|
@ -104,7 +107,6 @@ github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K
|
|||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo=
|
||||
|
|
@ -114,7 +116,6 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7
|
|||
github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM=
|
||||
github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw=
|
||||
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
|
|
@ -126,15 +127,19 @@ github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2w
|
|||
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
|
|
@ -150,40 +155,34 @@ github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3h
|
|||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/codeclysm/extract v2.2.0+incompatible h1:q3wyckoA30bhUSiwdQezMqVhwd8+WGE64/GL//LtUhI=
|
||||
github.com/codeclysm/extract v2.2.0+incompatible/go.mod h1:2nhFMPHiU9At61hz+12bfrlpXSUrOnK+wR+KlGO4Uks=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
||||
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
|
||||
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs=
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
|
||||
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
|
||||
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
|
||||
github.com/dgraph-io/ristretto v0.1.1-0.20220403145359-8e850b710d6d h1:Wrc3UKTS+cffkOx0xRGFC+ZesNuTfn0ThvEC72N0krk=
|
||||
github.com/dgraph-io/ristretto v0.1.1-0.20220403145359-8e850b710d6d/go.mod h1:RAy2GVV4sTWVlNMavv3xhLsk18rxhfhDnombTe6EF5c=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ=
|
||||
github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
|
|
@ -191,25 +190,22 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh
|
|||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
||||
github.com/ethereum/go-ethereum v1.10.18 h1:hLEd5M+UD0GJWPaROiYMRgZXl6bi5YwoTJSthsx5CZw=
|
||||
github.com/ethereum/go-ethereum v1.10.18/go.mod h1:RD3NhcSBjZpj3k+SnQq24wBrmnmie78P5R/P62iNBD8=
|
||||
github.com/ethereum/go-ethereum v1.10.15 h1:E9o0kMbD8HXhp7g6UwIwntY05WTDheCGziMhegcBsQw=
|
||||
github.com/ethereum/go-ethereum v1.10.15/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY=
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
|
|
@ -221,8 +217,6 @@ github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUork
|
|||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||
|
|
@ -233,12 +227,10 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
|||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
|
||||
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
|
|
@ -251,8 +243,6 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
|
|||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
|
||||
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
|
|
@ -263,10 +253,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
|
|||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
|
|
@ -281,10 +269,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
|||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
|
@ -318,13 +305,11 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
|
|||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||
github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U=
|
||||
github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo=
|
||||
github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4=
|
||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
|
|
@ -355,7 +340,7 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
|
|||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
|
|
@ -370,7 +355,7 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
|||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
|
||||
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
|
|
@ -379,22 +364,18 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslC
|
|||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
|
||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
|
||||
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk=
|
||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||
github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
|
||||
github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM=
|
||||
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY=
|
||||
github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=
|
||||
|
|
@ -407,17 +388,14 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y
|
|||
github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
|
||||
github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
|
||||
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
|
||||
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
|
||||
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
|
||||
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
|
||||
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
|
||||
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
|
@ -429,64 +407,28 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
|||
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/ansiterm v0.0.0-20160907234532-b99631de12cf/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
||||
github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA=
|
||||
github.com/juju/cmd v0.0.0-20171107070456-e74f39857ca0/go.mod h1:yWJQHl73rdSX4DHVKGqkAip+huBslxRwS8m9CrOLq18=
|
||||
github.com/juju/collections v0.0.0-20200605021417-0d0ec82b7271/go.mod h1:5XgO71dV1JClcOJE+4dzdn4HrI5LiyKd7PlVG6eZYhY=
|
||||
github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/errors v0.0.0-20200330140219-3fe23663418f/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/errors v0.0.0-20220203013757-bd733f3c86b9 h1:EJHbsNpQyupmMeWTq7inn+5L/WZ7JfzCVPJ+DP9McCQ=
|
||||
github.com/juju/errors v0.0.0-20220203013757-bd733f3c86b9/go.mod h1:TRm7EVGA3mQOqSVcBySRY7a9Y1/gyVhh/WTCnc5sD4U=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/juju/httpprof v0.0.0-20141217160036-14bf14c30767/go.mod h1:+MaLYz4PumRkkyHYeXJ2G5g5cIW0sli2bOfpmbaMV/g=
|
||||
github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/loggo v0.0.0-20200526014432-9ce3a2e09b5e/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4 h1:NO5tuyw++EGLnz56Q8KMyDZRwJwWO8jQnj285J3FOmY=
|
||||
github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4/go.mod h1:NIXFioti1SmKAlKNuUwbMenNdef59IF52+ZzuOmHYkg=
|
||||
github.com/juju/mgo/v2 v2.0.0-20210302023703-70d5d206e208 h1:/WiCm+Vpj87e4QWuWwPD/bNE9kDrWCLvPBHOQNcG2+A=
|
||||
github.com/juju/mgo/v2 v2.0.0-20210302023703-70d5d206e208/go.mod h1:0OChplkvPTZ174D2FYZXg4IB9hbEwyHkD+zT+/eK+Fg=
|
||||
github.com/juju/mutex v0.0.0-20171110020013-1fe2a4bf0a3a/go.mod h1:Y3oOzHH8CQ0Ppt0oCKJ2JFO81/EsWenH5AEqigLH+yY=
|
||||
github.com/juju/retry v0.0.0-20151029024821-62c620325291/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4=
|
||||
github.com/juju/retry v0.0.0-20180821225755-9058e192b216/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4=
|
||||
github.com/juju/testing v0.0.0-20180402130637-44801989f0f7/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/juju/testing v0.0.0-20210302031854-2c7ee8570c07/go.mod h1:7lxZW0B50+xdGFkvhAb8bwAGt6IU87JB1H9w4t8MNVM=
|
||||
github.com/juju/testing v0.0.0-20220202055744-1ad0816210a6/go.mod h1:QgWc2UdIPJ8t3rnvv95tFNOsQDfpXYEZDbP281o3b2c=
|
||||
github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494 h1:XEDzpuZb8Ma7vLja3+5hzUqVTvAqm5Y+ygvnDs5iTMM=
|
||||
github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494/go.mod h1:rUquetT0ALL48LHZhyRGvjjBH8xZaZ8dFClulKK5wK4=
|
||||
github.com/juju/utils v0.0.0-20180424094159-2000ea4ff043/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk=
|
||||
github.com/juju/utils v0.0.0-20200116185830-d40c2fe10647/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk=
|
||||
github.com/juju/utils/v2 v2.0.0-20200923005554-4646bfea2ef1/go.mod h1:fdlDtQlzundleLLz/ggoYinEt/LmnrpNKcNTABQATNI=
|
||||
github.com/juju/utils/v3 v3.0.0-20220130232349-cd7ecef0e94a/go.mod h1:LzwbbEN7buYjySp4nqnti6c6olSqRXUk6RkbSUUP1n8=
|
||||
github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
|
||||
github.com/juju/version v0.0.0-20180108022336-b64dbd566305/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
|
||||
github.com/juju/version v0.0.0-20191219164919-81c1be00b9a6/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
|
||||
github.com/juju/version/v2 v2.0.0-20211007103408-2e8da085dc23/go.mod h1:Ljlbryh9sYaUSGXucslAEDf0A2XUSGvDbHJgW8ps6nc=
|
||||
github.com/julienschmidt/httprouter v1.1.1-0.20151013225520-77a895ad01eb/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
|
||||
github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||
github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||
github.com/kardianos/minwinsvc v1.0.0 h1:+JfAi8IBJna0jY2dJGZqi7o15z13JelFIklJCAENALA=
|
||||
github.com/kardianos/minwinsvc v1.0.0/go.mod h1:Bgd0oc+D0Qo3bBytmNtyRKVlp85dAloLKhfxanPFFRc=
|
||||
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
|
||||
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
|
||||
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
|
||||
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
|
||||
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=
|
||||
github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
|
@ -503,10 +445,8 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
|
|
@ -516,11 +456,9 @@ github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ=
|
|||
github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lucas-clemente/quic-go v0.26.0 h1:ALBQXr9UJ8A1LyzvceX4jd9QFsHvlI0RR6BkV16o00A=
|
||||
github.com/lucas-clemente/quic-go v0.26.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20160125035106-4fbf7632a2c6/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
|
|
@ -531,10 +469,6 @@ github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWCh
|
|||
github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||
github.com/masterzen/azure-sdk-for-go v3.2.0-beta.0.20161014135628-ee4f0065d00c+incompatible/go.mod h1:mf8fjOu33zCqxUjuiU3I8S1lJMyEAlH+0F2+M5xl3hE=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20161014151040-7a535cd943fc/go.mod h1:CfZSN7zwz5gJiFhZJz49Uzk7mEBHIceWmbFmYx7Hf7E=
|
||||
github.com/masterzen/xmlpath v0.0.0-20140218185901-13f4951698ad/go.mod h1:A0zPC53iKKKcXYxr4ROjpQRQ5FgJXtelNdSmHHuq/tY=
|
||||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e h1:DP5RC0Z3XdyBEW5dKt8YPeN6vZbm6OzVaGVp7f1BQRM=
|
||||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e/go.mod h1:NgPCr+UavRGH6n5jmdX8DuqFZ4JiCWIJoZiuhTRLSUg=
|
||||
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw2QV3YD/fRrzEDPNGgTlJlvXY0EHHnT87wF3OA=
|
||||
|
|
@ -542,52 +476,44 @@ 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-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4=
|
||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220531163017-35e1cabf12ee h1:56sxEWrwB3eOmwjP2S4JsrQf29uBUaf+8WrbQJmjaGE=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220531163017-35e1cabf12ee/go.mod h1:jX38yp3SSLJNftBg3PXU1ayd0PCLIiDHQ4xAc9DIixk=
|
||||
github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48 h1:W0sjjC6yjskHX4mb0nk3p0fXAlbU5bAFUFeEtlrPASE=
|
||||
github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48/go.mod h1:ulJzsVOTssIVp1j/m5eI//4VpAGDkMt5NrRuAVX7wpc=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220713083127-fc2ea1e62e46 h1:5X/kXY3nwqKOwwrE9tnMKrjbsi3PHigQYvrvDBSntO8=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20220713083127-fc2ea1e62e46/go.mod h1:jX38yp3SSLJNftBg3PXU1ayd0PCLIiDHQ4xAc9DIixk=
|
||||
github.com/matrix-org/pinecone v0.0.0-20220708135211-1ce778fcde6a h1:DdG8vXMlZ65EAtc4V+3t7zHZ2Gqs24pSnyXS+4BRHUs=
|
||||
github.com/matrix-org/pinecone v0.0.0-20220708135211-1ce778fcde6a/go.mod h1:ulJzsVOTssIVp1j/m5eI//4VpAGDkMt5NrRuAVX7wpc=
|
||||
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=
|
||||
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 h1:eCEHXWDv9Rm335MSuB49mFUK44bwZPFSDde3ORE3syk=
|
||||
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=
|
||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||
github.com/mattn/go-colorable v0.0.6/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.13 h1:1tj15ngiFfcZzii7yd82foL+ks+ouQcj8j/TPq3fk1I=
|
||||
github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/mattomatic/dijkstra v0.0.0-20130617153013-6f6d134eb237/go.mod h1:UOnLAUmVG5paym8pD3C4B9BQylUDC2vXFJJpT7JrlEA=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
||||
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=
|
||||
github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 h1:yH0SvLzcbZxcJXho2yh7CqdENGMQe73Cw3woZBpPli0=
|
||||
|
|
@ -599,10 +525,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
|
|
@ -619,8 +543,8 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
|
|||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/neilalexander/nats-server/v2 v2.8.3-0.20220513095553-73a9a246d34f h1:Fc+TjdV1mOy0oISSzfoxNWdTqjg7tN/Vdgf+B2cwvdo=
|
||||
github.com/neilalexander/nats-server/v2 v2.8.3-0.20220513095553-73a9a246d34f/go.mod h1:vIdpKz3OG+DCg4q/xVPdXHoztEyKDWRtykQ4N7hd7C4=
|
||||
github.com/neilalexander/nats.go v1.13.1-0.20220419101051-b262d9f0be1e h1:kNIzIzj2OvnlreA+sTJ12nWJzTP3OSLNKDL/Iq9mF6Y=
|
||||
github.com/neilalexander/nats.go v1.13.1-0.20220419101051-b262d9f0be1e/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
||||
github.com/neilalexander/nats.go v1.13.1-0.20220621084451-ac518c356673 h1:TcKfa3Tf0qwUotv63PQVu2d1bBoLi2iEA4RHVMGDh5M=
|
||||
github.com/neilalexander/nats.go v1.13.1-0.20220621084451-ac518c356673/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9 h1:lrVQzBtkeQEGGYUHwSX1XPe1E5GL6U3KYCNe2G4bncQ=
|
||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9/go.mod h1:NPHGhPc0/wudcaCqL/H5AOddkRf8GPRhzOujuUKGQu8=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
|
|
@ -628,20 +552,19 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S
|
|||
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79 h1:Dmx8g2747UTVPzSkmohk84S3g/uWqd6+f4SSLPhLcfA=
|
||||
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79/go.mod h1:E26fwEtRNigBfFfHDWsklmo0T7Ixbg0XXgck+Hq4O9k=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
|
|
@ -660,13 +583,11 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm
|
|||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
@ -703,11 +624,11 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
|||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/relvacode/iso8601 v1.1.0 h1:2nV8sp0eOjpoKQ2vD3xSDygsjAx37NHG2UlZiCkDH4I=
|
||||
github.com/relvacode/iso8601 v1.1.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
|
||||
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
|
@ -716,13 +637,10 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
|
|||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
|
|
@ -759,13 +677,11 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
|
|||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spruceid/siwe-go v0.2.0 h1:MkBZ/TpPlh1mBhul3h/XLSNZJAbbaHF587Q/VQbhPI0=
|
||||
github.com/spruceid/siwe-go v0.2.0/go.mod h1:rvV+8/z/ryBKqdw9RcexFgtcsrDlESOGR38sPdVWbSI=
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
|
@ -780,7 +696,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
|||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
|
|
@ -793,9 +708,7 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso
|
|||
github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
|
||||
github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
|
||||
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
||||
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
||||
|
|
@ -804,16 +717,12 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK
|
|||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
|
|
@ -821,23 +730,15 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
|||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.4.3 h1:LNS7kNpKzFlxQ9xmD5tfmMEvzwa+utBoD6pV9t2a8q4=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.4.3/go.mod h1:A1/8kOQT7vzBxlkQtLf1KzJR0cbfL/2zjOCiYOAdjjo=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
|
|
@ -852,11 +753,10 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
|||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
|
@ -864,7 +764,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
|
@ -873,7 +772,6 @@ golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm
|
|||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/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-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
|
@ -890,7 +788,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
|
@ -920,22 +817,18 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
|
|
@ -944,7 +837,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
|
@ -962,8 +854,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
|
|
@ -971,15 +861,11 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca h1:xTaFYiPROfpPhqrfTIDXj0ri1SpfueYT951s4bAuDO8=
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
|
@ -1008,7 +894,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -1021,7 +906,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -1075,19 +959,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/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-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
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-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM=
|
||||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
@ -1098,7 +979,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0=
|
||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
|
@ -1114,14 +994,12 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm
|
|||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
|
|
@ -1138,7 +1016,6 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn
|
|||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
|
|
@ -1169,8 +1046,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI=
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
@ -1280,34 +1157,26 @@ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+Rur
|
|||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v1 v1.0.0-20161222125816-442357a80af5/go.mod h1:u0ALmqvLRxLI95fkdCEWrE6mhWYZW1aMOJHp5YXLHTg=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/h2non/bimg.v1 v1.1.9 h1:wZIUbeOnwr37Ta4aofhIv8OI8v4ujpjXC9mXnAGpQjM=
|
||||
gopkg.in/h2non/bimg.v1 v1.1.9/go.mod h1:PgsZL7dLwUbsGm1NYps320GxGgvQNTnecMCZqxV11So=
|
||||
gopkg.in/h2non/gock.v1 v1.0.14 h1:fTeu9fcUvSnLNacYvYI54h+1/XEteDyHvrVCZEEEYNM=
|
||||
gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/httprequest.v1 v1.1.1/go.mod h1:/CkavNL+g3qLOrpFHVrEx4NKepeqR4XTZWNj4sGGjz0=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI=
|
||||
gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o=
|
||||
gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
@ -1317,12 +1186,11 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
|
|
@ -1336,8 +1204,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
|||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
launchpad.net/xmlpath v0.0.0-20130614043138-000000000004/go.mod h1:vqyExLOM3qBx7mvYRkoxjSCF945s0mbe7YynlKYXtsA=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
|
|
|||
|
|
@ -1,18 +1,9 @@
|
|||
package caching
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
const (
|
||||
FederationEventCacheName = "federation_event"
|
||||
FederationEventCacheMaxEntries = 256
|
||||
FederationEventCacheMutable = true // to allow use of Unset only
|
||||
FederationEventCacheMaxAge = CacheNoMaxAge
|
||||
)
|
||||
|
||||
// FederationCache contains the subset of functions needed for
|
||||
// a federation event cache.
|
||||
type FederationCache interface {
|
||||
|
|
@ -26,43 +17,25 @@ type FederationCache interface {
|
|||
}
|
||||
|
||||
func (c Caches) GetFederationQueuedPDU(eventNID int64) (*gomatrixserverlib.HeaderedEvent, bool) {
|
||||
key := fmt.Sprintf("%d", eventNID)
|
||||
val, found := c.FederationEvents.Get(key)
|
||||
if found && val != nil {
|
||||
if event, ok := val.(*gomatrixserverlib.HeaderedEvent); ok {
|
||||
return event, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
return c.FederationPDUs.Get(eventNID)
|
||||
}
|
||||
|
||||
func (c Caches) StoreFederationQueuedPDU(eventNID int64, event *gomatrixserverlib.HeaderedEvent) {
|
||||
key := fmt.Sprintf("%d", eventNID)
|
||||
c.FederationEvents.Set(key, event)
|
||||
c.FederationPDUs.Set(eventNID, event)
|
||||
}
|
||||
|
||||
func (c Caches) EvictFederationQueuedPDU(eventNID int64) {
|
||||
key := fmt.Sprintf("%d", eventNID)
|
||||
c.FederationEvents.Unset(key)
|
||||
c.FederationPDUs.Unset(eventNID)
|
||||
}
|
||||
|
||||
func (c Caches) GetFederationQueuedEDU(eventNID int64) (*gomatrixserverlib.EDU, bool) {
|
||||
key := fmt.Sprintf("%d", eventNID)
|
||||
val, found := c.FederationEvents.Get(key)
|
||||
if found && val != nil {
|
||||
if event, ok := val.(*gomatrixserverlib.EDU); ok {
|
||||
return event, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
return c.FederationEDUs.Get(eventNID)
|
||||
}
|
||||
|
||||
func (c Caches) StoreFederationQueuedEDU(eventNID int64, event *gomatrixserverlib.EDU) {
|
||||
key := fmt.Sprintf("%d", eventNID)
|
||||
c.FederationEvents.Set(key, event)
|
||||
c.FederationEDUs.Set(eventNID, event)
|
||||
}
|
||||
|
||||
func (c Caches) EvictFederationQueuedEDU(eventNID int64) {
|
||||
key := fmt.Sprintf("%d", eventNID)
|
||||
c.FederationEvents.Unset(key)
|
||||
c.FederationEDUs.Unset(eventNID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +1,35 @@
|
|||
package caching
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
)
|
||||
|
||||
const (
|
||||
LazyLoadCacheName = "lazy_load_members"
|
||||
LazyLoadCacheMaxEntries = 128
|
||||
LazyLoadCacheMaxUserEntries = 128
|
||||
LazyLoadCacheMutable = true
|
||||
LazyLoadCacheMaxAge = time.Minute * 30
|
||||
)
|
||||
type lazyLoadingCacheKey struct {
|
||||
UserID string // the user we're querying on behalf of
|
||||
DeviceID string // the user we're querying on behalf of
|
||||
RoomID string // the room in question
|
||||
TargetUserID string // the user whose membership we're asking about
|
||||
}
|
||||
|
||||
type LazyLoadCache interface {
|
||||
StoreLazyLoadedUser(device *userapi.Device, roomID, userID, eventID string)
|
||||
IsLazyLoadedUserCached(device *userapi.Device, roomID, userID string) (string, bool)
|
||||
}
|
||||
|
||||
func (c Caches) lazyLoadCacheForUser(device *userapi.Device) (*InMemoryLRUCachePartition, error) {
|
||||
cacheName := fmt.Sprintf("%s/%s", device.UserID, device.ID)
|
||||
userCache, ok := c.LazyLoading.Get(cacheName)
|
||||
if ok && userCache != nil {
|
||||
if cache, ok := userCache.(*InMemoryLRUCachePartition); ok {
|
||||
return cache, nil
|
||||
}
|
||||
}
|
||||
cache, err := NewInMemoryLRUCachePartition(
|
||||
LazyLoadCacheName,
|
||||
LazyLoadCacheMutable,
|
||||
LazyLoadCacheMaxUserEntries,
|
||||
LazyLoadCacheMaxAge,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.LazyLoading.Set(cacheName, cache)
|
||||
go cacheCleaner(cache)
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func (c Caches) StoreLazyLoadedUser(device *userapi.Device, roomID, userID, eventID string) {
|
||||
cache, err := c.lazyLoadCacheForUser(device)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cacheKey := fmt.Sprintf("%s/%s/%s/%s", device.UserID, device.ID, roomID, userID)
|
||||
cache.Set(cacheKey, eventID)
|
||||
c.LazyLoading.Set(lazyLoadingCacheKey{
|
||||
UserID: device.UserID,
|
||||
DeviceID: device.ID,
|
||||
RoomID: roomID,
|
||||
TargetUserID: userID,
|
||||
}, eventID)
|
||||
}
|
||||
|
||||
func (c Caches) IsLazyLoadedUserCached(device *userapi.Device, roomID, userID string) (string, bool) {
|
||||
cache, err := c.lazyLoadCacheForUser(device)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("%s/%s/%s/%s", device.UserID, device.ID, roomID, userID)
|
||||
val, ok := cache.Get(cacheKey)
|
||||
if !ok {
|
||||
return "", ok
|
||||
}
|
||||
return val.(string), ok
|
||||
return c.LazyLoading.Get(lazyLoadingCacheKey{
|
||||
UserID: device.UserID,
|
||||
DeviceID: device.ID,
|
||||
RoomID: roomID,
|
||||
TargetUserID: userID,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
21
internal/caching/cache_roomevents.go
Normal file
21
internal/caching/cache_roomevents.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package caching
|
||||
|
||||
import (
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// RoomServerEventsCache contains the subset of functions needed for
|
||||
// a roomserver event cache.
|
||||
type RoomServerEventsCache interface {
|
||||
GetRoomServerEvent(eventNID types.EventNID) (*gomatrixserverlib.Event, bool)
|
||||
StoreRoomServerEvent(eventNID types.EventNID, event *gomatrixserverlib.Event)
|
||||
}
|
||||
|
||||
func (c Caches) GetRoomServerEvent(eventNID types.EventNID) (*gomatrixserverlib.Event, bool) {
|
||||
return c.RoomServerEvents.Get(int64(eventNID))
|
||||
}
|
||||
|
||||
func (c Caches) StoreRoomServerEvent(eventNID types.EventNID, event *gomatrixserverlib.Event) {
|
||||
c.RoomServerEvents.Set(int64(eventNID), event)
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package caching
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
)
|
||||
|
||||
|
|
@ -14,35 +12,22 @@ import (
|
|||
// used from other components as we currently have no way to invalidate
|
||||
// the cache in downstream components.
|
||||
|
||||
const (
|
||||
RoomInfoCacheName = "roominfo"
|
||||
RoomInfoCacheMaxEntries = 1024
|
||||
RoomInfoCacheMutable = true
|
||||
RoomInfoCacheMaxAge = time.Minute * 5
|
||||
)
|
||||
|
||||
// RoomInfosCache contains the subset of functions needed for
|
||||
// a room Info cache. It must only be used from the roomserver only
|
||||
// It is not safe for use from other components.
|
||||
type RoomInfoCache interface {
|
||||
GetRoomInfo(roomID string) (roomInfo types.RoomInfo, ok bool)
|
||||
StoreRoomInfo(roomID string, roomInfo types.RoomInfo)
|
||||
GetRoomInfo(roomID string) (roomInfo *types.RoomInfo, ok bool)
|
||||
StoreRoomInfo(roomID string, roomInfo *types.RoomInfo)
|
||||
}
|
||||
|
||||
// GetRoomInfo must only be called from the roomserver only. It is not
|
||||
// safe for use from other components.
|
||||
func (c Caches) GetRoomInfo(roomID string) (types.RoomInfo, bool) {
|
||||
val, found := c.RoomInfos.Get(roomID)
|
||||
if found && val != nil {
|
||||
if roomInfo, ok := val.(types.RoomInfo); ok {
|
||||
return roomInfo, true
|
||||
}
|
||||
}
|
||||
return types.RoomInfo{}, false
|
||||
func (c Caches) GetRoomInfo(roomID string) (*types.RoomInfo, bool) {
|
||||
return c.RoomInfos.Get(roomID)
|
||||
}
|
||||
|
||||
// StoreRoomInfo must only be called from the roomserver only. It is not
|
||||
// safe for use from other components.
|
||||
func (c Caches) StoreRoomInfo(roomID string, roomInfo types.RoomInfo) {
|
||||
func (c Caches) StoreRoomInfo(roomID string, roomInfo *types.RoomInfo) {
|
||||
c.RoomInfos.Set(roomID, roomInfo)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,14 @@
|
|||
package caching
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
)
|
||||
|
||||
const (
|
||||
RoomServerRoomIDsCacheName = "roomserver_room_ids"
|
||||
RoomServerRoomIDsCacheMaxEntries = 1024
|
||||
RoomServerRoomIDsCacheMutable = false
|
||||
RoomServerRoomIDsCacheMaxAge = CacheNoMaxAge
|
||||
)
|
||||
|
||||
type RoomServerCaches interface {
|
||||
RoomServerNIDsCache
|
||||
RoomVersionCache
|
||||
RoomInfoCache
|
||||
RoomServerEventsCache
|
||||
}
|
||||
|
||||
// RoomServerNIDsCache contains the subset of functions needed for
|
||||
|
|
@ -27,15 +19,9 @@ type RoomServerNIDsCache interface {
|
|||
}
|
||||
|
||||
func (c Caches) GetRoomServerRoomID(roomNID types.RoomNID) (string, bool) {
|
||||
val, found := c.RoomServerRoomIDs.Get(strconv.Itoa(int(roomNID)))
|
||||
if found && val != nil {
|
||||
if roomID, ok := val.(string); ok {
|
||||
return roomID, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
return c.RoomServerRoomIDs.Get(int64(roomNID))
|
||||
}
|
||||
|
||||
func (c Caches) StoreRoomServerRoomID(roomNID types.RoomNID, roomID string) {
|
||||
c.RoomServerRoomIDs.Set(strconv.Itoa(int(roomNID)), roomID)
|
||||
c.RoomServerRoomIDs.Set(int64(roomNID), roomID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,6 @@ package caching
|
|||
|
||||
import "github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
const (
|
||||
RoomVersionCacheName = "room_versions"
|
||||
RoomVersionCacheMaxEntries = 1024
|
||||
RoomVersionCacheMutable = false
|
||||
RoomVersionCacheMaxAge = CacheNoMaxAge
|
||||
)
|
||||
|
||||
// RoomVersionsCache contains the subset of functions needed for
|
||||
// a room version cache.
|
||||
type RoomVersionCache interface {
|
||||
|
|
@ -17,13 +10,7 @@ type RoomVersionCache interface {
|
|||
}
|
||||
|
||||
func (c Caches) GetRoomVersion(roomID string) (gomatrixserverlib.RoomVersion, bool) {
|
||||
val, found := c.RoomVersions.Get(roomID)
|
||||
if found && val != nil {
|
||||
if roomVersion, ok := val.(gomatrixserverlib.RoomVersion); ok {
|
||||
return roomVersion, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
return c.RoomVersions.Get(roomID)
|
||||
}
|
||||
|
||||
func (c Caches) StoreRoomVersion(roomID string, roomVersion gomatrixserverlib.RoomVersion) {
|
||||
|
|
|
|||
|
|
@ -6,13 +6,6 @@ import (
|
|||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
const (
|
||||
ServerKeyCacheName = "server_key"
|
||||
ServerKeyCacheMaxEntries = 4096
|
||||
ServerKeyCacheMutable = true
|
||||
ServerKeyCacheMaxAge = CacheNoMaxAge
|
||||
)
|
||||
|
||||
// ServerKeyCache contains the subset of functions needed for
|
||||
// a server key cache.
|
||||
type ServerKeyCache interface {
|
||||
|
|
@ -34,18 +27,13 @@ func (c Caches) GetServerKey(
|
|||
) (gomatrixserverlib.PublicKeyLookupResult, bool) {
|
||||
key := fmt.Sprintf("%s/%s", request.ServerName, request.KeyID)
|
||||
val, found := c.ServerKeys.Get(key)
|
||||
if found && val != nil {
|
||||
if keyLookupResult, ok := val.(gomatrixserverlib.PublicKeyLookupResult); ok {
|
||||
if !keyLookupResult.WasValidAt(timestamp, true) {
|
||||
// The key wasn't valid at the requested timestamp so don't
|
||||
// return it. The caller will have to work out what to do.
|
||||
c.ServerKeys.Unset(key)
|
||||
return gomatrixserverlib.PublicKeyLookupResult{}, false
|
||||
}
|
||||
return keyLookupResult, true
|
||||
}
|
||||
if found && !val.WasValidAt(timestamp, true) {
|
||||
// The key wasn't valid at the requested timestamp so don't
|
||||
// return it. The caller will have to work out what to do.
|
||||
c.ServerKeys.Unset(key)
|
||||
return gomatrixserverlib.PublicKeyLookupResult{}, false
|
||||
}
|
||||
return gomatrixserverlib.PublicKeyLookupResult{}, false
|
||||
return val, found
|
||||
}
|
||||
|
||||
func (c Caches) StoreServerKey(
|
||||
|
|
|
|||
|
|
@ -1,31 +1,16 @@
|
|||
package caching
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
const (
|
||||
SpaceSummaryRoomsCacheName = "space_summary_rooms"
|
||||
SpaceSummaryRoomsCacheMaxEntries = 100
|
||||
SpaceSummaryRoomsCacheMutable = true
|
||||
SpaceSummaryRoomsCacheMaxAge = time.Minute * 5
|
||||
)
|
||||
|
||||
type SpaceSummaryRoomsCache interface {
|
||||
GetSpaceSummary(roomID string) (r gomatrixserverlib.MSC2946SpacesResponse, ok bool)
|
||||
StoreSpaceSummary(roomID string, r gomatrixserverlib.MSC2946SpacesResponse)
|
||||
}
|
||||
|
||||
func (c Caches) GetSpaceSummary(roomID string) (r gomatrixserverlib.MSC2946SpacesResponse, ok bool) {
|
||||
val, found := c.SpaceSummaryRooms.Get(roomID)
|
||||
if found && val != nil {
|
||||
if resp, ok := val.(gomatrixserverlib.MSC2946SpacesResponse); ok {
|
||||
return resp, true
|
||||
}
|
||||
}
|
||||
return r, false
|
||||
return c.SpaceSummaryRooms.Get(roomID)
|
||||
}
|
||||
|
||||
func (c Caches) StoreSpaceSummary(roomID string, r gomatrixserverlib.MSC2946SpacesResponse) {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,52 @@
|
|||
// 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 caching
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// Caches contains a set of references to caches. They may be
|
||||
// different implementations as long as they satisfy the Cache
|
||||
// interface.
|
||||
type Caches struct {
|
||||
RoomVersions Cache // RoomVersionCache
|
||||
ServerKeys Cache // ServerKeyCache
|
||||
RoomServerRoomNIDs Cache // RoomServerNIDsCache
|
||||
RoomServerRoomIDs Cache // RoomServerNIDsCache
|
||||
RoomInfos Cache // RoomInfoCache
|
||||
FederationEvents Cache // FederationEventsCache
|
||||
SpaceSummaryRooms Cache // SpaceSummaryRoomsCache
|
||||
LazyLoading Cache // LazyLoadCache
|
||||
RoomVersions Cache[string, gomatrixserverlib.RoomVersion] // room ID -> room version
|
||||
ServerKeys Cache[string, gomatrixserverlib.PublicKeyLookupResult] // server name -> server keys
|
||||
RoomServerRoomNIDs Cache[string, types.RoomNID] // room ID -> room NID
|
||||
RoomServerRoomIDs Cache[int64, string] // room NID -> room ID
|
||||
RoomServerEvents Cache[int64, *gomatrixserverlib.Event] // event NID -> event
|
||||
RoomInfos Cache[string, *types.RoomInfo] // room ID -> room info
|
||||
FederationPDUs Cache[int64, *gomatrixserverlib.HeaderedEvent] // queue NID -> PDU
|
||||
FederationEDUs Cache[int64, *gomatrixserverlib.EDU] // queue NID -> EDU
|
||||
SpaceSummaryRooms Cache[string, gomatrixserverlib.MSC2946SpacesResponse] // room ID -> space response
|
||||
LazyLoading Cache[lazyLoadingCacheKey, string] // composite key -> event ID
|
||||
}
|
||||
|
||||
// Cache is the interface that an implementation must satisfy.
|
||||
type Cache interface {
|
||||
Get(key string) (value interface{}, ok bool)
|
||||
Set(key string, value interface{})
|
||||
Unset(key string)
|
||||
type Cache[K keyable, T any] interface {
|
||||
Get(key K) (value T, ok bool)
|
||||
Set(key K, value T)
|
||||
Unset(key K)
|
||||
}
|
||||
|
||||
const CacheNoMaxAge = time.Duration(0)
|
||||
type keyable interface {
|
||||
// from https://github.com/dgraph-io/ristretto/blob/8e850b710d6df0383c375ec6a7beae4ce48fc8d5/z/z.go#L34
|
||||
uint64 | string | []byte | byte | int | int32 | uint32 | int64 | lazyLoadingCacheKey
|
||||
}
|
||||
|
||||
type costable interface {
|
||||
CacheCost() int
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,189 +0,0 @@
|
|||
package caching
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) {
|
||||
roomVersions, err := NewInMemoryLRUCachePartition(
|
||||
RoomVersionCacheName,
|
||||
RoomVersionCacheMutable,
|
||||
RoomVersionCacheMaxEntries,
|
||||
RoomVersionCacheMaxAge,
|
||||
enablePrometheus,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverKeys, err := NewInMemoryLRUCachePartition(
|
||||
ServerKeyCacheName,
|
||||
ServerKeyCacheMutable,
|
||||
ServerKeyCacheMaxEntries,
|
||||
ServerKeyCacheMaxAge,
|
||||
enablePrometheus,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roomServerRoomIDs, err := NewInMemoryLRUCachePartition(
|
||||
RoomServerRoomIDsCacheName,
|
||||
RoomServerRoomIDsCacheMutable,
|
||||
RoomServerRoomIDsCacheMaxEntries,
|
||||
RoomServerRoomIDsCacheMaxAge,
|
||||
enablePrometheus,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roomInfos, err := NewInMemoryLRUCachePartition(
|
||||
RoomInfoCacheName,
|
||||
RoomInfoCacheMutable,
|
||||
RoomInfoCacheMaxEntries,
|
||||
RoomInfoCacheMaxAge,
|
||||
enablePrometheus,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
federationEvents, err := NewInMemoryLRUCachePartition(
|
||||
FederationEventCacheName,
|
||||
FederationEventCacheMutable,
|
||||
FederationEventCacheMaxEntries,
|
||||
FederationEventCacheMaxAge,
|
||||
enablePrometheus,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spaceRooms, err := NewInMemoryLRUCachePartition(
|
||||
SpaceSummaryRoomsCacheName,
|
||||
SpaceSummaryRoomsCacheMutable,
|
||||
SpaceSummaryRoomsCacheMaxEntries,
|
||||
SpaceSummaryRoomsCacheMaxAge,
|
||||
enablePrometheus,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lazyLoadCache, err := NewInMemoryLRUCachePartition(
|
||||
LazyLoadCacheName,
|
||||
LazyLoadCacheMutable,
|
||||
LazyLoadCacheMaxEntries,
|
||||
LazyLoadCacheMaxAge,
|
||||
enablePrometheus,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go cacheCleaner(
|
||||
roomVersions, serverKeys, roomServerRoomIDs,
|
||||
roomInfos, federationEvents, spaceRooms, lazyLoadCache,
|
||||
)
|
||||
return &Caches{
|
||||
RoomVersions: roomVersions,
|
||||
ServerKeys: serverKeys,
|
||||
RoomServerRoomIDs: roomServerRoomIDs,
|
||||
RoomInfos: roomInfos,
|
||||
FederationEvents: federationEvents,
|
||||
SpaceSummaryRooms: spaceRooms,
|
||||
LazyLoading: lazyLoadCache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func cacheCleaner(caches ...*InMemoryLRUCachePartition) {
|
||||
for {
|
||||
time.Sleep(time.Minute)
|
||||
for _, cache := range caches {
|
||||
// Hold onto the last 10% of the cache entries, since
|
||||
// otherwise a quiet period might cause us to evict all
|
||||
// cache entries entirely.
|
||||
if cache.lru.Len() > cache.maxEntries/10 {
|
||||
cache.lru.RemoveOldest()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type InMemoryLRUCachePartition struct {
|
||||
name string
|
||||
mutable bool
|
||||
maxEntries int
|
||||
maxAge time.Duration
|
||||
lru *lru.Cache
|
||||
}
|
||||
|
||||
type inMemoryLRUCacheEntry struct {
|
||||
value interface{}
|
||||
created time.Time
|
||||
}
|
||||
|
||||
func NewInMemoryLRUCachePartition(name string, mutable bool, maxEntries int, maxAge time.Duration, enablePrometheus bool) (*InMemoryLRUCachePartition, error) {
|
||||
var err error
|
||||
cache := InMemoryLRUCachePartition{
|
||||
name: name,
|
||||
mutable: mutable,
|
||||
maxEntries: maxEntries,
|
||||
maxAge: maxAge,
|
||||
}
|
||||
cache.lru, err = lru.New(maxEntries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if enablePrometheus {
|
||||
promauto.NewGaugeFunc(prometheus.GaugeOpts{
|
||||
Namespace: "dendrite",
|
||||
Subsystem: "caching_in_memory_lru",
|
||||
Name: name,
|
||||
}, func() float64 {
|
||||
return float64(cache.lru.Len())
|
||||
})
|
||||
}
|
||||
return &cache, nil
|
||||
}
|
||||
|
||||
func (c *InMemoryLRUCachePartition) Set(key string, value interface{}) {
|
||||
if !c.mutable {
|
||||
if peek, ok := c.lru.Peek(key); ok {
|
||||
if entry, ok := peek.(*inMemoryLRUCacheEntry); ok && entry.value != value {
|
||||
panic(fmt.Sprintf("invalid use of immutable cache tries to mutate existing value of %q", key))
|
||||
}
|
||||
}
|
||||
}
|
||||
c.lru.Add(key, &inMemoryLRUCacheEntry{
|
||||
value: value,
|
||||
created: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *InMemoryLRUCachePartition) Unset(key string) {
|
||||
if !c.mutable {
|
||||
panic(fmt.Sprintf("invalid use of immutable cache tries to unset value of %q", key))
|
||||
}
|
||||
c.lru.Remove(key)
|
||||
}
|
||||
|
||||
func (c *InMemoryLRUCachePartition) Get(key string) (value interface{}, ok bool) {
|
||||
v, ok := c.lru.Get(key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
entry, ok := v.(*inMemoryLRUCacheEntry)
|
||||
switch {
|
||||
case ok && c.maxAge == CacheNoMaxAge:
|
||||
return entry.value, ok // There's no maximum age policy
|
||||
case ok && time.Since(entry.created) < c.maxAge:
|
||||
return entry.value, ok // The value for the key isn't stale
|
||||
default:
|
||||
// Either the key was found and it was stale, or the key
|
||||
// wasn't found at all
|
||||
c.lru.Remove(key)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
200
internal/caching/impl_ristretto.go
Normal file
200
internal/caching/impl_ristretto.go
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// 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 caching
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/dgraph-io/ristretto"
|
||||
"github.com/dgraph-io/ristretto/z"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
const (
|
||||
roomVersionsCache byte = iota + 1
|
||||
serverKeysCache
|
||||
roomNIDsCache
|
||||
roomIDsCache
|
||||
roomEventsCache
|
||||
roomInfosCache
|
||||
federationPDUsCache
|
||||
federationEDUsCache
|
||||
spaceSummaryRoomsCache
|
||||
lazyLoadingCache
|
||||
)
|
||||
|
||||
func NewRistrettoCache(maxCost config.DataUnit, maxAge time.Duration, enablePrometheus bool) *Caches {
|
||||
cache, err := ristretto.NewCache(&ristretto.Config{
|
||||
NumCounters: 1e5, // 10x number of expected cache items, affects bloom filter size, gives us room for 10,000 currently
|
||||
BufferItems: 64, // recommended by the ristretto godocs as a sane buffer size value
|
||||
MaxCost: int64(maxCost),
|
||||
Metrics: true,
|
||||
KeyToHash: func(key interface{}) (uint64, uint64) {
|
||||
return z.KeyToHash(key)
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if enablePrometheus {
|
||||
promauto.NewGaugeFunc(prometheus.GaugeOpts{
|
||||
Namespace: "dendrite",
|
||||
Subsystem: "caching_ristretto",
|
||||
Name: "ratio",
|
||||
}, func() float64 {
|
||||
return float64(cache.Metrics.Ratio())
|
||||
})
|
||||
promauto.NewGaugeFunc(prometheus.GaugeOpts{
|
||||
Namespace: "dendrite",
|
||||
Subsystem: "caching_ristretto",
|
||||
Name: "cost",
|
||||
}, func() float64 {
|
||||
return float64(cache.Metrics.CostAdded() - cache.Metrics.CostEvicted())
|
||||
})
|
||||
}
|
||||
return &Caches{
|
||||
RoomVersions: &RistrettoCachePartition[string, gomatrixserverlib.RoomVersion]{ // room ID -> room version
|
||||
cache: cache,
|
||||
Prefix: roomVersionsCache,
|
||||
MaxAge: maxAge,
|
||||
},
|
||||
ServerKeys: &RistrettoCachePartition[string, gomatrixserverlib.PublicKeyLookupResult]{ // server name -> server keys
|
||||
cache: cache,
|
||||
Prefix: serverKeysCache,
|
||||
Mutable: true,
|
||||
MaxAge: maxAge,
|
||||
},
|
||||
RoomServerRoomNIDs: &RistrettoCachePartition[string, types.RoomNID]{ // room ID -> room NID
|
||||
cache: cache,
|
||||
Prefix: roomNIDsCache,
|
||||
MaxAge: maxAge,
|
||||
},
|
||||
RoomServerRoomIDs: &RistrettoCachePartition[int64, string]{ // room NID -> room ID
|
||||
cache: cache,
|
||||
Prefix: roomIDsCache,
|
||||
MaxAge: maxAge,
|
||||
},
|
||||
RoomServerEvents: &RistrettoCostedCachePartition[int64, *gomatrixserverlib.Event]{ // event NID -> event
|
||||
&RistrettoCachePartition[int64, *gomatrixserverlib.Event]{
|
||||
cache: cache,
|
||||
Prefix: roomEventsCache,
|
||||
MaxAge: maxAge,
|
||||
},
|
||||
},
|
||||
RoomInfos: &RistrettoCachePartition[string, *types.RoomInfo]{ // room ID -> room info
|
||||
cache: cache,
|
||||
Prefix: roomInfosCache,
|
||||
Mutable: true,
|
||||
MaxAge: maxAge,
|
||||
},
|
||||
FederationPDUs: &RistrettoCostedCachePartition[int64, *gomatrixserverlib.HeaderedEvent]{ // queue NID -> PDU
|
||||
&RistrettoCachePartition[int64, *gomatrixserverlib.HeaderedEvent]{
|
||||
cache: cache,
|
||||
Prefix: federationPDUsCache,
|
||||
Mutable: true,
|
||||
MaxAge: lesserOf(time.Hour/2, maxAge),
|
||||
},
|
||||
},
|
||||
FederationEDUs: &RistrettoCostedCachePartition[int64, *gomatrixserverlib.EDU]{ // queue NID -> EDU
|
||||
&RistrettoCachePartition[int64, *gomatrixserverlib.EDU]{
|
||||
cache: cache,
|
||||
Prefix: federationEDUsCache,
|
||||
Mutable: true,
|
||||
MaxAge: lesserOf(time.Hour/2, maxAge),
|
||||
},
|
||||
},
|
||||
SpaceSummaryRooms: &RistrettoCachePartition[string, gomatrixserverlib.MSC2946SpacesResponse]{ // room ID -> space response
|
||||
cache: cache,
|
||||
Prefix: spaceSummaryRoomsCache,
|
||||
Mutable: true,
|
||||
MaxAge: maxAge,
|
||||
},
|
||||
LazyLoading: &RistrettoCachePartition[lazyLoadingCacheKey, string]{ // composite key -> event ID
|
||||
cache: cache,
|
||||
Prefix: lazyLoadingCache,
|
||||
Mutable: true,
|
||||
MaxAge: maxAge,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type RistrettoCostedCachePartition[k keyable, v costable] struct {
|
||||
*RistrettoCachePartition[k, v]
|
||||
}
|
||||
|
||||
func (c *RistrettoCostedCachePartition[K, V]) Set(key K, value V) {
|
||||
cost := value.CacheCost()
|
||||
c.setWithCost(key, value, int64(cost))
|
||||
}
|
||||
|
||||
type RistrettoCachePartition[K keyable, V any] struct {
|
||||
cache *ristretto.Cache
|
||||
Prefix byte
|
||||
Mutable bool
|
||||
MaxAge time.Duration
|
||||
}
|
||||
|
||||
func (c *RistrettoCachePartition[K, V]) setWithCost(key K, value V, cost int64) {
|
||||
bkey := fmt.Sprintf("%c%v", c.Prefix, key)
|
||||
if !c.Mutable {
|
||||
if v, ok := c.cache.Get(bkey); ok && v != nil && !reflect.DeepEqual(v, value) {
|
||||
panic(fmt.Sprintf("invalid use of immutable cache tries to change value of %v from %v to %v", key, v, value))
|
||||
}
|
||||
}
|
||||
c.cache.SetWithTTL(bkey, value, int64(len(bkey))+cost, c.MaxAge)
|
||||
}
|
||||
|
||||
func (c *RistrettoCachePartition[K, V]) Set(key K, value V) {
|
||||
var cost int64
|
||||
if cv, ok := any(value).(string); ok {
|
||||
cost = int64(len(cv))
|
||||
} else {
|
||||
cost = int64(unsafe.Sizeof(value))
|
||||
}
|
||||
c.setWithCost(key, value, cost)
|
||||
}
|
||||
|
||||
func (c *RistrettoCachePartition[K, V]) Unset(key K) {
|
||||
bkey := fmt.Sprintf("%c%v", c.Prefix, key)
|
||||
if !c.Mutable {
|
||||
panic(fmt.Sprintf("invalid use of immutable cache tries to unset value of %v", key))
|
||||
}
|
||||
c.cache.Del(bkey)
|
||||
}
|
||||
|
||||
func (c *RistrettoCachePartition[K, V]) Get(key K) (value V, ok bool) {
|
||||
bkey := fmt.Sprintf("%c%v", c.Prefix, key)
|
||||
v, ok := c.cache.Get(bkey)
|
||||
if !ok || v == nil {
|
||||
var empty V
|
||||
return empty, false
|
||||
}
|
||||
value, ok = v.(V)
|
||||
return
|
||||
}
|
||||
|
||||
func lesserOf(a, b time.Duration) time.Duration {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
|
@ -170,20 +170,18 @@ func truncateAuthAndPrevEvents(auth, prev []gomatrixserverlib.EventReference) (
|
|||
|
||||
// RedactEvent redacts the given event and sets the unsigned field appropriately. This should be used by
|
||||
// downstream components to the roomserver when an OutputTypeRedactedEvent occurs.
|
||||
func RedactEvent(redactionEvent, redactedEvent *gomatrixserverlib.Event) (*gomatrixserverlib.Event, error) {
|
||||
func RedactEvent(redactionEvent, redactedEvent *gomatrixserverlib.Event) error {
|
||||
// sanity check
|
||||
if redactionEvent.Type() != gomatrixserverlib.MRoomRedaction {
|
||||
return nil, fmt.Errorf("RedactEvent: redactionEvent isn't a redaction event, is '%s'", redactionEvent.Type())
|
||||
return fmt.Errorf("RedactEvent: redactionEvent isn't a redaction event, is '%s'", redactionEvent.Type())
|
||||
}
|
||||
r := redactedEvent.Redact()
|
||||
err := r.SetUnsignedField("redacted_because", redactionEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
redactedEvent.Redact()
|
||||
if err := redactedEvent.SetUnsignedField("redacted_because", redactionEvent); err != nil {
|
||||
return err
|
||||
}
|
||||
// NOTSPEC: sytest relies on this unspecced field existing :(
|
||||
err = r.SetUnsignedField("redacted_by", redactionEvent.EventID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err := redactedEvent.SetUnsignedField("redacted_by", redactionEvent.EventID()); err != nil {
|
||||
return err
|
||||
}
|
||||
return r, nil
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ type RateLimits struct {
|
|||
enabled bool
|
||||
requestThreshold int64
|
||||
cooloffDuration time.Duration
|
||||
exemptUserIDs map[string]struct{}
|
||||
}
|
||||
|
||||
func NewRateLimits(cfg *config.RateLimiting) *RateLimits {
|
||||
|
|
@ -25,6 +27,10 @@ func NewRateLimits(cfg *config.RateLimiting) *RateLimits {
|
|||
enabled: cfg.Enabled,
|
||||
requestThreshold: cfg.Threshold,
|
||||
cooloffDuration: time.Duration(cfg.CooloffMS) * time.Millisecond,
|
||||
exemptUserIDs: map[string]struct{}{},
|
||||
}
|
||||
for _, userID := range cfg.ExemptUserIDs {
|
||||
l.exemptUserIDs[userID] = struct{}{}
|
||||
}
|
||||
if l.enabled {
|
||||
go l.clean()
|
||||
|
|
@ -52,7 +58,7 @@ func (l *RateLimits) clean() {
|
|||
}
|
||||
}
|
||||
|
||||
func (l *RateLimits) Limit(req *http.Request) *util.JSONResponse {
|
||||
func (l *RateLimits) Limit(req *http.Request, device *userapi.Device) *util.JSONResponse {
|
||||
// If rate limiting is disabled then do nothing.
|
||||
if !l.enabled {
|
||||
return nil
|
||||
|
|
@ -67,9 +73,26 @@ func (l *RateLimits) Limit(req *http.Request) *util.JSONResponse {
|
|||
|
||||
// First of all, work out if X-Forwarded-For was sent to us. If not
|
||||
// then we'll just use the IP address of the caller.
|
||||
caller := req.RemoteAddr
|
||||
if forwardedFor := req.Header.Get("X-Forwarded-For"); forwardedFor != "" {
|
||||
caller = forwardedFor
|
||||
var caller string
|
||||
if device != nil {
|
||||
switch device.AccountType {
|
||||
case userapi.AccountTypeAdmin:
|
||||
return nil // don't rate-limit server administrators
|
||||
case userapi.AccountTypeAppService:
|
||||
return nil // don't rate-limit appservice users
|
||||
default:
|
||||
if _, ok := l.exemptUserIDs[device.UserID]; ok {
|
||||
// If the user is exempt from rate limiting then do nothing.
|
||||
return nil
|
||||
}
|
||||
caller = device.UserID + device.ID
|
||||
}
|
||||
} else {
|
||||
if forwardedFor := req.Header.Get("X-Forwarded-For"); forwardedFor != "" {
|
||||
caller = forwardedFor
|
||||
} else {
|
||||
caller = req.RemoteAddr
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the caller's channel, if they have one.
|
||||
|
|
|
|||
|
|
@ -27,3 +27,18 @@ func MapsUnion(a map[string]interface{}, b map[string]interface{}) map[string]in
|
|||
|
||||
return c
|
||||
}
|
||||
|
||||
// Make a copy of the map
|
||||
func MapCopy(m map[string]interface{}) map[string]interface{} {
|
||||
cp := make(map[string]interface{})
|
||||
for k, v := range m {
|
||||
vm, ok := v.(map[string]interface{})
|
||||
if ok {
|
||||
cp[k] = MapCopy(vm)
|
||||
} else {
|
||||
cp[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return cp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ var build string
|
|||
const (
|
||||
VersionMajor = 0
|
||||
VersionMinor = 8
|
||||
VersionPatch = 7
|
||||
VersionPatch = 9
|
||||
VersionTag = "" // example: "rc1"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -62,8 +62,6 @@ type FederationKeyAPI interface {
|
|||
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
||||
QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse)
|
||||
QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse)
|
||||
// InputDeviceListUpdate from a federated server EDU
|
||||
InputDeviceListUpdate(ctx context.Context, req *InputDeviceListUpdateRequest, res *InputDeviceListUpdateResponse)
|
||||
PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse)
|
||||
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
|
||||
}
|
||||
|
|
@ -337,11 +335,3 @@ type QuerySignaturesResponse struct {
|
|||
// The request error, if any
|
||||
Error *KeyError
|
||||
}
|
||||
|
||||
type InputDeviceListUpdateRequest struct {
|
||||
Event gomatrixserverlib.DeviceListUpdateEvent
|
||||
}
|
||||
|
||||
type InputDeviceListUpdateResponse struct {
|
||||
Error *KeyError
|
||||
}
|
||||
|
|
|
|||
82
keyserver/consumers/devicelistupdate.go
Normal file
82
keyserver/consumers/devicelistupdate.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// 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 consumers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/matrix-org/dendrite/keyserver/internal"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DeviceListUpdateConsumer consumes device list updates that came in over federation.
|
||||
type DeviceListUpdateConsumer struct {
|
||||
ctx context.Context
|
||||
jetstream nats.JetStreamContext
|
||||
durable string
|
||||
topic string
|
||||
updater *internal.DeviceListUpdater
|
||||
}
|
||||
|
||||
// NewDeviceListUpdateConsumer creates a new DeviceListConsumer. Call Start() to begin consuming from key servers.
|
||||
func NewDeviceListUpdateConsumer(
|
||||
process *process.ProcessContext,
|
||||
cfg *config.KeyServer,
|
||||
js nats.JetStreamContext,
|
||||
updater *internal.DeviceListUpdater,
|
||||
) *DeviceListUpdateConsumer {
|
||||
return &DeviceListUpdateConsumer{
|
||||
ctx: process.Context(),
|
||||
jetstream: js,
|
||||
durable: cfg.Matrix.JetStream.Prefixed("KeyServerInputDeviceListConsumer"),
|
||||
topic: cfg.Matrix.JetStream.Prefixed(jetstream.InputDeviceListUpdate),
|
||||
updater: updater,
|
||||
}
|
||||
}
|
||||
|
||||
// Start consuming from key servers
|
||||
func (t *DeviceListUpdateConsumer) Start() error {
|
||||
return jetstream.JetStreamConsumer(
|
||||
t.ctx, t.jetstream, t.topic, t.durable, t.onMessage,
|
||||
nats.DeliverAll(), nats.ManualAck(),
|
||||
)
|
||||
}
|
||||
|
||||
// onMessage is called in response to a message received on the
|
||||
// key change events topic from the key server.
|
||||
func (t *DeviceListUpdateConsumer) onMessage(ctx context.Context, msg *nats.Msg) bool {
|
||||
var m gomatrixserverlib.DeviceListUpdateEvent
|
||||
if err := json.Unmarshal(msg.Data, &m); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to read from device list update input topic")
|
||||
return true
|
||||
}
|
||||
err := t.updater.Update(ctx, m)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"user_id": m.UserID,
|
||||
"device_id": m.DeviceID,
|
||||
"stream_id": m.StreamID,
|
||||
"prev_id": m.PrevID,
|
||||
}).WithError(err).Errorf("Failed to update device list")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -47,17 +47,6 @@ func (a *KeyInternalAPI) SetUserAPI(i userapi.KeyserverUserAPI) {
|
|||
a.UserAPI = i
|
||||
}
|
||||
|
||||
func (a *KeyInternalAPI) InputDeviceListUpdate(
|
||||
ctx context.Context, req *api.InputDeviceListUpdateRequest, res *api.InputDeviceListUpdateResponse,
|
||||
) {
|
||||
err := a.Updater.Update(ctx, req.Event)
|
||||
if err != nil {
|
||||
res.Error = &api.KeyError{
|
||||
Err: fmt.Sprintf("failed to update device list: %s", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *KeyInternalAPI) QueryKeyChanges(ctx context.Context, req *api.QueryKeyChangesRequest, res *api.QueryKeyChangesResponse) {
|
||||
userIDs, latest, err := a.DB.KeyChanges(ctx, req.Offset, req.ToOffset)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -63,20 +63,6 @@ type httpKeyInternalAPI struct {
|
|||
func (h *httpKeyInternalAPI) SetUserAPI(i userapi.KeyserverUserAPI) {
|
||||
// no-op: doesn't need it
|
||||
}
|
||||
func (h *httpKeyInternalAPI) InputDeviceListUpdate(
|
||||
ctx context.Context, req *api.InputDeviceListUpdateRequest, res *api.InputDeviceListUpdateResponse,
|
||||
) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "InputDeviceListUpdate")
|
||||
defer span.Finish()
|
||||
|
||||
apiURL := h.apiURL + InputDeviceListUpdatePath
|
||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, req, res)
|
||||
if err != nil {
|
||||
res.Error = &api.KeyError{
|
||||
Err: err.Error(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *httpKeyInternalAPI) PerformClaimKeys(
|
||||
ctx context.Context,
|
||||
|
|
|
|||
|
|
@ -25,17 +25,6 @@ import (
|
|||
)
|
||||
|
||||
func AddRoutes(internalAPIMux *mux.Router, s api.KeyInternalAPI) {
|
||||
internalAPIMux.Handle(InputDeviceListUpdatePath,
|
||||
httputil.MakeInternalAPI("inputDeviceListUpdate", func(req *http.Request) util.JSONResponse {
|
||||
request := api.InputDeviceListUpdateRequest{}
|
||||
response := api.InputDeviceListUpdateResponse{}
|
||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
s.InputDeviceListUpdate(req.Context(), &request, &response)
|
||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||
}),
|
||||
)
|
||||
internalAPIMux.Handle(PerformClaimKeysPath,
|
||||
httputil.MakeInternalAPI("performClaimKeys", func(req *http.Request) util.JSONResponse {
|
||||
request := api.PerformClaimKeysRequest{}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
fedsenderapi "github.com/matrix-org/dendrite/federationapi/api"
|
||||
"github.com/matrix-org/dendrite/keyserver/api"
|
||||
"github.com/matrix-org/dendrite/keyserver/consumers"
|
||||
"github.com/matrix-org/dendrite/keyserver/internal"
|
||||
"github.com/matrix-org/dendrite/keyserver/inthttp"
|
||||
"github.com/matrix-org/dendrite/keyserver/producers"
|
||||
|
|
@ -59,10 +60,17 @@ func NewInternalAPI(
|
|||
updater := internal.NewDeviceListUpdater(db, ap, keyChangeProducer, fedClient, 8) // 8 workers TODO: configurable
|
||||
ap.Updater = updater
|
||||
go func() {
|
||||
if err := updater.Start(); err != nil {
|
||||
if err = updater.Start(); err != nil {
|
||||
logrus.WithError(err).Panicf("failed to start device list updater")
|
||||
}
|
||||
}()
|
||||
|
||||
dlConsumer := consumers.NewDeviceListUpdateConsumer(
|
||||
base.ProcessContext, cfg, js, updater,
|
||||
)
|
||||
if err = dlConsumer.Start(); err != nil {
|
||||
logrus.WithError(err).Panic("failed to start device list consumer")
|
||||
}
|
||||
|
||||
return ap
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,26 @@
|
|||
package storage
|
||||
package storage_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/keyserver/api"
|
||||
"github.com/matrix-org/dendrite/keyserver/storage"
|
||||
"github.com/matrix-org/dendrite/keyserver/types"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/dendrite/test/testrig"
|
||||
)
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
func MustCreateDatabase(t *testing.T) (Database, func()) {
|
||||
tmpfile, err := ioutil.TempFile("", "keyserver_storage_test")
|
||||
func MustCreateDatabase(t *testing.T, dbType test.DBType) (storage.Database, func()) {
|
||||
base, close := testrig.CreateBaseDendrite(t, dbType)
|
||||
db, err := storage.NewDatabase(base, &base.Cfg.KeyServer.Database)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
t.Logf("Database %s", tmpfile.Name())
|
||||
db, err := NewDatabase(nil, &config.DatabaseOptions{
|
||||
ConnectionString: config.DataSource(fmt.Sprintf("file://%s", tmpfile.Name())),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to NewDatabase: %s", err)
|
||||
}
|
||||
return db, func() {
|
||||
os.Remove(tmpfile.Name())
|
||||
t.Fatalf("failed to create new database: %v", err)
|
||||
}
|
||||
return db, close
|
||||
}
|
||||
|
||||
func MustNotError(t *testing.T, err error) {
|
||||
|
|
@ -42,151 +32,159 @@ func MustNotError(t *testing.T, err error) {
|
|||
}
|
||||
|
||||
func TestKeyChanges(t *testing.T) {
|
||||
db, clean := MustCreateDatabase(t)
|
||||
defer clean()
|
||||
_, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
deviceChangeIDB, err := db.StoreKeyChange(ctx, "@bob:localhost")
|
||||
MustNotError(t, err)
|
||||
deviceChangeIDC, err := db.StoreKeyChange(ctx, "@charlie:localhost")
|
||||
MustNotError(t, err)
|
||||
userIDs, latest, err := db.KeyChanges(ctx, deviceChangeIDB, types.OffsetNewest)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to KeyChanges: %s", err)
|
||||
}
|
||||
if latest != deviceChangeIDC {
|
||||
t.Fatalf("KeyChanges: got latest=%d want %d", latest, deviceChangeIDC)
|
||||
}
|
||||
if !reflect.DeepEqual(userIDs, []string{"@charlie:localhost"}) {
|
||||
t.Fatalf("KeyChanges: wrong user_ids: %v", userIDs)
|
||||
}
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, clean := MustCreateDatabase(t, dbType)
|
||||
defer clean()
|
||||
_, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
deviceChangeIDB, err := db.StoreKeyChange(ctx, "@bob:localhost")
|
||||
MustNotError(t, err)
|
||||
deviceChangeIDC, err := db.StoreKeyChange(ctx, "@charlie:localhost")
|
||||
MustNotError(t, err)
|
||||
userIDs, latest, err := db.KeyChanges(ctx, deviceChangeIDB, types.OffsetNewest)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to KeyChanges: %s", err)
|
||||
}
|
||||
if latest != deviceChangeIDC {
|
||||
t.Fatalf("KeyChanges: got latest=%d want %d", latest, deviceChangeIDC)
|
||||
}
|
||||
if !reflect.DeepEqual(userIDs, []string{"@charlie:localhost"}) {
|
||||
t.Fatalf("KeyChanges: wrong user_ids: %v", userIDs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestKeyChangesNoDupes(t *testing.T) {
|
||||
db, clean := MustCreateDatabase(t)
|
||||
defer clean()
|
||||
deviceChangeIDA, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
deviceChangeIDB, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
if deviceChangeIDA == deviceChangeIDB {
|
||||
t.Fatalf("Expected change ID to be different even when inserting key change for the same user, got %d for both changes", deviceChangeIDA)
|
||||
}
|
||||
deviceChangeID, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
userIDs, latest, err := db.KeyChanges(ctx, 0, types.OffsetNewest)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to KeyChanges: %s", err)
|
||||
}
|
||||
if latest != deviceChangeID {
|
||||
t.Fatalf("KeyChanges: got latest=%d want %d", latest, deviceChangeID)
|
||||
}
|
||||
if !reflect.DeepEqual(userIDs, []string{"@alice:localhost"}) {
|
||||
t.Fatalf("KeyChanges: wrong user_ids: %v", userIDs)
|
||||
}
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, clean := MustCreateDatabase(t, dbType)
|
||||
defer clean()
|
||||
deviceChangeIDA, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
deviceChangeIDB, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
if deviceChangeIDA == deviceChangeIDB {
|
||||
t.Fatalf("Expected change ID to be different even when inserting key change for the same user, got %d for both changes", deviceChangeIDA)
|
||||
}
|
||||
deviceChangeID, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
userIDs, latest, err := db.KeyChanges(ctx, 0, types.OffsetNewest)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to KeyChanges: %s", err)
|
||||
}
|
||||
if latest != deviceChangeID {
|
||||
t.Fatalf("KeyChanges: got latest=%d want %d", latest, deviceChangeID)
|
||||
}
|
||||
if !reflect.DeepEqual(userIDs, []string{"@alice:localhost"}) {
|
||||
t.Fatalf("KeyChanges: wrong user_ids: %v", userIDs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestKeyChangesUpperLimit(t *testing.T) {
|
||||
db, clean := MustCreateDatabase(t)
|
||||
defer clean()
|
||||
deviceChangeIDA, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
deviceChangeIDB, err := db.StoreKeyChange(ctx, "@bob:localhost")
|
||||
MustNotError(t, err)
|
||||
_, err = db.StoreKeyChange(ctx, "@charlie:localhost")
|
||||
MustNotError(t, err)
|
||||
userIDs, latest, err := db.KeyChanges(ctx, deviceChangeIDA, deviceChangeIDB)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to KeyChanges: %s", err)
|
||||
}
|
||||
if latest != deviceChangeIDB {
|
||||
t.Fatalf("KeyChanges: got latest=%d want %d", latest, deviceChangeIDB)
|
||||
}
|
||||
if !reflect.DeepEqual(userIDs, []string{"@bob:localhost"}) {
|
||||
t.Fatalf("KeyChanges: wrong user_ids: %v", userIDs)
|
||||
}
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, clean := MustCreateDatabase(t, dbType)
|
||||
defer clean()
|
||||
deviceChangeIDA, err := db.StoreKeyChange(ctx, "@alice:localhost")
|
||||
MustNotError(t, err)
|
||||
deviceChangeIDB, err := db.StoreKeyChange(ctx, "@bob:localhost")
|
||||
MustNotError(t, err)
|
||||
_, err = db.StoreKeyChange(ctx, "@charlie:localhost")
|
||||
MustNotError(t, err)
|
||||
userIDs, latest, err := db.KeyChanges(ctx, deviceChangeIDA, deviceChangeIDB)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to KeyChanges: %s", err)
|
||||
}
|
||||
if latest != deviceChangeIDB {
|
||||
t.Fatalf("KeyChanges: got latest=%d want %d", latest, deviceChangeIDB)
|
||||
}
|
||||
if !reflect.DeepEqual(userIDs, []string{"@bob:localhost"}) {
|
||||
t.Fatalf("KeyChanges: wrong user_ids: %v", userIDs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// The purpose of this test is to make sure that the storage layer is generating sequential stream IDs per user,
|
||||
// and that they are returned correctly when querying for device keys.
|
||||
func TestDeviceKeysStreamIDGeneration(t *testing.T) {
|
||||
var err error
|
||||
db, clean := MustCreateDatabase(t)
|
||||
defer clean()
|
||||
alice := "@alice:TestDeviceKeysStreamIDGeneration"
|
||||
bob := "@bob:TestDeviceKeysStreamIDGeneration"
|
||||
msgs := []api.DeviceMessage{
|
||||
{
|
||||
Type: api.TypeDeviceKeyUpdate,
|
||||
DeviceKeys: &api.DeviceKeys{
|
||||
DeviceID: "AAA",
|
||||
UserID: alice,
|
||||
KeyJSON: []byte(`{"key":"v1"}`),
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, clean := MustCreateDatabase(t, dbType)
|
||||
defer clean()
|
||||
alice := "@alice:TestDeviceKeysStreamIDGeneration"
|
||||
bob := "@bob:TestDeviceKeysStreamIDGeneration"
|
||||
msgs := []api.DeviceMessage{
|
||||
{
|
||||
Type: api.TypeDeviceKeyUpdate,
|
||||
DeviceKeys: &api.DeviceKeys{
|
||||
DeviceID: "AAA",
|
||||
UserID: alice,
|
||||
KeyJSON: []byte(`{"key":"v1"}`),
|
||||
},
|
||||
// StreamID: 1
|
||||
},
|
||||
// StreamID: 1
|
||||
},
|
||||
{
|
||||
Type: api.TypeDeviceKeyUpdate,
|
||||
DeviceKeys: &api.DeviceKeys{
|
||||
DeviceID: "AAA",
|
||||
UserID: bob,
|
||||
KeyJSON: []byte(`{"key":"v1"}`),
|
||||
{
|
||||
Type: api.TypeDeviceKeyUpdate,
|
||||
DeviceKeys: &api.DeviceKeys{
|
||||
DeviceID: "AAA",
|
||||
UserID: bob,
|
||||
KeyJSON: []byte(`{"key":"v1"}`),
|
||||
},
|
||||
// StreamID: 1 as this is a different user
|
||||
},
|
||||
// StreamID: 1 as this is a different user
|
||||
},
|
||||
{
|
||||
Type: api.TypeDeviceKeyUpdate,
|
||||
DeviceKeys: &api.DeviceKeys{
|
||||
DeviceID: "another_device",
|
||||
UserID: alice,
|
||||
KeyJSON: []byte(`{"key":"v1"}`),
|
||||
{
|
||||
Type: api.TypeDeviceKeyUpdate,
|
||||
DeviceKeys: &api.DeviceKeys{
|
||||
DeviceID: "another_device",
|
||||
UserID: alice,
|
||||
KeyJSON: []byte(`{"key":"v1"}`),
|
||||
},
|
||||
// StreamID: 2 as this is a 2nd device key
|
||||
},
|
||||
// StreamID: 2 as this is a 2nd device key
|
||||
},
|
||||
}
|
||||
MustNotError(t, db.StoreLocalDeviceKeys(ctx, msgs))
|
||||
if msgs[0].StreamID != 1 {
|
||||
t.Fatalf("Expected StoreLocalDeviceKeys to set StreamID=1 but got %d", msgs[0].StreamID)
|
||||
}
|
||||
if msgs[1].StreamID != 1 {
|
||||
t.Fatalf("Expected StoreLocalDeviceKeys to set StreamID=1 (different user) but got %d", msgs[1].StreamID)
|
||||
}
|
||||
if msgs[2].StreamID != 2 {
|
||||
t.Fatalf("Expected StoreLocalDeviceKeys to set StreamID=2 (another device) but got %d", msgs[2].StreamID)
|
||||
}
|
||||
|
||||
// updating a device sets the next stream ID for that user
|
||||
msgs = []api.DeviceMessage{
|
||||
{
|
||||
Type: api.TypeDeviceKeyUpdate,
|
||||
DeviceKeys: &api.DeviceKeys{
|
||||
DeviceID: "AAA",
|
||||
UserID: alice,
|
||||
KeyJSON: []byte(`{"key":"v2"}`),
|
||||
},
|
||||
// StreamID: 3
|
||||
},
|
||||
}
|
||||
MustNotError(t, db.StoreLocalDeviceKeys(ctx, msgs))
|
||||
if msgs[0].StreamID != 3 {
|
||||
t.Fatalf("Expected StoreLocalDeviceKeys to set StreamID=3 (new key same device) but got %d", msgs[0].StreamID)
|
||||
}
|
||||
|
||||
// Querying for device keys returns the latest stream IDs
|
||||
msgs, err = db.DeviceKeysForUser(ctx, alice, []string{"AAA", "another_device"}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("DeviceKeysForUser returned error: %s", err)
|
||||
}
|
||||
wantStreamIDs := map[string]int64{
|
||||
"AAA": 3,
|
||||
"another_device": 2,
|
||||
}
|
||||
if len(msgs) != len(wantStreamIDs) {
|
||||
t.Fatalf("DeviceKeysForUser: wrong number of devices, got %d want %d", len(msgs), len(wantStreamIDs))
|
||||
}
|
||||
for _, m := range msgs {
|
||||
if m.StreamID != wantStreamIDs[m.DeviceID] {
|
||||
t.Errorf("DeviceKeysForUser: wrong returned stream ID for key, got %d want %d", m.StreamID, wantStreamIDs[m.DeviceID])
|
||||
}
|
||||
}
|
||||
MustNotError(t, db.StoreLocalDeviceKeys(ctx, msgs))
|
||||
if msgs[0].StreamID != 1 {
|
||||
t.Fatalf("Expected StoreLocalDeviceKeys to set StreamID=1 but got %d", msgs[0].StreamID)
|
||||
}
|
||||
if msgs[1].StreamID != 1 {
|
||||
t.Fatalf("Expected StoreLocalDeviceKeys to set StreamID=1 (different user) but got %d", msgs[1].StreamID)
|
||||
}
|
||||
if msgs[2].StreamID != 2 {
|
||||
t.Fatalf("Expected StoreLocalDeviceKeys to set StreamID=2 (another device) but got %d", msgs[2].StreamID)
|
||||
}
|
||||
|
||||
// updating a device sets the next stream ID for that user
|
||||
msgs = []api.DeviceMessage{
|
||||
{
|
||||
Type: api.TypeDeviceKeyUpdate,
|
||||
DeviceKeys: &api.DeviceKeys{
|
||||
DeviceID: "AAA",
|
||||
UserID: alice,
|
||||
KeyJSON: []byte(`{"key":"v2"}`),
|
||||
},
|
||||
// StreamID: 3
|
||||
},
|
||||
}
|
||||
MustNotError(t, db.StoreLocalDeviceKeys(ctx, msgs))
|
||||
if msgs[0].StreamID != 3 {
|
||||
t.Fatalf("Expected StoreLocalDeviceKeys to set StreamID=3 (new key same device) but got %d", msgs[0].StreamID)
|
||||
}
|
||||
|
||||
// Querying for device keys returns the latest stream IDs
|
||||
msgs, err = db.DeviceKeysForUser(ctx, alice, []string{"AAA", "another_device"}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("DeviceKeysForUser returned error: %s", err)
|
||||
}
|
||||
wantStreamIDs := map[string]int64{
|
||||
"AAA": 3,
|
||||
"another_device": 2,
|
||||
}
|
||||
if len(msgs) != len(wantStreamIDs) {
|
||||
t.Fatalf("DeviceKeysForUser: wrong number of devices, got %d want %d", len(msgs), len(wantStreamIDs))
|
||||
}
|
||||
for _, m := range msgs {
|
||||
if m.StreamID != wantStreamIDs[m.DeviceID] {
|
||||
t.Errorf("DeviceKeysForUser: wrong returned stream ID for key, got %d want %d", m.StreamID, wantStreamIDs[m.DeviceID])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func Setup(
|
|||
uploadHandler := httputil.MakeAuthAPI(
|
||||
"upload", userAPI,
|
||||
func(req *http.Request, dev *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, dev); r != nil {
|
||||
return *r
|
||||
}
|
||||
return Upload(req, cfg, dev, db, activeThumbnailGeneration)
|
||||
|
|
@ -70,7 +70,7 @@ func Setup(
|
|||
)
|
||||
|
||||
configHandler := httputil.MakeAuthAPI("config", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
respondSize := &cfg.MaxFileSizeBytes
|
||||
|
|
@ -126,7 +126,7 @@ func makeDownloadAPI(
|
|||
// Ratelimit requests
|
||||
// NOTSPEC: The spec says everything at /media/ should be rate limited, but this causes issues with thumbnails (#2243)
|
||||
if name != "thumbnail" {
|
||||
if r := rateLimits.Limit(req); r != nil {
|
||||
if r := rateLimits.Limit(req, nil); r != nil {
|
||||
if err := json.NewEncoder(w).Encode(r); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -140,11 +140,8 @@ type ClientRoomserverAPI interface {
|
|||
|
||||
// PerformRoomUpgrade upgrades a room to a newer version
|
||||
PerformRoomUpgrade(ctx context.Context, req *PerformRoomUpgradeRequest, resp *PerformRoomUpgradeResponse)
|
||||
PerformAdminEvacuateRoom(
|
||||
ctx context.Context,
|
||||
req *PerformAdminEvacuateRoomRequest,
|
||||
res *PerformAdminEvacuateRoomResponse,
|
||||
)
|
||||
PerformAdminEvacuateRoom(ctx context.Context, req *PerformAdminEvacuateRoomRequest, res *PerformAdminEvacuateRoomResponse)
|
||||
PerformAdminEvacuateUser(ctx context.Context, req *PerformAdminEvacuateUserRequest, res *PerformAdminEvacuateUserResponse)
|
||||
PerformPeek(ctx context.Context, req *PerformPeekRequest, res *PerformPeekResponse)
|
||||
PerformUnpeek(ctx context.Context, req *PerformUnpeekRequest, res *PerformUnpeekResponse)
|
||||
PerformInvite(ctx context.Context, req *PerformInviteRequest, res *PerformInviteResponse) error
|
||||
|
|
@ -161,6 +158,7 @@ type UserRoomserverAPI interface {
|
|||
QueryLatestEventsAndStateAPI
|
||||
QueryCurrentState(ctx context.Context, req *QueryCurrentStateRequest, res *QueryCurrentStateResponse) error
|
||||
QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error
|
||||
PerformAdminEvacuateUser(ctx context.Context, req *PerformAdminEvacuateUserRequest, res *PerformAdminEvacuateUserResponse)
|
||||
}
|
||||
|
||||
type FederationRoomserverAPI interface {
|
||||
|
|
|
|||
|
|
@ -113,6 +113,15 @@ func (t *RoomserverInternalAPITrace) PerformAdminEvacuateRoom(
|
|||
util.GetLogger(ctx).Infof("PerformAdminEvacuateRoom req=%+v res=%+v", js(req), js(res))
|
||||
}
|
||||
|
||||
func (t *RoomserverInternalAPITrace) PerformAdminEvacuateUser(
|
||||
ctx context.Context,
|
||||
req *PerformAdminEvacuateUserRequest,
|
||||
res *PerformAdminEvacuateUserResponse,
|
||||
) {
|
||||
t.Impl.PerformAdminEvacuateUser(ctx, req, res)
|
||||
util.GetLogger(ctx).Infof("PerformAdminEvacuateUser req=%+v res=%+v", js(req), js(res))
|
||||
}
|
||||
|
||||
func (t *RoomserverInternalAPITrace) PerformInboundPeek(
|
||||
ctx context.Context,
|
||||
req *PerformInboundPeekRequest,
|
||||
|
|
|
|||
|
|
@ -161,6 +161,8 @@ type OutputNewRoomEvent struct {
|
|||
// The transaction ID of the send request if sent by a local user and one
|
||||
// was specified
|
||||
TransactionID *TransactionID `json:"transaction_id,omitempty"`
|
||||
// The history visibility of the event.
|
||||
HistoryVisibility gomatrixserverlib.HistoryVisibility `json:"history_visibility"`
|
||||
}
|
||||
|
||||
func (o *OutputNewRoomEvent) NeededStateEventIDs() ([]*gomatrixserverlib.HeaderedEvent, []string) {
|
||||
|
|
@ -187,7 +189,8 @@ func (o *OutputNewRoomEvent) NeededStateEventIDs() ([]*gomatrixserverlib.Headere
|
|||
// should build their current room state up from OutputNewRoomEvents only.
|
||||
type OutputOldRoomEvent struct {
|
||||
// The Event.
|
||||
Event *gomatrixserverlib.HeaderedEvent `json:"event"`
|
||||
Event *gomatrixserverlib.HeaderedEvent `json:"event"`
|
||||
HistoryVisibility gomatrixserverlib.HistoryVisibility `json:"history_visibility"`
|
||||
}
|
||||
|
||||
// An OutputNewInviteEvent is written whenever an invite becomes active.
|
||||
|
|
|
|||
|
|
@ -223,3 +223,12 @@ type PerformAdminEvacuateRoomResponse struct {
|
|||
Affected []string `json:"affected"`
|
||||
Error *PerformError
|
||||
}
|
||||
|
||||
type PerformAdminEvacuateUserRequest struct {
|
||||
UserID string `json:"user_id"`
|
||||
}
|
||||
|
||||
type PerformAdminEvacuateUserResponse struct {
|
||||
Affected []string `json:"affected"`
|
||||
Error *PerformError
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,11 +216,10 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias(
|
|||
return err
|
||||
}
|
||||
|
||||
err = api.SendEvents(ctx, r.RSAPI, api.KindNew, []*gomatrixserverlib.HeaderedEvent{newEvent}, r.ServerName, r.ServerName, nil, false)
|
||||
err = api.SendEvents(ctx, r, api.KindNew, []*gomatrixserverlib.HeaderedEvent{newEvent}, r.ServerName, r.ServerName, nil, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,11 @@ import (
|
|||
"github.com/matrix-org/dendrite/roomserver/internal/input"
|
||||
"github.com/matrix-org/dendrite/roomserver/internal/perform"
|
||||
"github.com/matrix-org/dendrite/roomserver/internal/query"
|
||||
"github.com/matrix-org/dendrite/roomserver/producers"
|
||||
"github.com/matrix-org/dendrite/roomserver/storage"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
|
@ -37,6 +40,7 @@ type RoomserverInternalAPI struct {
|
|||
*perform.Upgrader
|
||||
*perform.Admin
|
||||
ProcessContext *process.ProcessContext
|
||||
Base *base.BaseDendrite
|
||||
DB storage.Database
|
||||
Cfg *config.RoomServer
|
||||
Cache caching.RoomServerCaches
|
||||
|
|
@ -49,34 +53,43 @@ type RoomserverInternalAPI struct {
|
|||
JetStream nats.JetStreamContext
|
||||
Durable string
|
||||
InputRoomEventTopic string // JetStream topic for new input room events
|
||||
OutputRoomEventTopic string // JetStream topic for new output room events
|
||||
OutputProducer *producers.RoomEventProducer
|
||||
PerspectiveServerNames []gomatrixserverlib.ServerName
|
||||
}
|
||||
|
||||
func NewRoomserverAPI(
|
||||
processCtx *process.ProcessContext, cfg *config.RoomServer, roomserverDB storage.Database,
|
||||
consumer nats.JetStreamContext, nc *nats.Conn,
|
||||
inputRoomEventTopic, outputRoomEventTopic string,
|
||||
caches caching.RoomServerCaches, perspectiveServerNames []gomatrixserverlib.ServerName,
|
||||
base *base.BaseDendrite, roomserverDB storage.Database,
|
||||
js nats.JetStreamContext, nc *nats.Conn,
|
||||
) *RoomserverInternalAPI {
|
||||
var perspectiveServerNames []gomatrixserverlib.ServerName
|
||||
for _, kp := range base.Cfg.FederationAPI.KeyPerspectives {
|
||||
perspectiveServerNames = append(perspectiveServerNames, kp.ServerName)
|
||||
}
|
||||
|
||||
serverACLs := acls.NewServerACLs(roomserverDB)
|
||||
producer := &producers.RoomEventProducer{
|
||||
Topic: string(base.Cfg.Global.JetStream.Prefixed(jetstream.OutputRoomEvent)),
|
||||
JetStream: js,
|
||||
ACLs: serverACLs,
|
||||
}
|
||||
a := &RoomserverInternalAPI{
|
||||
ProcessContext: processCtx,
|
||||
ProcessContext: base.ProcessContext,
|
||||
DB: roomserverDB,
|
||||
Cfg: cfg,
|
||||
Cache: caches,
|
||||
ServerName: cfg.Matrix.ServerName,
|
||||
Base: base,
|
||||
Cfg: &base.Cfg.RoomServer,
|
||||
Cache: base.Caches,
|
||||
ServerName: base.Cfg.Global.ServerName,
|
||||
PerspectiveServerNames: perspectiveServerNames,
|
||||
InputRoomEventTopic: inputRoomEventTopic,
|
||||
OutputRoomEventTopic: outputRoomEventTopic,
|
||||
JetStream: consumer,
|
||||
InputRoomEventTopic: base.Cfg.Global.JetStream.Prefixed(jetstream.InputRoomEvent),
|
||||
OutputProducer: producer,
|
||||
JetStream: js,
|
||||
NATSClient: nc,
|
||||
Durable: cfg.Matrix.JetStream.Durable("RoomserverInputConsumer"),
|
||||
Durable: base.Cfg.Global.JetStream.Durable("RoomserverInputConsumer"),
|
||||
ServerACLs: serverACLs,
|
||||
Queryer: &query.Queryer{
|
||||
DB: roomserverDB,
|
||||
Cache: caches,
|
||||
ServerName: cfg.Matrix.ServerName,
|
||||
Cache: base.Caches,
|
||||
ServerName: base.Cfg.Global.ServerName,
|
||||
ServerACLs: serverACLs,
|
||||
},
|
||||
// perform-er structs get initialised when we have a federation sender to use
|
||||
|
|
@ -92,19 +105,20 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederatio
|
|||
r.KeyRing = keyRing
|
||||
|
||||
r.Inputer = &input.Inputer{
|
||||
Cfg: r.Cfg,
|
||||
ProcessContext: r.ProcessContext,
|
||||
DB: r.DB,
|
||||
InputRoomEventTopic: r.InputRoomEventTopic,
|
||||
OutputRoomEventTopic: r.OutputRoomEventTopic,
|
||||
JetStream: r.JetStream,
|
||||
NATSClient: r.NATSClient,
|
||||
Durable: nats.Durable(r.Durable),
|
||||
ServerName: r.Cfg.Matrix.ServerName,
|
||||
FSAPI: fsAPI,
|
||||
KeyRing: keyRing,
|
||||
ACLs: r.ServerACLs,
|
||||
Queryer: r.Queryer,
|
||||
Cfg: &r.Base.Cfg.RoomServer,
|
||||
Base: r.Base,
|
||||
ProcessContext: r.Base.ProcessContext,
|
||||
DB: r.DB,
|
||||
InputRoomEventTopic: r.InputRoomEventTopic,
|
||||
OutputProducer: r.OutputProducer,
|
||||
JetStream: r.JetStream,
|
||||
NATSClient: r.NATSClient,
|
||||
Durable: nats.Durable(r.Durable),
|
||||
ServerName: r.Cfg.Matrix.ServerName,
|
||||
FSAPI: fsAPI,
|
||||
KeyRing: keyRing,
|
||||
ACLs: r.ServerACLs,
|
||||
Queryer: r.Queryer,
|
||||
}
|
||||
r.Inviter = &perform.Inviter{
|
||||
DB: r.DB,
|
||||
|
|
@ -170,6 +184,7 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederatio
|
|||
Cfg: r.Cfg,
|
||||
Inputer: r.Inputer,
|
||||
Queryer: r.Queryer,
|
||||
Leaver: r.Leaver,
|
||||
}
|
||||
|
||||
if err := r.Inputer.Start(); err != nil {
|
||||
|
|
@ -198,7 +213,7 @@ func (r *RoomserverInternalAPI) PerformInvite(
|
|||
if len(outputEvents) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.WriteOutputEvents(req.Event.RoomID(), outputEvents)
|
||||
return r.OutputProducer.ProduceRoomEvents(req.Event.RoomID(), outputEvents)
|
||||
}
|
||||
|
||||
func (r *RoomserverInternalAPI) PerformLeave(
|
||||
|
|
@ -214,7 +229,7 @@ func (r *RoomserverInternalAPI) PerformLeave(
|
|||
if len(outputEvents) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.WriteOutputEvents(req.RoomID, outputEvents)
|
||||
return r.OutputProducer.ProduceRoomEvents(req.RoomID, outputEvents)
|
||||
}
|
||||
|
||||
func (r *RoomserverInternalAPI) PerformForget(
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ import (
|
|||
"github.com/matrix-org/dendrite/roomserver/acls"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver/internal/query"
|
||||
"github.com/matrix-org/dendrite/roomserver/producers"
|
||||
"github.com/matrix-org/dendrite/roomserver/storage"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
|
|
@ -37,16 +39,8 @@ import (
|
|||
"github.com/nats-io/nats.go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/sirupsen/logrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
var keyContentFields = map[string]string{
|
||||
"m.room.join_rules": "join_rule",
|
||||
"m.room.history_visibility": "history_visibility",
|
||||
"m.room.member": "membership",
|
||||
}
|
||||
|
||||
// Inputer is responsible for consuming from the roomserver input
|
||||
// streams and processing the events. All input events are queued
|
||||
// into a single NATS stream and the order is preserved strictly.
|
||||
|
|
@ -75,19 +69,20 @@ var keyContentFields = map[string]string{
|
|||
// up, so they will do nothing until a new event comes in for B
|
||||
// or C.
|
||||
type Inputer struct {
|
||||
Cfg *config.RoomServer
|
||||
ProcessContext *process.ProcessContext
|
||||
DB storage.Database
|
||||
NATSClient *nats.Conn
|
||||
JetStream nats.JetStreamContext
|
||||
Durable nats.SubOpt
|
||||
ServerName gomatrixserverlib.ServerName
|
||||
FSAPI fedapi.RoomserverFederationAPI
|
||||
KeyRing gomatrixserverlib.JSONVerifier
|
||||
ACLs *acls.ServerACLs
|
||||
InputRoomEventTopic string
|
||||
OutputRoomEventTopic string
|
||||
workers sync.Map // room ID -> *worker
|
||||
Cfg *config.RoomServer
|
||||
Base *base.BaseDendrite
|
||||
ProcessContext *process.ProcessContext
|
||||
DB storage.Database
|
||||
NATSClient *nats.Conn
|
||||
JetStream nats.JetStreamContext
|
||||
Durable nats.SubOpt
|
||||
ServerName gomatrixserverlib.ServerName
|
||||
FSAPI fedapi.RoomserverFederationAPI
|
||||
KeyRing gomatrixserverlib.JSONVerifier
|
||||
ACLs *acls.ServerACLs
|
||||
InputRoomEventTopic string
|
||||
OutputProducer *producers.RoomEventProducer
|
||||
workers sync.Map // room ID -> *worker
|
||||
|
||||
Queryer *query.Queryer
|
||||
}
|
||||
|
|
@ -167,7 +162,9 @@ func (r *Inputer) startWorkerForRoom(roomID string) {
|
|||
// will look to see if we have a worker for that room which has its
|
||||
// own consumer. If we don't, we'll start one.
|
||||
func (r *Inputer) Start() error {
|
||||
prometheus.MustRegister(roomserverInputBackpressure, processRoomEventDuration)
|
||||
if r.Base.EnableMetrics {
|
||||
prometheus.MustRegister(roomserverInputBackpressure, processRoomEventDuration)
|
||||
}
|
||||
_, err := r.JetStream.Subscribe(
|
||||
"", // This is blank because we specified it in BindStream.
|
||||
func(m *nats.Msg) {
|
||||
|
|
@ -370,58 +367,6 @@ func (r *Inputer) InputRoomEvents(
|
|||
}
|
||||
}
|
||||
|
||||
// WriteOutputEvents implements OutputRoomEventWriter
|
||||
func (r *Inputer) WriteOutputEvents(roomID string, updates []api.OutputEvent) error {
|
||||
var err error
|
||||
for _, update := range updates {
|
||||
msg := &nats.Msg{
|
||||
Subject: r.OutputRoomEventTopic,
|
||||
Header: nats.Header{},
|
||||
}
|
||||
msg.Header.Set(jetstream.RoomID, roomID)
|
||||
msg.Data, err = json.Marshal(update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger := log.WithFields(log.Fields{
|
||||
"room_id": roomID,
|
||||
"type": update.Type,
|
||||
})
|
||||
if update.NewRoomEvent != nil {
|
||||
eventType := update.NewRoomEvent.Event.Type()
|
||||
logger = logger.WithFields(log.Fields{
|
||||
"event_type": eventType,
|
||||
"event_id": update.NewRoomEvent.Event.EventID(),
|
||||
"adds_state": len(update.NewRoomEvent.AddsStateEventIDs),
|
||||
"removes_state": len(update.NewRoomEvent.RemovesStateEventIDs),
|
||||
"send_as_server": update.NewRoomEvent.SendAsServer,
|
||||
"sender": update.NewRoomEvent.Event.Sender(),
|
||||
})
|
||||
if update.NewRoomEvent.Event.StateKey() != nil {
|
||||
logger = logger.WithField("state_key", *update.NewRoomEvent.Event.StateKey())
|
||||
}
|
||||
contentKey := keyContentFields[eventType]
|
||||
if contentKey != "" {
|
||||
value := gjson.GetBytes(update.NewRoomEvent.Event.Content(), contentKey)
|
||||
if value.Exists() {
|
||||
logger = logger.WithField("content_value", value.String())
|
||||
}
|
||||
}
|
||||
|
||||
if eventType == "m.room.server_acl" && update.NewRoomEvent.Event.StateKeyEquals("") {
|
||||
ev := update.NewRoomEvent.Event.Unwrap()
|
||||
defer r.ACLs.OnServerACLUpdate(ev)
|
||||
}
|
||||
}
|
||||
logger.Tracef("Producing to topic '%s'", r.OutputRoomEventTopic)
|
||||
if _, err := r.JetStream.PublishMsg(msg); err != nil {
|
||||
logger.WithError(err).Errorf("Failed to produce to topic '%s': %s", r.OutputRoomEventTopic, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var roomserverInputBackpressure = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "dendrite",
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
@ -75,6 +76,11 @@ func (r *Inputer) processRoomEvent(
|
|||
default:
|
||||
}
|
||||
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "processRoomEvent")
|
||||
span.SetTag("room_id", input.Event.RoomID())
|
||||
span.SetTag("event_id", input.Event.EventID())
|
||||
defer span.Finish()
|
||||
|
||||
// Measure how long it takes to process this event.
|
||||
started := time.Now()
|
||||
defer func() {
|
||||
|
|
@ -289,6 +295,22 @@ func (r *Inputer) processRoomEvent(
|
|||
}
|
||||
}
|
||||
|
||||
// Get the state before the event so that we can work out if the event was
|
||||
// allowed at the time, and also to get the history visibility. We won't
|
||||
// bother doing this if the event was already rejected as it just ends up
|
||||
// burning CPU time.
|
||||
historyVisibility := gomatrixserverlib.HistoryVisibilityJoined // Default to restrictive.
|
||||
if rejectionErr == nil && !isRejected && !softfail {
|
||||
var err error
|
||||
historyVisibility, rejectionErr, err = r.processStateBefore(ctx, input, missingPrev)
|
||||
if err != nil {
|
||||
return fmt.Errorf("r.processStateBefore: %w", err)
|
||||
}
|
||||
if rejectionErr != nil {
|
||||
isRejected = true
|
||||
}
|
||||
}
|
||||
|
||||
// Store the event.
|
||||
_, _, stateAtEvent, redactionEvent, redactedEventID, err := r.DB.StoreEvent(ctx, event, authEventNIDs, isRejected || softfail)
|
||||
if err != nil {
|
||||
|
|
@ -297,11 +319,9 @@ func (r *Inputer) processRoomEvent(
|
|||
|
||||
// if storing this event results in it being redacted then do so.
|
||||
if !isRejected && redactedEventID == event.EventID() {
|
||||
r, rerr := eventutil.RedactEvent(redactionEvent, event)
|
||||
if rerr != nil {
|
||||
if err = eventutil.RedactEvent(redactionEvent, event); err != nil {
|
||||
return fmt.Errorf("eventutil.RedactEvent: %w", rerr)
|
||||
}
|
||||
event = r
|
||||
}
|
||||
|
||||
// For outliers we can stop after we've stored the event itself as it
|
||||
|
|
@ -354,15 +374,17 @@ func (r *Inputer) processRoomEvent(
|
|||
input.SendAsServer, // send as server
|
||||
input.TransactionID, // transaction ID
|
||||
input.HasState, // rewrites state?
|
||||
historyVisibility, // the history visibility before the event
|
||||
); err != nil {
|
||||
return fmt.Errorf("r.updateLatestEvents: %w", err)
|
||||
}
|
||||
case api.KindOld:
|
||||
err = r.WriteOutputEvents(event.RoomID(), []api.OutputEvent{
|
||||
err = r.OutputProducer.ProduceRoomEvents(event.RoomID(), []api.OutputEvent{
|
||||
{
|
||||
Type: api.OutputTypeOldRoomEvent,
|
||||
OldRoomEvent: &api.OutputOldRoomEvent{
|
||||
Event: headered,
|
||||
Event: headered,
|
||||
HistoryVisibility: historyVisibility,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -376,7 +398,7 @@ func (r *Inputer) processRoomEvent(
|
|||
// so notify downstream components to redact this event - they should have it if they've
|
||||
// been tracking our output log.
|
||||
if redactedEventID != "" {
|
||||
err = r.WriteOutputEvents(event.RoomID(), []api.OutputEvent{
|
||||
err = r.OutputProducer.ProduceRoomEvents(event.RoomID(), []api.OutputEvent{
|
||||
{
|
||||
Type: api.OutputTypeRedactedEvent,
|
||||
RedactedEvent: &api.OutputRedactedEvent{
|
||||
|
|
@ -396,6 +418,100 @@ func (r *Inputer) processRoomEvent(
|
|||
return nil
|
||||
}
|
||||
|
||||
// processStateBefore works out what the state is before the event and
|
||||
// then checks the event auths against the state at the time. It also
|
||||
// tries to determine what the history visibility was of the event at
|
||||
// the time, so that it can be sent in the output event to downstream
|
||||
// components.
|
||||
// nolint:nakedret
|
||||
func (r *Inputer) processStateBefore(
|
||||
ctx context.Context,
|
||||
input *api.InputRoomEvent,
|
||||
missingPrev bool,
|
||||
) (historyVisibility gomatrixserverlib.HistoryVisibility, rejectionErr error, err error) {
|
||||
historyVisibility = gomatrixserverlib.HistoryVisibilityJoined // Default to restrictive.
|
||||
event := input.Event.Unwrap()
|
||||
isCreateEvent := event.Type() == gomatrixserverlib.MRoomCreate && event.StateKeyEquals("")
|
||||
var stateBeforeEvent []*gomatrixserverlib.Event
|
||||
switch {
|
||||
case isCreateEvent:
|
||||
// There's no state before a create event so there is nothing
|
||||
// else to do.
|
||||
return
|
||||
case input.HasState:
|
||||
// If we're overriding the state then we need to go and retrieve
|
||||
// them from the database. It's a hard error if they are missing.
|
||||
stateEvents, err := r.DB.EventsFromIDs(ctx, input.StateEventIDs)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("r.DB.EventsFromIDs: %w", err)
|
||||
}
|
||||
stateBeforeEvent = make([]*gomatrixserverlib.Event, 0, len(stateEvents))
|
||||
for _, entry := range stateEvents {
|
||||
stateBeforeEvent = append(stateBeforeEvent, entry.Event)
|
||||
}
|
||||
case missingPrev:
|
||||
// We don't know all of the prev events, so we can't work out
|
||||
// the state before the event. Reject it in that case.
|
||||
rejectionErr = fmt.Errorf("event %q has missing prev events", event.EventID())
|
||||
return
|
||||
case len(event.PrevEventIDs()) == 0:
|
||||
// There should be prev events since it's not a create event.
|
||||
// A non-create event that claims to have no prev events is
|
||||
// invalid, so reject it.
|
||||
rejectionErr = fmt.Errorf("event %q must have prev events", event.EventID())
|
||||
return
|
||||
default:
|
||||
// For all non-create events, there must be prev events, so we'll
|
||||
// ask the query API for the relevant tuples needed for auth. We
|
||||
// will include the history visibility here even though we don't
|
||||
// actually need it for auth, because we want to send it in the
|
||||
// output events.
|
||||
tuplesNeeded := gomatrixserverlib.StateNeededForAuth([]*gomatrixserverlib.Event{event}).Tuples()
|
||||
tuplesNeeded = append(tuplesNeeded, gomatrixserverlib.StateKeyTuple{
|
||||
EventType: gomatrixserverlib.MRoomHistoryVisibility,
|
||||
StateKey: "",
|
||||
})
|
||||
stateBeforeReq := &api.QueryStateAfterEventsRequest{
|
||||
RoomID: event.RoomID(),
|
||||
PrevEventIDs: event.PrevEventIDs(),
|
||||
StateToFetch: tuplesNeeded,
|
||||
}
|
||||
stateBeforeRes := &api.QueryStateAfterEventsResponse{}
|
||||
if err := r.Queryer.QueryStateAfterEvents(ctx, stateBeforeReq, stateBeforeRes); err != nil {
|
||||
return "", nil, fmt.Errorf("r.Queryer.QueryStateAfterEvents: %w", err)
|
||||
}
|
||||
switch {
|
||||
case !stateBeforeRes.RoomExists:
|
||||
rejectionErr = fmt.Errorf("room %q does not exist", event.RoomID())
|
||||
return
|
||||
case !stateBeforeRes.PrevEventsExist:
|
||||
rejectionErr = fmt.Errorf("prev events of %q are not known", event.EventID())
|
||||
return
|
||||
default:
|
||||
stateBeforeEvent = gomatrixserverlib.UnwrapEventHeaders(stateBeforeRes.StateEvents)
|
||||
}
|
||||
}
|
||||
// At this point, stateBeforeEvent should be populated either by
|
||||
// the supplied state in the input request, or from the prev events.
|
||||
// Check whether the event is allowed or not.
|
||||
stateBeforeAuth := gomatrixserverlib.NewAuthEvents(stateBeforeEvent)
|
||||
if rejectionErr = gomatrixserverlib.Allowed(event, &stateBeforeAuth); rejectionErr != nil {
|
||||
return
|
||||
}
|
||||
// Work out what the history visibility was at the time of the
|
||||
// event.
|
||||
for _, event := range stateBeforeEvent {
|
||||
if event.Type() != gomatrixserverlib.MRoomHistoryVisibility || !event.StateKeyEquals("") {
|
||||
continue
|
||||
}
|
||||
if hisVis, err := event.HistoryVisibility(); err == nil {
|
||||
historyVisibility = hisVis
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// fetchAuthEvents will check to see if any of the
|
||||
// auth events specified by the given event are unknown. If they are
|
||||
// then we will go off and request them from the federation and then
|
||||
|
|
@ -411,6 +527,9 @@ func (r *Inputer) fetchAuthEvents(
|
|||
known map[string]*types.Event,
|
||||
servers []gomatrixserverlib.ServerName,
|
||||
) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "fetchAuthEvents")
|
||||
defer span.Finish()
|
||||
|
||||
unknown := map[string]struct{}{}
|
||||
authEventIDs := event.AuthEventIDs()
|
||||
if len(authEventIDs) == 0 {
|
||||
|
|
@ -526,6 +645,9 @@ func (r *Inputer) calculateAndSetState(
|
|||
event *gomatrixserverlib.Event,
|
||||
isRejected bool,
|
||||
) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "calculateAndSetState")
|
||||
defer span.Finish()
|
||||
|
||||
var succeeded bool
|
||||
updater, err := r.DB.GetRoomUpdater(ctx, roomInfo)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
@ -55,7 +56,11 @@ func (r *Inputer) updateLatestEvents(
|
|||
sendAsServer string,
|
||||
transactionID *api.TransactionID,
|
||||
rewritesState bool,
|
||||
historyVisibility gomatrixserverlib.HistoryVisibility,
|
||||
) (err error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "updateLatestEvents")
|
||||
defer span.Finish()
|
||||
|
||||
var succeeded bool
|
||||
updater, err := r.DB.GetRoomUpdater(ctx, roomInfo)
|
||||
if err != nil {
|
||||
|
|
@ -65,15 +70,16 @@ func (r *Inputer) updateLatestEvents(
|
|||
defer sqlutil.EndTransactionWithCheck(updater, &succeeded, &err)
|
||||
|
||||
u := latestEventsUpdater{
|
||||
ctx: ctx,
|
||||
api: r,
|
||||
updater: updater,
|
||||
roomInfo: roomInfo,
|
||||
stateAtEvent: stateAtEvent,
|
||||
event: event,
|
||||
sendAsServer: sendAsServer,
|
||||
transactionID: transactionID,
|
||||
rewritesState: rewritesState,
|
||||
ctx: ctx,
|
||||
api: r,
|
||||
updater: updater,
|
||||
roomInfo: roomInfo,
|
||||
stateAtEvent: stateAtEvent,
|
||||
event: event,
|
||||
sendAsServer: sendAsServer,
|
||||
transactionID: transactionID,
|
||||
rewritesState: rewritesState,
|
||||
historyVisibility: historyVisibility,
|
||||
}
|
||||
|
||||
if err = u.doUpdateLatestEvents(); err != nil {
|
||||
|
|
@ -115,6 +121,8 @@ type latestEventsUpdater struct {
|
|||
// The snapshots of current state before and after processing this event
|
||||
oldStateNID types.StateSnapshotNID
|
||||
newStateNID types.StateSnapshotNID
|
||||
// The history visibility of the event itself (from the state before the event).
|
||||
historyVisibility gomatrixserverlib.HistoryVisibility
|
||||
}
|
||||
|
||||
func (u *latestEventsUpdater) doUpdateLatestEvents() error {
|
||||
|
|
@ -184,7 +192,7 @@ func (u *latestEventsUpdater) doUpdateLatestEvents() error {
|
|||
// send the event asynchronously but we would need to ensure that 1) the events are written to the log in
|
||||
// the correct order, 2) that pending writes are resent across restarts. In order to avoid writing all the
|
||||
// necessary bookkeeping we'll keep the event sending synchronous for now.
|
||||
if err = u.api.WriteOutputEvents(u.event.RoomID(), updates); err != nil {
|
||||
if err = u.api.OutputProducer.ProduceRoomEvents(u.event.RoomID(), updates); err != nil {
|
||||
return fmt.Errorf("u.api.WriteOutputEvents: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -200,6 +208,9 @@ func (u *latestEventsUpdater) doUpdateLatestEvents() error {
|
|||
}
|
||||
|
||||
func (u *latestEventsUpdater) latestState() error {
|
||||
span, ctx := opentracing.StartSpanFromContext(u.ctx, "processEventWithMissingState")
|
||||
defer span.Finish()
|
||||
|
||||
var err error
|
||||
roomState := state.NewStateResolution(u.updater, u.roomInfo)
|
||||
|
||||
|
|
@ -246,7 +257,7 @@ func (u *latestEventsUpdater) latestState() error {
|
|||
// of the state after the events. The snapshot state will be resolved
|
||||
// using the correct state resolution algorithm for the room.
|
||||
u.newStateNID, err = roomState.CalculateAndStoreStateAfterEvents(
|
||||
u.ctx, latestStateAtEvents,
|
||||
ctx, latestStateAtEvents,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("roomState.CalculateAndStoreStateAfterEvents: %w", err)
|
||||
|
|
@ -258,7 +269,7 @@ func (u *latestEventsUpdater) latestState() error {
|
|||
// another list of added ones. Replacing a value for a state-key tuple
|
||||
// will result one removed (the old event) and one added (the new event).
|
||||
u.removed, u.added, err = roomState.DifferenceBetweeenStateSnapshots(
|
||||
u.ctx, u.oldStateNID, u.newStateNID,
|
||||
ctx, u.oldStateNID, u.newStateNID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("roomState.DifferenceBetweenStateSnapshots: %w", err)
|
||||
|
|
@ -278,7 +289,7 @@ func (u *latestEventsUpdater) latestState() error {
|
|||
// Also work out the state before the event removes and the event
|
||||
// adds.
|
||||
u.stateBeforeEventRemoves, u.stateBeforeEventAdds, err = roomState.DifferenceBetweeenStateSnapshots(
|
||||
u.ctx, u.newStateNID, u.stateAtEvent.BeforeStateSnapshotNID,
|
||||
ctx, u.newStateNID, u.stateAtEvent.BeforeStateSnapshotNID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("roomState.DifferenceBetweeenStateSnapshots: %w", err)
|
||||
|
|
@ -294,6 +305,9 @@ func (u *latestEventsUpdater) calculateLatest(
|
|||
newEvent *gomatrixserverlib.Event,
|
||||
newStateAndRef types.StateAtEventAndReference,
|
||||
) (bool, error) {
|
||||
span, _ := opentracing.StartSpanFromContext(u.ctx, "calculateLatest")
|
||||
defer span.Finish()
|
||||
|
||||
// First of all, get a list of all of the events in our current
|
||||
// set of forward extremities.
|
||||
existingRefs := make(map[string]*types.StateAtEventAndReference)
|
||||
|
|
@ -355,12 +369,13 @@ func (u *latestEventsUpdater) makeOutputNewRoomEvent() (*api.OutputEvent, error)
|
|||
}
|
||||
|
||||
ore := api.OutputNewRoomEvent{
|
||||
Event: u.event.Headered(u.roomInfo.RoomVersion),
|
||||
RewritesState: u.rewritesState,
|
||||
LastSentEventID: u.lastEventIDSent,
|
||||
LatestEventIDs: latestEventIDs,
|
||||
TransactionID: u.transactionID,
|
||||
SendAsServer: u.sendAsServer,
|
||||
Event: u.event.Headered(u.roomInfo.RoomVersion),
|
||||
RewritesState: u.rewritesState,
|
||||
LastSentEventID: u.lastEventIDSent,
|
||||
LatestEventIDs: latestEventIDs,
|
||||
TransactionID: u.transactionID,
|
||||
SendAsServer: u.sendAsServer,
|
||||
HistoryVisibility: u.historyVisibility,
|
||||
}
|
||||
|
||||
eventIDMap, err := u.stateEventMap()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/matrix-org/dendrite/roomserver/storage/shared"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// updateMembership updates the current membership and the invites for each
|
||||
|
|
@ -34,6 +35,9 @@ func (r *Inputer) updateMemberships(
|
|||
updater *shared.RoomUpdater,
|
||||
removed, added []types.StateEntry,
|
||||
) ([]api.OutputEvent, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "updateMemberships")
|
||||
defer span.Finish()
|
||||
|
||||
changes := membershipChanges(removed, added)
|
||||
var eventNIDs []types.EventNID
|
||||
for _, change := range changes {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
@ -59,6 +60,9 @@ type missingStateReq struct {
|
|||
func (t *missingStateReq) processEventWithMissingState(
|
||||
ctx context.Context, e *gomatrixserverlib.Event, roomVersion gomatrixserverlib.RoomVersion,
|
||||
) (*parsedRespState, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "processEventWithMissingState")
|
||||
defer span.Finish()
|
||||
|
||||
// We are missing the previous events for this events.
|
||||
// This means that there is a gap in our view of the history of the
|
||||
// room. There two ways that we can handle such a gap:
|
||||
|
|
@ -235,6 +239,9 @@ func (t *missingStateReq) processEventWithMissingState(
|
|||
}
|
||||
|
||||
func (t *missingStateReq) lookupResolvedStateBeforeEvent(ctx context.Context, e *gomatrixserverlib.Event, roomVersion gomatrixserverlib.RoomVersion) (*parsedRespState, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "lookupResolvedStateBeforeEvent")
|
||||
defer span.Finish()
|
||||
|
||||
type respState struct {
|
||||
// A snapshot is considered trustworthy if it came from our own roomserver.
|
||||
// That's because the state will have been through state resolution once
|
||||
|
|
@ -310,6 +317,9 @@ func (t *missingStateReq) lookupResolvedStateBeforeEvent(ctx context.Context, e
|
|||
// lookupStateAfterEvent returns the room state after `eventID`, which is the state before eventID with the state of `eventID` (if it's a state event)
|
||||
// added into the mix.
|
||||
func (t *missingStateReq) lookupStateAfterEvent(ctx context.Context, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string) (*parsedRespState, bool, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "lookupStateAfterEvent")
|
||||
defer span.Finish()
|
||||
|
||||
// try doing all this locally before we resort to querying federation
|
||||
respState := t.lookupStateAfterEventLocally(ctx, roomID, eventID)
|
||||
if respState != nil {
|
||||
|
|
@ -361,6 +371,9 @@ func (t *missingStateReq) cacheAndReturn(ev *gomatrixserverlib.Event) *gomatrixs
|
|||
}
|
||||
|
||||
func (t *missingStateReq) lookupStateAfterEventLocally(ctx context.Context, roomID, eventID string) *parsedRespState {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "lookupStateAfterEventLocally")
|
||||
defer span.Finish()
|
||||
|
||||
var res parsedRespState
|
||||
roomInfo, err := t.db.RoomInfo(ctx, roomID)
|
||||
if err != nil {
|
||||
|
|
@ -435,12 +448,17 @@ func (t *missingStateReq) lookupStateAfterEventLocally(ctx context.Context, room
|
|||
// the server supports.
|
||||
func (t *missingStateReq) lookupStateBeforeEvent(ctx context.Context, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string) (
|
||||
*parsedRespState, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "lookupStateBeforeEvent")
|
||||
defer span.Finish()
|
||||
|
||||
// Attempt to fetch the missing state using /state_ids and /events
|
||||
return t.lookupMissingStateViaStateIDs(ctx, roomID, eventID, roomVersion)
|
||||
}
|
||||
|
||||
func (t *missingStateReq) resolveStatesAndCheck(ctx context.Context, roomVersion gomatrixserverlib.RoomVersion, states []*parsedRespState, backwardsExtremity *gomatrixserverlib.Event) (*parsedRespState, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "resolveStatesAndCheck")
|
||||
defer span.Finish()
|
||||
|
||||
var authEventList []*gomatrixserverlib.Event
|
||||
var stateEventList []*gomatrixserverlib.Event
|
||||
for _, state := range states {
|
||||
|
|
@ -484,6 +502,9 @@ retryAllowedState:
|
|||
// get missing events for `e`. If `isGapFilled`=true then `newEvents` contains all the events to inject,
|
||||
// without `e`. If `isGapFilled=false` then `newEvents` contains the response to /get_missing_events
|
||||
func (t *missingStateReq) getMissingEvents(ctx context.Context, e *gomatrixserverlib.Event, roomVersion gomatrixserverlib.RoomVersion) (newEvents []*gomatrixserverlib.Event, isGapFilled, prevStateKnown bool, err error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "getMissingEvents")
|
||||
defer span.Finish()
|
||||
|
||||
logger := util.GetLogger(ctx).WithField("event_id", e.EventID()).WithField("room_id", e.RoomID())
|
||||
latest, _, _, err := t.db.LatestEventIDs(ctx, t.roomInfo.RoomNID)
|
||||
if err != nil {
|
||||
|
|
@ -608,6 +629,9 @@ func (t *missingStateReq) isPrevStateKnown(ctx context.Context, e *gomatrixserve
|
|||
func (t *missingStateReq) lookupMissingStateViaState(
|
||||
ctx context.Context, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion,
|
||||
) (respState *parsedRespState, err error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "lookupMissingStateViaState")
|
||||
defer span.Finish()
|
||||
|
||||
state, err := t.federation.LookupState(ctx, t.origin, roomID, eventID, roomVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -637,6 +661,9 @@ func (t *missingStateReq) lookupMissingStateViaState(
|
|||
|
||||
func (t *missingStateReq) lookupMissingStateViaStateIDs(ctx context.Context, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion) (
|
||||
*parsedRespState, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "lookupMissingStateViaStateIDs")
|
||||
defer span.Finish()
|
||||
|
||||
util.GetLogger(ctx).WithField("room_id", roomID).Infof("lookupMissingStateViaStateIDs %s", eventID)
|
||||
// fetch the state event IDs at the time of the event
|
||||
stateIDs, err := t.federation.LookupStateIDs(ctx, t.origin, roomID, eventID)
|
||||
|
|
@ -799,6 +826,9 @@ func (t *missingStateReq) createRespStateFromStateIDs(
|
|||
}
|
||||
|
||||
func (t *missingStateReq) lookupEvent(ctx context.Context, roomVersion gomatrixserverlib.RoomVersion, _, missingEventID string, localFirst bool) (*gomatrixserverlib.Event, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "lookupEvent")
|
||||
defer span.Finish()
|
||||
|
||||
if localFirst {
|
||||
// fetch from the roomserver
|
||||
events, err := t.db.EventsFromIDs(ctx, []string{missingEventID})
|
||||
|
|
|
|||
|
|
@ -48,10 +48,6 @@ func TestSingleTransactionOnInput(t *testing.T) {
|
|||
Kind: api.KindOutlier, // don't panic if we generate an output event
|
||||
Event: event.Headered(gomatrixserverlib.RoomVersionV6),
|
||||
}
|
||||
cache, err := caching.NewInMemoryLRUCache(false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db, err := storage.Open(
|
||||
nil,
|
||||
&config.DatabaseOptions{
|
||||
|
|
@ -59,7 +55,7 @@ func TestSingleTransactionOnInput(t *testing.T) {
|
|||
MaxOpenConnections: 1,
|
||||
MaxIdleConnections: 1,
|
||||
},
|
||||
cache,
|
||||
caching.NewRistrettoCache(8*1024*1024, time.Hour, false),
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("PostgreSQL not available (%s), skipping", err)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ package perform
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
|
@ -34,6 +35,7 @@ type Admin struct {
|
|||
Cfg *config.RoomServer
|
||||
Queryer *query.Queryer
|
||||
Inputer *input.Inputer
|
||||
Leaver *Leaver
|
||||
}
|
||||
|
||||
// PerformEvacuateRoom will remove all local users from the given room.
|
||||
|
|
@ -160,3 +162,71 @@ func (r *Admin) PerformAdminEvacuateRoom(
|
|||
inputRes := &api.InputRoomEventsResponse{}
|
||||
r.Inputer.InputRoomEvents(ctx, inputReq, inputRes)
|
||||
}
|
||||
|
||||
func (r *Admin) PerformAdminEvacuateUser(
|
||||
ctx context.Context,
|
||||
req *api.PerformAdminEvacuateUserRequest,
|
||||
res *api.PerformAdminEvacuateUserResponse,
|
||||
) {
|
||||
_, domain, err := gomatrixserverlib.SplitID('@', req.UserID)
|
||||
if err != nil {
|
||||
res.Error = &api.PerformError{
|
||||
Code: api.PerformErrorBadRequest,
|
||||
Msg: fmt.Sprintf("Malformed user ID: %s", err),
|
||||
}
|
||||
return
|
||||
}
|
||||
if domain != r.Cfg.Matrix.ServerName {
|
||||
res.Error = &api.PerformError{
|
||||
Code: api.PerformErrorBadRequest,
|
||||
Msg: "Can only evacuate local users using this endpoint",
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
roomIDs, err := r.DB.GetRoomsByMembership(ctx, req.UserID, gomatrixserverlib.Join)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
res.Error = &api.PerformError{
|
||||
Code: api.PerformErrorBadRequest,
|
||||
Msg: fmt.Sprintf("r.DB.GetRoomsByMembership: %s", err),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
inviteRoomIDs, err := r.DB.GetRoomsByMembership(ctx, req.UserID, gomatrixserverlib.Invite)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
res.Error = &api.PerformError{
|
||||
Code: api.PerformErrorBadRequest,
|
||||
Msg: fmt.Sprintf("r.DB.GetRoomsByMembership: %s", err),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for _, roomID := range append(roomIDs, inviteRoomIDs...) {
|
||||
leaveReq := &api.PerformLeaveRequest{
|
||||
RoomID: roomID,
|
||||
UserID: req.UserID,
|
||||
}
|
||||
leaveRes := &api.PerformLeaveResponse{}
|
||||
outputEvents, err := r.Leaver.PerformLeave(ctx, leaveReq, leaveRes)
|
||||
if err != nil {
|
||||
res.Error = &api.PerformError{
|
||||
Code: api.PerformErrorBadRequest,
|
||||
Msg: fmt.Sprintf("r.Leaver.PerformLeave: %s", err),
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(outputEvents) == 0 {
|
||||
continue
|
||||
}
|
||||
if err := r.Inputer.OutputProducer.ProduceRoomEvents(roomID, outputEvents); err != nil {
|
||||
res.Error = &api.PerformError{
|
||||
Code: api.PerformErrorBadRequest,
|
||||
Msg: fmt.Sprintf("r.Inputer.WriteOutputEvents: %s", err),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
res.Affected = append(res.Affected, roomID)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
|
|
@ -206,8 +207,17 @@ func (r *Backfiller) fetchAndStoreMissingEvents(ctx context.Context, roomVer gom
|
|||
}
|
||||
logger.Infof("returned %d PDUs which made events %+v", len(res.PDUs), result)
|
||||
for _, res := range result {
|
||||
if res.Error != nil {
|
||||
logger.WithError(res.Error).Warn("event failed PDU checks")
|
||||
switch err := res.Error.(type) {
|
||||
case nil:
|
||||
case gomatrixserverlib.SignatureErr:
|
||||
// The signature of the event might not be valid anymore, for example if
|
||||
// the key ID was reused with a different signature.
|
||||
logger.WithError(err).Errorf("event failed PDU checks, storing anyway")
|
||||
case gomatrixserverlib.AuthChainErr, gomatrixserverlib.AuthRulesErr:
|
||||
logger.WithError(err).Warn("event failed PDU checks")
|
||||
continue
|
||||
default:
|
||||
logger.WithError(err).Warn("event failed PDU checks")
|
||||
continue
|
||||
}
|
||||
missingMap[id] = res.Event
|
||||
|
|
@ -306,6 +316,7 @@ FederationHit:
|
|||
b.eventIDToBeforeStateIDs[targetEvent.EventID()] = res
|
||||
return res, nil
|
||||
}
|
||||
sentry.CaptureException(lastErr) // temporary to see if we might need to raise the server limit
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
|
|
@ -366,19 +377,25 @@ func (b *backfillRequester) StateBeforeEvent(ctx context.Context, roomVer gomatr
|
|||
}
|
||||
}
|
||||
|
||||
c := gomatrixserverlib.FederatedStateProvider{
|
||||
FedClient: b.fsAPI,
|
||||
RememberAuthEvents: false,
|
||||
Server: b.servers[0],
|
||||
var lastErr error
|
||||
for _, srv := range b.servers {
|
||||
c := gomatrixserverlib.FederatedStateProvider{
|
||||
FedClient: b.fsAPI,
|
||||
RememberAuthEvents: false,
|
||||
Server: srv,
|
||||
}
|
||||
result, err := c.StateBeforeEvent(ctx, roomVer, event, eventIDs)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
for eventID, ev := range result {
|
||||
b.eventIDMap[eventID] = ev
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
result, err := c.StateBeforeEvent(ctx, roomVer, event, eventIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for eventID, ev := range result {
|
||||
b.eventIDMap[eventID] = ev
|
||||
}
|
||||
return result, nil
|
||||
sentry.CaptureException(lastErr) // temporary to see if we might need to raise the server limit
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
// ServersAtEvent is called when trying to determine which server to request from.
|
||||
|
|
@ -576,12 +593,11 @@ func persistEvents(ctx context.Context, db storage.Database, events []*gomatrixs
|
|||
// redacted, which we don't care about since we aren't returning it in this backfill.
|
||||
if redactedEventID == ev.EventID() {
|
||||
eventToRedact := ev.Unwrap()
|
||||
redactedEvent, err := eventutil.RedactEvent(redactionEvent, eventToRedact)
|
||||
if err != nil {
|
||||
if err := eventutil.RedactEvent(redactionEvent, eventToRedact); err != nil {
|
||||
logrus.WithError(err).WithField("event_id", ev.EventID()).Error("Failed to redact event")
|
||||
continue
|
||||
}
|
||||
ev = redactedEvent.Headered(ev.RoomVersion)
|
||||
ev = eventToRedact.Headered(ev.RoomVersion)
|
||||
events[j] = ev
|
||||
}
|
||||
backfilledEventMap[ev.EventID()] = types.Event{
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func (r *InboundPeeker) PerformInboundPeek(
|
|||
response.AuthChainEvents = append(response.AuthChainEvents, event.Headered(info.RoomVersion))
|
||||
}
|
||||
|
||||
err = r.Inputer.WriteOutputEvents(request.RoomID, []api.OutputEvent{
|
||||
err = r.Inputer.OutputProducer.ProduceRoomEvents(request.RoomID, []api.OutputEvent{
|
||||
{
|
||||
Type: api.OutputTypeNewInboundPeek,
|
||||
NewInboundPeek: &api.OutputNewInboundPeek{
|
||||
|
|
|
|||
|
|
@ -56,7 +56,14 @@ func (r *Inviter) PerformInvite(
|
|||
return nil, fmt.Errorf("failed to load RoomInfo: %w", err)
|
||||
}
|
||||
|
||||
_, domain, _ := gomatrixserverlib.SplitID('@', targetUserID)
|
||||
_, domain, err := gomatrixserverlib.SplitID('@', targetUserID)
|
||||
if err != nil {
|
||||
res.Error = &api.PerformError{
|
||||
Code: api.PerformErrorBadRequest,
|
||||
Msg: fmt.Sprintf("The user ID %q is invalid!", targetUserID),
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
isTargetLocal := domain == r.Cfg.Matrix.ServerName
|
||||
isOriginLocal := event.Origin() == r.Cfg.Matrix.ServerName
|
||||
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ func (r *Peeker) performPeekRoomByID(
|
|||
|
||||
// TODO: handle federated peeks
|
||||
|
||||
err = r.Inputer.WriteOutputEvents(roomID, []api.OutputEvent{
|
||||
err = r.Inputer.OutputProducer.ProduceRoomEvents(roomID, []api.OutputEvent{
|
||||
{
|
||||
Type: api.OutputTypeNewPeek,
|
||||
NewPeek: &api.OutputNewPeek{
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ func (r *Unpeeker) performUnpeekRoomByID(
|
|||
|
||||
// TODO: handle federated peeks
|
||||
|
||||
err = r.Inputer.WriteOutputEvents(req.RoomID, []api.OutputEvent{
|
||||
err = r.Inputer.OutputProducer.ProduceRoomEvents(req.RoomID, []api.OutputEvent{
|
||||
{
|
||||
Type: api.OutputTypeRetirePeek,
|
||||
RetirePeek: &api.OutputRetirePeek{
|
||||
|
|
|
|||
|
|
@ -105,13 +105,13 @@ func (r *Upgrader) performRoomUpgrade(
|
|||
return "", pErr
|
||||
}
|
||||
|
||||
// 5. Send the tombstone event to the old room (must do this before we set the new canonical_alias)
|
||||
if pErr = r.sendHeaderedEvent(ctx, tombstoneEvent); pErr != nil {
|
||||
// Send the setup events to the new room
|
||||
if pErr = r.sendInitialEvents(ctx, evTime, userID, newRoomID, string(req.RoomVersion), eventsToMake); pErr != nil {
|
||||
return "", pErr
|
||||
}
|
||||
|
||||
// Send the setup events to the new room
|
||||
if pErr = r.sendInitialEvents(ctx, evTime, userID, newRoomID, string(req.RoomVersion), eventsToMake); pErr != nil {
|
||||
// 5. Send the tombstone event to the old room
|
||||
if pErr = r.sendHeaderedEvent(ctx, tombstoneEvent, string(r.Cfg.Matrix.ServerName)); pErr != nil {
|
||||
return "", pErr
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ func (r *Upgrader) getRoomPowerLevels(ctx context.Context, roomID string) (*goma
|
|||
if err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error()
|
||||
return nil, &api.PerformError{
|
||||
Msg: "powerLevel event was not actually a power level event",
|
||||
Msg: "Power level event was invalid or malformed",
|
||||
}
|
||||
}
|
||||
return powerLevelContent, nil
|
||||
|
|
@ -182,7 +182,7 @@ func (r *Upgrader) restrictOldRoomPowerLevels(ctx context.Context, evTime time.T
|
|||
return resErr
|
||||
}
|
||||
} else {
|
||||
if resErr = r.sendHeaderedEvent(ctx, restrictedPowerLevelsHeadered); resErr != nil {
|
||||
if resErr = r.sendHeaderedEvent(ctx, restrictedPowerLevelsHeadered, api.DoNotSendToOtherServers); resErr != nil {
|
||||
return resErr
|
||||
}
|
||||
}
|
||||
|
|
@ -198,7 +198,7 @@ func moveLocalAliases(ctx context.Context,
|
|||
aliasRes := api.GetAliasesForRoomIDResponse{}
|
||||
if err = URSAPI.GetAliasesForRoomID(ctx, &aliasReq, &aliasRes); err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "Could not get aliases for old room",
|
||||
Msg: fmt.Sprintf("Failed to get old room aliases: %s", err),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ func moveLocalAliases(ctx context.Context,
|
|||
removeAliasRes := api.RemoveRoomAliasResponse{}
|
||||
if err = URSAPI.RemoveRoomAlias(ctx, &removeAliasReq, &removeAliasRes); err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "api.RemoveRoomAlias failed",
|
||||
Msg: fmt.Sprintf("Failed to remove old room alias: %s", err),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ func moveLocalAliases(ctx context.Context,
|
|||
setAliasRes := api.SetRoomAliasResponse{}
|
||||
if err = URSAPI.SetRoomAlias(ctx, &setAliasReq, &setAliasRes); err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "api.SetRoomAlias failed",
|
||||
Msg: fmt.Sprintf("Failed to set new room alias: %s", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -253,7 +253,7 @@ func (r *Upgrader) clearOldCanonicalAliasEvent(ctx context.Context, oldRoom *api
|
|||
return resErr
|
||||
}
|
||||
} else {
|
||||
if resErr = r.sendHeaderedEvent(ctx, emptyCanonicalAliasEvent); resErr != nil {
|
||||
if resErr = r.sendHeaderedEvent(ctx, emptyCanonicalAliasEvent, api.DoNotSendToOtherServers); resErr != nil {
|
||||
return resErr
|
||||
}
|
||||
}
|
||||
|
|
@ -509,7 +509,7 @@ func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, user
|
|||
err = builder.SetContent(e.Content)
|
||||
if err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "builder.SetContent failed",
|
||||
Msg: fmt.Sprintf("Failed to set content of new %q event: %s", builder.Type, err),
|
||||
}
|
||||
}
|
||||
if i > 0 {
|
||||
|
|
@ -519,13 +519,13 @@ func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, user
|
|||
event, err = r.buildEvent(&builder, &authEvents, evTime, gomatrixserverlib.RoomVersion(newVersion))
|
||||
if err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "buildEvent failed",
|
||||
Msg: fmt.Sprintf("Failed to build new %q event: %s", builder.Type, err),
|
||||
}
|
||||
}
|
||||
|
||||
if err = gomatrixserverlib.Allowed(event, &authEvents); err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "gomatrixserverlib.Allowed failed",
|
||||
Msg: fmt.Sprintf("Failed to auth new %q event: %s", builder.Type, err),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -534,7 +534,7 @@ func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, user
|
|||
err = authEvents.AddEvent(event)
|
||||
if err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "authEvents.AddEvent failed",
|
||||
Msg: fmt.Sprintf("Failed to add new %q event to auth set: %s", builder.Type, err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -550,7 +550,7 @@ func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, user
|
|||
}
|
||||
if err = api.SendInputRoomEvents(ctx, r.URSAPI, inputs, false); err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "api.SendInputRoomEvents failed",
|
||||
Msg: fmt.Sprintf("Failed to send new room %q to roomserver: %s", newRoomID, err),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -582,7 +582,7 @@ func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, user
|
|||
err := builder.SetContent(event.Content)
|
||||
if err != nil {
|
||||
return nil, &api.PerformError{
|
||||
Msg: "builder.SetContent failed",
|
||||
Msg: fmt.Sprintf("Failed to set new %q event content: %s", builder.Type, err),
|
||||
}
|
||||
}
|
||||
var queryRes api.QueryLatestEventsAndStateResponse
|
||||
|
|
@ -607,7 +607,7 @@ func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, user
|
|||
}
|
||||
} else if err != nil {
|
||||
return nil, &api.PerformError{
|
||||
Msg: "eventutil.BuildEvent failed",
|
||||
Msg: fmt.Sprintf("Failed to build new %q event: %s", builder.Type, err),
|
||||
}
|
||||
}
|
||||
// check to see if this user can perform this operation
|
||||
|
|
@ -619,7 +619,7 @@ func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, user
|
|||
if err = gomatrixserverlib.Allowed(headeredEvent.Event, &provider); err != nil {
|
||||
return nil, &api.PerformError{
|
||||
Code: api.PerformErrorNotAllowed,
|
||||
Msg: err.Error(), // TODO: Is this error string comprehensible to the client?
|
||||
Msg: fmt.Sprintf("Failed to auth new %q event: %s", builder.Type, err), // TODO: Is this error string comprehensible to the client?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -666,17 +666,18 @@ func createTemporaryPowerLevels(powerLevelContent *gomatrixserverlib.PowerLevelC
|
|||
func (r *Upgrader) sendHeaderedEvent(
|
||||
ctx context.Context,
|
||||
headeredEvent *gomatrixserverlib.HeaderedEvent,
|
||||
sendAsServer string,
|
||||
) *api.PerformError {
|
||||
var inputs []api.InputRoomEvent
|
||||
inputs = append(inputs, api.InputRoomEvent{
|
||||
Kind: api.KindNew,
|
||||
Event: headeredEvent,
|
||||
Origin: r.Cfg.Matrix.ServerName,
|
||||
SendAsServer: api.DoNotSendToOtherServers,
|
||||
SendAsServer: sendAsServer,
|
||||
})
|
||||
if err := api.SendInputRoomEvents(ctx, r.URSAPI, inputs, false); err != nil {
|
||||
return &api.PerformError{
|
||||
Msg: "api.SendInputRoomEvents failed",
|
||||
Msg: fmt.Sprintf("Failed to send new %q event to roomserver: %s", headeredEvent.Type(), err),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -703,7 +704,7 @@ func (r *Upgrader) buildEvent(
|
|||
r.Cfg.Matrix.PrivateKey, roomVersion,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot build event %s : Builder failed to build. %w", builder.Type, err)
|
||||
return nil, err
|
||||
}
|
||||
return event, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -778,11 +778,18 @@ func (r *Queryer) QueryRestrictedJoinAllowed(ctx context.Context, req *api.Query
|
|||
} else if !allowRestrictedJoins {
|
||||
return nil
|
||||
}
|
||||
// Start off by populating the "resident" flag in the response. If we
|
||||
// come across any rooms in the request that are missing, we will unset
|
||||
// the flag.
|
||||
res.Resident = true
|
||||
// Get the join rules to work out if the join rule is "restricted".
|
||||
joinRulesEvent, err := r.DB.GetStateEvent(ctx, req.RoomID, gomatrixserverlib.MRoomJoinRules, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("r.DB.GetStateEvent: %w", err)
|
||||
}
|
||||
if joinRulesEvent == nil {
|
||||
return nil
|
||||
}
|
||||
var joinRules gomatrixserverlib.JoinRuleContent
|
||||
if err = json.Unmarshal(joinRulesEvent.Content(), &joinRules); err != nil {
|
||||
return fmt.Errorf("json.Unmarshal: %w", err)
|
||||
|
|
@ -792,10 +799,6 @@ func (r *Queryer) QueryRestrictedJoinAllowed(ctx context.Context, req *api.Query
|
|||
if !res.Restricted {
|
||||
return nil
|
||||
}
|
||||
// Start off by populating the "resident" flag in the response. If we
|
||||
// come across any rooms in the request that are missing, we will unset
|
||||
// the flag.
|
||||
res.Resident = true
|
||||
// If the user is already invited to the room then the join is allowed
|
||||
// but we don't specify an authorised via user, since the event auth
|
||||
// will allow the join anyway.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ const (
|
|||
RoomserverPerformInboundPeekPath = "/roomserver/performInboundPeek"
|
||||
RoomserverPerformForgetPath = "/roomserver/performForget"
|
||||
RoomserverPerformAdminEvacuateRoomPath = "/roomserver/performAdminEvacuateRoom"
|
||||
RoomserverPerformAdminEvacuateUserPath = "/roomserver/performAdminEvacuateUser"
|
||||
|
||||
// Query operations
|
||||
RoomserverQueryLatestEventsAndStatePath = "/roomserver/queryLatestEventsAndState"
|
||||
|
|
@ -305,6 +306,23 @@ func (h *httpRoomserverInternalAPI) PerformAdminEvacuateRoom(
|
|||
}
|
||||
}
|
||||
|
||||
func (h *httpRoomserverInternalAPI) PerformAdminEvacuateUser(
|
||||
ctx context.Context,
|
||||
req *api.PerformAdminEvacuateUserRequest,
|
||||
res *api.PerformAdminEvacuateUserResponse,
|
||||
) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformAdminEvacuateUser")
|
||||
defer span.Finish()
|
||||
|
||||
apiURL := h.roomserverURL + RoomserverPerformAdminEvacuateUserPath
|
||||
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, req, res)
|
||||
if err != nil {
|
||||
res.Error = &api.PerformError{
|
||||
Msg: fmt.Sprintf("failed to communicate with roomserver: %s", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// QueryLatestEventsAndState implements RoomserverQueryAPI
|
||||
func (h *httpRoomserverInternalAPI) QueryLatestEventsAndState(
|
||||
ctx context.Context,
|
||||
|
|
|
|||
|
|
@ -129,6 +129,17 @@ func AddRoutes(r api.RoomserverInternalAPI, internalAPIMux *mux.Router) {
|
|||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||
}),
|
||||
)
|
||||
internalAPIMux.Handle(RoomserverPerformAdminEvacuateUserPath,
|
||||
httputil.MakeInternalAPI("performAdminEvacuateUser", func(req *http.Request) util.JSONResponse {
|
||||
var request api.PerformAdminEvacuateUserRequest
|
||||
var response api.PerformAdminEvacuateUserResponse
|
||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
r.PerformAdminEvacuateUser(req.Context(), &request, &response)
|
||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||
}),
|
||||
)
|
||||
internalAPIMux.Handle(
|
||||
RoomserverQueryPublishedRoomsPath,
|
||||
httputil.MakeInternalAPI("queryPublishedRooms", func(req *http.Request) util.JSONResponse {
|
||||
|
|
|
|||
89
roomserver/producers/roomevent.go
Normal file
89
roomserver/producers/roomevent.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// 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 producers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/acls"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/nats-io/nats.go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
var keyContentFields = map[string]string{
|
||||
"m.room.join_rules": "join_rule",
|
||||
"m.room.history_visibility": "history_visibility",
|
||||
"m.room.member": "membership",
|
||||
}
|
||||
|
||||
type RoomEventProducer struct {
|
||||
Topic string
|
||||
ACLs *acls.ServerACLs
|
||||
JetStream nats.JetStreamContext
|
||||
}
|
||||
|
||||
func (r *RoomEventProducer) ProduceRoomEvents(roomID string, updates []api.OutputEvent) error {
|
||||
var err error
|
||||
for _, update := range updates {
|
||||
msg := &nats.Msg{
|
||||
Subject: r.Topic,
|
||||
Header: nats.Header{},
|
||||
}
|
||||
msg.Header.Set(jetstream.RoomID, roomID)
|
||||
msg.Data, err = json.Marshal(update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger := log.WithFields(log.Fields{
|
||||
"room_id": roomID,
|
||||
"type": update.Type,
|
||||
})
|
||||
if update.NewRoomEvent != nil {
|
||||
eventType := update.NewRoomEvent.Event.Type()
|
||||
logger = logger.WithFields(log.Fields{
|
||||
"event_type": eventType,
|
||||
"event_id": update.NewRoomEvent.Event.EventID(),
|
||||
"adds_state": len(update.NewRoomEvent.AddsStateEventIDs),
|
||||
"removes_state": len(update.NewRoomEvent.RemovesStateEventIDs),
|
||||
"send_as_server": update.NewRoomEvent.SendAsServer,
|
||||
"sender": update.NewRoomEvent.Event.Sender(),
|
||||
})
|
||||
if update.NewRoomEvent.Event.StateKey() != nil {
|
||||
logger = logger.WithField("state_key", *update.NewRoomEvent.Event.StateKey())
|
||||
}
|
||||
contentKey := keyContentFields[eventType]
|
||||
if contentKey != "" {
|
||||
value := gjson.GetBytes(update.NewRoomEvent.Event.Content(), contentKey)
|
||||
if value.Exists() {
|
||||
logger = logger.WithField("content_value", value.String())
|
||||
}
|
||||
}
|
||||
|
||||
if eventType == "m.room.server_acl" && update.NewRoomEvent.Event.StateKeyEquals("") {
|
||||
ev := update.NewRoomEvent.Event.Unwrap()
|
||||
defer r.ACLs.OnServerACLUpdate(ev)
|
||||
}
|
||||
}
|
||||
logger.Tracef("Producing to topic '%s'", r.Topic)
|
||||
if _, err := r.JetStream.PublishMsg(msg); err != nil {
|
||||
logger.WithError(err).Errorf("Failed to produce to topic '%s': %s", r.Topic, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -17,13 +17,10 @@ package roomserver
|
|||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver/inthttp"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/internal"
|
||||
"github.com/matrix-org/dendrite/roomserver/inthttp"
|
||||
"github.com/matrix-org/dendrite/roomserver/storage"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
@ -40,11 +37,6 @@ func NewInternalAPI(
|
|||
) api.RoomserverInternalAPI {
|
||||
cfg := &base.Cfg.RoomServer
|
||||
|
||||
var perspectiveServerNames []gomatrixserverlib.ServerName
|
||||
for _, kp := range base.Cfg.FederationAPI.KeyPerspectives {
|
||||
perspectiveServerNames = append(perspectiveServerNames, kp.ServerName)
|
||||
}
|
||||
|
||||
roomserverDB, err := storage.Open(base, &cfg.Database, base.Caches)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Panicf("failed to connect to room server db")
|
||||
|
|
@ -53,9 +45,6 @@ func NewInternalAPI(
|
|||
js, nc := base.NATS.Prepare(base.ProcessContext, &cfg.Matrix.JetStream)
|
||||
|
||||
return internal.NewRoomserverAPI(
|
||||
base.ProcessContext, cfg, roomserverDB, js, nc,
|
||||
cfg.Matrix.JetStream.Prefixed(jetstream.InputRoomEvent),
|
||||
cfg.Matrix.JetStream.Prefixed(jetstream.OutputRoomEvent),
|
||||
base.Caches, perspectiveServerNames,
|
||||
base, roomserverDB, js, nc,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
69
roomserver/roomserver_test.go
Normal file
69
roomserver/roomserver_test.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package roomserver_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver/storage"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/dendrite/test/testrig"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
func mustCreateDatabase(t *testing.T, dbType test.DBType) (*base.BaseDendrite, storage.Database, func()) {
|
||||
base, close := testrig.CreateBaseDendrite(t, dbType)
|
||||
db, err := storage.Open(base, &base.Cfg.KeyServer.Database, base.Caches)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create Database: %v", err)
|
||||
}
|
||||
return base, db, close
|
||||
}
|
||||
|
||||
func Test_SharedUsers(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
room := test.NewRoom(t, alice, test.RoomPreset(test.PresetTrustedPrivateChat))
|
||||
|
||||
// Invite and join Bob
|
||||
room.CreateAndInsert(t, alice, gomatrixserverlib.MRoomMember, map[string]interface{}{
|
||||
"membership": "invite",
|
||||
}, test.WithStateKey(bob.ID))
|
||||
room.CreateAndInsert(t, bob, gomatrixserverlib.MRoomMember, map[string]interface{}{
|
||||
"membership": "join",
|
||||
}, test.WithStateKey(bob.ID))
|
||||
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
base, _, close := mustCreateDatabase(t, dbType)
|
||||
defer close()
|
||||
|
||||
rsAPI := roomserver.NewInternalAPI(base)
|
||||
// SetFederationAPI starts the room event input consumer
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
// Create the room
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
// Query the shared users for Alice, there should only be Bob.
|
||||
// This is used by the SyncAPI keychange consumer.
|
||||
res := &api.QuerySharedUsersResponse{}
|
||||
if err := rsAPI.QuerySharedUsers(ctx, &api.QuerySharedUsersRequest{UserID: alice.ID}, res); err != nil {
|
||||
t.Fatalf("unable to query known users: %v", err)
|
||||
}
|
||||
if _, ok := res.UserIDsToCount[bob.ID]; !ok {
|
||||
t.Fatalf("expected to find %s in shared users, but didn't: %+v", bob.ID, res.UserIDsToCount)
|
||||
}
|
||||
// Also verify that we get the expected result when specifying OtherUserIDs.
|
||||
// This is used by the SyncAPI when getting device list changes.
|
||||
if err := rsAPI.QuerySharedUsers(ctx, &api.QuerySharedUsersRequest{UserID: alice.ID, OtherUserIDs: []string{bob.ID}}, res); err != nil {
|
||||
t.Fatalf("unable to query known users: %v", err)
|
||||
}
|
||||
if _, ok := res.UserIDsToCount[bob.ID]; !ok {
|
||||
t.Fatalf("expected to find %s in shared users, but didn't: %+v", bob.ID, res.UserIDsToCount)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -20,9 +20,11 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
|
|
@ -62,6 +64,9 @@ func NewStateResolution(db StateResolutionStorage, roomInfo *types.RoomInfo) Sta
|
|||
func (v *StateResolution) LoadStateAtSnapshot(
|
||||
ctx context.Context, stateNID types.StateSnapshotNID,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.LoadStateAtSnapshot")
|
||||
defer span.Finish()
|
||||
|
||||
stateBlockNIDLists, err := v.db.StateBlockNIDs(ctx, []types.StateSnapshotNID{stateNID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -100,9 +105,12 @@ func (v *StateResolution) LoadStateAtSnapshot(
|
|||
func (v *StateResolution) LoadStateAtEvent(
|
||||
ctx context.Context, eventID string,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.LoadStateAtEvent")
|
||||
defer span.Finish()
|
||||
|
||||
snapshotNID, err := v.db.SnapshotNIDFromEventID(ctx, eventID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LoadStateAtEvent.SnapshotNIDFromEventID failed for event %s : %s", eventID, err)
|
||||
return nil, fmt.Errorf("LoadStateAtEvent.SnapshotNIDFromEventID failed for event %s : %w", eventID, err)
|
||||
}
|
||||
if snapshotNID == 0 {
|
||||
return nil, fmt.Errorf("LoadStateAtEvent.SnapshotNIDFromEventID(%s) returned 0 NID, was this event stored?", eventID)
|
||||
|
|
@ -122,6 +130,9 @@ func (v *StateResolution) LoadStateAtEvent(
|
|||
func (v *StateResolution) LoadCombinedStateAfterEvents(
|
||||
ctx context.Context, prevStates []types.StateAtEvent,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.LoadCombinedStateAfterEvents")
|
||||
defer span.Finish()
|
||||
|
||||
stateNIDs := make([]types.StateSnapshotNID, len(prevStates))
|
||||
for i, state := range prevStates {
|
||||
stateNIDs[i] = state.BeforeStateSnapshotNID
|
||||
|
|
@ -194,6 +205,9 @@ func (v *StateResolution) LoadCombinedStateAfterEvents(
|
|||
func (v *StateResolution) DifferenceBetweeenStateSnapshots(
|
||||
ctx context.Context, oldStateNID, newStateNID types.StateSnapshotNID,
|
||||
) (removed, added []types.StateEntry, err error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.DifferenceBetweeenStateSnapshots")
|
||||
defer span.Finish()
|
||||
|
||||
if oldStateNID == newStateNID {
|
||||
// If the snapshot NIDs are the same then nothing has changed
|
||||
return nil, nil, nil
|
||||
|
|
@ -255,6 +269,9 @@ func (v *StateResolution) LoadStateAtSnapshotForStringTuples(
|
|||
stateNID types.StateSnapshotNID,
|
||||
stateKeyTuples []gomatrixserverlib.StateKeyTuple,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.LoadStateAtSnapshotForStringTuples")
|
||||
defer span.Finish()
|
||||
|
||||
numericTuples, err := v.stringTuplesToNumericTuples(ctx, stateKeyTuples)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -269,6 +286,9 @@ func (v *StateResolution) stringTuplesToNumericTuples(
|
|||
ctx context.Context,
|
||||
stringTuples []gomatrixserverlib.StateKeyTuple,
|
||||
) ([]types.StateKeyTuple, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.stringTuplesToNumericTuples")
|
||||
defer span.Finish()
|
||||
|
||||
eventTypes := make([]string, len(stringTuples))
|
||||
stateKeys := make([]string, len(stringTuples))
|
||||
for i := range stringTuples {
|
||||
|
|
@ -311,6 +331,9 @@ func (v *StateResolution) loadStateAtSnapshotForNumericTuples(
|
|||
stateNID types.StateSnapshotNID,
|
||||
stateKeyTuples []types.StateKeyTuple,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.loadStateAtSnapshotForNumericTuples")
|
||||
defer span.Finish()
|
||||
|
||||
stateBlockNIDLists, err := v.db.StateBlockNIDs(ctx, []types.StateSnapshotNID{stateNID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -359,6 +382,9 @@ func (v *StateResolution) LoadStateAfterEventsForStringTuples(
|
|||
prevStates []types.StateAtEvent,
|
||||
stateKeyTuples []gomatrixserverlib.StateKeyTuple,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.LoadStateAfterEventsForStringTuples")
|
||||
defer span.Finish()
|
||||
|
||||
numericTuples, err := v.stringTuplesToNumericTuples(ctx, stateKeyTuples)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -371,6 +397,9 @@ func (v *StateResolution) loadStateAfterEventsForNumericTuples(
|
|||
prevStates []types.StateAtEvent,
|
||||
stateKeyTuples []types.StateKeyTuple,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.loadStateAfterEventsForNumericTuples")
|
||||
defer span.Finish()
|
||||
|
||||
if len(prevStates) == 1 {
|
||||
// Fast path for a single event.
|
||||
prevState := prevStates[0]
|
||||
|
|
@ -543,6 +572,9 @@ func (v *StateResolution) CalculateAndStoreStateBeforeEvent(
|
|||
event *gomatrixserverlib.Event,
|
||||
isRejected bool,
|
||||
) (types.StateSnapshotNID, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.CalculateAndStoreStateBeforeEvent")
|
||||
defer span.Finish()
|
||||
|
||||
// Load the state at the prev events.
|
||||
prevStates, err := v.db.StateAtEventIDs(ctx, event.PrevEventIDs())
|
||||
if err != nil {
|
||||
|
|
@ -559,6 +591,9 @@ func (v *StateResolution) CalculateAndStoreStateAfterEvents(
|
|||
ctx context.Context,
|
||||
prevStates []types.StateAtEvent,
|
||||
) (types.StateSnapshotNID, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.CalculateAndStoreStateAfterEvents")
|
||||
defer span.Finish()
|
||||
|
||||
metrics := calculateStateMetrics{startTime: time.Now(), prevEventLength: len(prevStates)}
|
||||
|
||||
if len(prevStates) == 0 {
|
||||
|
|
@ -631,6 +666,9 @@ func (v *StateResolution) calculateAndStoreStateAfterManyEvents(
|
|||
prevStates []types.StateAtEvent,
|
||||
metrics calculateStateMetrics,
|
||||
) (types.StateSnapshotNID, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.calculateAndStoreStateAfterManyEvents")
|
||||
defer span.Finish()
|
||||
|
||||
state, algorithm, conflictLength, err :=
|
||||
v.calculateStateAfterManyEvents(ctx, v.roomInfo.RoomVersion, prevStates)
|
||||
metrics.algorithm = algorithm
|
||||
|
|
@ -649,6 +687,9 @@ func (v *StateResolution) calculateStateAfterManyEvents(
|
|||
ctx context.Context, roomVersion gomatrixserverlib.RoomVersion,
|
||||
prevStates []types.StateAtEvent,
|
||||
) (state []types.StateEntry, algorithm string, conflictLength int, err error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.calculateStateAfterManyEvents")
|
||||
defer span.Finish()
|
||||
|
||||
var combined []types.StateEntry
|
||||
// Conflict resolution.
|
||||
// First stage: load the state after each of the prev events.
|
||||
|
|
@ -701,6 +742,9 @@ func (v *StateResolution) resolveConflicts(
|
|||
ctx context.Context, version gomatrixserverlib.RoomVersion,
|
||||
notConflicted, conflicted []types.StateEntry,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.resolveConflicts")
|
||||
defer span.Finish()
|
||||
|
||||
stateResAlgo, err := version.StateResAlgorithm()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -725,6 +769,8 @@ func (v *StateResolution) resolveConflictsV1(
|
|||
ctx context.Context,
|
||||
notConflicted, conflicted []types.StateEntry,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.resolveConflictsV1")
|
||||
defer span.Finish()
|
||||
|
||||
// Load the conflicted events
|
||||
conflictedEvents, eventIDMap, err := v.loadStateEvents(ctx, conflicted)
|
||||
|
|
@ -788,6 +834,9 @@ func (v *StateResolution) resolveConflictsV2(
|
|||
ctx context.Context,
|
||||
notConflicted, conflicted []types.StateEntry,
|
||||
) ([]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.resolveConflictsV2")
|
||||
defer span.Finish()
|
||||
|
||||
estimate := len(conflicted) + len(notConflicted)
|
||||
eventIDMap := make(map[string]types.StateEntry, estimate)
|
||||
|
||||
|
|
@ -815,31 +864,47 @@ func (v *StateResolution) resolveConflictsV2(
|
|||
authEvents := make([]*gomatrixserverlib.Event, 0, estimate*3)
|
||||
gotAuthEvents := make(map[string]struct{}, estimate*3)
|
||||
authDifference := make([]*gomatrixserverlib.Event, 0, estimate)
|
||||
knownAuthEvents := make(map[string]types.Event, estimate*3)
|
||||
|
||||
// For each conflicted event, let's try and get the needed auth events.
|
||||
for _, conflictedEvent := range conflictedEvents {
|
||||
// Work out which auth events we need to load.
|
||||
key := conflictedEvent.EventID()
|
||||
if err = func() error {
|
||||
span, sctx := opentracing.StartSpanFromContext(ctx, "StateResolution.loadAuthEvents")
|
||||
defer span.Finish()
|
||||
|
||||
// Store the newly found auth events in the auth set for this event.
|
||||
var authEventMap map[string]types.StateEntry
|
||||
authSets[key], authEventMap, err = v.loadAuthEvents(ctx, conflictedEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range authEventMap {
|
||||
eventIDMap[k] = v
|
||||
loader := authEventLoader{
|
||||
v: v,
|
||||
lookupFromDB: make([]string, 0, len(conflictedEvents)*3),
|
||||
lookupFromMem: make([]string, 0, len(conflictedEvents)*3),
|
||||
lookedUpEvents: make([]types.Event, 0, len(conflictedEvents)*3),
|
||||
eventMap: map[string]types.Event{},
|
||||
}
|
||||
for _, conflictedEvent := range conflictedEvents {
|
||||
// Work out which auth events we need to load.
|
||||
key := conflictedEvent.EventID()
|
||||
|
||||
// Only add auth events into the authEvents slice once, otherwise the
|
||||
// check for the auth difference can become expensive and produce
|
||||
// duplicate entries, which just waste memory and CPU time.
|
||||
for _, event := range authSets[key] {
|
||||
if _, ok := gotAuthEvents[event.EventID()]; !ok {
|
||||
authEvents = append(authEvents, event)
|
||||
gotAuthEvents[event.EventID()] = struct{}{}
|
||||
// Store the newly found auth events in the auth set for this event.
|
||||
var authEventMap map[string]types.StateEntry
|
||||
authSets[key], authEventMap, err = loader.loadAuthEvents(sctx, conflictedEvent, knownAuthEvents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range authEventMap {
|
||||
eventIDMap[k] = v
|
||||
}
|
||||
|
||||
// Only add auth events into the authEvents slice once, otherwise the
|
||||
// check for the auth difference can become expensive and produce
|
||||
// duplicate entries, which just waste memory and CPU time.
|
||||
for _, event := range authSets[key] {
|
||||
if _, ok := gotAuthEvents[event.EventID()]; !ok {
|
||||
authEvents = append(authEvents, event)
|
||||
gotAuthEvents[event.EventID()] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Kill the reference to this so that the GC may pick it up, since we no
|
||||
|
|
@ -870,19 +935,29 @@ func (v *StateResolution) resolveConflictsV2(
|
|||
// Look through all of the auth events that we've been given and work out if
|
||||
// there are any events which don't appear in all of the auth sets. If they
|
||||
// don't then we add them to the auth difference.
|
||||
for _, event := range authEvents {
|
||||
if !isInAllAuthLists(event) {
|
||||
authDifference = append(authDifference, event)
|
||||
func() {
|
||||
span, _ := opentracing.StartSpanFromContext(ctx, "isInAllAuthLists")
|
||||
defer span.Finish()
|
||||
|
||||
for _, event := range authEvents {
|
||||
if !isInAllAuthLists(event) {
|
||||
authDifference = append(authDifference, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Resolve the conflicts.
|
||||
resolvedEvents := gomatrixserverlib.ResolveStateConflictsV2(
|
||||
conflictedEvents,
|
||||
nonConflictedEvents,
|
||||
authEvents,
|
||||
authDifference,
|
||||
)
|
||||
resolvedEvents := func() []*gomatrixserverlib.Event {
|
||||
span, _ := opentracing.StartSpanFromContext(ctx, "gomatrixserverlib.ResolveStateConflictsV2")
|
||||
defer span.Finish()
|
||||
|
||||
return gomatrixserverlib.ResolveStateConflictsV2(
|
||||
conflictedEvents,
|
||||
nonConflictedEvents,
|
||||
authEvents,
|
||||
authDifference,
|
||||
)
|
||||
}()
|
||||
|
||||
// Map from the full events back to numeric state entries.
|
||||
for _, resolvedEvent := range resolvedEvents {
|
||||
|
|
@ -947,9 +1022,12 @@ func (v *StateResolution) stateKeyTuplesNeeded(stateKeyNIDMap map[string]types.E
|
|||
func (v *StateResolution) loadStateEvents(
|
||||
ctx context.Context, entries []types.StateEntry,
|
||||
) ([]*gomatrixserverlib.Event, map[string]types.StateEntry, error) {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "StateResolution.loadStateEvents")
|
||||
defer span.Finish()
|
||||
|
||||
result := make([]*gomatrixserverlib.Event, 0, len(entries))
|
||||
eventEntries := make([]types.StateEntry, 0, len(entries))
|
||||
eventNIDs := make([]types.EventNID, 0, len(entries))
|
||||
eventNIDs := make(types.EventNIDs, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if e, ok := v.events[entry.EventNID]; ok {
|
||||
result = append(result, e)
|
||||
|
|
@ -975,43 +1053,86 @@ func (v *StateResolution) loadStateEvents(
|
|||
return result, eventIDMap, nil
|
||||
}
|
||||
|
||||
type authEventLoader struct {
|
||||
sync.Mutex
|
||||
v *StateResolution
|
||||
lookupFromDB []string // scratch space
|
||||
lookupFromMem []string // scratch space
|
||||
lookedUpEvents []types.Event // scratch space
|
||||
eventMap map[string]types.Event
|
||||
}
|
||||
|
||||
// loadAuthEvents loads all of the auth events for a given event recursively,
|
||||
// along with a map that contains state entries for all of the auth events.
|
||||
func (v *StateResolution) loadAuthEvents(
|
||||
ctx context.Context, event *gomatrixserverlib.Event,
|
||||
func (l *authEventLoader) loadAuthEvents(
|
||||
ctx context.Context, event *gomatrixserverlib.Event, eventMap map[string]types.Event,
|
||||
) ([]*gomatrixserverlib.Event, map[string]types.StateEntry, error) {
|
||||
eventMap := map[string]struct{}{}
|
||||
var lookup []string
|
||||
var authEvents []types.Event
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
authEvents := []types.Event{} // our returned list
|
||||
included := map[string]struct{}{} // dedupes authEvents above
|
||||
queue := event.AuthEventIDs()
|
||||
for i := 0; i < len(queue); i++ {
|
||||
lookup = lookup[:0]
|
||||
// Reuse the same underlying memory, since it reduces the
|
||||
// amount of allocations we make the more times we call
|
||||
// loadAuthEvents.
|
||||
l.lookupFromDB = l.lookupFromDB[:0]
|
||||
l.lookupFromMem = l.lookupFromMem[:0]
|
||||
l.lookedUpEvents = l.lookedUpEvents[:0]
|
||||
|
||||
// Separate out the list of events in the queue based on if
|
||||
// we think we already know the event in memory or not.
|
||||
for _, authEventID := range queue {
|
||||
if _, ok := eventMap[authEventID]; ok {
|
||||
if _, ok := included[authEventID]; ok {
|
||||
continue
|
||||
}
|
||||
lookup = append(lookup, authEventID)
|
||||
if _, ok := eventMap[authEventID]; ok {
|
||||
l.lookupFromMem = append(l.lookupFromMem, authEventID)
|
||||
} else {
|
||||
l.lookupFromDB = append(l.lookupFromDB, authEventID)
|
||||
}
|
||||
}
|
||||
if len(lookup) == 0 {
|
||||
// If there's nothing to do, stop here.
|
||||
if len(l.lookupFromDB) == 0 && len(l.lookupFromMem) == 0 {
|
||||
break
|
||||
}
|
||||
events, err := v.db.EventsFromIDs(ctx, lookup)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("v.db.EventsFromIDs: %w", err)
|
||||
|
||||
// If we need to get events from the database, go and fetch
|
||||
// those now.
|
||||
if len(l.lookupFromDB) > 0 {
|
||||
eventsFromDB, err := l.v.db.EventsFromIDs(ctx, l.lookupFromDB)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("v.db.EventsFromIDs: %w", err)
|
||||
}
|
||||
l.lookedUpEvents = append(l.lookedUpEvents, eventsFromDB...)
|
||||
for _, event := range eventsFromDB {
|
||||
eventMap[event.EventID()] = event
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the gaps with events that we already have in memory.
|
||||
if len(l.lookupFromMem) > 0 {
|
||||
for _, eventID := range l.lookupFromMem {
|
||||
l.lookedUpEvents = append(l.lookedUpEvents, eventMap[eventID])
|
||||
}
|
||||
}
|
||||
|
||||
// From the events that we've retrieved, work out which auth
|
||||
// events to look up on the next iteration.
|
||||
add := map[string]struct{}{}
|
||||
for _, event := range events {
|
||||
eventMap[event.EventID()] = struct{}{}
|
||||
for _, event := range l.lookedUpEvents {
|
||||
authEvents = append(authEvents, event)
|
||||
included[event.EventID()] = struct{}{}
|
||||
|
||||
for _, authEventID := range event.AuthEventIDs() {
|
||||
if _, ok := eventMap[authEventID]; ok {
|
||||
if _, ok := included[authEventID]; ok {
|
||||
continue
|
||||
}
|
||||
add[authEventID] = struct{}{}
|
||||
}
|
||||
for authEventID := range add {
|
||||
queue = append(queue, authEventID)
|
||||
}
|
||||
}
|
||||
for authEventID := range add {
|
||||
queue = append(queue, authEventID)
|
||||
}
|
||||
}
|
||||
authEventTypes := map[string]struct{}{}
|
||||
|
|
@ -1028,11 +1149,11 @@ func (v *StateResolution) loadAuthEvents(
|
|||
for eventStateKey := range authEventStateKeys {
|
||||
lookupAuthEventStateKeys = append(lookupAuthEventStateKeys, eventStateKey)
|
||||
}
|
||||
eventTypes, err := v.db.EventTypeNIDs(ctx, lookupAuthEventTypes)
|
||||
eventTypes, err := l.v.db.EventTypeNIDs(ctx, lookupAuthEventTypes)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("v.db.EventTypeNIDs: %w", err)
|
||||
}
|
||||
eventStateKeys, err := v.db.EventStateKeyNIDs(ctx, lookupAuthEventStateKeys)
|
||||
eventStateKeys, err := l.v.db.EventStateKeyNIDs(ctx, lookupAuthEventStateKeys)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("v.db.EventStateKeyNIDs: %w", err)
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue