mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-06 14:33:10 -06:00
Merge pull request #81 from globekeeper/release/upstream-v0.13.5-16
Release/upstream v0.13.5 16
This commit is contained in:
commit
7e16873f2f
2
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
2
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
|
|
@ -62,6 +62,6 @@ If you can identify any relevant log snippets from server logs, please include
|
|||
those (please be careful to remove any personal or private data). Please surround them with
|
||||
``` (three backticks, on a line on their own), so that they are formatted legibly.
|
||||
|
||||
Alternatively, please send logs to @kegan:matrix.org or @neilalexander:matrix.org
|
||||
Alternatively, please send logs to @kegan:matrix.org, @s7evink:matrix.org or @devonh:one.ems.host
|
||||
with a link to the respective Github issue, thanks!
|
||||
-->
|
||||
|
|
|
|||
10
.github/workflows/dendrite.yml
vendored
10
.github/workflows/dendrite.yml
vendored
|
|
@ -123,7 +123,7 @@ jobs:
|
|||
with:
|
||||
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- run: go test -json -v ./... 2>&1 | gotestfmt
|
||||
- run: go test -json -v ./... 2>&1 | gotestfmt -hide all
|
||||
env:
|
||||
POSTGRES_HOST: localhost
|
||||
POSTGRES_USER: postgres
|
||||
|
|
@ -255,7 +255,7 @@ jobs:
|
|||
key: ${{ runner.os }}-go-stable-test-race-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-stable-test-race-
|
||||
- run: go test -race -json -v -coverpkg=./... -coverprofile=cover.out $(go list ./... | grep -v /cmd/dendrite*) 2>&1 | gotestfmt
|
||||
- run: go test -race -json -v -coverpkg=./... -coverprofile=cover.out $(go list ./... | grep -v /cmd/dendrite*) 2>&1 | gotestfmt -hide all
|
||||
env:
|
||||
POSTGRES_HOST: localhost
|
||||
POSTGRES_USER: postgres
|
||||
|
|
@ -280,6 +280,8 @@ jobs:
|
|||
with:
|
||||
go-version: "stable"
|
||||
cache: true
|
||||
- name: Docker version
|
||||
run: docker version
|
||||
- name: Build upgrade-tests
|
||||
run: go build ./cmd/dendrite-upgrade-tests
|
||||
- name: Test upgrade (PostgreSQL)
|
||||
|
|
@ -298,6 +300,8 @@ jobs:
|
|||
with:
|
||||
go-version: "stable"
|
||||
cache: true
|
||||
- name: Docker version
|
||||
run: docker version
|
||||
- name: Build upgrade-tests
|
||||
run: go build ./cmd/dendrite-upgrade-tests
|
||||
- name: Test upgrade (PostgreSQL)
|
||||
|
|
@ -421,7 +425,7 @@ jobs:
|
|||
# Run Complement
|
||||
- run: |
|
||||
set -o pipefail &&
|
||||
go test -v -json -tags dendrite_blacklist ./tests/... 2>&1 | gotestfmt
|
||||
go test -v -json -tags dendrite_blacklist ./tests ./tests/csapi 2>&1 | gotestfmt -hide all
|
||||
shell: bash
|
||||
name: Run Complement Tests
|
||||
env:
|
||||
|
|
|
|||
22
.github/workflows/docker.yml
vendored
22
.github/workflows/docker.yml
vendored
|
|
@ -32,10 +32,6 @@ jobs:
|
|||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
run: |
|
||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
echo "BUILD=$(git rev-parse --short HEAD || \"\")" >> $GITHUB_ENV
|
||||
BRANCH=$(git symbolic-ref --short HEAD | tr -d \/)
|
||||
[ ${BRANCH} == "main" ] && BRANCH=""
|
||||
echo "BRANCH=${BRANCH}" >> $GITHUB_ENV
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
|
|
@ -57,10 +53,9 @@ jobs:
|
|||
id: docker_build_monolith
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache
|
||||
cache-to: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache,mode=max
|
||||
context: .
|
||||
build-args: FLAGS=-X github.com/matrix-org/dendrite/internal.branch=${{ env.BRANCH }} -X github.com/matrix-org/dendrite/internal.build=${{ env.BUILD }}
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
push: true
|
||||
tags: |
|
||||
|
|
@ -75,7 +70,6 @@ jobs:
|
|||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
context: .
|
||||
build-args: FLAGS=-X github.com/matrix-org/dendrite/internal.branch=${{ env.BRANCH }} -X github.com/matrix-org/dendrite/internal.build=${{ env.BUILD }}
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
push: true
|
||||
tags: |
|
||||
|
|
@ -109,10 +103,6 @@ jobs:
|
|||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
run: |
|
||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
echo "BUILD=$(git rev-parse --short HEAD || \"\")" >> $GITHUB_ENV
|
||||
BRANCH=$(git symbolic-ref --short HEAD | tr -d \/)
|
||||
[ ${BRANCH} == "main" ] && BRANCH=""
|
||||
echo "BRANCH=${BRANCH}" >> $GITHUB_ENV
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
|
|
@ -137,7 +127,6 @@ jobs:
|
|||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
context: .
|
||||
build-args: FLAGS=-X github.com/matrix-org/dendrite/internal.branch=${{ env.BRANCH }} -X github.com/matrix-org/dendrite/internal.build=${{ env.BUILD }}
|
||||
file: ./build/docker/Dockerfile.demo-pinecone
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
push: true
|
||||
|
|
@ -153,7 +142,6 @@ jobs:
|
|||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
context: .
|
||||
build-args: FLAGS=-X github.com/matrix-org/dendrite/internal.branch=${{ env.BRANCH }} -X github.com/matrix-org/dendrite/internal.build=${{ env.BUILD }}
|
||||
file: ./build/docker/Dockerfile.demo-pinecone
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
push: true
|
||||
|
|
@ -176,10 +164,6 @@ jobs:
|
|||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
run: |
|
||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
echo "BUILD=$(git rev-parse --short HEAD || \"\")" >> $GITHUB_ENV
|
||||
BRANCH=$(git symbolic-ref --short HEAD | tr -d \/)
|
||||
[ ${BRANCH} == "main" ] && BRANCH=""
|
||||
echo "BRANCH=${BRANCH}" >> $GITHUB_ENV
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
|
|
@ -204,7 +188,6 @@ jobs:
|
|||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
context: .
|
||||
build-args: FLAGS=-X github.com/matrix-org/dendrite/internal.branch=${{ env.BRANCH }} -X github.com/matrix-org/dendrite/internal.build=${{ env.BUILD }}
|
||||
file: ./build/docker/Dockerfile.demo-yggdrasil
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
push: true
|
||||
|
|
@ -220,7 +203,6 @@ jobs:
|
|||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
context: .
|
||||
build-args: FLAGS=-X github.com/matrix-org/dendrite/internal.branch=${{ env.BRANCH }} -X github.com/matrix-org/dendrite/internal.build=${{ env.BUILD }}
|
||||
file: ./build/docker/Dockerfile.demo-yggdrasil
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
push: true
|
||||
|
|
|
|||
2
.github/workflows/helm.yml
vendored
2
.github/workflows/helm.yml
vendored
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
version: v3.10.0
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.4.1
|
||||
uses: helm/chart-releaser-action@ed43eb303604cbc0eeec8390544f7748dc6c790d # specific commit, since `mark_as_latest` is not yet in a release
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
with:
|
||||
|
|
|
|||
97
.github/workflows/k8s.yml
vendored
97
.github/workflows/k8s.yml
vendored
|
|
@ -40,52 +40,53 @@ jobs:
|
|||
run: ct lint --config helm/ct.yaml
|
||||
|
||||
# only bother to run if lint step reports a change to the helm chart
|
||||
install:
|
||||
needs:
|
||||
- lint
|
||||
if: ${{ needs.lint.outputs.changed == 'true' }}
|
||||
name: Install Helm charts
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.checkoutCommit }}
|
||||
- name: Install Kubernetes tools
|
||||
uses: yokawasa/action-setup-kube-tools@v0.8.2
|
||||
with:
|
||||
setup-tools: |
|
||||
helmv3
|
||||
helm: "3.10.3"
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@v2.3.1
|
||||
- name: Create k3d cluster
|
||||
uses: nolar/setup-k3d-k3s@v1
|
||||
with:
|
||||
version: v1.21
|
||||
- name: Remove node taints
|
||||
run: |
|
||||
kubectl taint --all=true nodes node.cloudprovider.kubernetes.io/uninitialized- || true
|
||||
- name: Run chart-testing (install)
|
||||
run: ct install --config helm/ct.yaml
|
||||
# GlobeKeeper: Skipping this as it not needed for now and it is failing due to env.
|
||||
# install:
|
||||
# needs:
|
||||
# - lint
|
||||
# if: ${{ needs.lint.outputs.changed == 'true' }}
|
||||
# name: Install Helm charts
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v3
|
||||
# with:
|
||||
# fetch-depth: 0
|
||||
# ref: ${{ inputs.checkoutCommit }}
|
||||
# - name: Install Kubernetes tools
|
||||
# uses: yokawasa/action-setup-kube-tools@v0.8.2
|
||||
# with:
|
||||
# setup-tools: |
|
||||
# helmv3
|
||||
# helm: "3.10.3"
|
||||
# - uses: actions/setup-python@v4
|
||||
# with:
|
||||
# python-version: "3.10"
|
||||
# - name: Set up chart-testing
|
||||
# uses: helm/chart-testing-action@v2.3.1
|
||||
# - name: Create k3d cluster
|
||||
# uses: nolar/setup-k3d-k3s@v1
|
||||
# with:
|
||||
# version: v1.21
|
||||
# - name: Remove node taints
|
||||
# run: |
|
||||
# kubectl taint --all=true nodes node.cloudprovider.kubernetes.io/uninitialized- || true
|
||||
# - name: Run chart-testing (install)
|
||||
# run: ct install --config helm/ct.yaml
|
||||
|
||||
# Install the chart using helm directly and test with create-account
|
||||
- name: Install chart
|
||||
run: |
|
||||
helm install --values helm/dendrite/ci/ct-postgres-sharedsecret-values.yaml dendrite helm/dendrite
|
||||
- name: Wait for Postgres and Dendrite to be up
|
||||
run: |
|
||||
kubectl wait --for=condition=ready --timeout=90s pod -l app.kubernetes.io/name=postgresql || kubectl get pods -A
|
||||
kubectl wait --for=condition=ready --timeout=90s pod -l app.kubernetes.io/name=dendrite || kubectl get pods -A
|
||||
kubectl get pods -A
|
||||
kubectl get services
|
||||
kubectl get ingress
|
||||
kubectl logs -l app.kubernetes.io/name=dendrite
|
||||
- name: Run create account
|
||||
run: |
|
||||
podName=$(kubectl get pods -l app.kubernetes.io/name=dendrite -o name)
|
||||
kubectl exec "${podName}" -- /usr/bin/create-account -username alice -password somerandompassword
|
||||
# # Install the chart using helm directly and test with create-account
|
||||
# - name: Install chart
|
||||
# run: |
|
||||
# helm install --values helm/dendrite/ci/ct-postgres-sharedsecret-values.yaml dendrite helm/dendrite
|
||||
# - name: Wait for Postgres and Dendrite to be up
|
||||
# run: |
|
||||
# kubectl wait --for=condition=ready --timeout=90s pod -l app.kubernetes.io/name=postgresql || kubectl get pods -A
|
||||
# kubectl wait --for=condition=ready --timeout=90s pod -l app.kubernetes.io/name=dendrite || kubectl get pods -A
|
||||
# kubectl get pods -A
|
||||
# kubectl get services
|
||||
# kubectl get ingress
|
||||
# kubectl logs -l app.kubernetes.io/name=dendrite
|
||||
# - name: Run create account
|
||||
# run: |
|
||||
# podName=$(kubectl get pods -l app.kubernetes.io/name=dendrite -o name)
|
||||
# kubectl exec "${podName}" -- /usr/bin/create-account -username alice -password somerandompassword
|
||||
2
.github/workflows/schedules.yaml
vendored
2
.github/workflows/schedules.yaml
vendored
|
|
@ -128,7 +128,7 @@ jobs:
|
|||
# See https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md specifically GOROOT_1_17_X64
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
||||
go get -v github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||
- name: Run actions/checkout@v3 for dendrite
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
|
|
|
|||
24
CHANGES.md
24
CHANGES.md
|
|
@ -1,5 +1,29 @@
|
|||
# Changelog
|
||||
|
||||
## Dendrite 0.13.5 (2023-12-12)
|
||||
|
||||
Upgrading to this version is **highly** recommended, as it fixes several long-standing bugs in
|
||||
our CanonicalJSON implementation.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Convert unicode escapes to lowercase (gomatrixserverlib)
|
||||
- Fix canonical json utf-16 surrogate pair detection logic (gomatrixserverlib)
|
||||
- Handle negative zero and exponential numbers in Canonical JSON verification (gomatrixserverlib)
|
||||
- Avoid logging unnecessary messages when unable to fetch server keys if multiple fetchers are used (gomatrixserverlib)
|
||||
- Issues around the device list updater have been fixed, which should ensure that there are always
|
||||
workers available to process incoming device list updates.
|
||||
- A panic in the `/hierarchy` endpoints used for spaces has been fixed (client-server and server-server API)
|
||||
- Fixes around the way we handle database transactions (including a potential connection leak)
|
||||
- ACLs are now updated when received as outliers
|
||||
- A race condition, which could lead to bridges instantly leaving a room after joining it, between the SyncAPI and
|
||||
Appservices has been fixed
|
||||
|
||||
### Features
|
||||
|
||||
- **Appservice login is now supported!**
|
||||
- Users can now kick themselves (used by some bridges)
|
||||
|
||||
## Dendrite 0.13.4 (2023-10-25)
|
||||
|
||||
Upgrading to this version is **highly** recommended, as it fixes a long-standing bug in the state resolution
|
||||
|
|
|
|||
18
Dockerfile
18
Dockerfile
|
|
@ -1,9 +1,12 @@
|
|||
#syntax=docker/dockerfile:1.2
|
||||
|
||||
#
|
||||
# base installs required dependencies and runs go mod download to cache dependencies
|
||||
#
|
||||
# Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved
|
||||
ARG BUILDPLATFORM=${BUILDPLATFORM}
|
||||
FROM --platform=$BUILDPLATFORM docker.io/golang:1.21-alpine AS base
|
||||
RUN apk --update --no-cache add bash build-base curl
|
||||
FROM --platform=$BUILDPLATFORM docker.io/golang:1.21-alpine3.18 AS base
|
||||
RUN apk --update --no-cache add bash build-base curl git
|
||||
|
||||
#
|
||||
# build creates all needed binaries
|
||||
|
|
@ -12,8 +15,9 @@ FROM --platform=$BUILDPLATFORM base AS build
|
|||
WORKDIR /src
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG FLAGS
|
||||
|
||||
# Mount volumes using the -v flag instead of --mount to avoid requiring BuildKit which is not easily supported using cloudbuild.
|
||||
# Not mounting volumes using the --mount flag to avoid requiring BuildKit which is not easily supported using cloudbuild.
|
||||
COPY . .
|
||||
RUN mkdir -p /root/.cache/go-build && \
|
||||
mkdir -p /go/pkg/mod
|
||||
|
|
@ -22,10 +26,10 @@ VOLUME /go/pkg/mod
|
|||
|
||||
# Run the build command in multiple RUN commands
|
||||
RUN USERARCH=`go env GOARCH`
|
||||
ENV GOARCH="$TARGETARCH"
|
||||
ENV GOOS="linux"
|
||||
RUN GOARCH="$TARGETARCH"
|
||||
RUN GOOS="linux"
|
||||
RUN CGO_ENABLED=$([ "$TARGETARCH" = "$USERARCH" ] && echo "1" || echo "0")
|
||||
RUN go build -v -trimpath -o /out/ ./cmd/...
|
||||
RUN go build -v -ldflags="${FLAGS}" -trimpath -o /out/ ./cmd/...
|
||||
|
||||
#
|
||||
# Builds the Dendrite image containing all required binaries
|
||||
|
|
@ -36,8 +40,8 @@ LABEL org.opencontainers.image.title="Dendrite"
|
|||
LABEL org.opencontainers.image.description="Next-generation Matrix homeserver written in Go"
|
||||
LABEL org.opencontainers.image.source="https://github.com/matrix-org/dendrite"
|
||||
LABEL org.opencontainers.image.licenses="Apache-2.0"
|
||||
LABEL org.opencontainers.image.vendor="The Matrix.org Foundation C.I.C."
|
||||
LABEL org.opencontainers.image.documentation="https://matrix-org.github.io/dendrite/"
|
||||
LABEL org.opencontainers.image.vendor="The Matrix.org Foundation C.I.C."
|
||||
|
||||
COPY --from=build /out/create-account /usr/bin/create-account
|
||||
COPY --from=build /out/generate-config /usr/bin/generate-config
|
||||
|
|
|
|||
|
|
@ -14,7 +14,18 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/syncapi"
|
||||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/matrix-org/dendrite/appservice"
|
||||
"github.com/matrix-org/dendrite/appservice/api"
|
||||
|
|
@ -32,6 +43,10 @@ import (
|
|||
"github.com/matrix-org/dendrite/test/testrig"
|
||||
)
|
||||
|
||||
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||
return &statistics.ServerStatistics{}, nil
|
||||
}
|
||||
|
||||
func TestAppserviceInternalAPI(t *testing.T) {
|
||||
|
||||
// Set expected results
|
||||
|
|
@ -144,7 +159,7 @@ func TestAppserviceInternalAPI(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI)
|
||||
|
||||
runCases(t, asAPI)
|
||||
|
|
@ -239,7 +254,7 @@ func TestAppserviceInternalAPI_UnixSocket_Simple(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI)
|
||||
|
||||
t.Run("UserIDExists", func(t *testing.T) {
|
||||
|
|
@ -378,7 +393,7 @@ func TestRoomserverConsumerOneInvite(t *testing.T) {
|
|||
// Create required internal APIs
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil)
|
||||
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
// start the consumer
|
||||
appservice.NewInternalAPI(processCtx, cfg, natsInstance, usrAPI, rsAPI)
|
||||
|
||||
|
|
@ -402,3 +417,190 @@ func TestRoomserverConsumerOneInvite(t *testing.T) {
|
|||
close(evChan)
|
||||
})
|
||||
}
|
||||
|
||||
// Note: If this test panics, it is because we timed out waiting for the
|
||||
// join event to come through to the appservice and we close the DB/shutdown Dendrite. This makes the
|
||||
// syncAPI unhappy, as it is unable to write to the database.
|
||||
func TestOutputAppserviceEvent(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType)
|
||||
defer closeDB()
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
natsInstance := &jetstream.NATSInstance{}
|
||||
|
||||
evChan := make(chan struct{})
|
||||
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
// Create required internal APIs
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
|
||||
// Create the router, so we can hit `/joined_members`
|
||||
routers := httputil.NewRouters()
|
||||
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
bob: {},
|
||||
}
|
||||
|
||||
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
clientapi.AddPublicRoutes(processCtx, routers, cfg, natsInstance, nil, rsAPI, nil, nil, nil, usrAPI, nil, nil, caching.DisableMetrics)
|
||||
createAccessTokens(t, accessTokens, usrAPI, processCtx.Context(), routers)
|
||||
|
||||
room := test.NewRoom(t, alice)
|
||||
|
||||
// Invite Bob
|
||||
room.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": "invite",
|
||||
}, test.WithStateKey(bob.ID))
|
||||
|
||||
// create a dummy AS url, handling the events
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var txn consumers.ApplicationServiceTransaction
|
||||
err := json.NewDecoder(r.Body).Decode(&txn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, ev := range txn.Events {
|
||||
if ev.Type != spec.MRoomMember {
|
||||
continue
|
||||
}
|
||||
if ev.StateKey != nil && *ev.StateKey == bob.ID {
|
||||
membership := gjson.GetBytes(ev.Content, "membership").Str
|
||||
t.Logf("Processing membership: %s", membership)
|
||||
switch membership {
|
||||
case spec.Invite:
|
||||
// Accept the invite
|
||||
joinEv := room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": "join",
|
||||
}, test.WithStateKey(bob.ID))
|
||||
|
||||
if err := rsapi.SendEvents(context.Background(), rsAPI, rsapi.KindNew, []*types.HeaderedEvent{joinEv}, "test", "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
case spec.Join: // the AS has received the join event, now hit `/joined_members` to validate that
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/rooms/"+room.ID+"/joined_members", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||
routers.Client.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
// Both Alice and Bob should be joined. If not, we have a race condition
|
||||
if !gjson.GetBytes(rec.Body.Bytes(), "joined."+alice.ID).Exists() {
|
||||
t.Errorf("Alice is not joined to the room") // in theory should not happen
|
||||
}
|
||||
if !gjson.GetBytes(rec.Body.Bytes(), "joined."+bob.ID).Exists() {
|
||||
t.Errorf("Bob is not joined to the room")
|
||||
}
|
||||
evChan <- struct{}{}
|
||||
default:
|
||||
t.Fatalf("Unexpected membership: %s", membership)
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
as := &config.ApplicationService{
|
||||
ID: "someID",
|
||||
URL: srv.URL,
|
||||
ASToken: "",
|
||||
HSToken: "",
|
||||
SenderLocalpart: "senderLocalPart",
|
||||
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
|
||||
"users": {{RegexpObject: regexp.MustCompile(bob.ID)}},
|
||||
"aliases": {{RegexpObject: regexp.MustCompile(room.ID)}},
|
||||
},
|
||||
}
|
||||
as.CreateHTTPClient(cfg.AppServiceAPI.DisableTLSValidation)
|
||||
|
||||
// Create a dummy application service
|
||||
cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as}
|
||||
|
||||
// Prepare AS Streams on the old topic to validate that they get deleted
|
||||
jsCtx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||
|
||||
token := jetstream.Tokenise(as.ID)
|
||||
if err := jetstream.JetStreamConsumer(
|
||||
processCtx.Context(), jsCtx, cfg.Global.JetStream.Prefixed(jetstream.OutputRoomEvent),
|
||||
cfg.Global.JetStream.Durable("Appservice_"+token),
|
||||
50, // maximum number of events to send in a single transaction
|
||||
func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||
return true
|
||||
},
|
||||
); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Start the syncAPI to have `/joined_members` available
|
||||
syncapi.AddPublicRoutes(processCtx, routers, cfg, cm, natsInstance, usrAPI, rsAPI, caches, caching.DisableMetrics)
|
||||
|
||||
// start the consumer
|
||||
appservice.NewInternalAPI(processCtx, cfg, natsInstance, usrAPI, rsAPI)
|
||||
|
||||
// At this point, the old JetStream consumers should be deleted
|
||||
for consumer := range jsCtx.Consumers(cfg.Global.JetStream.Prefixed(jetstream.OutputRoomEvent)) {
|
||||
if consumer.Name == cfg.Global.JetStream.Durable("Appservice_"+token)+"Pull" {
|
||||
t.Fatalf("Consumer still exists")
|
||||
}
|
||||
}
|
||||
|
||||
// Create the room, this triggers the AS to receive an invite for Bob.
|
||||
if err := rsapi.SendEvents(context.Background(), rsAPI, rsapi.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
// Pretty generous timeout duration...
|
||||
case <-time.After(time.Millisecond * 1000): // wait for the AS to process the events
|
||||
t.Errorf("Timed out waiting for join event")
|
||||
case <-evChan:
|
||||
}
|
||||
close(evChan)
|
||||
})
|
||||
}
|
||||
|
||||
type userDevice struct {
|
||||
accessToken string
|
||||
deviceID string
|
||||
password string
|
||||
}
|
||||
|
||||
func createAccessTokens(t *testing.T, accessTokens map[*test.User]userDevice, userAPI uapi.UserInternalAPI, ctx context.Context, routers httputil.Routers) {
|
||||
t.Helper()
|
||||
for u := range accessTokens {
|
||||
localpart, serverName, _ := gomatrixserverlib.SplitID('@', u.ID)
|
||||
userRes := &uapi.PerformAccountCreationResponse{}
|
||||
password := util.RandomString(8)
|
||||
if err := userAPI.PerformAccountCreation(ctx, &uapi.PerformAccountCreationRequest{
|
||||
AccountType: u.AccountType,
|
||||
Localpart: localpart,
|
||||
ServerName: serverName,
|
||||
Password: password,
|
||||
}, userRes); err != nil {
|
||||
t.Errorf("failed to create account: %s", err)
|
||||
}
|
||||
req := test.NewRequest(t, http.MethodPost, "/_matrix/client/v3/login", test.WithJSONBody(t, map[string]interface{}{
|
||||
"type": authtypes.LoginTypePassword,
|
||||
"identifier": map[string]interface{}{
|
||||
"type": "m.id.user",
|
||||
"user": u.ID,
|
||||
},
|
||||
"password": password,
|
||||
}))
|
||||
rec := httptest.NewRecorder()
|
||||
routers.Client.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("failed to login: %s", rec.Body.String())
|
||||
}
|
||||
accessTokens[u] = userDevice{
|
||||
accessToken: gjson.GetBytes(rec.Body.Bytes(), "access_token").String(),
|
||||
deviceID: gjson.GetBytes(rec.Body.Bytes(), "device_id").String(),
|
||||
password: password,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,13 +71,14 @@ func NewOutputRoomEventConsumer(
|
|||
ctx: process.Context(),
|
||||
cfg: cfg,
|
||||
jetstream: js,
|
||||
topic: cfg.Matrix.JetStream.Prefixed(jetstream.OutputRoomEvent),
|
||||
topic: cfg.Matrix.JetStream.Prefixed(jetstream.OutputAppserviceEvent),
|
||||
rsAPI: rsAPI,
|
||||
}
|
||||
}
|
||||
|
||||
// Start consuming from room servers
|
||||
func (s *OutputRoomEventConsumer) Start() error {
|
||||
durableNames := make([]string, 0, len(s.cfg.Derived.ApplicationServices))
|
||||
for _, as := range s.cfg.Derived.ApplicationServices {
|
||||
appsvc := as
|
||||
state := &appserviceState{
|
||||
|
|
@ -95,6 +96,15 @@ func (s *OutputRoomEventConsumer) Start() error {
|
|||
); err != nil {
|
||||
return fmt.Errorf("failed to create %q consumer: %w", token, err)
|
||||
}
|
||||
durableNames = append(durableNames, s.cfg.Matrix.JetStream.Durable("Appservice_"+token))
|
||||
}
|
||||
// Cleanup any consumers still existing on the OutputRoomEvent stream
|
||||
// to avoid messages not being deleted
|
||||
for _, consumerName := range durableNames {
|
||||
err := s.jetstream.DeleteConsumer(s.cfg.Matrix.JetStream.Prefixed(jetstream.OutputRoomEvent), consumerName+"Pull")
|
||||
if err != nil && err != nats.ErrConsumerNotFound {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ func (m *DendriteMonolith) Start() {
|
|||
processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true,
|
||||
)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||
|
||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||
rsAPI.SetAppserviceAPI(asAPI)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestAdminCreateToken(t *testing.T) {
|
|||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
|
|
@ -196,7 +196,7 @@ func TestAdminListRegistrationTokens(t *testing.T) {
|
|||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
|
|
@ -314,7 +314,7 @@ func TestAdminGetRegistrationToken(t *testing.T) {
|
|||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
|
|
@ -415,7 +415,7 @@ func TestAdminDeleteRegistrationToken(t *testing.T) {
|
|||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
|
|
@ -509,7 +509,7 @@ func TestAdminUpdateRegistrationToken(t *testing.T) {
|
|||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
|
|
@ -693,7 +693,7 @@ func TestAdminResetPassword(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
// Needed for changing the password/login
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
// We mostly need the userAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
|
|
@ -791,7 +791,7 @@ func TestPurgeRoom(t *testing.T) {
|
|||
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, rsAPI, caches, nil, true)
|
||||
rsAPI.SetFederationAPI(fsAPI, nil)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
syncapi.AddPublicRoutes(processCtx, routers, cfg, cm, &natsInstance, userAPI, rsAPI, caches, caching.DisableMetrics)
|
||||
|
||||
// Create the room
|
||||
|
|
@ -863,7 +863,7 @@ func TestAdminEvacuateRoom(t *testing.T) {
|
|||
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, rsAPI, caches, nil, true)
|
||||
rsAPI.SetFederationAPI(fsAPI, nil)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// Create the room
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", api.DoNotSendToOtherServers, nil, false); err != nil {
|
||||
|
|
@ -964,7 +964,7 @@ func TestAdminEvacuateUser(t *testing.T) {
|
|||
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, basepkg.CreateFederationClient(cfg, nil), rsAPI, caches, nil, true)
|
||||
rsAPI.SetFederationAPI(fsAPI, nil)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// Create the room
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", api.DoNotSendToOtherServers, nil, false); err != nil {
|
||||
|
|
@ -1055,7 +1055,7 @@ func TestAdminMarkAsStale(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
|
@ -33,8 +32,18 @@ import (
|
|||
// called after authorization has completed, with the result of the authorization.
|
||||
// If the final return value is non-nil, an error occurred and the cleanup function
|
||||
// is nil.
|
||||
func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.ClientUserAPI, cfg *config.ClientAPI, rt *ratelimit.RtFailedLogin) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||
reqBytes, err := io.ReadAll(r)
|
||||
// func LoginFromJSONReader(ctx context.Context, r io.Reader,
|
||||
//
|
||||
// useraccountAPI uapi.ClientUserAPI,
|
||||
// cfg *config.ClientAPI, rt *ratelimit.RtFailedLogin) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||
func LoginFromJSONReader(
|
||||
req *http.Request,
|
||||
useraccountAPI uapi.ClientUserAPI,
|
||||
userAPI UserInternalAPIForLogin,
|
||||
cfg *config.ClientAPI,
|
||||
rt *ratelimit.RtFailedLogin,
|
||||
) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||
reqBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
err := &util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
|
|
@ -63,13 +72,26 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.C
|
|||
Config: cfg,
|
||||
Rt: rt,
|
||||
InhibitDevice: header.InhibitDevice,
|
||||
UserLoginAPI: useraccountAPI,
|
||||
}
|
||||
case authtypes.LoginTypeToken:
|
||||
typ = &LoginTypeToken{
|
||||
UserAPI: useraccountAPI,
|
||||
Config: cfg,
|
||||
}
|
||||
case authtypes.LoginTypeApplicationService:
|
||||
token, err := ExtractAccessToken(req)
|
||||
if err != nil {
|
||||
err := &util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.MissingToken(err.Error()),
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
typ = &LoginTypeApplicationService{
|
||||
Config: cfg,
|
||||
Token: token,
|
||||
}
|
||||
case authtypes.LoginTypeJwt:
|
||||
typ = &LoginTypeTokenJwt{
|
||||
Config: cfg,
|
||||
|
|
@ -82,7 +104,7 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.C
|
|||
return nil, nil, &err
|
||||
}
|
||||
|
||||
return typ.LoginFromJSON(ctx, reqBytes)
|
||||
return typ.LoginFromJSON(req.Context(), reqBytes)
|
||||
}
|
||||
|
||||
// UserInternalAPIForLogin contains the aspects of UserAPI required for logging in.
|
||||
|
|
|
|||
55
clientapi/auth/login_application_service.go
Normal file
55
clientapi/auth/login_application_service.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
// LoginTypeApplicationService describes how to authenticate as an
|
||||
// application service
|
||||
type LoginTypeApplicationService struct {
|
||||
Config *config.ClientAPI
|
||||
Token string
|
||||
}
|
||||
|
||||
// Name implements Type
|
||||
func (t *LoginTypeApplicationService) Name() string {
|
||||
return authtypes.LoginTypeApplicationService
|
||||
}
|
||||
|
||||
// LoginFromJSON implements Type
|
||||
func (t *LoginTypeApplicationService) LoginFromJSON(
|
||||
ctx context.Context, reqBytes []byte,
|
||||
) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||
var r Login
|
||||
if err := httputil.UnmarshalJSON(reqBytes, &r); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_, err := internal.ValidateApplicationServiceRequest(t.Config, r.Identifier.User, t.Token)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cleanup := func(ctx context.Context, j *util.JSONResponse) {}
|
||||
return &r, cleanup, nil
|
||||
}
|
||||
|
|
@ -17,7 +17,9 @@ package auth
|
|||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -34,8 +36,9 @@ func TestLoginFromJSONReader(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
|
||||
tsts := []struct {
|
||||
Name string
|
||||
Body string
|
||||
Name string
|
||||
Body string
|
||||
Token string
|
||||
|
||||
WantUsername string
|
||||
WantDeviceID string
|
||||
|
|
@ -63,6 +66,30 @@ func TestLoginFromJSONReader(t *testing.T) {
|
|||
WantDeviceID: "adevice",
|
||||
WantDeletedTokens: []string{"atoken"},
|
||||
},
|
||||
{
|
||||
Name: "appServiceWorksUserID",
|
||||
Body: `{
|
||||
"type": "m.login.application_service",
|
||||
"identifier": { "type": "m.id.user", "user": "@alice:example.com" },
|
||||
"device_id": "adevice"
|
||||
}`,
|
||||
Token: "astoken",
|
||||
|
||||
WantUsername: "@alice:example.com",
|
||||
WantDeviceID: "adevice",
|
||||
},
|
||||
{
|
||||
Name: "appServiceWorksLocalpart",
|
||||
Body: `{
|
||||
"type": "m.login.application_service",
|
||||
"identifier": { "type": "m.id.user", "user": "alice" },
|
||||
"device_id": "adevice"
|
||||
}`,
|
||||
Token: "astoken",
|
||||
|
||||
WantUsername: "alice",
|
||||
WantDeviceID: "adevice",
|
||||
},
|
||||
}
|
||||
for _, tst := range tsts {
|
||||
t.Run(tst.Name, func(t *testing.T) {
|
||||
|
|
@ -73,14 +100,38 @@ func TestLoginFromJSONReader(t *testing.T) {
|
|||
ServerName: serverName,
|
||||
},
|
||||
},
|
||||
Derived: &config.Derived{
|
||||
ApplicationServices: []config.ApplicationService{
|
||||
{
|
||||
ID: "anapplicationservice",
|
||||
ASToken: "astoken",
|
||||
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
|
||||
"users": {
|
||||
{
|
||||
Exclusive: true,
|
||||
Regex: "@alice:example.com",
|
||||
RegexpObject: regexp.MustCompile("@alice:example.com"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RtFailedLogin: ratelimit.RtFailedLoginConfig{
|
||||
Enabled: false,
|
||||
},
|
||||
}
|
||||
login, cleanup, err := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, cfg, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("LoginFromJSONReader failed: %+v", err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tst.Body))
|
||||
if tst.Token != "" {
|
||||
req.Header.Add("Authorization", "Bearer "+tst.Token)
|
||||
}
|
||||
|
||||
login, cleanup, jsonErr := LoginFromJSONReader(req, &userAPI, &userAPI, cfg, nil)
|
||||
if jsonErr != nil {
|
||||
t.Fatalf("LoginFromJSONReader failed: %+v", jsonErr)
|
||||
}
|
||||
|
||||
cleanup(ctx, &util.JSONResponse{Code: http.StatusOK})
|
||||
|
||||
if login.Username() != tst.WantUsername {
|
||||
|
|
@ -108,8 +159,9 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
|
||||
tsts := []struct {
|
||||
Name string
|
||||
Body string
|
||||
Name string
|
||||
Body string
|
||||
Token string
|
||||
|
||||
WantErrCode spec.MatrixErrorCode
|
||||
}{
|
||||
|
|
@ -146,6 +198,45 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
|||
}`,
|
||||
WantErrCode: spec.ErrorInvalidParam,
|
||||
},
|
||||
{
|
||||
Name: "noASToken",
|
||||
Body: `{
|
||||
"type": "m.login.application_service",
|
||||
"identifier": { "type": "m.id.user", "user": "@alice:example.com" },
|
||||
"device_id": "adevice"
|
||||
}`,
|
||||
WantErrCode: "M_MISSING_TOKEN",
|
||||
},
|
||||
{
|
||||
Name: "badASToken",
|
||||
Token: "badastoken",
|
||||
Body: `{
|
||||
"type": "m.login.application_service",
|
||||
"identifier": { "type": "m.id.user", "user": "@alice:example.com" },
|
||||
"device_id": "adevice"
|
||||
}`,
|
||||
WantErrCode: "M_UNKNOWN_TOKEN",
|
||||
},
|
||||
{
|
||||
Name: "badASNamespace",
|
||||
Token: "astoken",
|
||||
Body: `{
|
||||
"type": "m.login.application_service",
|
||||
"identifier": { "type": "m.id.user", "user": "@bob:example.com" },
|
||||
"device_id": "adevice"
|
||||
}`,
|
||||
WantErrCode: "M_EXCLUSIVE",
|
||||
},
|
||||
{
|
||||
Name: "badASUserID",
|
||||
Token: "astoken",
|
||||
Body: `{
|
||||
"type": "m.login.application_service",
|
||||
"identifier": { "type": "m.id.user", "user": "@alice:wrong.example.com" },
|
||||
"device_id": "adevice"
|
||||
}`,
|
||||
WantErrCode: "M_INVALID_USERNAME",
|
||||
},
|
||||
}
|
||||
for _, tst := range tsts {
|
||||
t.Run(tst.Name, func(t *testing.T) {
|
||||
|
|
@ -156,8 +247,30 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
|||
ServerName: serverName,
|
||||
},
|
||||
},
|
||||
Derived: &config.Derived{
|
||||
ApplicationServices: []config.ApplicationService{
|
||||
{
|
||||
ID: "anapplicationservice",
|
||||
ASToken: "astoken",
|
||||
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
|
||||
"users": {
|
||||
{
|
||||
Exclusive: true,
|
||||
Regex: "@alice:example.com",
|
||||
RegexpObject: regexp.MustCompile("@alice:example.com"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, cleanup, errRes := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, cfg, nil)
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tst.Body))
|
||||
if tst.Token != "" {
|
||||
req.Header.Add("Authorization", "Bearer "+tst.Token)
|
||||
}
|
||||
|
||||
_, cleanup, errRes := LoginFromJSONReader(req, &userAPI, &userAPI, cfg, nil)
|
||||
if errRes == nil {
|
||||
cleanup(ctx, nil)
|
||||
t.Fatalf("LoginFromJSONReader err: got %+v, want code %q", errRes, tst.WantErrCode)
|
||||
|
|
@ -170,7 +283,6 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
|||
|
||||
type fakeUserInternalAPI struct {
|
||||
uapi.ClientUserAPI
|
||||
UserInternalAPIForLogin
|
||||
DeletedTokens []string
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/userapi/api"
|
||||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
|
@ -44,11 +45,10 @@ const email = "email"
|
|||
|
||||
// LoginTypePassword implements https://matrix.org/docs/spec/client_server/r0.6.1#password-based
|
||||
type LoginTypePassword struct {
|
||||
UserApi api.ClientUserAPI
|
||||
UserApi uapi.ClientUserAPI
|
||||
Config *config.ClientAPI
|
||||
Rt *ratelimit.RtFailedLogin
|
||||
InhibitDevice bool
|
||||
UserLoginAPI api.UserLoginAPI
|
||||
}
|
||||
|
||||
func (t *LoginTypePassword) Name() string {
|
||||
|
|
@ -195,7 +195,7 @@ func (t *LoginTypePassword) authenticateDb(ctx context.Context, localpart string
|
|||
// If we couldn't find the user by the lower cased localpart, try the provided
|
||||
// localpart as is.
|
||||
if !res.Exists {
|
||||
err = t.UserLoginAPI.QueryAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{
|
||||
err = t.UserApi.QueryAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{
|
||||
Localpart: localpart,
|
||||
ServerName: domain,
|
||||
PlaintextPassword: password,
|
||||
|
|
@ -339,7 +339,7 @@ func (t *LoginTypePassword) isLdapAdmin(conn *ldap.Conn, username string) (bool,
|
|||
|
||||
func (t *LoginTypePassword) getOrCreateAccount(ctx context.Context, localpart string, domain spec.ServerName, admin bool) (*api.Account, *util.JSONResponse) {
|
||||
var existing api.QueryAccountByLocalpartResponse
|
||||
err := t.UserLoginAPI.QueryAccountByLocalpart(ctx, &api.QueryAccountByLocalpartRequest{
|
||||
err := t.UserApi.QueryAccountByLocalpart(ctx, &api.QueryAccountByLocalpartRequest{
|
||||
Localpart: localpart,
|
||||
ServerName: domain,
|
||||
}, &existing)
|
||||
|
|
@ -359,7 +359,7 @@ func (t *LoginTypePassword) getOrCreateAccount(ctx context.Context, localpart st
|
|||
accountType = api.AccountTypeAdmin
|
||||
}
|
||||
var created api.PerformAccountCreationResponse
|
||||
err = t.UserLoginAPI.PerformAccountCreation(ctx, &api.PerformAccountCreationRequest{
|
||||
err = t.UserApi.PerformAccountCreation(ctx, &api.PerformAccountCreationRequest{
|
||||
AppServiceID: "ldap",
|
||||
Localpart: localpart,
|
||||
Password: uuid.New().String(),
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ type LoginCleanupFunc func(context.Context, *util.JSONResponse)
|
|||
// https://matrix.org/docs/spec/client_server/r0.6.1#identifier-types
|
||||
type LoginIdentifier struct {
|
||||
Type string `json:"type"`
|
||||
// when type = m.id.user
|
||||
// when type = m.id.user or m.id.application_service
|
||||
User string `json:"user"`
|
||||
// when type = m.id.thirdparty
|
||||
Medium string `json:"medium"`
|
||||
|
|
@ -114,9 +114,8 @@ type UserInteractive struct {
|
|||
|
||||
func NewUserInteractive(userAccountAPI api.ClientUserAPI, cfg *config.ClientAPI) *UserInteractive {
|
||||
typePassword := &LoginTypePassword{
|
||||
UserApi: userAccountAPI,
|
||||
UserLoginAPI: userAccountAPI,
|
||||
Config: cfg,
|
||||
UserApi: userAccountAPI,
|
||||
Config: cfg,
|
||||
}
|
||||
return &UserInteractive{
|
||||
Flows: []userInteractiveFlow{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
"github.com/matrix-org/dendrite/appservice"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
"github.com/matrix-org/dendrite/internal/pushrules"
|
||||
|
|
@ -31,6 +32,7 @@ import (
|
|||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrix"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
|
|
@ -46,6 +48,10 @@ type userDevice struct {
|
|||
password string
|
||||
}
|
||||
|
||||
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||
return &statistics.ServerStatistics{}, nil
|
||||
}
|
||||
|
||||
func TestGetPutDevices(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
|
|
@ -118,7 +124,7 @@ func TestGetPutDevices(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -167,7 +173,7 @@ func TestDeleteDevice(t *testing.T) {
|
|||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI/ for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -271,7 +277,7 @@ func TestDeleteDevices(t *testing.T) {
|
|||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI/ for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -419,7 +425,7 @@ func TestSetDisplayname(t *testing.T) {
|
|||
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
asPI := appservice.NewInternalAPI(processCtx, cfg, natsInstance, userAPI, rsAPI)
|
||||
|
||||
AddPublicRoutes(processCtx, routers, cfg, natsInstance, base.CreateFederationClient(cfg, nil), rsAPI, asPI, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -531,7 +537,7 @@ func TestSetAvatarURL(t *testing.T) {
|
|||
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
asPI := appservice.NewInternalAPI(processCtx, cfg, natsInstance, userAPI, rsAPI)
|
||||
|
||||
AddPublicRoutes(processCtx, routers, cfg, natsInstance, base.CreateFederationClient(cfg, nil), rsAPI, asPI, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -609,7 +615,7 @@ func TestTyping(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
// Needed to create accounts
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
|
|
@ -693,7 +699,7 @@ func TestMembership(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
// Needed to create accounts
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
rsAPI.SetUserAPI(userAPI)
|
||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -932,7 +938,7 @@ func TestCapabilities(t *testing.T) {
|
|||
// Needed to create accounts
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
|
|
@ -979,7 +985,7 @@ func TestTurnserver(t *testing.T) {
|
|||
// Needed to create accounts
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
//rsAPI.SetUserAPI(userAPI)
|
||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -1075,12 +1081,12 @@ func TestTurnserver(t *testing.T) {
|
|||
// routers := httputil.NewRouters()
|
||||
// cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
|
||||
// // Needed to create accounts
|
||||
// rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
||||
// rsAPI.SetFederationAPI(nil, nil)
|
||||
// userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
// // We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||
// AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
// Needed to create accounts
|
||||
// rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
||||
// rsAPI.SetFederationAPI(nil, nil)
|
||||
// userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
// // We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||
// AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
// // Create the users in the userapi and login
|
||||
// accessTokens := map[*test.User]userDevice{
|
||||
|
|
@ -1254,7 +1260,7 @@ func TestPushRules(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -1641,7 +1647,7 @@ func TestKeys(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -2121,7 +2127,7 @@ func TestKeyBackup(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
|
|
@ -21,6 +22,10 @@ import (
|
|||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
)
|
||||
|
||||
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||
return &statistics.ServerStatistics{}, nil
|
||||
}
|
||||
|
||||
func TestJoinRoomByIDOrAlias(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
|
|
@ -36,7 +41,7 @@ func TestJoinRoomByIDOrAlias(t *testing.T) {
|
|||
natsInstance := jetstream.NATSInstance{}
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil) // creates the rs.Inputer etc
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||
|
||||
// Create the users in the userapi
|
||||
|
|
|
|||
|
|
@ -65,9 +65,8 @@ func UploadCrossSigningDeviceKeys(
|
|||
}
|
||||
}
|
||||
typePassword := auth.LoginTypePassword{
|
||||
UserApi: accountAPI,
|
||||
UserLoginAPI: accountAPI,
|
||||
Config: cfg,
|
||||
UserApi: accountAPI,
|
||||
Config: cfg,
|
||||
}
|
||||
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
|
||||
return *authErr
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/clientapi/ratelimit"
|
||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
|
|
@ -42,15 +43,6 @@ type flow struct {
|
|||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func passwordLogin() flows {
|
||||
f := flows{}
|
||||
s := flow{
|
||||
Type: "m.login.password",
|
||||
}
|
||||
f.Flows = append(f.Flows, s)
|
||||
return f
|
||||
}
|
||||
|
||||
// Login implements GET and POST /login
|
||||
func Login(
|
||||
req *http.Request, userAPI userapi.ClientUserAPI,
|
||||
|
|
@ -58,13 +50,19 @@ func Login(
|
|||
rt *ratelimit.RtFailedLogin,
|
||||
) util.JSONResponse {
|
||||
if req.Method == http.MethodGet {
|
||||
// TODO: support other forms of login other than password, depending on config options
|
||||
loginFlows := []flow{{Type: authtypes.LoginTypePassword}}
|
||||
if len(cfg.Derived.ApplicationServices) > 0 {
|
||||
loginFlows = append(loginFlows, flow{Type: authtypes.LoginTypeApplicationService})
|
||||
}
|
||||
// TODO: support other forms of login, depending on config options
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: passwordLogin(),
|
||||
JSON: flows{
|
||||
Flows: loginFlows,
|
||||
},
|
||||
}
|
||||
} else if req.Method == http.MethodPost {
|
||||
login, cleanup, authErr := auth.LoginFromJSONReader(req.Context(), req.Body, userAPI, cfg, rt)
|
||||
login, cleanup, authErr := auth.LoginFromJSONReader(req, userAPI, userAPI, cfg, rt)
|
||||
if authErr != nil {
|
||||
return *authErr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func TestLogin(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
// Needed for /login
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the userAPI for this test, so nil for other APIs/caches etc.
|
||||
Setup(routers, cfg, nil, nil, userAPI, nil, nil, nil, nil, nil, nil, nil, caching.DisableMetrics)
|
||||
|
|
@ -114,6 +114,44 @@ func TestLogin(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
|
||||
// Inject a dummy application service, so we have a "m.login.application_service"
|
||||
// in the login flows
|
||||
as := &config.ApplicationService{}
|
||||
cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as}
|
||||
|
||||
t.Run("Supported log-in flows are returned", func(t *testing.T) {
|
||||
req := test.NewRequest(t, http.MethodGet, "/_matrix/client/v3/login")
|
||||
rec := httptest.NewRecorder()
|
||||
routers.Client.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("failed to get log-in flows: %s", rec.Body.String())
|
||||
}
|
||||
|
||||
t.Logf("response: %s", rec.Body.String())
|
||||
resp := flows{}
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
appServiceFound := false
|
||||
passwordFound := false
|
||||
for _, flow := range resp.Flows {
|
||||
if flow.Type == "m.login.password" {
|
||||
passwordFound = true
|
||||
} else if flow.Type == "m.login.application_service" {
|
||||
appServiceFound = true
|
||||
} else {
|
||||
t.Fatalf("got unknown login flow: %s", flow.Type)
|
||||
}
|
||||
}
|
||||
if !appServiceFound {
|
||||
t.Fatal("m.login.application_service missing from login flows")
|
||||
}
|
||||
if !passwordFound {
|
||||
t.Fatal("m.login.password missing from login flows")
|
||||
}
|
||||
})
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req := test.NewRequest(t, http.MethodPost, "/_matrix/client/v3/login", test.WithJSONBody(t, map[string]interface{}{
|
||||
|
|
|
|||
|
|
@ -181,18 +181,6 @@ func SendKick(
|
|||
return *errRes
|
||||
}
|
||||
|
||||
pl, errRes := getPowerlevels(req, rsAPI, roomID)
|
||||
if errRes != nil {
|
||||
return *errRes
|
||||
}
|
||||
allowedToKick := pl.UserLevel(*senderID) >= pl.Kick
|
||||
if !allowedToKick {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden("You don't have permission to kick this user, power level too low."),
|
||||
}
|
||||
}
|
||||
|
||||
bodyUserID, err := spec.NewUserID(body.UserID, true)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
|
|
@ -200,6 +188,19 @@ func SendKick(
|
|||
JSON: spec.BadJSON("body userID is invalid"),
|
||||
}
|
||||
}
|
||||
|
||||
pl, errRes := getPowerlevels(req, rsAPI, roomID)
|
||||
if errRes != nil {
|
||||
return *errRes
|
||||
}
|
||||
allowedToKick := pl.UserLevel(*senderID) >= pl.Kick || bodyUserID.String() == deviceUserID.String()
|
||||
if !allowedToKick {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden("You don't have permission to kick this user, power level too low."),
|
||||
}
|
||||
}
|
||||
|
||||
var queryRes roomserverAPI.QueryMembershipForUserResponse
|
||||
err = rsAPI.QueryMembershipForUser(req.Context(), &roomserverAPI.QueryMembershipForUserRequest{
|
||||
RoomID: roomID,
|
||||
|
|
|
|||
|
|
@ -23,13 +23,12 @@ import (
|
|||
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
|
||||
"github.com/matrix-org/dendrite/userapi/api"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func SetReceipt(req *http.Request, userAPI api.ClientUserAPI, syncProducer *producers.SyncAPIProducer, device *userapi.Device, roomID, receiptType, eventID string) util.JSONResponse {
|
||||
func SetReceipt(req *http.Request, userAPI userapi.ClientUserAPI, syncProducer *producers.SyncAPIProducer, device *userapi.Device, roomID, receiptType, eventID string) util.JSONResponse {
|
||||
timestamp := spec.AsTimestamp(time.Now())
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"roomID": roomID,
|
||||
|
|
@ -54,13 +53,13 @@ func SetReceipt(req *http.Request, userAPI api.ClientUserAPI, syncProducer *prod
|
|||
}
|
||||
}
|
||||
|
||||
dataReq := api.InputAccountDataRequest{
|
||||
dataReq := userapi.InputAccountDataRequest{
|
||||
UserID: device.UserID,
|
||||
DataType: "m.fully_read",
|
||||
RoomID: roomID,
|
||||
AccountData: data,
|
||||
}
|
||||
dataRes := api.InputAccountDataResponse{}
|
||||
dataRes := userapi.InputAccountDataResponse{}
|
||||
if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("userAPI.InputAccountData failed")
|
||||
return util.ErrorResponse(err)
|
||||
|
|
|
|||
|
|
@ -649,6 +649,16 @@ func handleGuestRegistration(
|
|||
}
|
||||
}
|
||||
|
||||
// localpartMatchesExclusiveNamespaces will check if a given username matches any
|
||||
// application service's exclusive users namespace
|
||||
func localpartMatchesExclusiveNamespaces(
|
||||
cfg *config.ClientAPI,
|
||||
localpart string,
|
||||
) bool {
|
||||
userID := userutil.MakeUserID(localpart, cfg.Matrix.ServerName)
|
||||
return cfg.Derived.ExclusiveApplicationServicesUsernameRegexp.MatchString(userID)
|
||||
}
|
||||
|
||||
// handleRegistrationFlow will direct and complete registration flow stages
|
||||
// that the client has requested.
|
||||
// nolint: gocyclo
|
||||
|
|
@ -697,7 +707,7 @@ func handleRegistrationFlow(
|
|||
// If an access token is provided, ignore this check this is an appservice
|
||||
// request and we will validate in validateApplicationService
|
||||
if len(cfg.Derived.ApplicationServices) != 0 &&
|
||||
UsernameMatchesExclusiveNamespaces(cfg, r.Username) {
|
||||
localpartMatchesExclusiveNamespaces(cfg, r.Username) {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.ASExclusive("This username is reserved by an application service."),
|
||||
|
|
@ -801,7 +811,7 @@ func handleApplicationServiceRegistration(
|
|||
|
||||
// Check application service register user request is valid.
|
||||
// The application service's ID is returned if so.
|
||||
appserviceID, err := validateApplicationService(
|
||||
appserviceID, err := internal.ValidateApplicationServiceRequest(
|
||||
cfg, r.Username, accessToken,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -298,25 +298,29 @@ func Test_register(t *testing.T) {
|
|||
guestsDisabled bool
|
||||
enableRecaptcha bool
|
||||
captchaBody string
|
||||
wantResponse util.JSONResponse
|
||||
// in case of an error, the expected response
|
||||
wantErrorResponse util.JSONResponse
|
||||
// in case of success, the expected username assigned
|
||||
wantUsername string
|
||||
}{
|
||||
{
|
||||
name: "disallow guests",
|
||||
kind: "guest",
|
||||
guestsDisabled: true,
|
||||
wantResponse: util.JSONResponse{
|
||||
wantErrorResponse: util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden(`Guest registration is disabled on "test"`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "allow guests",
|
||||
kind: "guest",
|
||||
name: "allow guests",
|
||||
kind: "guest",
|
||||
wantUsername: "1",
|
||||
},
|
||||
{
|
||||
name: "unknown login type",
|
||||
loginType: "im.not.known",
|
||||
wantResponse: util.JSONResponse{
|
||||
wantErrorResponse: util.JSONResponse{
|
||||
Code: http.StatusNotImplemented,
|
||||
JSON: spec.Unknown("unknown/unimplemented auth type"),
|
||||
},
|
||||
|
|
@ -324,25 +328,33 @@ func Test_register(t *testing.T) {
|
|||
{
|
||||
name: "disabled registration",
|
||||
registrationDisabled: true,
|
||||
wantResponse: util.JSONResponse{
|
||||
wantErrorResponse: util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden(`Registration is disabled on "test"`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "successful registration, numeric ID",
|
||||
username: "",
|
||||
password: "someRandomPassword",
|
||||
forceEmpty: true,
|
||||
name: "successful registration, numeric ID",
|
||||
username: "",
|
||||
password: "someRandomPassword",
|
||||
forceEmpty: true,
|
||||
wantUsername: "2",
|
||||
},
|
||||
{
|
||||
name: "successful registration",
|
||||
username: "success",
|
||||
},
|
||||
{
|
||||
name: "successful registration, sequential numeric ID",
|
||||
username: "",
|
||||
password: "someRandomPassword",
|
||||
forceEmpty: true,
|
||||
wantUsername: "3",
|
||||
},
|
||||
{
|
||||
name: "failing registration - user already exists",
|
||||
username: "success",
|
||||
wantResponse: util.JSONResponse{
|
||||
wantErrorResponse: util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.UserInUse("Desired user ID is already taken."),
|
||||
},
|
||||
|
|
@ -352,14 +364,14 @@ func Test_register(t *testing.T) {
|
|||
username: "LOWERCASED", // this is going to be lower-cased
|
||||
},
|
||||
{
|
||||
name: "invalid username",
|
||||
username: "#totalyNotValid",
|
||||
wantResponse: *internal.UsernameResponse(internal.ErrUsernameInvalid),
|
||||
name: "invalid username",
|
||||
username: "#totalyNotValid",
|
||||
wantErrorResponse: *internal.UsernameResponse(internal.ErrUsernameInvalid),
|
||||
},
|
||||
{
|
||||
name: "numeric username is forbidden",
|
||||
username: "1337",
|
||||
wantResponse: util.JSONResponse{
|
||||
wantErrorResponse: util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidUsername("Numeric user IDs are reserved"),
|
||||
},
|
||||
|
|
@ -367,7 +379,7 @@ func Test_register(t *testing.T) {
|
|||
{
|
||||
name: "disabled recaptcha login",
|
||||
loginType: authtypes.LoginTypeRecaptcha,
|
||||
wantResponse: util.JSONResponse{
|
||||
wantErrorResponse: util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Unknown(ErrCaptchaDisabled.Error()),
|
||||
},
|
||||
|
|
@ -376,7 +388,7 @@ func Test_register(t *testing.T) {
|
|||
name: "enabled recaptcha, no response defined",
|
||||
enableRecaptcha: true,
|
||||
loginType: authtypes.LoginTypeRecaptcha,
|
||||
wantResponse: util.JSONResponse{
|
||||
wantErrorResponse: util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.BadJSON(ErrMissingResponse.Error()),
|
||||
},
|
||||
|
|
@ -386,7 +398,7 @@ func Test_register(t *testing.T) {
|
|||
enableRecaptcha: true,
|
||||
loginType: authtypes.LoginTypeRecaptcha,
|
||||
captchaBody: `notvalid`,
|
||||
wantResponse: util.JSONResponse{
|
||||
wantErrorResponse: util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.BadJSON(ErrInvalidCaptcha.Error()),
|
||||
},
|
||||
|
|
@ -398,11 +410,11 @@ func Test_register(t *testing.T) {
|
|||
captchaBody: `success`,
|
||||
},
|
||||
{
|
||||
name: "captcha invalid from remote",
|
||||
enableRecaptcha: true,
|
||||
loginType: authtypes.LoginTypeRecaptcha,
|
||||
captchaBody: `i should fail for other reasons`,
|
||||
wantResponse: util.JSONResponse{Code: http.StatusInternalServerError, JSON: spec.InternalServerError{}},
|
||||
name: "captcha invalid from remote",
|
||||
enableRecaptcha: true,
|
||||
loginType: authtypes.LoginTypeRecaptcha,
|
||||
captchaBody: `i should fail for other reasons`,
|
||||
wantErrorResponse: util.JSONResponse{Code: http.StatusInternalServerError, JSON: spec.InternalServerError{}},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -416,7 +428,7 @@ func Test_register(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
|
@ -486,8 +498,8 @@ func Test_register(t *testing.T) {
|
|||
t.Fatalf("unexpected registration flows: %+v, want %+v", r.Flows, cfg.Derived.Registration.Flows)
|
||||
}
|
||||
case spec.MatrixError:
|
||||
if !reflect.DeepEqual(tc.wantResponse, resp) {
|
||||
t.Fatalf("(%s), unexpected response: %+v, want: %+v", tc.name, resp, tc.wantResponse)
|
||||
if !reflect.DeepEqual(tc.wantErrorResponse, resp) {
|
||||
t.Fatalf("(%s), unexpected response: %+v, want: %+v", tc.name, resp, tc.wantErrorResponse)
|
||||
}
|
||||
return
|
||||
case registerResponse:
|
||||
|
|
@ -505,6 +517,13 @@ func Test_register(t *testing.T) {
|
|||
if r.DeviceID == "" {
|
||||
t.Fatalf("missing deviceID in response")
|
||||
}
|
||||
// if an expected username is provided, assert that it is a match
|
||||
if tc.wantUsername != "" {
|
||||
wantUserID := strings.ToLower(fmt.Sprintf("@%s:%s", tc.wantUsername, "test"))
|
||||
if wantUserID != r.UserID {
|
||||
t.Fatalf("unexpected userID: %s, want %s", r.UserID, wantUserID)
|
||||
}
|
||||
}
|
||||
return
|
||||
default:
|
||||
t.Logf("Got response: %T", resp.JSON)
|
||||
|
|
@ -541,44 +560,29 @@ func Test_register(t *testing.T) {
|
|||
|
||||
resp = Register(req, userAPI, &cfg.ClientAPI)
|
||||
|
||||
switch resp.JSON.(type) {
|
||||
case spec.InternalServerError:
|
||||
if !reflect.DeepEqual(tc.wantResponse, resp) {
|
||||
t.Fatalf("unexpected response: %+v, want: %+v", resp, tc.wantResponse)
|
||||
switch rr := resp.JSON.(type) {
|
||||
case spec.InternalServerError, spec.MatrixError, util.JSONResponse:
|
||||
if !reflect.DeepEqual(tc.wantErrorResponse, resp) {
|
||||
t.Fatalf("unexpected response: %+v, want: %+v", resp, tc.wantErrorResponse)
|
||||
}
|
||||
return
|
||||
case spec.MatrixError:
|
||||
if !reflect.DeepEqual(tc.wantResponse, resp) {
|
||||
t.Fatalf("unexpected response: %+v, want: %+v", resp, tc.wantResponse)
|
||||
case registerResponse:
|
||||
// validate the response
|
||||
if tc.wantUsername != "" {
|
||||
// if an expected username is provided, assert that it is a match
|
||||
wantUserID := strings.ToLower(fmt.Sprintf("@%s:%s", tc.wantUsername, "test"))
|
||||
if wantUserID != rr.UserID {
|
||||
t.Fatalf("unexpected userID: %s, want %s", rr.UserID, wantUserID)
|
||||
}
|
||||
}
|
||||
return
|
||||
case util.JSONResponse:
|
||||
if !reflect.DeepEqual(tc.wantResponse, resp) {
|
||||
t.Fatalf("unexpected response: %+v, want: %+v", resp, tc.wantResponse)
|
||||
if rr.DeviceID != *reg.DeviceID {
|
||||
t.Fatalf("unexpected deviceID: %s, want %s", rr.DeviceID, *reg.DeviceID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rr, ok := resp.JSON.(registerResponse)
|
||||
if !ok {
|
||||
t.Fatalf("expected a registerresponse, got %T", resp.JSON)
|
||||
}
|
||||
|
||||
// validate the response
|
||||
if tc.forceEmpty {
|
||||
// when not supplying a username, one will be generated. Given this _SHOULD_ be
|
||||
// the second user, set the username accordingly
|
||||
reg.Username = "2"
|
||||
}
|
||||
wantUserID := strings.ToLower(fmt.Sprintf("@%s:%s", reg.Username, "test"))
|
||||
if wantUserID != rr.UserID {
|
||||
t.Fatalf("unexpected userID: %s, want %s", rr.UserID, wantUserID)
|
||||
}
|
||||
if rr.DeviceID != *reg.DeviceID {
|
||||
t.Fatalf("unexpected deviceID: %s, want %s", rr.DeviceID, *reg.DeviceID)
|
||||
}
|
||||
if rr.AccessToken == "" {
|
||||
t.Fatalf("missing accessToken in response")
|
||||
if rr.AccessToken == "" {
|
||||
t.Fatalf("missing accessToken in response")
|
||||
}
|
||||
default:
|
||||
t.Fatalf("expected one of internalservererror, matrixerror, jsonresponse, registerresponse, got %T", resp.JSON)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -596,7 +600,7 @@ func TestRegisterUserWithDisplayName(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
deviceName, deviceID := "deviceName", "deviceID"
|
||||
expectedDisplayName := "DisplayName"
|
||||
response := completeRegistration(
|
||||
|
|
@ -638,7 +642,7 @@ func TestRegisterAdminUsingSharedSecret(t *testing.T) {
|
|||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
expectedDisplayName := "rabbit"
|
||||
jsonStr := []byte(`{"admin":true,"mac":"24dca3bba410e43fe64b9b5c28306693bf3baa9f","nonce":"759f047f312b99ff428b21d581256f8592b8976e58bc1b543972dc6147e529a79657605b52d7becd160ff5137f3de11975684319187e06901955f79e5a6c5a79","password":"wonderland","username":"alice","displayname":"rabbit"}`)
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ func SendEvent(
|
|||
req.Context(), rsAPI,
|
||||
api.KindNew,
|
||||
[]*types.HeaderedEvent{
|
||||
&types.HeaderedEvent{PDU: e},
|
||||
{PDU: e},
|
||||
},
|
||||
device.UserDomain(),
|
||||
domain,
|
||||
|
|
|
|||
|
|
@ -213,14 +213,15 @@ func main() {
|
|||
natsInstance := jetstream.NATSInstance{}
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.EnableMetrics)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation)
|
||||
|
||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||
rsAPI.SetAppserviceAPI(asAPI)
|
||||
fsAPI := federationapi.NewInternalAPI(
|
||||
processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true,
|
||||
)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||
|
||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||
rsAPI.SetAppserviceAPI(asAPI)
|
||||
|
||||
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||
|
||||
monolith := setup.Monolith{
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ func main() {
|
|||
// dependency. Other components also need updating after their dependencies are up.
|
||||
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federationClient)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federationClient, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||
|
||||
rsAPI.SetAppserviceAPI(asAPI)
|
||||
|
|
|
|||
|
|
@ -325,6 +325,10 @@ user_api:
|
|||
auto_join_rooms:
|
||||
# - "#main:matrix.org"
|
||||
|
||||
# The number of workers to start for the DeviceListUpdater. Defaults to 8.
|
||||
# This only needs updating if the "InputDeviceListUpdate" stream keeps growing indefinitely.
|
||||
# worker_count: 8
|
||||
|
||||
# Configuration for Opentracing.
|
||||
# See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on
|
||||
# how this works and how to set it up.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ No, although a good portion of the Matrix specification has been implemented. Mo
|
|||
|
||||
Dendrite development is currently supported by a small team of developers and due to those limited resources, the majority of the effort is focused on getting Dendrite to be
|
||||
specification complete. If there are major features you're requesting (e.g. new administration endpoints), we'd like to strongly encourage you to join the community in supporting
|
||||
the development efforts through [contributing](../development/contributing).
|
||||
the development efforts through [contributing](./development/CONTRIBUTING.md).
|
||||
|
||||
## Is there a migration path from Synapse to Dendrite?
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ This can be done by performing a room upgrade. Use the command `/upgraderoom <ve
|
|||
|
||||
## How do I reset somebody's password on my server?
|
||||
|
||||
Use the admin endpoint [resetpassword](./administration/adminapi#post-_dendriteadminresetpassworduserid)
|
||||
Use the admin endpoint [resetpassword](./administration/4_adminapi.md#post-_dendriteadminresetpassworduserid)
|
||||
|
||||
## Should I use PostgreSQL or SQLite for my databases?
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ docker run --rm --entrypoint="/usr/bin/generate-keys" \
|
|||
-v $(pwd)/config:/mnt \
|
||||
matrixdotorg/dendrite-monolith:latest \
|
||||
-private-key /mnt/matrix_key.pem
|
||||
|
||||
# Windows equivalent: docker run --rm --entrypoint="/usr/bin/generate-keys" -v %cd%/config:/mnt matrixdotorg/dendrite-monolith:latest -private-key /mnt/matrix_key.pem
|
||||
```
|
||||
(**NOTE**: This only needs to be executed **once**, as you otherwise overwrite the key)
|
||||
|
||||
|
|
@ -44,6 +46,8 @@ docker run --rm --entrypoint="/bin/sh" \
|
|||
-dir /var/dendrite/ \
|
||||
-db postgres://dendrite:itsasecret@postgres/dendrite?sslmode=disable \
|
||||
-server YourDomainHere > /mnt/dendrite.yaml"
|
||||
|
||||
# Windows equivalent: docker run --rm --entrypoint="/bin/sh" -v %cd%/config:/mnt matrixdotorg/dendrite-monolith:latest -c "/usr/bin/generate-config -dir /var/dendrite/ -db postgres://dendrite:itsasecret@postgres/dendrite?sslmode=disable -server YourDomainHere > /mnt/dendrite.yaml"
|
||||
```
|
||||
|
||||
You can then change `config/dendrite.yaml` to your liking.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/api"
|
||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
"github.com/matrix-org/dendrite/federationapi/consumers"
|
||||
"github.com/matrix-org/dendrite/federationapi/internal"
|
||||
|
|
@ -102,7 +101,7 @@ func NewInternalAPI(
|
|||
caches *caching.Caches,
|
||||
keyRing *gomatrixserverlib.KeyRing,
|
||||
resetBlacklist bool,
|
||||
) api.FederationInternalAPI {
|
||||
) *internal.FederationInternalAPI {
|
||||
cfg := &dendriteCfg.FederationAPI
|
||||
|
||||
federationDB, err := storage.NewDatabase(processContext.Context(), cm, &cfg.Database, caches, dendriteCfg.Global.IsLocalServerName)
|
||||
|
|
@ -126,7 +125,7 @@ func NewInternalAPI(
|
|||
queues := queue.NewOutgoingQueues(
|
||||
federationDB, processContext,
|
||||
cfg.Matrix.DisableFederation,
|
||||
cfg.Matrix.ServerName, federation, rsAPI, &stats,
|
||||
cfg.Matrix.ServerName, federation, &stats,
|
||||
signingInfo,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ func NewFederationInternalAPI(
|
|||
}
|
||||
}
|
||||
|
||||
func (a *FederationInternalAPI) isBlacklistedOrBackingOff(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||
func (a *FederationInternalAPI) IsBlacklistedOrBackingOff(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||
stats := a.statistics.ForServer(s)
|
||||
if stats.Blacklisted() {
|
||||
return stats, &api.FederationClientError{
|
||||
|
|
@ -151,7 +151,7 @@ func failBlacklistableError(err error, stats *statistics.ServerStatistics) (unti
|
|||
func (a *FederationInternalAPI) doRequestIfNotBackingOffOrBlacklisted(
|
||||
s spec.ServerName, request func() (interface{}, error),
|
||||
) (interface{}, error) {
|
||||
stats, err := a.isBlacklistedOrBackingOff(s)
|
||||
stats, err := a.IsBlacklistedOrBackingOff(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func TestFederationClientQueryKeys(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedapi := FederationInternalAPI{
|
||||
|
|
@ -96,7 +96,7 @@ func TestFederationClientQueryKeysBlacklisted(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedapi := FederationInternalAPI{
|
||||
|
|
@ -126,7 +126,7 @@ func TestFederationClientQueryKeysFailure(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedapi := FederationInternalAPI{
|
||||
|
|
@ -156,7 +156,7 @@ func TestFederationClientClaimKeys(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedapi := FederationInternalAPI{
|
||||
|
|
@ -187,7 +187,7 @@ func TestFederationClientClaimKeysBlacklisted(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedapi := FederationInternalAPI{
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func TestPerformWakeupServers(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedAPI := NewFederationInternalAPI(
|
||||
|
|
@ -116,7 +116,7 @@ func TestQueryRelayServers(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedAPI := NewFederationInternalAPI(
|
||||
|
|
@ -157,7 +157,7 @@ func TestRemoveRelayServers(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedAPI := NewFederationInternalAPI(
|
||||
|
|
@ -197,7 +197,7 @@ func TestPerformDirectoryLookup(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedAPI := NewFederationInternalAPI(
|
||||
|
|
@ -236,7 +236,7 @@ func TestPerformDirectoryLookupRelaying(t *testing.T) {
|
|||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
cfg.Matrix.ServerName, fedClient, nil, &stats,
|
||||
cfg.Matrix.ServerName, fedClient, &stats,
|
||||
nil,
|
||||
)
|
||||
fedAPI := NewFederationInternalAPI(
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ import (
|
|||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage/shared/receipt"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
)
|
||||
|
|
@ -53,7 +52,6 @@ type destinationQueue struct {
|
|||
db storage.Database
|
||||
process *process.ProcessContext
|
||||
signing map[spec.ServerName]*fclient.SigningIdentity
|
||||
rsAPI api.FederationRoomserverAPI
|
||||
client fclient.FederationClient // federation client
|
||||
origin spec.ServerName // origin of requests
|
||||
destination spec.ServerName // destination of requests
|
||||
|
|
|
|||
|
|
@ -27,12 +27,10 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/sirupsen/logrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage/shared/receipt"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
)
|
||||
|
|
@ -43,7 +41,6 @@ type OutgoingQueues struct {
|
|||
db storage.Database
|
||||
process *process.ProcessContext
|
||||
disabled bool
|
||||
rsAPI api.FederationRoomserverAPI
|
||||
origin spec.ServerName
|
||||
client fclient.FederationClient
|
||||
statistics *statistics.Statistics
|
||||
|
|
@ -90,7 +87,6 @@ func NewOutgoingQueues(
|
|||
disabled bool,
|
||||
origin spec.ServerName,
|
||||
client fclient.FederationClient,
|
||||
rsAPI api.FederationRoomserverAPI,
|
||||
statistics *statistics.Statistics,
|
||||
signing []*fclient.SigningIdentity,
|
||||
) *OutgoingQueues {
|
||||
|
|
@ -98,7 +94,6 @@ func NewOutgoingQueues(
|
|||
disabled: disabled,
|
||||
process: process,
|
||||
db: db,
|
||||
rsAPI: rsAPI,
|
||||
origin: origin,
|
||||
client: client,
|
||||
statistics: statistics,
|
||||
|
|
@ -162,7 +157,6 @@ func (oqs *OutgoingQueues) getQueue(destination spec.ServerName) *destinationQue
|
|||
queues: oqs,
|
||||
db: oqs.db,
|
||||
process: oqs.process,
|
||||
rsAPI: oqs.rsAPI,
|
||||
origin: oqs.origin,
|
||||
destination: destination,
|
||||
client: oqs.client,
|
||||
|
|
@ -213,18 +207,6 @@ func (oqs *OutgoingQueues) SendEvent(
|
|||
delete(destmap, local)
|
||||
}
|
||||
|
||||
// Check if any of the destinations are prohibited by server ACLs.
|
||||
for destination := range destmap {
|
||||
if api.IsServerBannedFromRoom(
|
||||
oqs.process.Context(),
|
||||
oqs.rsAPI,
|
||||
ev.RoomID().String(),
|
||||
destination,
|
||||
) {
|
||||
delete(destmap, destination)
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no remaining destinations then give up.
|
||||
if len(destmap) == 0 {
|
||||
return nil
|
||||
|
|
@ -303,24 +285,6 @@ func (oqs *OutgoingQueues) SendEDU(
|
|||
delete(destmap, local)
|
||||
}
|
||||
|
||||
// There is absolutely no guarantee that the EDU will have a room_id
|
||||
// field, as it is not required by the spec. However, if it *does*
|
||||
// (e.g. typing notifications) then we should try to make sure we don't
|
||||
// bother sending them to servers that are prohibited by the server
|
||||
// ACLs.
|
||||
if result := gjson.GetBytes(e.Content, "room_id"); result.Exists() {
|
||||
for destination := range destmap {
|
||||
if api.IsServerBannedFromRoom(
|
||||
oqs.process.Context(),
|
||||
oqs.rsAPI,
|
||||
result.Str,
|
||||
destination,
|
||||
) {
|
||||
delete(destmap, destination)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no remaining destinations then give up.
|
||||
if len(destmap) == 0 {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import (
|
|||
|
||||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||
rsapi "github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
|
|
@ -65,15 +64,6 @@ func mustCreateFederationDatabase(t *testing.T, dbType test.DBType, realDatabase
|
|||
}
|
||||
}
|
||||
|
||||
type stubFederationRoomServerAPI struct {
|
||||
rsapi.FederationRoomserverAPI
|
||||
}
|
||||
|
||||
func (r *stubFederationRoomServerAPI) QueryServerBannedFromRoom(ctx context.Context, req *rsapi.QueryServerBannedFromRoomRequest, res *rsapi.QueryServerBannedFromRoomResponse) error {
|
||||
res.Banned = false
|
||||
return nil
|
||||
}
|
||||
|
||||
type stubFederationClient struct {
|
||||
fclient.FederationClient
|
||||
shouldTxSucceed bool
|
||||
|
|
@ -126,7 +116,6 @@ func testSetup(failuresUntilBlacklist uint32, failuresUntilAssumedOffline uint32
|
|||
txCount: *atomic.NewUint32(0),
|
||||
txRelayCount: *atomic.NewUint32(0),
|
||||
}
|
||||
rs := &stubFederationRoomServerAPI{}
|
||||
|
||||
stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline)
|
||||
signingInfo := []*fclient.SigningIdentity{
|
||||
|
|
@ -136,7 +125,7 @@ func testSetup(failuresUntilBlacklist uint32, failuresUntilAssumedOffline uint32
|
|||
ServerName: "localhost",
|
||||
},
|
||||
}
|
||||
queues := NewOutgoingQueues(db, processContext, false, "localhost", fc, rs, &stats, signingInfo)
|
||||
queues := NewOutgoingQueues(db, processContext, false, "localhost", fc, &stats, signingInfo)
|
||||
|
||||
return db, fc, queues, processContext, close
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import (
|
|||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||
fedAPI "github.com/matrix-org/dendrite/federationapi"
|
||||
fedInternal "github.com/matrix-org/dendrite/federationapi/internal"
|
||||
"github.com/matrix-org/dendrite/federationapi/routing"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
|
|
@ -67,11 +66,8 @@ func TestHandleQueryProfile(t *testing.T) {
|
|||
keyRing := serverKeyAPI.KeyRing()
|
||||
fedapi := fedAPI.NewInternalAPI(processCtx, cfg, cm, &natsInstance, &fedClient, nil, nil, keyRing, true)
|
||||
userapi := fakeUserAPI{}
|
||||
r, ok := fedapi.(*fedInternal.FederationInternalAPI)
|
||||
if !ok {
|
||||
panic("This is a programming error.")
|
||||
}
|
||||
routing.Setup(routers, cfg, nil, r, keyRing, &fedClient, &userapi, &cfg.MSCs, nil, caching.DisableMetrics)
|
||||
|
||||
routing.Setup(routers, cfg, nil, fedapi, keyRing, &fedClient, &userapi, &cfg.MSCs, nil, caching.DisableMetrics)
|
||||
|
||||
handler := fedMux.Get(routing.QueryProfileRouteName).GetHandler().ServeHTTP
|
||||
_, sk, _ := ed25519.GenerateKey(nil)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||
fedAPI "github.com/matrix-org/dendrite/federationapi"
|
||||
fedInternal "github.com/matrix-org/dendrite/federationapi/internal"
|
||||
"github.com/matrix-org/dendrite/federationapi/routing"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
|
|
@ -65,11 +64,8 @@ func TestHandleQueryDirectory(t *testing.T) {
|
|||
keyRing := serverKeyAPI.KeyRing()
|
||||
fedapi := fedAPI.NewInternalAPI(processCtx, cfg, cm, &natsInstance, &fedClient, nil, nil, keyRing, true)
|
||||
userapi := fakeUserAPI{}
|
||||
r, ok := fedapi.(*fedInternal.FederationInternalAPI)
|
||||
if !ok {
|
||||
panic("This is a programming error.")
|
||||
}
|
||||
routing.Setup(routers, cfg, nil, r, keyRing, &fedClient, &userapi, &cfg.MSCs, nil, caching.DisableMetrics)
|
||||
|
||||
routing.Setup(routers, cfg, nil, fedapi, keyRing, &fedClient, &userapi, &cfg.MSCs, nil, caching.DisableMetrics)
|
||||
|
||||
handler := fedMux.Get(routing.QueryDirectoryRouteName).GetHandler().ServeHTTP
|
||||
_, sk, _ := ed25519.GenerateKey(nil)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||
fedAPI "github.com/matrix-org/dendrite/federationapi"
|
||||
fedInternal "github.com/matrix-org/dendrite/federationapi/internal"
|
||||
"github.com/matrix-org/dendrite/federationapi/routing"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
|
|
@ -62,11 +61,8 @@ func TestHandleSend(t *testing.T) {
|
|||
fedapi := fedAPI.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, nil, nil, nil, true)
|
||||
serverKeyAPI := &signing.YggdrasilKeys{}
|
||||
keyRing := serverKeyAPI.KeyRing()
|
||||
r, ok := fedapi.(*fedInternal.FederationInternalAPI)
|
||||
if !ok {
|
||||
panic("This is a programming error.")
|
||||
}
|
||||
routing.Setup(routers, cfg, nil, r, keyRing, nil, nil, &cfg.MSCs, nil, caching.DisableMetrics)
|
||||
|
||||
routing.Setup(routers, cfg, nil, fedapi, keyRing, nil, nil, &cfg.MSCs, nil, caching.DisableMetrics)
|
||||
|
||||
handler := fedMux.Get(routing.SendRouteName).GetHandler().ServeHTTP
|
||||
_, sk, _ := ed25519.GenerateKey(nil)
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ func (s *notaryServerKeysMetadataStatements) SelectKeys(ctx context.Context, txn
|
|||
}
|
||||
results = append(results, sk)
|
||||
}
|
||||
return results, nil
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
func (s *notaryServerKeysMetadataStatements) DeleteOldJSONResponses(ctx context.Context, txn *sql.Tx) error {
|
||||
|
|
|
|||
|
|
@ -109,5 +109,5 @@ func (s *queueJSONStatements) SelectQueueJSON(
|
|||
}
|
||||
blobs[nid] = blob
|
||||
}
|
||||
return blobs, err
|
||||
return blobs, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ func (s *relayServersStatements) SelectRelayServers(
|
|||
}
|
||||
result = append(result, spec.ServerName(relayServer))
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *relayServersStatements) DeleteRelayServers(
|
||||
|
|
|
|||
|
|
@ -94,12 +94,14 @@ func (s *serverSigningKeyStatements) BulkSelectServerKeys(
|
|||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "bulkSelectServerKeys: rows.close() failed")
|
||||
results := map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult{}
|
||||
|
||||
var serverName string
|
||||
var keyID string
|
||||
var key string
|
||||
var validUntilTS int64
|
||||
var expiredTS int64
|
||||
var vk gomatrixserverlib.VerifyKey
|
||||
for rows.Next() {
|
||||
var serverName string
|
||||
var keyID string
|
||||
var key string
|
||||
var validUntilTS int64
|
||||
var expiredTS int64
|
||||
if err = rows.Scan(&serverName, &keyID, &validUntilTS, &expiredTS, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -107,7 +109,6 @@ func (s *serverSigningKeyStatements) BulkSelectServerKeys(
|
|||
ServerName: spec.ServerName(serverName),
|
||||
KeyID: gomatrixserverlib.KeyID(keyID),
|
||||
}
|
||||
vk := gomatrixserverlib.VerifyKey{}
|
||||
err = vk.Key.Decode(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -216,5 +216,5 @@ func joinedHostsFromStmt(
|
|||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ func (s *notaryServerKeysMetadataStatements) SelectKeys(ctx context.Context, txn
|
|||
}
|
||||
results = append(results, sk)
|
||||
}
|
||||
return results, nil
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
func (s *notaryServerKeysMetadataStatements) DeleteOldJSONResponses(ctx context.Context, txn *sql.Tx) error {
|
||||
|
|
|
|||
|
|
@ -135,5 +135,5 @@ func (s *queueJSONStatements) SelectQueueJSON(
|
|||
}
|
||||
blobs[nid] = blob
|
||||
}
|
||||
return blobs, err
|
||||
return blobs, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ func (s *relayServersStatements) SelectRelayServers(
|
|||
}
|
||||
result = append(result, spec.ServerName(relayServer))
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *relayServersStatements) DeleteRelayServers(
|
||||
|
|
|
|||
|
|
@ -98,12 +98,13 @@ func (s *serverSigningKeyStatements) BulkSelectServerKeys(
|
|||
err := sqlutil.RunLimitedVariablesQuery(
|
||||
ctx, bulkSelectServerSigningKeysSQL, s.db, iKeyIDs, sqlutil.SQLite3MaxVariables,
|
||||
func(rows *sql.Rows) error {
|
||||
var serverName string
|
||||
var keyID string
|
||||
var key string
|
||||
var validUntilTS int64
|
||||
var expiredTS int64
|
||||
var vk gomatrixserverlib.VerifyKey
|
||||
for rows.Next() {
|
||||
var serverName string
|
||||
var keyID string
|
||||
var key string
|
||||
var validUntilTS int64
|
||||
var expiredTS int64
|
||||
if err := rows.Scan(&serverName, &keyID, &validUntilTS, &expiredTS, &key); err != nil {
|
||||
return fmt.Errorf("bulkSelectServerKeys: %v", err)
|
||||
}
|
||||
|
|
@ -111,7 +112,6 @@ func (s *serverSigningKeyStatements) BulkSelectServerKeys(
|
|||
ServerName: spec.ServerName(serverName),
|
||||
KeyID: gomatrixserverlib.KeyID(keyID),
|
||||
}
|
||||
vk := gomatrixserverlib.VerifyKey{}
|
||||
err := vk.Key.Decode(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bulkSelectServerKeys: %v", err)
|
||||
|
|
|
|||
116
federationapi/storage/tables/server_key_table_test.go
Normal file
116
federationapi/storage/tables/server_key_table_test.go
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
package tables_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/storage/postgres"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage/sqlite3"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage/tables"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func mustCreateServerKeyDB(t *testing.T, dbType test.DBType) (tables.FederationServerSigningKeys, func()) {
|
||||
connStr, close := test.PrepareDBConnectionString(t, dbType)
|
||||
db, err := sqlutil.Open(&config.DatabaseOptions{
|
||||
ConnectionString: config.DataSource(connStr),
|
||||
}, sqlutil.NewExclusiveWriter())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open database: %s", err)
|
||||
}
|
||||
var tab tables.FederationServerSigningKeys
|
||||
switch dbType {
|
||||
case test.DBTypePostgres:
|
||||
tab, err = postgres.NewPostgresServerSigningKeysTable(db)
|
||||
case test.DBTypeSQLite:
|
||||
tab, err = sqlite3.NewSQLiteServerSigningKeysTable(db)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create table: %s", err)
|
||||
}
|
||||
return tab, close
|
||||
}
|
||||
|
||||
func TestServerKeysTable(t *testing.T) {
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
tab, close := mustCreateServerKeyDB(t, dbType)
|
||||
t.Cleanup(func() {
|
||||
close()
|
||||
cancel()
|
||||
})
|
||||
|
||||
req := gomatrixserverlib.PublicKeyLookupRequest{
|
||||
ServerName: "localhost",
|
||||
KeyID: "ed25519:test",
|
||||
}
|
||||
expectedTimestamp := spec.AsTimestamp(time.Now().Add(time.Hour))
|
||||
res := gomatrixserverlib.PublicKeyLookupResult{
|
||||
VerifyKey: gomatrixserverlib.VerifyKey{Key: make(spec.Base64Bytes, 0)},
|
||||
ExpiredTS: 0,
|
||||
ValidUntilTS: expectedTimestamp,
|
||||
}
|
||||
|
||||
// Insert the key
|
||||
err := tab.UpsertServerKeys(ctx, nil, req, res)
|
||||
assert.NoError(t, err)
|
||||
|
||||
selectKeys := map[gomatrixserverlib.PublicKeyLookupRequest]spec.Timestamp{
|
||||
req: spec.AsTimestamp(time.Now()),
|
||||
}
|
||||
gotKeys, err := tab.BulkSelectServerKeys(ctx, nil, selectKeys)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Now we should have a key for the req above
|
||||
assert.NotNil(t, gotKeys[req])
|
||||
assert.Equal(t, res, gotKeys[req])
|
||||
|
||||
// "Expire" the key by setting ExpireTS to a non-zero value and ValidUntilTS to 0
|
||||
expectedTimestamp = spec.AsTimestamp(time.Now())
|
||||
res.ExpiredTS = expectedTimestamp
|
||||
res.ValidUntilTS = 0
|
||||
|
||||
// Update the key
|
||||
err = tab.UpsertServerKeys(ctx, nil, req, res)
|
||||
assert.NoError(t, err)
|
||||
|
||||
gotKeys, err = tab.BulkSelectServerKeys(ctx, nil, selectKeys)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// The key should be expired
|
||||
assert.NotNil(t, gotKeys[req])
|
||||
assert.Equal(t, res, gotKeys[req])
|
||||
|
||||
// Upsert a different key to validate querying multiple keys
|
||||
req2 := gomatrixserverlib.PublicKeyLookupRequest{
|
||||
ServerName: "notlocalhost",
|
||||
KeyID: "ed25519:test2",
|
||||
}
|
||||
expectedTimestamp2 := spec.AsTimestamp(time.Now().Add(time.Hour))
|
||||
res2 := gomatrixserverlib.PublicKeyLookupResult{
|
||||
VerifyKey: gomatrixserverlib.VerifyKey{Key: make(spec.Base64Bytes, 0)},
|
||||
ExpiredTS: 0,
|
||||
ValidUntilTS: expectedTimestamp2,
|
||||
}
|
||||
|
||||
err = tab.UpsertServerKeys(ctx, nil, req2, res2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Select multiple keys
|
||||
selectKeys[req2] = spec.AsTimestamp(time.Now())
|
||||
|
||||
gotKeys, err = tab.BulkSelectServerKeys(ctx, nil, selectKeys)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// We now should receive two keys, one of which is expired
|
||||
assert.Equal(t, 2, len(gotKeys))
|
||||
assert.Equal(t, res2, gotKeys[req2])
|
||||
assert.Equal(t, res, gotKeys[req])
|
||||
})
|
||||
}
|
||||
8
go.mod
8
go.mod
|
|
@ -9,7 +9,7 @@ require (
|
|||
github.com/blevesearch/bleve/v2 v2.3.8
|
||||
github.com/codeclysm/extract v2.2.0+incompatible
|
||||
github.com/dgraph-io/ristretto v0.1.1
|
||||
github.com/docker/docker v24.0.5+incompatible
|
||||
github.com/docker/docker v24.0.7+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/getsentry/sentry-go v0.14.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
|
|
@ -22,7 +22,7 @@ require (
|
|||
github.com/lib/pq v1.10.9
|
||||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20231024124730-58af9a2712ca
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20231212115925-41497b7563eb
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
|
||||
github.com/matryer/is v1.4.1
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
|
|
@ -44,7 +44,7 @@ require (
|
|||
go.uber.org/atomic v1.10.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819
|
||||
golang.org/x/image v0.5.0
|
||||
golang.org/x/image v0.10.0
|
||||
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/term v0.13.0
|
||||
|
|
@ -104,7 +104,7 @@ require (
|
|||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.5.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.4 // indirect
|
||||
github.com/nats-io/nkeys v0.4.6 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
|
|
|
|||
17
go.sum
17
go.sum
|
|
@ -93,8 +93,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC
|
|||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
|
||||
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
|
|
@ -185,8 +185,8 @@ github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e h1:DP5RC0Z3XdyBE
|
|||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e/go.mod h1:NgPCr+UavRGH6n5jmdX8DuqFZ4JiCWIJoZiuhTRLSUg=
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U=
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20231024124730-58af9a2712ca h1:JCP72vU4Vcmur2071RwYVOSoekR+ZjbC03wZD5lAAK0=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20231024124730-58af9a2712ca/go.mod h1:M8m7seOroO5ePlgxA7AFZymnG90Cnh94rYQyngSrZkk=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20231212115925-41497b7563eb h1:Nn+Fr96oi7bIfdOwX5A2L6A2MZCM+lqwLe4/+3+nYj8=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20231212115925-41497b7563eb/go.mod h1:M8m7seOroO5ePlgxA7AFZymnG90Cnh94rYQyngSrZkk=
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y=
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66/go.mod h1:iBI1foelCqA09JJgPV0FYz4qA5dUXYOxMi57FxKBdd4=
|
||||
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
||||
|
|
@ -223,8 +223,8 @@ github.com/nats-io/nats-server/v2 v2.9.23 h1:6Wj6H6QpP9FMlpCyWUaNu2yeZ/qGj+mdRkZ
|
|||
github.com/nats-io/nats-server/v2 v2.9.23/go.mod h1:wEjrEy9vnqIGE4Pqz4/c75v9Pmaq7My2IgFmnykc4C0=
|
||||
github.com/nats-io/nats.go v1.28.0 h1:Th4G6zdsz2d0OqXdfzKLClo6bOfoI/b1kInhRtFIy5c=
|
||||
github.com/nats-io/nats.go v1.28.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
|
||||
github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA=
|
||||
github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
|
||||
github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY=
|
||||
github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9 h1:lrVQzBtkeQEGGYUHwSX1XPe1E5GL6U3KYCNe2G4bncQ=
|
||||
|
|
@ -328,8 +328,8 @@ golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N0
|
|||
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=
|
||||
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
||||
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
|
||||
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e h1:zSgtO19fpg781xknwqiQPmOHaASr6E7ZVlTseLd9Fx4=
|
||||
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
||||
|
|
@ -395,6 +395,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
apiVersion: v2
|
||||
name: dendrite
|
||||
version: "0.13.5"
|
||||
appVersion: "0.13.4"
|
||||
version: "0.13.6"
|
||||
appVersion: "0.13.5"
|
||||
description: Dendrite Matrix Homeserver
|
||||
type: application
|
||||
keywords:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
# dendrite
|
||||
|
||||
  
|
||||
  
|
||||
Dendrite Matrix Homeserver
|
||||
|
||||
Status: **NOT PRODUCTION READY**
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@
|
|||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Registerd Users",
|
||||
"title": "Registered Users",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ import (
|
|||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
|
@ -100,10 +103,139 @@ func UsernameResponse(err error) *util.JSONResponse {
|
|||
|
||||
// ValidateApplicationServiceUsername returns an error if the username is invalid for an application service
|
||||
func ValidateApplicationServiceUsername(localpart string, domain spec.ServerName) error {
|
||||
if id := fmt.Sprintf("@%s:%s", localpart, domain); len(id) > maxUsernameLength {
|
||||
userID := userutil.MakeUserID(localpart, domain)
|
||||
return ValidateApplicationServiceUserID(userID)
|
||||
}
|
||||
|
||||
func ValidateApplicationServiceUserID(userID string) error {
|
||||
if len(userID) > maxUsernameLength {
|
||||
return ErrUsernameTooLong
|
||||
} else if !validUsernameRegex.MatchString(localpart) {
|
||||
}
|
||||
|
||||
localpart, _, err := gomatrixserverlib.SplitID('@', userID)
|
||||
if err != nil || !validUsernameRegex.MatchString(localpart) {
|
||||
return ErrUsernameInvalid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// userIDIsWithinApplicationServiceNamespace checks to see if a given userID
|
||||
// falls within any of the namespaces of a given Application Service. If no
|
||||
// Application Service is given, it will check to see if it matches any
|
||||
// Application Service's namespace.
|
||||
func userIDIsWithinApplicationServiceNamespace(
|
||||
cfg *config.ClientAPI,
|
||||
userID string,
|
||||
appservice *config.ApplicationService,
|
||||
) bool {
|
||||
var localpart, domain, err = gomatrixserverlib.SplitID('@', userID)
|
||||
if err != nil {
|
||||
// Not a valid userID
|
||||
return false
|
||||
}
|
||||
|
||||
if !cfg.Matrix.IsLocalServerName(domain) {
|
||||
// This is a federated userID
|
||||
return false
|
||||
}
|
||||
|
||||
if localpart == appservice.SenderLocalpart {
|
||||
// This is the application service bot userID
|
||||
return true
|
||||
}
|
||||
|
||||
// Loop through given application service's namespaces and see if any match
|
||||
for _, namespace := range appservice.NamespaceMap["users"] {
|
||||
// Application service namespaces are checked for validity in config
|
||||
if namespace.RegexpObject.MatchString(userID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// usernameMatchesMultipleExclusiveNamespaces will check if a given username matches
|
||||
// more than one exclusive namespace. More than one is not allowed
|
||||
func userIDMatchesMultipleExclusiveNamespaces(
|
||||
cfg *config.ClientAPI,
|
||||
userID string,
|
||||
) bool {
|
||||
// Check namespaces and see if more than one match
|
||||
matchCount := 0
|
||||
for _, appservice := range cfg.Derived.ApplicationServices {
|
||||
if appservice.OwnsNamespaceCoveringUserId(userID) {
|
||||
if matchCount++; matchCount > 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateApplicationServiceRequest checks if a provided application service
|
||||
// token corresponds to one that is registered, and, if so, checks if the
|
||||
// supplied userIDOrLocalpart is within that application service's namespace.
|
||||
//
|
||||
// As long as these two requirements are met, the matched application service
|
||||
// ID will be returned. Otherwise, it will return a JSON response with the
|
||||
// appropriate error message.
|
||||
func ValidateApplicationServiceRequest(
|
||||
cfg *config.ClientAPI,
|
||||
userIDOrLocalpart string,
|
||||
accessToken string,
|
||||
) (string, *util.JSONResponse) {
|
||||
localpart, domain, err := userutil.ParseUsernameParam(userIDOrLocalpart, cfg.Matrix)
|
||||
if err != nil {
|
||||
return "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.InvalidUsername(err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
userID := userutil.MakeUserID(localpart, domain)
|
||||
|
||||
// Check if the token if the application service is valid with one we have
|
||||
// registered in the config.
|
||||
var matchedApplicationService *config.ApplicationService
|
||||
for _, appservice := range cfg.Derived.ApplicationServices {
|
||||
if appservice.ASToken == accessToken {
|
||||
matchedApplicationService = &appservice
|
||||
break
|
||||
}
|
||||
}
|
||||
if matchedApplicationService == nil {
|
||||
return "", &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.UnknownToken("Supplied access_token does not match any known application service"),
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the desired username is within at least one of the application service's namespaces.
|
||||
if !userIDIsWithinApplicationServiceNamespace(cfg, userID, matchedApplicationService) {
|
||||
// If we didn't find any matches, return M_EXCLUSIVE
|
||||
return "", &util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.ASExclusive(fmt.Sprintf(
|
||||
"Supplied username %s did not match any namespaces for application service ID: %s", userIDOrLocalpart, matchedApplicationService.ID)),
|
||||
}
|
||||
}
|
||||
|
||||
// Check this user does not fit multiple application service namespaces
|
||||
if userIDMatchesMultipleExclusiveNamespaces(cfg, userID) {
|
||||
return "", &util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.ASExclusive(fmt.Sprintf(
|
||||
"Supplied username %s matches multiple exclusive application service namespaces. Only 1 match allowed", userIDOrLocalpart)),
|
||||
}
|
||||
}
|
||||
|
||||
// Check username application service is trying to register is valid
|
||||
if err := ValidateApplicationServiceUserID(userID); err != nil {
|
||||
return "", UsernameResponse(err)
|
||||
}
|
||||
|
||||
// No errors, registration valid
|
||||
return matchedApplicationService.ID, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ package internal
|
|||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
|
@ -38,7 +40,7 @@ func Test_validatePassword(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotErr := ValidatePassword(tt.password)
|
||||
if !reflect.DeepEqual(gotErr, tt.wantError) {
|
||||
t.Errorf("validatePassword() = %v, wantJSON %v", gotErr, tt.wantError)
|
||||
t.Errorf("validatePassword() = %v, wantError %v", gotErr, tt.wantError)
|
||||
}
|
||||
|
||||
if got := PasswordResponse(gotErr); !reflect.DeepEqual(got, tt.wantJSON) {
|
||||
|
|
@ -167,3 +169,133 @@ func Test_validateUsername(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This method tests validation of the provided Application Service token and
|
||||
// username that they're registering
|
||||
func TestValidateApplicationServiceRequest(t *testing.T) {
|
||||
// Create a fake application service
|
||||
regex := "@_appservice_.*"
|
||||
fakeNamespace := config.ApplicationServiceNamespace{
|
||||
Exclusive: true,
|
||||
Regex: regex,
|
||||
RegexpObject: regexp.MustCompile(regex),
|
||||
}
|
||||
fakeSenderLocalpart := "_appservice_bot"
|
||||
fakeApplicationService := config.ApplicationService{
|
||||
ID: "FakeAS",
|
||||
URL: "null",
|
||||
ASToken: "1234",
|
||||
HSToken: "4321",
|
||||
SenderLocalpart: fakeSenderLocalpart,
|
||||
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
|
||||
"users": {fakeNamespace},
|
||||
},
|
||||
}
|
||||
|
||||
// Create a second fake application service where userIDs ending in
|
||||
// "_overlap" overlap with the first.
|
||||
regex = "@_.*_overlap"
|
||||
fakeNamespace = config.ApplicationServiceNamespace{
|
||||
Exclusive: true,
|
||||
Regex: regex,
|
||||
RegexpObject: regexp.MustCompile(regex),
|
||||
}
|
||||
fakeApplicationServiceOverlap := config.ApplicationService{
|
||||
ID: "FakeASOverlap",
|
||||
URL: fakeApplicationService.URL,
|
||||
ASToken: fakeApplicationService.ASToken,
|
||||
HSToken: fakeApplicationService.HSToken,
|
||||
SenderLocalpart: "_appservice_bot_overlap",
|
||||
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
|
||||
"users": {fakeNamespace},
|
||||
},
|
||||
}
|
||||
|
||||
// Set up a config
|
||||
fakeConfig := &config.Dendrite{}
|
||||
fakeConfig.Defaults(config.DefaultOpts{
|
||||
Generate: true,
|
||||
})
|
||||
fakeConfig.Global.ServerName = "localhost"
|
||||
fakeConfig.ClientAPI.Derived.ApplicationServices = []config.ApplicationService{fakeApplicationService, fakeApplicationServiceOverlap}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
localpart string
|
||||
asToken string
|
||||
wantError bool
|
||||
wantASID string
|
||||
}{
|
||||
// Access token is correct, userID omitted so we are acting as SenderLocalpart
|
||||
{
|
||||
name: "correct access token but omitted userID",
|
||||
localpart: fakeSenderLocalpart,
|
||||
asToken: fakeApplicationService.ASToken,
|
||||
wantError: false,
|
||||
wantASID: fakeApplicationService.ID,
|
||||
},
|
||||
// Access token is incorrect, userID omitted so we are acting as SenderLocalpart
|
||||
{
|
||||
name: "incorrect access token but omitted userID",
|
||||
localpart: fakeSenderLocalpart,
|
||||
asToken: "xxxx",
|
||||
wantError: true,
|
||||
wantASID: "",
|
||||
},
|
||||
// Access token is correct, acting as valid userID
|
||||
{
|
||||
name: "correct access token and valid userID",
|
||||
localpart: "_appservice_bob",
|
||||
asToken: fakeApplicationService.ASToken,
|
||||
wantError: false,
|
||||
wantASID: fakeApplicationService.ID,
|
||||
},
|
||||
// Access token is correct, acting as invalid userID
|
||||
{
|
||||
name: "correct access token but invalid userID",
|
||||
localpart: "_something_else",
|
||||
asToken: fakeApplicationService.ASToken,
|
||||
wantError: true,
|
||||
wantASID: "",
|
||||
},
|
||||
// Access token is correct, acting as userID that matches two exclusive namespaces
|
||||
{
|
||||
name: "correct access token but non-exclusive userID",
|
||||
localpart: "_appservice_overlap",
|
||||
asToken: fakeApplicationService.ASToken,
|
||||
wantError: true,
|
||||
wantASID: "",
|
||||
},
|
||||
// Access token is correct, acting as matching userID that is too long
|
||||
{
|
||||
name: "correct access token but too long userID",
|
||||
localpart: "_appservice_" + strings.Repeat("a", maxUsernameLength),
|
||||
asToken: fakeApplicationService.ASToken,
|
||||
wantError: true,
|
||||
wantASID: "",
|
||||
},
|
||||
// Access token is correct, acting as userID that matches but is invalid
|
||||
{
|
||||
name: "correct access token and matching but invalid userID",
|
||||
localpart: "@_appservice_bob::",
|
||||
asToken: fakeApplicationService.ASToken,
|
||||
wantError: true,
|
||||
wantASID: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotASID, gotResp := ValidateApplicationServiceRequest(&fakeConfig.ClientAPI, tt.localpart, tt.asToken)
|
||||
if tt.wantError && gotResp == nil {
|
||||
t.Error("expected an error, but succeeded")
|
||||
}
|
||||
if !tt.wantError && gotResp != nil {
|
||||
t.Errorf("expected success, but returned error: %v", *gotResp)
|
||||
}
|
||||
if gotASID != tt.wantASID {
|
||||
t.Errorf("returned '%s', but expected '%s'", gotASID, tt.wantASID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ var build string
|
|||
const (
|
||||
VersionMajor = 0
|
||||
VersionMinor = 13
|
||||
VersionPatch = 4
|
||||
VersionPatch = 5
|
||||
VersionTag = "" // example: "rc1"
|
||||
|
||||
gitRevLen = 7 // 7 matches the displayed characters on github.com
|
||||
|
|
|
|||
|
|
@ -63,6 +63,40 @@ type downloadRequest struct {
|
|||
DownloadFilename string
|
||||
}
|
||||
|
||||
// Taken from: https://github.com/matrix-org/synapse/blob/c3627d0f99ed5a23479305dc2bd0e71ca25ce2b1/synapse/media/_base.py#L53C1-L84
|
||||
// A list of all content types that are "safe" to be rendered inline in a browser.
|
||||
var allowInlineTypes = map[types.ContentType]struct{}{
|
||||
"text/css": {},
|
||||
"text/plain": {},
|
||||
"text/csv": {},
|
||||
"application/json": {},
|
||||
"application/ld+json": {},
|
||||
// We allow some media files deemed as safe, which comes from the matrix-react-sdk.
|
||||
// https://github.com/matrix-org/matrix-react-sdk/blob/a70fcfd0bcf7f8c85986da18001ea11597989a7c/src/utils/blobs.ts#L51
|
||||
// SVGs are *intentionally* omitted.
|
||||
"image/jpeg": {},
|
||||
"image/gif": {},
|
||||
"image/png": {},
|
||||
"image/apng": {},
|
||||
"image/webp": {},
|
||||
"image/avif": {},
|
||||
"video/mp4": {},
|
||||
"video/webm": {},
|
||||
"video/ogg": {},
|
||||
"video/quicktime": {},
|
||||
"audio/mp4": {},
|
||||
"audio/webm": {},
|
||||
"audio/aac": {},
|
||||
"audio/mpeg": {},
|
||||
"audio/ogg": {},
|
||||
"audio/wave": {},
|
||||
"audio/wav": {},
|
||||
"audio/x-wav": {},
|
||||
"audio/x-pn-wav": {},
|
||||
"audio/flac": {},
|
||||
"audio/x-flac": {},
|
||||
}
|
||||
|
||||
// Download implements GET /download and GET /thumbnail
|
||||
// Files from this server (i.e. origin == cfg.ServerName) are served directly
|
||||
// Files from remote servers (i.e. origin != cfg.ServerName) are cached locally.
|
||||
|
|
@ -353,7 +387,7 @@ func (r *downloadRequest) addDownloadFilenameToHeaders(
|
|||
}
|
||||
|
||||
if len(filename) == 0 {
|
||||
w.Header().Set("Content-Disposition", "attachment")
|
||||
w.Header().Set("Content-Disposition", contentDispositionFor(""))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -383,20 +417,21 @@ func (r *downloadRequest) addDownloadFilenameToHeaders(
|
|||
unescaped = strings.ReplaceAll(unescaped, `\`, `\\"`)
|
||||
unescaped = strings.ReplaceAll(unescaped, `"`, `\"`)
|
||||
|
||||
disposition := contentDispositionFor(responseMetadata.ContentType)
|
||||
if isASCII {
|
||||
// For ASCII filenames, we should only quote the filename if
|
||||
// it needs to be done, e.g. it contains a space or a character
|
||||
// that would otherwise be parsed as a control character in the
|
||||
// Content-Disposition header
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf(
|
||||
`attachment; filename=%s%s%s`,
|
||||
quote, unescaped, quote,
|
||||
`%s; filename=%s%s%s`,
|
||||
disposition, quote, unescaped, quote,
|
||||
))
|
||||
} else {
|
||||
// For UTF-8 filenames, we quote always, as that's the standard
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf(
|
||||
`attachment; filename*=utf-8''%s`,
|
||||
url.QueryEscape(unescaped),
|
||||
`%s; filename*=utf-8''%s`,
|
||||
disposition, url.QueryEscape(unescaped),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -808,3 +843,12 @@ func (r *downloadRequest) fetchRemoteFile(
|
|||
|
||||
return types.Path(finalPath), duplicate, nil
|
||||
}
|
||||
|
||||
// contentDispositionFor returns the Content-Disposition for a given
|
||||
// content type.
|
||||
func contentDispositionFor(contentType types.ContentType) string {
|
||||
if _, ok := allowInlineTypes[contentType]; ok {
|
||||
return "inline"
|
||||
}
|
||||
return "attachment"
|
||||
}
|
||||
|
|
|
|||
13
mediaapi/routing/download_test.go
Normal file
13
mediaapi/routing/download_test.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_dispositionFor(t *testing.T) {
|
||||
assert.Equal(t, "attachment", contentDispositionFor(""), "empty content type")
|
||||
assert.Equal(t, "attachment", contentDispositionFor("image/svg"), "image/svg")
|
||||
assert.Equal(t, "inline", contentDispositionFor("image/jpeg"), "image/jpg")
|
||||
}
|
||||
|
|
@ -29,6 +29,8 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const MRoomServerACL = "m.room.server_acl"
|
||||
|
||||
type ServerACLDatabase interface {
|
||||
// GetKnownRooms returns a list of all rooms we know about.
|
||||
GetKnownRooms(ctx context.Context) ([]string, error)
|
||||
|
|
@ -57,7 +59,7 @@ func NewServerACLs(db ServerACLDatabase) *ServerACLs {
|
|||
// do then we'll process it into memory so that we have the regexes to
|
||||
// hand.
|
||||
for _, room := range rooms {
|
||||
state, err := db.GetStateEvent(ctx, room, "m.room.server_acl", "")
|
||||
state, err := db.GetStateEvent(ctx, room, MRoomServerACL, "")
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to get server ACLs for room %q", room)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/acls"
|
||||
"github.com/matrix-org/dendrite/roomserver/internal/helpers"
|
||||
|
||||
userAPI "github.com/matrix-org/dendrite/userapi/api"
|
||||
|
|
@ -491,6 +492,27 @@ func (r *Inputer) processRoomEvent(
|
|||
}
|
||||
}
|
||||
|
||||
// If this is a membership event, it is possible we newly joined a federated room and eventually
|
||||
// missed to update our m.room.server_acl - the following ensures we set the ACLs
|
||||
// TODO: This probably performs badly in benchmarks
|
||||
if event.Type() == spec.MRoomMember {
|
||||
membership, _ := event.Membership()
|
||||
if membership == spec.Join {
|
||||
_, serverName, _ := gomatrixserverlib.SplitID('@', *event.StateKey())
|
||||
// only handle local membership events
|
||||
if r.Cfg.Matrix.IsLocalServerName(serverName) {
|
||||
var aclEvent *types.HeaderedEvent
|
||||
aclEvent, err = r.DB.GetStateEvent(ctx, event.RoomID().String(), acls.MRoomServerACL, "")
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("failed to get server ACLs")
|
||||
}
|
||||
if aclEvent != nil {
|
||||
r.ACLs.OnServerACLUpdate(aclEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle remote room upgrades, e.g. remove published room
|
||||
if event.Type() == "m.room.tombstone" && event.StateKeyEquals("") && !r.Cfg.Matrix.IsLocalServerName(senderDomain) {
|
||||
if err = r.handleRemoteRoomUpgrade(ctx, event); err != nil {
|
||||
|
|
|
|||
|
|
@ -278,16 +278,11 @@ func (r *Queryer) queryMembershipForOptionalSenderID(ctx context.Context, roomID
|
|||
return nil
|
||||
}
|
||||
|
||||
membershipEventNID, membershipState, stillInRoom, isRoomforgotten, err := r.DB.GetMembership(ctx, info.RoomNID, *senderID)
|
||||
membershipEventNID, stillInRoom, isRoomforgotten, err := r.DB.GetMembership(ctx, info.RoomNID, *senderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if membershipState == tables.MembershipStateInvite {
|
||||
response.Membership = spec.Invite
|
||||
response.IsInRoom = true
|
||||
}
|
||||
|
||||
response.IsRoomForgotten = isRoomforgotten
|
||||
|
||||
if membershipEventNID == 0 {
|
||||
|
|
@ -446,7 +441,7 @@ func (r *Queryer) QueryMembershipsForRoom(
|
|||
return nil
|
||||
}
|
||||
|
||||
membershipEventNID, _, stillInRoom, isRoomforgotten, err := r.DB.GetMembership(ctx, info.RoomNID, request.SenderID)
|
||||
membershipEventNID, stillInRoom, isRoomforgotten, err := r.DB.GetMembership(ctx, info.RoomNID, request.SenderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -994,7 +989,7 @@ func (r *Queryer) CurrentStateEvent(ctx context.Context, roomID spec.RoomID, eve
|
|||
}
|
||||
|
||||
func (r *Queryer) UserJoinedToRoom(ctx context.Context, roomNID types.RoomNID, senderID spec.SenderID) (bool, error) {
|
||||
_, _, isIn, _, err := r.DB.GetMembership(ctx, roomNID, senderID)
|
||||
_, isIn, _, err := r.DB.GetMembership(ctx, roomNID, senderID)
|
||||
return isIn, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package query
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
|
|
@ -56,6 +57,12 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
break
|
||||
}
|
||||
|
||||
// If the context is canceled, we might still have discovered rooms
|
||||
// return them to the client and let the client know there _may_ be more rooms.
|
||||
if errors.Is(ctx.Err(), context.Canceled) {
|
||||
break
|
||||
}
|
||||
|
||||
// pop the stack
|
||||
queuedRoom := unvisited[len(unvisited)-1]
|
||||
unvisited = unvisited[:len(unvisited)-1]
|
||||
|
|
@ -112,6 +119,11 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
|
||||
pubRoom := publicRoomsChunk(ctx, querier, queuedRoom.RoomID)
|
||||
|
||||
if pubRoom == nil {
|
||||
util.GetLogger(ctx).WithField("room_id", queuedRoom.RoomID).Debug("unable to get publicRoomsChunk")
|
||||
continue
|
||||
}
|
||||
|
||||
discoveredRooms = append(discoveredRooms, fclient.RoomHierarchyRoom{
|
||||
PublicRoom: *pubRoom,
|
||||
RoomType: roomType,
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ func (r *RoomEventProducer) ProduceRoomEvents(roomID string, updates []api.Outpu
|
|||
}
|
||||
}
|
||||
|
||||
if eventType == "m.room.server_acl" && update.NewRoomEvent.Event.StateKeyEquals("") {
|
||||
if eventType == acls.MRoomServerACL && update.NewRoomEvent.Event.StateKeyEquals("") {
|
||||
ev := update.NewRoomEvent.Event.PDU
|
||||
defer r.ACLs.OnServerACLUpdate(ev)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
|
|
@ -15,6 +16,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/acls"
|
||||
"github.com/matrix-org/dendrite/roomserver/state"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/userapi"
|
||||
|
|
@ -34,6 +36,10 @@ import (
|
|||
"github.com/matrix-org/dendrite/test/testrig"
|
||||
)
|
||||
|
||||
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||
return &statistics.ServerStatistics{}, nil
|
||||
}
|
||||
|
||||
type FakeQuerier struct {
|
||||
api.QuerySenderIDAPI
|
||||
}
|
||||
|
|
@ -58,7 +64,7 @@ func TestUsers(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("kick users", func(t *testing.T) {
|
||||
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
rsAPI.SetUserAPI(usrAPI)
|
||||
testKickUsers(t, rsAPI, usrAPI)
|
||||
})
|
||||
|
|
@ -258,7 +264,7 @@ func TestPurgeRoom(t *testing.T) {
|
|||
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, rsAPI, caches, nil, true)
|
||||
rsAPI.SetFederationAPI(fsAPI, nil)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||
syncapi.AddPublicRoutes(processCtx, routers, cfg, cm, &natsInstance, userAPI, rsAPI, caches, caching.DisableMetrics)
|
||||
|
||||
// Create the room
|
||||
|
|
@ -1050,7 +1056,7 @@ func TestUpgrade(t *testing.T) {
|
|||
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
rsAPI.SetUserAPI(userAPI)
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
|
@ -1185,3 +1191,43 @@ func TestStateReset(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewServerACLs(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
roomWithACL := test.NewRoom(t, alice)
|
||||
|
||||
roomWithACL.CreateAndInsert(t, alice, acls.MRoomServerACL, acls.ServerACL{
|
||||
Allowed: []string{"*"},
|
||||
Denied: []string{"localhost"},
|
||||
AllowIPLiterals: false,
|
||||
}, test.WithStateKey(""))
|
||||
|
||||
roomWithoutACL := test.NewRoom(t, alice)
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType)
|
||||
defer closeDB()
|
||||
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
natsInstance := &jetstream.NATSInstance{}
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
// start JetStream listeners
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
|
||||
// let the RS create the events
|
||||
err := api.SendEvents(context.Background(), rsAPI, api.KindNew, roomWithACL.Events(), "test", "test", "test", nil, false)
|
||||
assert.NoError(t, err)
|
||||
err = api.SendEvents(context.Background(), rsAPI, api.KindNew, roomWithoutACL.Events(), "test", "test", "test", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
db, err := storage.Open(processCtx.Context(), cm, &cfg.RoomServer.Database, caches)
|
||||
assert.NoError(t, err)
|
||||
// create new server ACLs and verify server is banned/not banned
|
||||
serverACLs := acls.NewServerACLs(db)
|
||||
banned := serverACLs.IsServerBannedFromRoom("localhost", roomWithACL.ID)
|
||||
assert.Equal(t, true, banned)
|
||||
banned = serverACLs.IsServerBannedFromRoom("localhost", roomWithoutACL.ID)
|
||||
assert.Equal(t, false, banned)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ type Database interface {
|
|||
// in this room, along a boolean set to true if the user is still in this room,
|
||||
// false if not.
|
||||
// Returns an error if there was a problem talking to the database.
|
||||
GetMembership(ctx context.Context, roomNID types.RoomNID, requestSenderID spec.SenderID) (membershipEventNID types.EventNID, membershipNID tables.MembershipState, stillInRoom, isRoomForgotten bool, err error)
|
||||
GetMembership(ctx context.Context, roomNID types.RoomNID, requestSenderID spec.SenderID) (membershipEventNID types.EventNID, stillInRoom, isRoomForgotten bool, err error)
|
||||
// Lookup the membership event numeric IDs for all user that are or have
|
||||
// been members of a given room. Only lookup events of "join" membership if
|
||||
// joinOnly is set to true.
|
||||
|
|
|
|||
|
|
@ -249,6 +249,7 @@ func (s *eventStatements) BulkSelectSnapshotsFromEventIDs(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "BulkSelectSnapshotsFromEventIDs: rows.close() failed")
|
||||
|
||||
var eventID string
|
||||
var stateNID types.StateSnapshotNID
|
||||
|
|
@ -563,7 +564,7 @@ func (s *eventStatements) SelectRoomNIDsForEventNIDs(
|
|||
}
|
||||
result[eventNID] = roomNID
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func eventNIDsAsArray(eventNIDs []types.EventNID) pq.Int64Array {
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ func (s *membershipStatements) SelectRoomsWithMembership(
|
|||
}
|
||||
roomNIDs = append(roomNIDs, roomNID)
|
||||
}
|
||||
return roomNIDs, nil
|
||||
return roomNIDs, rows.Err()
|
||||
}
|
||||
|
||||
func (s *membershipStatements) SelectJoinedUsersSetForRooms(
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ func (s *roomStatements) SelectRoomIDsWithEvents(ctx context.Context, txn *sql.T
|
|||
}
|
||||
roomIDs = append(roomIDs, roomID)
|
||||
}
|
||||
return roomIDs, nil
|
||||
return roomIDs, rows.Err()
|
||||
}
|
||||
func (s *roomStatements) InsertRoomNID(
|
||||
ctx context.Context, txn *sql.Tx,
|
||||
|
|
@ -255,7 +255,7 @@ func (s *roomStatements) SelectRoomVersionsForRoomNIDs(
|
|||
}
|
||||
result[roomNID] = roomVersion
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *roomStatements) BulkSelectRoomIDs(ctx context.Context, txn *sql.Tx, roomNIDs []types.RoomNID) ([]string, error) {
|
||||
|
|
@ -277,7 +277,7 @@ func (s *roomStatements) BulkSelectRoomIDs(ctx context.Context, txn *sql.Tx, roo
|
|||
}
|
||||
roomIDs = append(roomIDs, roomID)
|
||||
}
|
||||
return roomIDs, nil
|
||||
return roomIDs, rows.Err()
|
||||
}
|
||||
|
||||
func (s *roomStatements) BulkSelectRoomNIDs(ctx context.Context, txn *sql.Tx, roomIDs []string) ([]types.RoomNID, error) {
|
||||
|
|
@ -299,7 +299,7 @@ func (s *roomStatements) BulkSelectRoomNIDs(ctx context.Context, txn *sql.Tx, ro
|
|||
}
|
||||
roomNIDs = append(roomNIDs, roomNID)
|
||||
}
|
||||
return roomNIDs, nil
|
||||
return roomNIDs, rows.Err()
|
||||
}
|
||||
|
||||
func roomNIDsAsArray(roomNIDs []types.RoomNID) pq.Int64Array {
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context,
|
|||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows")
|
||||
|
||||
resultMap := make(map[types.RoomNID]ed25519.PublicKey)
|
||||
|
||||
|
|
@ -173,5 +174,5 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context,
|
|||
}
|
||||
resultMap[roomNID] = pubkey
|
||||
}
|
||||
return resultMap, err
|
||||
return resultMap, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -491,14 +491,14 @@ func (d *Database) RemoveRoomAlias(ctx context.Context, alias string) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (d *Database) GetMembership(ctx context.Context, roomNID types.RoomNID, requestSenderID spec.SenderID) (membershipEventNID types.EventNID, membershipState tables.MembershipState, stillInRoom, isRoomforgotten bool, err error) {
|
||||
func (d *Database) GetMembership(ctx context.Context, roomNID types.RoomNID, requestSenderID spec.SenderID) (membershipEventNID types.EventNID, GetMembershipstillInGetMembershipRoom, isRoomforgotten bool, err error) {
|
||||
var requestSenderUserNID types.EventStateKeyNID
|
||||
err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
requestSenderUserNID, err = d.assignStateKeyNID(ctx, txn, string(requestSenderID))
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return 0, 0, false, false, fmt.Errorf("d.assignStateKeyNID: %w", err)
|
||||
return 0, false, false, fmt.Errorf("d.assignStateKeyNID: %w", err)
|
||||
}
|
||||
|
||||
senderMembershipEventNID, senderMembership, isRoomforgotten, err :=
|
||||
|
|
@ -507,12 +507,12 @@ func (d *Database) GetMembership(ctx context.Context, roomNID types.RoomNID, req
|
|||
)
|
||||
if err == sql.ErrNoRows {
|
||||
// The user has never been a member of that room
|
||||
return 0, 0, false, false, nil
|
||||
return 0, false, false, nil
|
||||
} else if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return senderMembershipEventNID, senderMembership, senderMembership == tables.MembershipStateJoin, isRoomforgotten, nil
|
||||
return senderMembershipEventNID, senderMembership == tables.MembershipStateJoin, isRoomforgotten, nil
|
||||
}
|
||||
|
||||
func (d *Database) GetMembershipEventNIDsForRoom(
|
||||
|
|
|
|||
|
|
@ -109,5 +109,5 @@ func (s *eventJSONStatements) BulkSelectEventJSON(
|
|||
}
|
||||
result.EventNID = types.EventNID(eventNID)
|
||||
}
|
||||
return results[:i], nil
|
||||
return results[:i], rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ func (s *eventStateKeyStatements) BulkSelectEventStateKeyNID(
|
|||
}
|
||||
result[stateKey] = types.EventStateKeyNID(stateKeyNID)
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *eventStateKeyStatements) BulkSelectEventStateKey(
|
||||
|
|
@ -167,5 +167,5 @@ func (s *eventStateKeyStatements) BulkSelectEventStateKey(
|
|||
}
|
||||
result[types.EventStateKeyNID(stateKeyNID)] = stateKey
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,5 +147,5 @@ func (s *eventTypeStatements) BulkSelectEventTypeNID(
|
|||
}
|
||||
result[eventType] = types.EventTypeNID(eventTypeNID)
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -310,6 +310,9 @@ func (s *eventStatements) BulkSelectStateEventByID(
|
|||
}
|
||||
results = append(results, result)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !excludeRejected && i != len(eventIDs) {
|
||||
// If there are fewer rows returned than IDs then we were asked to lookup event IDs we don't have.
|
||||
// We don't know which ones were missing because we don't return the string IDs in the query.
|
||||
|
|
@ -377,7 +380,7 @@ func (s *eventStatements) BulkSelectStateEventByNID(
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
return results[:i], err
|
||||
return results[:i], rows.Err()
|
||||
}
|
||||
|
||||
// bulkSelectStateAtEventByID lookups the state at a list of events by event ID.
|
||||
|
|
@ -425,6 +428,9 @@ func (s *eventStatements) BulkSelectStateAtEventByID(
|
|||
)
|
||||
}
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i != len(eventIDs) {
|
||||
return nil, types.MissingEventError(
|
||||
fmt.Sprintf("storage: event IDs missing from the database (%d != %d)", i, len(eventIDs)),
|
||||
|
|
@ -507,6 +513,9 @@ func (s *eventStatements) BulkSelectStateAtEventAndReference(
|
|||
result.BeforeStateSnapshotNID = types.StateSnapshotNID(stateSnapshotNID)
|
||||
result.EventID = eventID
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i != len(eventNIDs) {
|
||||
return nil, fmt.Errorf("storage: event NIDs missing from the database (%d != %d)", i, len(eventNIDs))
|
||||
}
|
||||
|
|
@ -544,6 +553,9 @@ func (s *eventStatements) BulkSelectEventID(ctx context.Context, txn *sql.Tx, ev
|
|||
}
|
||||
results[types.EventNID(eventNID)] = eventID
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i != len(eventNIDs) {
|
||||
return nil, fmt.Errorf("storage: event NIDs missing from the database (%d != %d)", i, len(eventNIDs))
|
||||
}
|
||||
|
|
@ -602,7 +614,7 @@ func (s *eventStatements) bulkSelectEventNID(ctx context.Context, txn *sql.Tx, e
|
|||
RoomNID: types.RoomNID(roomNID),
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
func (s *eventStatements) SelectMaxEventDepth(ctx context.Context, txn *sql.Tx, eventNIDs []types.EventNID) (int64, error) {
|
||||
|
|
@ -652,7 +664,7 @@ func (s *eventStatements) SelectRoomNIDsForEventNIDs(
|
|||
}
|
||||
result[eventNID] = roomNID
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func eventNIDsAsArray(eventNIDs []types.EventNID) string {
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ func (s *inviteStatements) UpdateInviteRetired(
|
|||
}
|
||||
eventIDs = append(eventIDs, inviteEventID)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
// now retire the invites
|
||||
stmt = sqlutil.TxStmt(txn, s.updateInviteRetiredStmt)
|
||||
_, err = stmt.ExecContext(ctx, roomNID, targetUserNID)
|
||||
|
|
@ -157,5 +160,5 @@ func (s *inviteStatements) SelectInviteActiveForUserInRoom(
|
|||
result = append(result, types.EventStateKeyNID(senderUserNID))
|
||||
eventIDs = append(eventIDs, eventID)
|
||||
}
|
||||
return result, eventIDs, eventJSON, nil
|
||||
return result, eventIDs, eventJSON, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,6 +250,7 @@ func (s *membershipStatements) SelectMembershipsFromRoom(
|
|||
}
|
||||
eventNIDs = append(eventNIDs, eNID)
|
||||
}
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -277,6 +278,7 @@ func (s *membershipStatements) SelectMembershipsFromRoomAndMembership(
|
|||
}
|
||||
eventNIDs = append(eventNIDs, eNID)
|
||||
}
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -313,7 +315,7 @@ func (s *membershipStatements) SelectRoomsWithMembership(
|
|||
}
|
||||
roomNIDs = append(roomNIDs, roomNID)
|
||||
}
|
||||
return roomNIDs, nil
|
||||
return roomNIDs, rows.Err()
|
||||
}
|
||||
|
||||
func (s *membershipStatements) SelectJoinedUsersSetForRooms(ctx context.Context, txn *sql.Tx, roomNIDs []types.RoomNID, userNIDs []types.EventStateKeyNID, localOnly bool) (map[types.EventStateKeyNID]int, error) {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ func (s *roomAliasesStatements) SelectAliasesFromRoomID(
|
|||
|
||||
aliases = append(aliases, alias)
|
||||
}
|
||||
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ func (s *roomStatements) SelectRoomIDsWithEvents(ctx context.Context, txn *sql.T
|
|||
}
|
||||
roomIDs = append(roomIDs, roomID)
|
||||
}
|
||||
return roomIDs, nil
|
||||
return roomIDs, rows.Err()
|
||||
}
|
||||
|
||||
func (s *roomStatements) SelectRoomInfo(ctx context.Context, txn *sql.Tx, roomID string) (*types.RoomInfo, error) {
|
||||
|
|
@ -265,7 +265,7 @@ func (s *roomStatements) SelectRoomVersionsForRoomNIDs(
|
|||
}
|
||||
result[roomNID] = roomVersion
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *roomStatements) BulkSelectRoomIDs(ctx context.Context, txn *sql.Tx, roomNIDs []types.RoomNID) ([]string, error) {
|
||||
|
|
@ -293,7 +293,7 @@ func (s *roomStatements) BulkSelectRoomIDs(ctx context.Context, txn *sql.Tx, roo
|
|||
}
|
||||
roomIDs = append(roomIDs, roomID)
|
||||
}
|
||||
return roomIDs, nil
|
||||
return roomIDs, rows.Err()
|
||||
}
|
||||
|
||||
func (s *roomStatements) BulkSelectRoomNIDs(ctx context.Context, txn *sql.Tx, roomIDs []string) ([]types.RoomNID, error) {
|
||||
|
|
@ -321,5 +321,5 @@ func (s *roomStatements) BulkSelectRoomNIDs(ctx context.Context, txn *sql.Tx, ro
|
|||
}
|
||||
roomNIDs = append(roomNIDs, roomNID)
|
||||
}
|
||||
return roomNIDs, nil
|
||||
return roomNIDs, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,13 +133,16 @@ func (s *stateSnapshotStatements) BulkSelectStateBlockNIDs(
|
|||
var stateBlockNIDsJSON string
|
||||
for ; rows.Next(); i++ {
|
||||
result := &results[i]
|
||||
if err := rows.Scan(&result.StateSnapshotNID, &stateBlockNIDsJSON); err != nil {
|
||||
if err = rows.Scan(&result.StateSnapshotNID, &stateBlockNIDsJSON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal([]byte(stateBlockNIDsJSON), &result.StateBlockNIDs); err != nil {
|
||||
if err = json.Unmarshal([]byte(stateBlockNIDsJSON), &result.StateBlockNIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i != len(stateNIDs) {
|
||||
return nil, types.MissingStateError(fmt.Sprintf("storage: state NIDs missing from the database (%d != %d)", i, len(stateNIDs)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context,
|
|||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows")
|
||||
|
||||
resultMap := make(map[types.RoomNID]ed25519.PublicKey)
|
||||
|
||||
|
|
@ -188,5 +189,5 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context,
|
|||
}
|
||||
resultMap[roomNID] = pubkey
|
||||
}
|
||||
return resultMap, err
|
||||
return resultMap, rows.Err()
|
||||
}
|
||||
|
|
|
|||
76
roomserver/storage/tables/interface_test.go
Normal file
76
roomserver/storage/tables/interface_test.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package tables
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestExtractContentValue(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
room := test.NewRoom(t, alice)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
event *types.HeaderedEvent
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "returns creator ID for create events",
|
||||
event: room.Events()[0],
|
||||
want: alice.ID,
|
||||
},
|
||||
{
|
||||
name: "returns the alias for canonical alias events",
|
||||
event: room.CreateEvent(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": "#test:test"}),
|
||||
want: "#test:test",
|
||||
},
|
||||
{
|
||||
name: "returns the history_visibility for history visibility events",
|
||||
event: room.CreateEvent(t, alice, spec.MRoomHistoryVisibility, map[string]string{"history_visibility": "shared"}),
|
||||
want: "shared",
|
||||
},
|
||||
{
|
||||
name: "returns the join rules for join_rules events",
|
||||
event: room.CreateEvent(t, alice, spec.MRoomJoinRules, map[string]string{"join_rule": "public"}),
|
||||
want: "public",
|
||||
},
|
||||
{
|
||||
name: "returns the membership for room_member events",
|
||||
event: room.CreateEvent(t, alice, spec.MRoomMember, map[string]string{"membership": "join"}, test.WithStateKey(alice.ID)),
|
||||
want: "join",
|
||||
},
|
||||
{
|
||||
name: "returns the room name for room_name events",
|
||||
event: room.CreateEvent(t, alice, spec.MRoomName, map[string]string{"name": "testing"}, test.WithStateKey(alice.ID)),
|
||||
want: "testing",
|
||||
},
|
||||
{
|
||||
name: "returns the room avatar for avatar events",
|
||||
event: room.CreateEvent(t, alice, spec.MRoomAvatar, map[string]string{"url": "mxc://testing"}, test.WithStateKey(alice.ID)),
|
||||
want: "mxc://testing",
|
||||
},
|
||||
{
|
||||
name: "returns the room topic for topic events",
|
||||
event: room.CreateEvent(t, alice, spec.MRoomTopic, map[string]string{"topic": "testing"}, test.WithStateKey(alice.ID)),
|
||||
want: "testing",
|
||||
},
|
||||
{
|
||||
name: "returns guest_access for guest access events",
|
||||
event: room.CreateEvent(t, alice, "m.room.guest_access", map[string]string{"guest_access": "forbidden"}, test.WithStateKey(alice.ID)),
|
||||
want: "forbidden",
|
||||
},
|
||||
{
|
||||
name: "returns empty string if key can't be found or unknown event",
|
||||
event: room.CreateEvent(t, alice, "idontexist", nil),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, ExtractContentValue(tt.event), "ExtractContentValue(%v)", tt.event)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -114,6 +114,11 @@ func (c *Global) Verify(configErrs *ConfigErrors) {
|
|||
checkNotEmpty(configErrs, "global.server_name", string(c.ServerName))
|
||||
checkNotEmpty(configErrs, "global.private_key", string(c.PrivateKeyPath))
|
||||
|
||||
// Check that client well-known has a proper format
|
||||
if c.WellKnownClientName != "" && !strings.HasPrefix(c.WellKnownClientName, "http://") && !strings.HasPrefix(c.WellKnownClientName, "https://") {
|
||||
configErrs.Add("The configuration for well_known_client_name does not have a proper format, consider adding http:// or https://. Some clients may fail to connect.")
|
||||
}
|
||||
|
||||
for _, v := range c.VirtualHosts {
|
||||
v.Verify(configErrs)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ global:
|
|||
key_id: ed25519:auto
|
||||
key_validity_period: 168h0m0s
|
||||
well_known_server_name: "localhost:443"
|
||||
well_known_client_name: "localhost:443"
|
||||
well_known_client_name: "https://localhost"
|
||||
trusted_third_party_id_servers:
|
||||
- matrix.org
|
||||
- vector.im
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ type UserAPI struct {
|
|||
// Users who register on this homeserver will automatically
|
||||
// be joined to the rooms listed under this option.
|
||||
AutoJoinRooms []string `yaml:"auto_join_rooms"`
|
||||
|
||||
// The number of workers to start for the DeviceListUpdater. Defaults to 8.
|
||||
// This only needs updating if the "InputDeviceListUpdate" stream keeps growing indefinitely.
|
||||
WorkerCount int `yaml:"worker_count"`
|
||||
}
|
||||
|
||||
const DefaultOpenIDTokenLifetimeMS = 3600000 // 60 minutes
|
||||
|
|
@ -28,6 +32,7 @@ const DefaultOpenIDTokenLifetimeMS = 3600000 // 60 minutes
|
|||
func (c *UserAPI) Defaults(opts DefaultOpts) {
|
||||
c.BCryptCost = bcrypt.DefaultCost
|
||||
c.OpenIDTokenLifetimeMS = DefaultOpenIDTokenLifetimeMS
|
||||
c.WorkerCount = 8
|
||||
if opts.Generate {
|
||||
if !opts.SingleDatabase {
|
||||
c.AccountDatabase.ConnectionString = "file:userapi_accounts.db"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
|
|
@ -38,7 +39,7 @@ func (s *NATSInstance) Prepare(process *process.ProcessContext, cfg *config.JetS
|
|||
defer natsLock.Unlock()
|
||||
// check if we need an in-process NATS Server
|
||||
if len(cfg.Addresses) != 0 {
|
||||
return setupNATS(cfg, nil)
|
||||
return setupNATS(process, cfg, nil)
|
||||
}
|
||||
if s.Server == nil {
|
||||
var err error
|
||||
|
|
@ -70,7 +71,7 @@ func (s *NATSInstance) Prepare(process *process.ProcessContext, cfg *config.JetS
|
|||
process.ComponentFinished()
|
||||
}()
|
||||
}
|
||||
if !s.ReadyForConnections(time.Second * 10) {
|
||||
if !s.ReadyForConnections(time.Second * 60) {
|
||||
logrus.Fatalln("NATS did not start in time")
|
||||
}
|
||||
// reuse existing connections
|
||||
|
|
@ -81,13 +82,14 @@ func (s *NATSInstance) Prepare(process *process.ProcessContext, cfg *config.JetS
|
|||
if err != nil {
|
||||
logrus.Fatalln("Failed to create NATS client")
|
||||
}
|
||||
js, _ := setupNATS(cfg, nc)
|
||||
js, _ := setupNATS(process, cfg, nc)
|
||||
s.js = js
|
||||
s.nc = nc
|
||||
return js, nc
|
||||
}
|
||||
|
||||
func setupNATS(cfg *config.JetStream, nc *natsclient.Conn) (natsclient.JetStreamContext, *natsclient.Conn) {
|
||||
// nolint:gocyclo
|
||||
func setupNATS(process *process.ProcessContext, cfg *config.JetStream, nc *natsclient.Conn) (natsclient.JetStreamContext, *natsclient.Conn) {
|
||||
var s nats.JetStreamContext
|
||||
var err error
|
||||
if nc == nil {
|
||||
|
|
@ -131,9 +133,102 @@ func setupNATS(cfg *config.JetStream, nc *natsclient.Conn) (natsclient.JetStream
|
|||
}
|
||||
|
||||
for _, stream := range streams { // streams are defined in streams.go
|
||||
err = configureStream(stream, cfg, s)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithField("stream", stream.Name).Fatal("unable to configure a stream")
|
||||
name := cfg.Prefixed(stream.Name)
|
||||
info, err := s.StreamInfo(name)
|
||||
if err != nil && err != natsclient.ErrStreamNotFound {
|
||||
logrus.WithError(err).Fatal("Unable to get stream info")
|
||||
}
|
||||
subjects := stream.Subjects
|
||||
if len(subjects) == 0 {
|
||||
// By default we want each stream to listen for the subjects
|
||||
// that are either an exact match for the stream name, or where
|
||||
// the first part of the subject is the stream name. ">" is a
|
||||
// wildcard in NATS for one or more subject tokens. In the case
|
||||
// that the stream is called "Foo", this will match any message
|
||||
// with the subject "Foo", "Foo.Bar" or "Foo.Bar.Baz" etc.
|
||||
subjects = []string{name, name + ".>"}
|
||||
}
|
||||
if info != nil {
|
||||
// If the stream config doesn't match what we expect, try to update
|
||||
// it. If that doesn't work then try to blow it away and we'll then
|
||||
// recreate it in the next section.
|
||||
// Each specific option that we set must be checked by hand, as if
|
||||
// you DeepEqual the whole config struct, it will always show that
|
||||
// there's a difference because the NATS Server will return defaults
|
||||
// in the stream info.
|
||||
switch {
|
||||
case !reflect.DeepEqual(info.Config.Subjects, subjects):
|
||||
fallthrough
|
||||
case info.Config.Retention != stream.Retention:
|
||||
fallthrough
|
||||
case info.Config.Storage != stream.Storage:
|
||||
fallthrough
|
||||
case info.Config.MaxAge != stream.MaxAge:
|
||||
// Try updating the stream first, as many things can be updated
|
||||
// non-destructively.
|
||||
if info, err = s.UpdateStream(stream); err != nil {
|
||||
logrus.WithError(err).Warnf("Unable to update stream %q, recreating...", name)
|
||||
// We failed to update the stream, this is a last attempt to get
|
||||
// things working but may result in data loss.
|
||||
if err = s.DeleteStream(name); err != nil {
|
||||
logrus.WithError(err).Fatalf("Unable to delete stream %q", name)
|
||||
}
|
||||
info = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if info == nil {
|
||||
// If we're trying to keep everything in memory (e.g. unit tests)
|
||||
// then overwrite the storage policy.
|
||||
if cfg.InMemory {
|
||||
stream.Storage = natsclient.MemoryStorage
|
||||
}
|
||||
|
||||
// Namespace the streams without modifying the original streams
|
||||
// array, otherwise we end up with namespaces on namespaces.
|
||||
namespaced := *stream
|
||||
namespaced.Name = name
|
||||
namespaced.Subjects = subjects
|
||||
if _, err = s.AddStream(&namespaced); err != nil {
|
||||
logger := logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"stream": namespaced.Name,
|
||||
"subjects": namespaced.Subjects,
|
||||
})
|
||||
|
||||
// If the stream was supposed to be in-memory to begin with
|
||||
// then an error here is fatal so we'll give up.
|
||||
if namespaced.Storage == natsclient.MemoryStorage {
|
||||
logger.WithError(err).Fatal("Unable to add in-memory stream")
|
||||
}
|
||||
|
||||
// The stream was supposed to be on disk. Let's try starting
|
||||
// Dendrite with the stream in-memory instead. That'll mean that
|
||||
// we can't recover anything that was queued on the disk but we
|
||||
// will still be able to start and run hopefully in the meantime.
|
||||
logger.WithError(err).Error("Unable to add stream")
|
||||
sentry.CaptureException(fmt.Errorf("Unable to add stream %q: %w", namespaced.Name, err))
|
||||
|
||||
namespaced.Storage = natsclient.MemoryStorage
|
||||
if _, err = s.AddStream(&namespaced); err != nil {
|
||||
// We tried to add the stream in-memory instead but something
|
||||
// went wrong. That's an unrecoverable situation so we will
|
||||
// give up at this point.
|
||||
logger.WithError(err).Fatal("Unable to add in-memory stream")
|
||||
}
|
||||
|
||||
if stream.Storage != namespaced.Storage {
|
||||
// We've managed to add the stream in memory. What's on the
|
||||
// disk will be left alone, but our ability to recover from a
|
||||
// future crash will be limited. Yell about it.
|
||||
err := fmt.Errorf("Stream %q is running in-memory; this may be due to data corruption in the JetStream storage directory", namespaced.Name)
|
||||
sentry.CaptureException(err)
|
||||
process.Degraded(err)
|
||||
}
|
||||
}
|
||||
// Kozi's code, which defers from the original dendrite code
|
||||
// err = configureStream(stream, cfg, s)
|
||||
// if err != nil {
|
||||
// logrus.WithError(err).WithField("stream", stream.Name).Fatal("unable to configure a stream")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ var (
|
|||
InputDeviceListUpdate = "InputDeviceListUpdate"
|
||||
InputSigningKeyUpdate = "InputSigningKeyUpdate"
|
||||
OutputRoomEvent = "OutputRoomEvent"
|
||||
OutputAppserviceEvent = "OutputAppserviceEvent"
|
||||
OutputSendToDeviceEvent = "OutputSendToDeviceEvent"
|
||||
OutputKeyChangeEvent = "OutputKeyChangeEvent"
|
||||
OutputTypingEvent = "OutputTypingEvent"
|
||||
|
|
@ -66,6 +67,11 @@ var streams = []*nats.StreamConfig{
|
|||
Retention: nats.InterestPolicy,
|
||||
Storage: nats.FileStorage,
|
||||
},
|
||||
{
|
||||
Name: OutputAppserviceEvent,
|
||||
Retention: nats.InterestPolicy,
|
||||
Storage: nats.FileStorage,
|
||||
},
|
||||
{
|
||||
Name: OutputSendToDeviceEvent,
|
||||
Retention: nats.InterestPolicy,
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ func (p *DB) ChildrenForParent(ctx context.Context, eventID, relType string, rec
|
|||
}
|
||||
children = append(children, evInfo)
|
||||
}
|
||||
return children, nil
|
||||
return children, rows.Err()
|
||||
}
|
||||
|
||||
func (p *DB) ParentForChild(ctx context.Context, eventID, relType string) (*eventInfo, error) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
"github.com/matrix-org/dendrite/syncapi/notifier"
|
||||
"github.com/matrix-org/dendrite/syncapi/producers"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage"
|
||||
"github.com/matrix-org/dendrite/syncapi/streams"
|
||||
"github.com/matrix-org/dendrite/syncapi/synctypes"
|
||||
|
|
@ -55,6 +56,7 @@ type OutputRoomEventConsumer struct {
|
|||
inviteStream streams.StreamProvider
|
||||
notifier *notifier.Notifier
|
||||
fts fulltext.Indexer
|
||||
asProducer *producers.AppserviceEventProducer
|
||||
}
|
||||
|
||||
// NewOutputRoomEventConsumer creates a new OutputRoomEventConsumer. Call Start() to begin consuming from room servers.
|
||||
|
|
@ -68,6 +70,7 @@ func NewOutputRoomEventConsumer(
|
|||
inviteStream streams.StreamProvider,
|
||||
rsAPI api.SyncRoomserverAPI,
|
||||
fts *fulltext.Search,
|
||||
asProducer *producers.AppserviceEventProducer,
|
||||
) *OutputRoomEventConsumer {
|
||||
return &OutputRoomEventConsumer{
|
||||
ctx: process.Context(),
|
||||
|
|
@ -81,6 +84,7 @@ func NewOutputRoomEventConsumer(
|
|||
inviteStream: inviteStream,
|
||||
rsAPI: rsAPI,
|
||||
fts: fts,
|
||||
asProducer: asProducer,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +123,11 @@ func (s *OutputRoomEventConsumer) onMessage(ctx context.Context, msgs []*nats.Ms
|
|||
}
|
||||
}
|
||||
err = s.onNewRoomEvent(s.ctx, *output.NewRoomEvent)
|
||||
if err == nil && s.asProducer != nil {
|
||||
if err = s.asProducer.ProduceRoomEvents(msg); err != nil {
|
||||
log.WithError(err).Warn("failed to produce OutputAppserviceEvent")
|
||||
}
|
||||
}
|
||||
case api.OutputTypeOldRoomEvent:
|
||||
err = s.onOldRoomEvent(s.ctx, *output.OldRoomEvent)
|
||||
case api.OutputTypeNewInviteEvent:
|
||||
|
|
|
|||
33
syncapi/producers/appservices.go
Normal file
33
syncapi/producers/appservices.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package producers
|
||||
|
||||
import (
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
// AppserviceEventProducer produces events for the appservice API to consume
|
||||
type AppserviceEventProducer struct {
|
||||
Topic string
|
||||
JetStream nats.JetStreamContext
|
||||
}
|
||||
|
||||
func (a *AppserviceEventProducer) ProduceRoomEvents(
|
||||
msg *nats.Msg,
|
||||
) error {
|
||||
msg.Subject = a.Topic
|
||||
_, err := a.JetStream.PublishMsg(msg)
|
||||
return err
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ func GetEvent(
|
|||
rsAPI api.SyncRoomserverAPI,
|
||||
) util.JSONResponse {
|
||||
ctx := req.Context()
|
||||
db, err := syncDB.NewDatabaseTransaction(ctx)
|
||||
db, err := syncDB.NewDatabaseSnapshot(ctx)
|
||||
logger := util.GetLogger(ctx).WithFields(logrus.Fields{
|
||||
"event_id": eventID,
|
||||
"room_id": rawRoomID,
|
||||
|
|
@ -56,6 +56,7 @@ func GetEvent(
|
|||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
defer db.Rollback() // nolint: errcheck
|
||||
|
||||
roomID, err := spec.NewRoomID(rawRoomID)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ func currentRoomStateRowsToStreamEvents(rows *sql.Rows) ([]types.StreamEvent, er
|
|||
})
|
||||
}
|
||||
|
||||
return events, nil
|
||||
return events, rows.Err()
|
||||
}
|
||||
|
||||
func rowsToEvents(rows *sql.Rows) ([]*rstypes.HeaderedEvent, error) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
rstypes "github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage/tables"
|
||||
|
|
@ -160,6 +161,7 @@ func (s *membershipsStatements) SelectMemberships(
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectMemberships: failed to close rows")
|
||||
var (
|
||||
eventID string
|
||||
)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ func (s *peekStatements) SelectPeekingDevices(
|
|||
devices = append(devices, types.PeekingDevice{UserID: userID, DeviceID: deviceID})
|
||||
result[roomID] = devices
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *peekStatements) SelectMaxPeekID(
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ func (p *presenceStatements) GetPresenceForUsers(
|
|||
presence.ClientFields.Presence = presence.Presence.String()
|
||||
result = append(result, presence)
|
||||
}
|
||||
return result, err
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (p *presenceStatements) GetMaxPresenceID(ctx context.Context, txn *sql.Tx) (pos types.StreamPosition, err error) {
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ func (s *currentRoomStateStatements) SelectJoinedUsers(
|
|||
users = append(users, userID)
|
||||
result[roomID] = users
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
// SelectJoinedUsersInRoom returns a map of room ID to a list of joined user IDs for a given room.
|
||||
|
|
@ -236,7 +236,7 @@ func (s *currentRoomStateStatements) SelectRoomIDsWithMembership(
|
|||
}
|
||||
result = append(result, roomID)
|
||||
}
|
||||
return result, nil
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
// SelectRoomIDsWithAnyMembership returns a map of all memberships for the given user.
|
||||
|
|
@ -419,7 +419,7 @@ func currentRoomStateRowsToStreamEvents(rows *sql.Rows) ([]types.StreamEvent, er
|
|||
})
|
||||
}
|
||||
|
||||
return events, nil
|
||||
return events, rows.Err()
|
||||
}
|
||||
|
||||
func rowsToEvents(rows *sql.Rows) ([]*rstypes.HeaderedEvent, error) {
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ func (s *inviteEventsStatements) SelectInviteEventsInRange(
|
|||
if lastPos == 0 {
|
||||
lastPos = r.To
|
||||
}
|
||||
return result, retired, lastPos, nil
|
||||
return result, retired, lastPos, rows.Err()
|
||||
}
|
||||
|
||||
func (s *inviteEventsStatements) SelectMaxInviteID(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue