Merge branch 'main' of github.com:matrix-org/dendrite into s7evink/consent-tracking
This commit is contained in:
commit
2b496be2c3
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
<!-- Please read docs/CONTRIBUTING.md before submitting your pull request -->
|
<!-- Please read docs/CONTRIBUTING.md before submitting your pull request -->
|
||||||
|
|
||||||
|
* [ ] I have added added tests for PR _or_ I have justified why this PR doesn't need tests.
|
||||||
* [ ] Pull request includes a [sign off](https://github.com/matrix-org/dendrite/blob/main/docs/CONTRIBUTING.md#sign-off)
|
* [ ] Pull request includes a [sign off](https://github.com/matrix-org/dendrite/blob/main/docs/CONTRIBUTING.md#sign-off)
|
||||||
|
|
||||||
Signed-off-by: `Your Name <your@email.example.org>`
|
Signed-off-by: `Your Name <your@email.example.org>`
|
||||||
|
|
34
.github/workflows/codeql-analysis.yml
vendored
34
.github/workflows/codeql-analysis.yml
vendored
|
@ -1,34 +0,0 @@
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: ["go"]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- run: git checkout HEAD^2
|
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v1
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v1
|
|
378
.github/workflows/dendrite.yml
vendored
Normal file
378
.github/workflows/dendrite.yml
vendored
Normal file
|
@ -0,0 +1,378 @@
|
||||||
|
name: Dendrite
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
wasm:
|
||||||
|
name: WASM build test
|
||||||
|
timeout-minutes: 5
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.16
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-wasm-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-wasm
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.npm
|
||||||
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-node-
|
||||||
|
|
||||||
|
- name: Reconfigure Git to use HTTPS auth for repo packages
|
||||||
|
run: >
|
||||||
|
git config --global url."https://github.com/".insteadOf
|
||||||
|
ssh://git@github.com/
|
||||||
|
|
||||||
|
- name: Install test dependencies
|
||||||
|
working-directory: ./test/wasm
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: ./test-dendritejs.sh
|
||||||
|
|
||||||
|
# Run golangci-lint
|
||||||
|
lint:
|
||||||
|
timeout-minutes: 5
|
||||||
|
name: Linting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
|
||||||
|
# run go test with different go versions
|
||||||
|
test:
|
||||||
|
timeout-minutes: 5
|
||||||
|
name: Unit tests (Go ${{ matrix.go }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Service containers to run with `container-job`
|
||||||
|
services:
|
||||||
|
# Label used to access the service container
|
||||||
|
postgres:
|
||||||
|
# Docker Hub image
|
||||||
|
image: postgres:13-alpine
|
||||||
|
# Provide the password for postgres
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: dendrite
|
||||||
|
ports:
|
||||||
|
# Maps tcp port 5432 on service container to the host
|
||||||
|
- 5432:5432
|
||||||
|
# Set health checks to wait until postgres has started
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
go: ["1.16", "1.17", "1.18"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go${{ matrix.go }}-test-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go${{ matrix.go }}-test-
|
||||||
|
- run: go test ./...
|
||||||
|
env:
|
||||||
|
POSTGRES_HOST: localhost
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: dendrite
|
||||||
|
|
||||||
|
# build Dendrite for linux with different architectures and go versions
|
||||||
|
build:
|
||||||
|
name: Build for Linux
|
||||||
|
timeout-minutes: 10
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
go: ["1.16", "1.17", "1.18"]
|
||||||
|
goos: ["linux"]
|
||||||
|
goarch: ["amd64", "386"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
- name: Install dependencies x86
|
||||||
|
if: ${{ matrix.goarch == '386' }}
|
||||||
|
run: sudo apt update && sudo apt-get install -y gcc-multilib
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go${{ matrix.go }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go${{ matrix.go }}-${{ matrix.goarch }}-
|
||||||
|
- env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
CGO_ENABLED: 1
|
||||||
|
run: go build -trimpath -v -o "bin/" ./cmd/...
|
||||||
|
|
||||||
|
# build for Windows 64-bit
|
||||||
|
build_windows:
|
||||||
|
name: Build for Windows
|
||||||
|
timeout-minutes: 10
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go: ["1.16", "1.17", "1.18"]
|
||||||
|
goos: ["windows"]
|
||||||
|
goarch: ["amd64"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup Go ${{ matrix.go }}
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt update && sudo apt install -y gcc-mingw-w64-x86-64 # install required gcc
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go${{ matrix.go }}-${{ matrix.goos }}-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go${{ matrix.go }}-${{ matrix.goos }}
|
||||||
|
- env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
CGO_ENABLED: 1
|
||||||
|
CC: "/usr/bin/x86_64-w64-mingw32-gcc"
|
||||||
|
run: go build -trimpath -v -o "bin/" ./cmd/...
|
||||||
|
|
||||||
|
# Dummy step to gate other tests on without repeating the whole list
|
||||||
|
initial-tests-done:
|
||||||
|
name: Initial tests passed
|
||||||
|
needs: [lint, test, build, build_windows]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !cancelled() }} # Run this even if prior jobs were skipped
|
||||||
|
steps:
|
||||||
|
- name: Check initial tests passed
|
||||||
|
uses: re-actors/alls-green@release/v1
|
||||||
|
with:
|
||||||
|
jobs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
|
# run database upgrade tests
|
||||||
|
upgrade_test:
|
||||||
|
name: Upgrade tests
|
||||||
|
timeout-minutes: 20
|
||||||
|
needs: initial-tests-done
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: "1.16"
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-upgrade-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-upgrade
|
||||||
|
- name: Build upgrade-tests
|
||||||
|
run: go build ./cmd/dendrite-upgrade-tests
|
||||||
|
- name: Test upgrade
|
||||||
|
run: ./dendrite-upgrade-tests --head .
|
||||||
|
|
||||||
|
# run Sytest in different variations
|
||||||
|
sytest:
|
||||||
|
timeout-minutes: 20
|
||||||
|
needs: initial-tests-done
|
||||||
|
name: "Sytest (${{ matrix.label }})"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- label: SQLite
|
||||||
|
|
||||||
|
- label: SQLite, full HTTP APIs
|
||||||
|
api: full-http
|
||||||
|
|
||||||
|
- label: PostgreSQL
|
||||||
|
postgres: postgres
|
||||||
|
|
||||||
|
- label: PostgreSQL, full HTTP APIs
|
||||||
|
postgres: postgres
|
||||||
|
api: full-http
|
||||||
|
container:
|
||||||
|
image: matrixdotorg/sytest-dendrite:latest
|
||||||
|
volumes:
|
||||||
|
- ${{ github.workspace }}:/src
|
||||||
|
env:
|
||||||
|
POSTGRES: ${{ matrix.postgres && 1}}
|
||||||
|
API: ${{ matrix.api && 1 }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Run Sytest
|
||||||
|
run: /bootstrap.sh dendrite
|
||||||
|
working-directory: /src
|
||||||
|
- name: Summarise results.tap
|
||||||
|
if: ${{ always() }}
|
||||||
|
run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
|
||||||
|
- name: Sytest List Maintenance
|
||||||
|
if: ${{ always() }}
|
||||||
|
run: /src/show-expected-fail-tests.sh /logs/results.tap /src/sytest-whitelist /src/sytest-blacklist
|
||||||
|
continue-on-error: true # not fatal
|
||||||
|
- name: Are We Synapse Yet?
|
||||||
|
if: ${{ always() }}
|
||||||
|
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
||||||
|
continue-on-error: true # not fatal
|
||||||
|
- name: Upload Sytest logs
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: ${{ always() }}
|
||||||
|
with:
|
||||||
|
name: Sytest Logs - ${{ job.status }} - (Dendrite, ${{ join(matrix.*, ', ') }})
|
||||||
|
path: |
|
||||||
|
/logs/results.tap
|
||||||
|
/logs/**/*.log*
|
||||||
|
|
||||||
|
# run Complement
|
||||||
|
complement:
|
||||||
|
name: "Complement (${{ matrix.label }})"
|
||||||
|
timeout-minutes: 20
|
||||||
|
needs: initial-tests-done
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- label: SQLite
|
||||||
|
|
||||||
|
- label: SQLite, full HTTP APIs
|
||||||
|
api: full-http
|
||||||
|
|
||||||
|
- label: PostgreSQL
|
||||||
|
postgres: Postgres
|
||||||
|
|
||||||
|
- label: PostgreSQL, full HTTP APIs
|
||||||
|
postgres: Postgres
|
||||||
|
api: full-http
|
||||||
|
steps:
|
||||||
|
# Env vars are set file a file given by $GITHUB_PATH. We need both Go 1.17 and GOPATH on env to run Complement.
|
||||||
|
# See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
|
||||||
|
- name: "Set Go Version"
|
||||||
|
run: |
|
||||||
|
echo "$GOROOT_1_17_X64/bin" >> $GITHUB_PATH
|
||||||
|
echo "~/go/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: "Install Complement Dependencies"
|
||||||
|
# We don't need to install Go because it is included on the Ubuntu 20.04 image:
|
||||||
|
# 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/haveyoudebuggedit/gotestfmt/v2/cmd/gotestfmt@latest
|
||||||
|
|
||||||
|
- name: Run actions/checkout@v2 for dendrite
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: dendrite
|
||||||
|
|
||||||
|
# Attempt to check out the same branch of Complement as the PR. If it
|
||||||
|
# doesn't exist, fallback to main.
|
||||||
|
- name: Checkout complement
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir -p complement
|
||||||
|
# Attempt to use the version of complement which best matches the current
|
||||||
|
# build. Depending on whether this is a PR or release, etc. we need to
|
||||||
|
# use different fallbacks.
|
||||||
|
#
|
||||||
|
# 1. First check if there's a similarly named branch (GITHUB_HEAD_REF
|
||||||
|
# for pull requests, otherwise GITHUB_REF).
|
||||||
|
# 2. Attempt to use the base branch, e.g. when merging into release-vX.Y
|
||||||
|
# (GITHUB_BASE_REF for pull requests).
|
||||||
|
# 3. Use the default complement branch ("master").
|
||||||
|
for BRANCH_NAME in "$GITHUB_HEAD_REF" "$GITHUB_BASE_REF" "${GITHUB_REF#refs/heads/}" "master"; do
|
||||||
|
# Skip empty branch names and merge commits.
|
||||||
|
if [[ -z "$BRANCH_NAME" || $BRANCH_NAME =~ ^refs/pull/.* ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
(wget -O - "https://github.com/matrix-org/complement/archive/$BRANCH_NAME.tar.gz" | tar -xz --strip-components=1 -C complement) && break
|
||||||
|
done
|
||||||
|
|
||||||
|
# Build initial Dendrite image
|
||||||
|
- run: docker build -t complement-dendrite -f build/scripts/Complement${{ matrix.postgres }}.Dockerfile .
|
||||||
|
working-directory: dendrite
|
||||||
|
|
||||||
|
# Run Complement
|
||||||
|
- run: |
|
||||||
|
set -o pipefail &&
|
||||||
|
go test -v -json -tags dendrite_blacklist ./tests/... 2>&1 | gotestfmt
|
||||||
|
shell: bash
|
||||||
|
name: Run Complement Tests
|
||||||
|
env:
|
||||||
|
COMPLEMENT_BASE_IMAGE: complement-dendrite:latest
|
||||||
|
API: ${{ matrix.api && 1 }}
|
||||||
|
working-directory: complement
|
||||||
|
|
||||||
|
integration-tests-done:
|
||||||
|
name: Integration tests passed
|
||||||
|
needs: [initial-tests-done, upgrade_test, sytest, complement]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !cancelled() }} # Run this even if prior jobs were skipped
|
||||||
|
steps:
|
||||||
|
- name: Check integration tests passed
|
||||||
|
uses: re-actors/alls-green@release/v1
|
||||||
|
with:
|
||||||
|
jobs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
|
update-docker-images:
|
||||||
|
name: Update Docker images
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
if: github.repository == 'matrix-org/dendrite' && github.ref_name == 'main'
|
||||||
|
needs: [integration-tests-done]
|
||||||
|
uses: matrix-org/dendrite/.github/workflows/docker.yml@main
|
||||||
|
secrets:
|
||||||
|
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
|
71
.github/workflows/docker-hub.yml
vendored
71
.github/workflows/docker-hub.yml
vendored
|
@ -1,71 +0,0 @@
|
||||||
# Based on https://github.com/docker/build-push-action
|
|
||||||
|
|
||||||
name: "Docker Hub"
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
env:
|
|
||||||
DOCKER_NAMESPACE: matrixdotorg
|
|
||||||
DOCKER_HUB_USER: dendritegithub
|
|
||||||
PLATFORMS: linux/amd64,linux/arm64,linux/arm/v7
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Monolith:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Get release tag
|
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ env.DOCKER_HUB_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build monolith image
|
|
||||||
id: docker_build_monolith
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./build/docker/Dockerfile.monolith
|
|
||||||
platforms: ${{ env.PLATFORMS }}
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:latest
|
|
||||||
${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:${{ env.RELEASE_VERSION }}
|
|
||||||
|
|
||||||
Polylith:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Get release tag
|
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ env.DOCKER_HUB_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build polylith image
|
|
||||||
id: docker_build_polylith
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./build/docker/Dockerfile.polylith
|
|
||||||
platforms: ${{ env.PLATFORMS }}
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:latest
|
|
||||||
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:${{ env.RELEASE_VERSION }}
|
|
139
.github/workflows/docker.yml
vendored
Normal file
139
.github/workflows/docker.yml
vendored
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
# Based on https://github.com/docker/build-push-action
|
||||||
|
|
||||||
|
name: "Docker"
|
||||||
|
|
||||||
|
on:
|
||||||
|
release: # A GitHub release was published
|
||||||
|
types: [published]
|
||||||
|
workflow_dispatch: # A build was manually requested
|
||||||
|
workflow_call: # Another pipeline called us
|
||||||
|
secrets:
|
||||||
|
DOCKER_TOKEN:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKER_NAMESPACE: matrixdotorg
|
||||||
|
DOCKER_HUB_USER: dendritegithub
|
||||||
|
GHCR_NAMESPACE: matrix-org
|
||||||
|
PLATFORMS: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
monolith:
|
||||||
|
name: Monolith image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Get release tag
|
||||||
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
|
- name: Login to GitHub Containers
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build main monolith image
|
||||||
|
if: github.ref_name == 'main'
|
||||||
|
id: docker_build_monolith
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
context: .
|
||||||
|
file: ./build/docker/Dockerfile.monolith
|
||||||
|
platforms: ${{ env.PLATFORMS }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }}
|
||||||
|
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }}
|
||||||
|
|
||||||
|
- name: Build release monolith image
|
||||||
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
|
id: docker_build_monolith_release
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
context: .
|
||||||
|
file: ./build/docker/Dockerfile.monolith
|
||||||
|
platforms: ${{ env.PLATFORMS }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:latest
|
||||||
|
${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:${{ env.RELEASE_VERSION }}
|
||||||
|
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:latest
|
||||||
|
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ env.RELEASE_VERSION }}
|
||||||
|
|
||||||
|
polylith:
|
||||||
|
name: Polylith image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Get release tag
|
||||||
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
|
- name: Login to GitHub Containers
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build main polylith image
|
||||||
|
if: github.ref_name == 'main'
|
||||||
|
id: docker_build_polylith
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
context: .
|
||||||
|
file: ./build/docker/Dockerfile.polylith
|
||||||
|
platforms: ${{ env.PLATFORMS }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }}
|
||||||
|
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }}
|
||||||
|
|
||||||
|
- name: Build release polylith image
|
||||||
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
|
id: docker_build_polylith_release
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
context: .
|
||||||
|
file: ./build/docker/Dockerfile.polylith
|
||||||
|
platforms: ${{ env.PLATFORMS }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:latest
|
||||||
|
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:${{ env.RELEASE_VERSION }}
|
||||||
|
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:latest
|
||||||
|
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ env.RELEASE_VERSION }}
|
71
.github/workflows/tests.yml
vendored
71
.github/workflows/tests.yml
vendored
|
@ -1,71 +0,0 @@
|
||||||
name: Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["main"]
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
complement:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
# Env vars are set file a file given by $GITHUB_PATH. We need both Go 1.17 and GOPATH on env to run Complement.
|
|
||||||
# See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
|
|
||||||
- name: "Set Go Version"
|
|
||||||
run: |
|
|
||||||
echo "$GOROOT_1_17_X64/bin" >> $GITHUB_PATH
|
|
||||||
echo "~/go/bin" >> $GITHUB_PATH
|
|
||||||
|
|
||||||
- name: "Install Complement Dependencies"
|
|
||||||
# We don't need to install Go because it is included on the Ubuntu 20.04 image:
|
|
||||||
# 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/haveyoudebuggedit/gotestfmt/v2/cmd/gotestfmt@latest
|
|
||||||
|
|
||||||
- name: Run actions/checkout@v2 for dendrite
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
path: dendrite
|
|
||||||
|
|
||||||
# Attempt to check out the same branch of Complement as the PR. If it
|
|
||||||
# doesn't exist, fallback to main.
|
|
||||||
- name: Checkout complement
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir -p complement
|
|
||||||
# Attempt to use the version of complement which best matches the current
|
|
||||||
# build. Depending on whether this is a PR or release, etc. we need to
|
|
||||||
# use different fallbacks.
|
|
||||||
#
|
|
||||||
# 1. First check if there's a similarly named branch (GITHUB_HEAD_REF
|
|
||||||
# for pull requests, otherwise GITHUB_REF).
|
|
||||||
# 2. Attempt to use the base branch, e.g. when merging into release-vX.Y
|
|
||||||
# (GITHUB_BASE_REF for pull requests).
|
|
||||||
# 3. Use the default complement branch ("master").
|
|
||||||
for BRANCH_NAME in "$GITHUB_HEAD_REF" "$GITHUB_BASE_REF" "${GITHUB_REF#refs/heads/}" "master"; do
|
|
||||||
# Skip empty branch names and merge commits.
|
|
||||||
if [[ -z "$BRANCH_NAME" || $BRANCH_NAME =~ ^refs/pull/.* ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
(wget -O - "https://github.com/matrix-org/complement/archive/$BRANCH_NAME.tar.gz" | tar -xz --strip-components=1 -C complement) && break
|
|
||||||
done
|
|
||||||
|
|
||||||
# Build initial Dendrite image
|
|
||||||
- run: docker build -t complement-dendrite -f build/scripts/Complement.Dockerfile .
|
|
||||||
working-directory: dendrite
|
|
||||||
|
|
||||||
# Run Complement
|
|
||||||
- run: |
|
|
||||||
set -o pipefail &&
|
|
||||||
go test -v -json -tags dendrite_blacklist ./tests/... 2>&1 | gotestfmt
|
|
||||||
shell: bash
|
|
||||||
name: Run Complement Tests
|
|
||||||
env:
|
|
||||||
COMPLEMENT_BASE_IMAGE: complement-dendrite:latest
|
|
||||||
working-directory: complement
|
|
49
.github/workflows/wasm.yml
vendored
49
.github/workflows/wasm.yml
vendored
|
@ -1,49 +0,0 @@
|
||||||
name: WebAssembly
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: 1.16.5
|
|
||||||
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cache/go-build
|
|
||||||
~/go/pkg/mod
|
|
||||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-go-
|
|
||||||
|
|
||||||
- name: Install Node
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.npm
|
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-node-
|
|
||||||
|
|
||||||
- name: Reconfigure Git to use HTTPS auth for repo packages
|
|
||||||
run: >
|
|
||||||
git config --global url."https://github.com/".insteadOf
|
|
||||||
ssh://git@github.com/
|
|
||||||
|
|
||||||
- name: Install test dependencies
|
|
||||||
working-directory: ./test/wasm
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: ./test-dendritejs.sh
|
|
65
CHANGES.md
65
CHANGES.md
|
@ -1,5 +1,70 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Dendrite 0.8.1 (2022-04-07)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* A bug which could result in the sync API deadlocking due to lock contention in the notifier has been fixed
|
||||||
|
|
||||||
|
## Dendrite 0.8.0 (2022-04-07)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Support for presence has been added
|
||||||
|
* Presence is not enabled by default
|
||||||
|
* The `global.presence.enable_inbound` and `global.presence.enable_outbound` configuration options allow configuring inbound and outbound presence separately
|
||||||
|
* Support for room upgrades via the `/room/{roomID}/upgrade` endpoint has been added (contributed by [DavidSpenler](https://github.com/DavidSpenler), [alexkursell](https://github.com/alexkursell))
|
||||||
|
* Support for ignoring users has been added
|
||||||
|
* Joined and invite user counts are now sent in the `/sync` room summaries
|
||||||
|
* Queued federation and stale device list updates will now be staggered at startup over an up-to 2 minute warm-up period, rather than happening all at once
|
||||||
|
* Memory pressure created by the sync notifier has been reduced
|
||||||
|
* The EDU server component has now been removed, with the work being moved to more relevant components
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* It is now possible to set the `power_level_content_override` when creating a room to include power levels over 100
|
||||||
|
* `/send_join` and `/state` responses will now not unmarshal the JSON twice
|
||||||
|
* The stream event consumer for push notifications will no longer request membership events that are irrelevant
|
||||||
|
* Appservices will no longer incorrectly receive state events twice
|
||||||
|
|
||||||
|
## Dendrite 0.7.0 (2022-03-25)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* The roomserver input API will now queue all events into NATS, which provides better crash resilience
|
||||||
|
* The roomserver input API now configures per-room consumers, which should use less memory
|
||||||
|
* Canonical aliases can now be added and removed
|
||||||
|
* MSC2946 Spaces Summary now works correctly, both locally and over federation
|
||||||
|
* Healthcheck endpoints are now available at:
|
||||||
|
* `/_dendrite/monitor/up`, which will return 200 when Dendrite is ready to accept requests
|
||||||
|
* `/_dendrite/monitor/health`, which will return 200 if healthy and 503 if degraded for some reason
|
||||||
|
* The `X-Matrix` federation authorisation header now includes a `destination` field, as per MSC3383
|
||||||
|
* The `/sync` endpoint now uses less memory by only ranging state for rooms that the user has participated in
|
||||||
|
* The `/messages` endpoint now accepts stream positions in both the `from` and `to` parameters
|
||||||
|
* Dendrite will now log a warning at startup if the file descriptor limit is set too low
|
||||||
|
* The federation client will now attempt to use HTTP/2 if available
|
||||||
|
* The federation client will now attempt to resume TLS sessions if possible, to reduce handshake overheads
|
||||||
|
* The built-in NATS Server has been updated to version 2.7.4
|
||||||
|
* NATS streams that don't match the desired configuration will now be recreated automatically
|
||||||
|
* When performing a graceful shutdown, Dendrite will now wait for NATS Server to shutdown completely, which should avoid some corruption of data on-disk
|
||||||
|
* The `create-account` tool has seen a number of improvements, will now ask for passwords automatically
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* The `/sync` endpoint will no longer lose state events when truncating the timeline for history visibility
|
||||||
|
* The `/context` endpoint now works correctly with `lazy_load_members`
|
||||||
|
* The `/directory/list/room/{roomID}` endpoint now correctly reports whether a room is published in the server room directory or not
|
||||||
|
* Some bugs around appservice username validation have been fixed
|
||||||
|
* Roomserver output messages are no longer unnecessarily inflated by state events, which should reduce the number of NATS message size errors
|
||||||
|
* Stream IDs for device list updates are now always 64-bit, which should fix some problems when running Dendrite on a 32-bit system
|
||||||
|
* Purging room state in the sync API has been fixed after a faulty database query was corrected
|
||||||
|
* The federation client will now release host records for remote destinations after 5 minutes instead of holding them in memory forever
|
||||||
|
* Remote media requests will now correctly return an error if the file cannot be found or downloaded
|
||||||
|
* A panic in the media API that could happen when the remote file doesn't exist has been fixed
|
||||||
|
* Various bugs around membership state and invites have been fixed
|
||||||
|
* The memberships table will now be correctly updated when rejecting a federated invite
|
||||||
|
* The client API and appservice API will now access the user database using the user API rather than accessing the database directly
|
||||||
|
|
||||||
## Dendrite 0.6.5 (2022-03-04)
|
## Dendrite 0.6.5 (2022-03-04)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
16
README.md
16
README.md
|
@ -1,4 +1,5 @@
|
||||||
# Dendrite [![Build Status](https://badge.buildkite.com/4be40938ab19f2bbc4a6c6724517353ee3ec1422e279faf374.svg?branch=master)](https://buildkite.com/matrix-dot-org/dendrite) [![Dendrite](https://img.shields.io/matrix/dendrite:matrix.org.svg?label=%23dendrite%3Amatrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#dendrite:matrix.org) [![Dendrite Dev](https://img.shields.io/matrix/dendrite-dev:matrix.org.svg?label=%23dendrite-dev%3Amatrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#dendrite-dev:matrix.org)
|
# Dendrite
|
||||||
|
[![Build status](https://github.com/matrix-org/dendrite/actions/workflows/dendrite.yml/badge.svg?event=push)](https://github.com/matrix-org/dendrite/actions/workflows/dendrite.yml) [![Dendrite](https://img.shields.io/matrix/dendrite:matrix.org.svg?label=%23dendrite%3Amatrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#dendrite:matrix.org) [![Dendrite Dev](https://img.shields.io/matrix/dendrite-dev:matrix.org.svg?label=%23dendrite-dev%3Amatrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#dendrite-dev:matrix.org)
|
||||||
|
|
||||||
Dendrite is a second-generation Matrix homeserver written in Go.
|
Dendrite is a second-generation Matrix homeserver written in Go.
|
||||||
It intends to provide an **efficient**, **reliable** and **scalable** alternative to [Synapse](https://github.com/matrix-org/synapse):
|
It intends to provide an **efficient**, **reliable** and **scalable** alternative to [Synapse](https://github.com/matrix-org/synapse):
|
||||||
|
@ -81,20 +82,17 @@ Then point your favourite Matrix client at `http://localhost:8008` or `https://l
|
||||||
|
|
||||||
We use a script called Are We Synapse Yet which checks Sytest compliance rates. Sytest is a black-box homeserver
|
We use a script called Are We Synapse Yet which checks Sytest compliance rates. Sytest is a black-box homeserver
|
||||||
test rig with around 900 tests. The script works out how many of these tests are passing on Dendrite and it
|
test rig with around 900 tests. The script works out how many of these tests are passing on Dendrite and it
|
||||||
updates with CI. As of March 2022 we're at around 76% CS API coverage and 95% Federation coverage, though check
|
updates with CI. As of April 2022 we're at around 83% CS API coverage and 95% Federation coverage, though check
|
||||||
CI for the latest numbers. In practice, this means you can communicate locally and via federation with Synapse
|
CI for the latest numbers. In practice, this means you can communicate locally and via federation with Synapse
|
||||||
servers such as matrix.org reasonably well. There's a long list of features that are not implemented, notably:
|
servers such as matrix.org reasonably well, although there are still some missing features (like Search).
|
||||||
|
|
||||||
- Search
|
|
||||||
- User Directory
|
|
||||||
- Presence
|
|
||||||
|
|
||||||
We are prioritising features that will benefit single-user homeservers first (e.g Receipts, E2E) rather
|
We are prioritising features that will benefit single-user homeservers first (e.g Receipts, E2E) rather
|
||||||
than features that massive deployments may be interested in (User Directory, OpenID, Guests, Admin APIs, AS API).
|
than features that massive deployments may be interested in (User Directory, OpenID, Guests, Admin APIs, AS API).
|
||||||
This means Dendrite supports amongst others:
|
This means Dendrite supports amongst others:
|
||||||
|
|
||||||
- Core room functionality (creating rooms, invites, auth rules)
|
- Core room functionality (creating rooms, invites, auth rules)
|
||||||
- Federation in rooms v1-v7
|
- Full support for room versions 1 to 7
|
||||||
|
- Experimental support for room versions 8 to 9
|
||||||
- Backfilling locally and via federation
|
- Backfilling locally and via federation
|
||||||
- Accounts, Profiles and Devices
|
- Accounts, Profiles and Devices
|
||||||
- Published room lists
|
- Published room lists
|
||||||
|
@ -107,6 +105,8 @@ This means Dendrite supports amongst others:
|
||||||
- Receipts
|
- Receipts
|
||||||
- Push
|
- Push
|
||||||
- Guests
|
- Guests
|
||||||
|
- User Directory
|
||||||
|
- Presence
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,10 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -85,7 +84,7 @@ func RetrieveUserProfile(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID string,
|
userID string,
|
||||||
asAPI AppServiceQueryAPI,
|
asAPI AppServiceQueryAPI,
|
||||||
accountDB userdb.Database,
|
profileAPI userapi.UserProfileAPI,
|
||||||
) (*authtypes.Profile, error) {
|
) (*authtypes.Profile, error) {
|
||||||
localpart, _, err := gomatrixserverlib.SplitID('@', userID)
|
localpart, _, err := gomatrixserverlib.SplitID('@', userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,10 +92,17 @@ func RetrieveUserProfile(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to query the user from the local database
|
// Try to query the user from the local database
|
||||||
profile, err := accountDB.GetProfileByLocalpart(ctx, localpart)
|
res := &userapi.QueryProfileResponse{}
|
||||||
if err != nil && err != sql.ErrNoRows {
|
err = profileAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: userID}, res)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if profile != nil {
|
}
|
||||||
|
profile := &authtypes.Profile{
|
||||||
|
Localpart: localpart,
|
||||||
|
DisplayName: res.DisplayName,
|
||||||
|
AvatarURL: res.AvatarURL,
|
||||||
|
}
|
||||||
|
if res.UserExists {
|
||||||
return profile, nil
|
return profile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,11 +119,15 @@ func RetrieveUserProfile(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to query the user from the local database again
|
// Try to query the user from the local database again
|
||||||
profile, err = accountDB.GetProfileByLocalpart(ctx, localpart)
|
err = profileAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: userID}, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// profile should not be nil at this point
|
// profile should not be nil at this point
|
||||||
return profile, nil
|
return &authtypes.Profile{
|
||||||
|
Localpart: localpart,
|
||||||
|
DisplayName: res.DisplayName,
|
||||||
|
AvatarURL: res.AvatarURL,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ func NewInternalAPI(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
js := jetstream.Prepare(&base.Cfg.Global.JetStream)
|
js, _ := jetstream.Prepare(base.ProcessContext, &base.Cfg.Global.JetStream)
|
||||||
|
|
||||||
// Create a connection to the appservice postgres DB
|
// Create a connection to the appservice postgres DB
|
||||||
appserviceDB, err := storage.NewDatabase(&base.Cfg.AppServiceAPI.Database)
|
appserviceDB, err := storage.NewDatabase(&base.Cfg.AppServiceAPI.Database)
|
||||||
|
|
|
@ -56,7 +56,7 @@ func NewOutputRoomEventConsumer(
|
||||||
ctx: process.Context(),
|
ctx: process.Context(),
|
||||||
jetstream: js,
|
jetstream: js,
|
||||||
durable: cfg.Global.JetStream.Durable("AppserviceRoomserverConsumer"),
|
durable: cfg.Global.JetStream.Durable("AppserviceRoomserverConsumer"),
|
||||||
topic: cfg.Global.JetStream.TopicFor(jetstream.OutputRoomEvent),
|
topic: cfg.Global.JetStream.Prefixed(jetstream.OutputRoomEvent),
|
||||||
asDB: appserviceDB,
|
asDB: appserviceDB,
|
||||||
rsAPI: rsAPI,
|
rsAPI: rsAPI,
|
||||||
serverName: string(cfg.Global.ServerName),
|
serverName: string(cfg.Global.ServerName),
|
||||||
|
@ -83,20 +83,29 @@ func (s *OutputRoomEventConsumer) onMessage(ctx context.Context, msg *nats.Msg)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if output.Type != api.OutputTypeNewRoomEvent {
|
if output.Type != api.OutputTypeNewRoomEvent || output.NewRoomEvent == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
events := []*gomatrixserverlib.HeaderedEvent{output.NewRoomEvent.Event}
|
newEventID := output.NewRoomEvent.Event.EventID()
|
||||||
|
events := make([]*gomatrixserverlib.HeaderedEvent, 0, len(output.NewRoomEvent.AddsStateEventIDs))
|
||||||
|
events = append(events, output.NewRoomEvent.Event)
|
||||||
if len(output.NewRoomEvent.AddsStateEventIDs) > 0 {
|
if len(output.NewRoomEvent.AddsStateEventIDs) > 0 {
|
||||||
eventsReq := &api.QueryEventsByIDRequest{
|
eventsReq := &api.QueryEventsByIDRequest{
|
||||||
EventIDs: output.NewRoomEvent.AddsStateEventIDs,
|
EventIDs: make([]string, 0, len(output.NewRoomEvent.AddsStateEventIDs)),
|
||||||
}
|
}
|
||||||
eventsRes := &api.QueryEventsByIDResponse{}
|
eventsRes := &api.QueryEventsByIDResponse{}
|
||||||
if err := s.rsAPI.QueryEventsByID(s.ctx, eventsReq, eventsRes); err != nil {
|
for _, eventID := range output.NewRoomEvent.AddsStateEventIDs {
|
||||||
return false
|
if eventID != newEventID {
|
||||||
|
eventsReq.EventIDs = append(eventsReq.EventIDs, eventID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(eventsReq.EventIDs) > 0 {
|
||||||
|
if err := s.rsAPI.QueryEventsByID(s.ctx, eventsReq, eventsRes); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
events = append(events, eventsRes.Events...)
|
||||||
}
|
}
|
||||||
events = append(events, eventsRes.Events...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send event to any relevant application services
|
// Send event to any relevant application services
|
||||||
|
|
|
@ -28,7 +28,6 @@ import (
|
||||||
|
|
||||||
// Database stores events intended to be later sent to application services
|
// Database stores events intended to be later sent to application services
|
||||||
type Database struct {
|
type Database struct {
|
||||||
sqlutil.PartitionOffsetStatements
|
|
||||||
events eventsStatements
|
events eventsStatements
|
||||||
txnID txnStatements
|
txnID txnStatements
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
@ -46,9 +45,6 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*Database, error) {
|
||||||
if err = result.prepare(); err != nil {
|
if err = result.prepare(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = result.PartitionOffsetStatements.Prepare(result.db, result.writer, "appservice"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import (
|
||||||
|
|
||||||
// Database stores events intended to be later sent to application services
|
// Database stores events intended to be later sent to application services
|
||||||
type Database struct {
|
type Database struct {
|
||||||
sqlutil.PartitionOffsetStatements
|
|
||||||
events eventsStatements
|
events eventsStatements
|
||||||
txnID txnStatements
|
txnID txnStatements
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
@ -45,9 +44,6 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*Database, error) {
|
||||||
if err = result.prepare(); err != nil {
|
if err = result.prepare(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = result.PartitionOffsetStatements.Prepare(result.db, result.writer, "appservice"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,10 +106,13 @@ rst Users cannot set notifications powerlevel higher than their own (2 subtests)
|
||||||
rst Both GET and PUT work
|
rst Both GET and PUT work
|
||||||
rct POST /rooms/:room_id/receipt can create receipts
|
rct POST /rooms/:room_id/receipt can create receipts
|
||||||
red POST /rooms/:room_id/read_markers can create read marker
|
red POST /rooms/:room_id/read_markers can create read marker
|
||||||
|
med POST /media/v3/upload can create an upload
|
||||||
med POST /media/r0/upload can create an upload
|
med POST /media/r0/upload can create an upload
|
||||||
|
med GET /media/v3/download can fetch the value again
|
||||||
med GET /media/r0/download can fetch the value again
|
med GET /media/r0/download can fetch the value again
|
||||||
cap GET /capabilities is present and well formed for registered user
|
cap GET /capabilities is present and well formed for registered user
|
||||||
cap GET /r0/capabilities is not public
|
cap GET /r0/capabilities is not public
|
||||||
|
cap GET /v3/capabilities is not public
|
||||||
reg Register with a recaptcha
|
reg Register with a recaptcha
|
||||||
reg registration is idempotent, without username specified
|
reg registration is idempotent, without username specified
|
||||||
reg registration is idempotent, with username specified
|
reg registration is idempotent, with username specified
|
||||||
|
@ -174,7 +177,9 @@ eph Ephemeral messages received from clients are correctly expired
|
||||||
ali Room aliases can contain Unicode
|
ali Room aliases can contain Unicode
|
||||||
f,ali Remote room alias queries can handle Unicode
|
f,ali Remote room alias queries can handle Unicode
|
||||||
ali Canonical alias can be set
|
ali Canonical alias can be set
|
||||||
|
ali Canonical alias can be set (3 subtests)
|
||||||
ali Canonical alias can include alt_aliases
|
ali Canonical alias can include alt_aliases
|
||||||
|
ali Canonical alias can include alt_aliases (4 subtests)
|
||||||
ali Regular users can add and delete aliases in the default room configuration
|
ali Regular users can add and delete aliases in the default room configuration
|
||||||
ali Regular users can add and delete aliases when m.room.aliases is restricted
|
ali Regular users can add and delete aliases when m.room.aliases is restricted
|
||||||
ali Deleting a non-existent alias should return a 404
|
ali Deleting a non-existent alias should return a 404
|
||||||
|
@ -478,6 +483,30 @@ rmv Inbound federation rejects invites which include invalid JSON for room versi
|
||||||
rmv Outbound federation rejects invite response which include invalid JSON for room version 6
|
rmv Outbound federation rejects invite response which include invalid JSON for room version 6
|
||||||
rmv Inbound federation rejects invite rejections which include invalid JSON for room version 6
|
rmv Inbound federation rejects invite rejections which include invalid JSON for room version 6
|
||||||
rmv Server rejects invalid JSON in a version 6 room
|
rmv Server rejects invalid JSON in a version 6 room
|
||||||
|
rmv User can create and send/receive messages in a room with version 7 (2 subtests)
|
||||||
|
rmv local user can join room with version 7
|
||||||
|
rmv User can invite local user to room with version 7
|
||||||
|
rmv remote user can join room with version 7
|
||||||
|
rmv User can invite remote user to room with version 7
|
||||||
|
rmv Remote user can backfill in a room with version 7
|
||||||
|
rmv Can reject invites over federation for rooms with version 7
|
||||||
|
rmv Can receive redactions from regular users over federation in room version 7
|
||||||
|
rmv User can create and send/receive messages in a room with version 8 (2 subtests)
|
||||||
|
rmv local user can join room with version 8
|
||||||
|
rmv User can invite local user to room with version 8
|
||||||
|
rmv remote user can join room with version 8
|
||||||
|
rmv User can invite remote user to room with version 8
|
||||||
|
rmv Remote user can backfill in a room with version 8
|
||||||
|
rmv Can reject invites over federation for rooms with version 8
|
||||||
|
rmv Can receive redactions from regular users over federation in room version 8
|
||||||
|
rmv User can create and send/receive messages in a room with version 9 (2 subtests)
|
||||||
|
rmv local user can join room with version 9
|
||||||
|
rmv User can invite local user to room with version 9
|
||||||
|
rmv remote user can join room with version 9
|
||||||
|
rmv User can invite remote user to room with version 9
|
||||||
|
rmv Remote user can backfill in a room with version 9
|
||||||
|
rmv Can reject invites over federation for rooms with version 9
|
||||||
|
rmv Can receive redactions from regular users over federation in room version 9
|
||||||
pre Presence changes are reported to local room members
|
pre Presence changes are reported to local room members
|
||||||
f,pre Presence changes are also reported to remote room members
|
f,pre Presence changes are also reported to remote room members
|
||||||
pre Presence changes to UNAVAILABLE are reported to local room members
|
pre Presence changes to UNAVAILABLE are reported to local room members
|
||||||
|
@ -772,12 +801,15 @@ app AS can make room aliases
|
||||||
app Regular users cannot create room aliases within the AS namespace
|
app Regular users cannot create room aliases within the AS namespace
|
||||||
app AS-ghosted users can use rooms via AS
|
app AS-ghosted users can use rooms via AS
|
||||||
app AS-ghosted users can use rooms themselves
|
app AS-ghosted users can use rooms themselves
|
||||||
|
app AS-ghosted users can use rooms via AS (2 subtests)
|
||||||
|
app AS-ghosted users can use rooms themselves (3 subtests)
|
||||||
app Ghost user must register before joining room
|
app Ghost user must register before joining room
|
||||||
app AS can set avatar for ghosted users
|
app AS can set avatar for ghosted users
|
||||||
app AS can set displayname for ghosted users
|
app AS can set displayname for ghosted users
|
||||||
app AS can't set displayname for random users
|
app AS can't set displayname for random users
|
||||||
app Inviting an AS-hosted user asks the AS server
|
app Inviting an AS-hosted user asks the AS server
|
||||||
app Accesing an AS-hosted room alias asks the AS server
|
app Accesing an AS-hosted room alias asks the AS server
|
||||||
|
app Accesing an AS-hosted room alias asks the AS server (2 subtests)
|
||||||
app Events in rooms with AS-hosted room aliases are sent to AS server
|
app Events in rooms with AS-hosted room aliases are sent to AS server
|
||||||
app AS user (not ghost) can join room without registering
|
app AS user (not ghost) can join room without registering
|
||||||
app AS user (not ghost) can join room without registering, with user_id query param
|
app AS user (not ghost) can join room without registering, with user_id query param
|
||||||
|
@ -789,6 +821,8 @@ app AS can publish rooms in their own list
|
||||||
app AS and main public room lists are separate
|
app AS and main public room lists are separate
|
||||||
app AS can deactivate a user
|
app AS can deactivate a user
|
||||||
psh Test that a message is pushed
|
psh Test that a message is pushed
|
||||||
|
psh Test that a message is pushed (6 subtests)
|
||||||
|
psh Test that rejected pushers are removed. (4 subtests)
|
||||||
psh Invites are pushed
|
psh Invites are pushed
|
||||||
psh Rooms with names are correctly named in pushed
|
psh Rooms with names are correctly named in pushed
|
||||||
psh Rooms with canonical alias are correctly named in pushed
|
psh Rooms with canonical alias are correctly named in pushed
|
||||||
|
@ -857,9 +891,12 @@ pre Left room members do not cause problems for presence
|
||||||
crm Rooms can be created with an initial invite list (SYN-205) (1 subtests)
|
crm Rooms can be created with an initial invite list (SYN-205) (1 subtests)
|
||||||
typ Typing notifications don't leak
|
typ Typing notifications don't leak
|
||||||
ban Non-present room members cannot ban others
|
ban Non-present room members cannot ban others
|
||||||
|
ban Non-present room members cannot ban others (3 subtests)
|
||||||
psh Getting push rules doesn't corrupt the cache SYN-390
|
psh Getting push rules doesn't corrupt the cache SYN-390
|
||||||
|
psh Getting push rules doesn't corrupt the cache SYN-390 (3 subtests)
|
||||||
inv Test that we can be reinvited to a room we created
|
inv Test that we can be reinvited to a room we created
|
||||||
syn Multiple calls to /sync should not cause 500 errors
|
syn Multiple calls to /sync should not cause 500 errors
|
||||||
|
syn Multiple calls to /sync should not cause 500 errors (6 subtests)
|
||||||
gst Guest user can call /events on another world_readable room (SYN-606)
|
gst Guest user can call /events on another world_readable room (SYN-606)
|
||||||
gst Real user can call /events on another world_readable room (SYN-606)
|
gst Real user can call /events on another world_readable room (SYN-606)
|
||||||
gst Events come down the correct room
|
gst Events come down the correct room
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
import sys
|
import os
|
||||||
|
|
||||||
# Usage: $ ./are-we-synapse-yet.py [-v] results.tap
|
# Usage: $ ./are-we-synapse-yet.py [-v] results.tap
|
||||||
# This script scans a results.tap file from Dendrite's CI process and spits out
|
# This script scans a results.tap file from Dendrite's CI process and spits out
|
||||||
|
@ -156,6 +156,7 @@ def parse_test_line(line):
|
||||||
# ✓ POST /register downcases capitals in usernames
|
# ✓ POST /register downcases capitals in usernames
|
||||||
# ...
|
# ...
|
||||||
def print_stats(header_name, gid_to_tests, gid_to_name, verbose):
|
def print_stats(header_name, gid_to_tests, gid_to_name, verbose):
|
||||||
|
ci = os.getenv("CI") # When running from GHA, this groups the subsections
|
||||||
subsections = [] # Registration: 100% (13/13 tests)
|
subsections = [] # Registration: 100% (13/13 tests)
|
||||||
subsection_test_names = {} # 'subsection name': ["✓ Test 1", "✓ Test 2", "× Test 3"]
|
subsection_test_names = {} # 'subsection name': ["✓ Test 1", "✓ Test 2", "× Test 3"]
|
||||||
total_passing = 0
|
total_passing = 0
|
||||||
|
@ -169,7 +170,7 @@ def print_stats(header_name, gid_to_tests, gid_to_name, verbose):
|
||||||
for name, passing in tests.items():
|
for name, passing in tests.items():
|
||||||
if passing:
|
if passing:
|
||||||
group_passing += 1
|
group_passing += 1
|
||||||
test_names_and_marks.append(f"{'✓' if passing else '×'} {name}")
|
test_names_and_marks.append(f"{'✅' if passing else '❌'} {name}")
|
||||||
|
|
||||||
total_tests += group_total
|
total_tests += group_total
|
||||||
total_passing += group_passing
|
total_passing += group_passing
|
||||||
|
@ -186,11 +187,11 @@ def print_stats(header_name, gid_to_tests, gid_to_name, verbose):
|
||||||
print("%s: %s (%d/%d tests)" % (header_name, pct, total_passing, total_tests))
|
print("%s: %s (%d/%d tests)" % (header_name, pct, total_passing, total_tests))
|
||||||
print("-" * (len(header_name)+1))
|
print("-" * (len(header_name)+1))
|
||||||
for line in subsections:
|
for line in subsections:
|
||||||
print(" %s" % (line,))
|
print("%s%s" % ("::group::" if ci and verbose else "", line,))
|
||||||
if verbose:
|
if verbose:
|
||||||
for test_name_and_pass_mark in subsection_test_names[line]:
|
for test_name_and_pass_mark in subsection_test_names[line]:
|
||||||
print(" %s" % (test_name_and_pass_mark,))
|
print(" %s" % (test_name_and_pass_mark,))
|
||||||
print("")
|
print("%s" % ("::endgroup::" if ci else ""))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
def main(results_tap_path, verbose):
|
def main(results_tap_path, verbose):
|
||||||
|
|
51
build.cmd
Normal file
51
build.cmd
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
:ENTRY_POINT
|
||||||
|
setlocal EnableDelayedExpansion
|
||||||
|
|
||||||
|
REM script base dir
|
||||||
|
set SCRIPTDIR=%~dp0
|
||||||
|
set PROJDIR=%SCRIPTDIR:~0,-1%
|
||||||
|
|
||||||
|
REM Put installed packages into ./bin
|
||||||
|
set GOBIN=%PROJDIR%\bin
|
||||||
|
|
||||||
|
set FLAGS=
|
||||||
|
|
||||||
|
REM Check if sources are under Git control
|
||||||
|
if not exist ".git" goto :CHECK_BIN
|
||||||
|
|
||||||
|
REM set BUILD=`git rev-parse --short HEAD \\ ""`
|
||||||
|
FOR /F "tokens=*" %%X IN ('git rev-parse --short HEAD') DO (
|
||||||
|
set BUILD=%%X
|
||||||
|
)
|
||||||
|
|
||||||
|
REM set BRANCH=`(git symbolic-ref --short HEAD \ tr -d \/ ) \\ ""`
|
||||||
|
FOR /F "tokens=*" %%X IN ('git symbolic-ref --short HEAD') DO (
|
||||||
|
set BRANCHRAW=%%X
|
||||||
|
set BRANCH=!BRANCHRAW:/=!
|
||||||
|
)
|
||||||
|
if "%BRANCH%" == "main" set BRANCH=
|
||||||
|
|
||||||
|
set FLAGS=-X github.com/matrix-org/dendrite/internal.branch=%BRANCH% -X github.com/matrix-org/dendrite/internal.build=%BUILD%
|
||||||
|
|
||||||
|
:CHECK_BIN
|
||||||
|
if exist "bin" goto :ALL_SET
|
||||||
|
mkdir "bin"
|
||||||
|
|
||||||
|
:ALL_SET
|
||||||
|
set CGO_ENABLED=1
|
||||||
|
for /D %%P in (cmd\*) do (
|
||||||
|
go build -trimpath -ldflags "%FLAGS%" -v -o ".\bin" ".\%%P"
|
||||||
|
)
|
||||||
|
|
||||||
|
set CGO_ENABLED=0
|
||||||
|
set GOOS=js
|
||||||
|
set GOARCH=wasm
|
||||||
|
go build -trimpath -ldflags "%FLAGS%" -o bin\main.wasm .\cmd\dendritejs-pinecone
|
||||||
|
|
||||||
|
goto :DONE
|
||||||
|
|
||||||
|
:DONE
|
||||||
|
echo Done
|
||||||
|
endlocal
|
|
@ -13,6 +13,10 @@ RUN go build -trimpath -o bin/ ./cmd/create-account
|
||||||
RUN go build -trimpath -o bin/ ./cmd/generate-keys
|
RUN go build -trimpath -o bin/ ./cmd/generate-keys
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
LABEL org.opencontainers.image.title="Dendrite (Monolith)"
|
||||||
|
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"
|
||||||
|
|
||||||
COPY --from=base /build/bin/* /usr/bin/
|
COPY --from=base /build/bin/* /usr/bin/
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,10 @@ RUN go build -trimpath -o bin/ ./cmd/create-account
|
||||||
RUN go build -trimpath -o bin/ ./cmd/generate-keys
|
RUN go build -trimpath -o bin/ ./cmd/generate-keys
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
LABEL org.opencontainers.image.title="Dendrite (Polylith)"
|
||||||
|
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"
|
||||||
|
|
||||||
COPY --from=base /build/bin/* /usr/bin/
|
COPY --from=base /build/bin/* /usr/bin/
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,17 @@ global:
|
||||||
- matrix.org
|
- matrix.org
|
||||||
- vector.im
|
- vector.im
|
||||||
|
|
||||||
|
# Disables federation. Dendrite will not be able to make any outbound HTTP requests
|
||||||
|
# to other servers and the federation API will not be exposed.
|
||||||
|
disable_federation: false
|
||||||
|
|
||||||
|
# Configures the handling of presence events.
|
||||||
|
presence:
|
||||||
|
# Whether inbound presence events are allowed, e.g. receiving presence events from other servers
|
||||||
|
enable_inbound: false
|
||||||
|
# Whether outbound presence events are allowed, e.g. sending presence events to other servers
|
||||||
|
enable_outbound: false
|
||||||
|
|
||||||
# Configuration for NATS JetStream
|
# Configuration for NATS JetStream
|
||||||
jetstream:
|
jetstream:
|
||||||
# A list of NATS Server addresses to connect to. If none are specified, an
|
# A list of NATS Server addresses to connect to. If none are specified, an
|
||||||
|
@ -160,12 +171,6 @@ client_api:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
cooloff_ms: 500
|
cooloff_ms: 500
|
||||||
|
|
||||||
# Configuration for the EDU server.
|
|
||||||
edu_server:
|
|
||||||
internal_api:
|
|
||||||
listen: http://0.0.0.0:7778
|
|
||||||
connect: http://edu_server:7778
|
|
||||||
|
|
||||||
# Configuration for the Federation API.
|
# Configuration for the Federation API.
|
||||||
federation_api:
|
federation_api:
|
||||||
internal_api:
|
internal_api:
|
||||||
|
@ -179,12 +184,6 @@ federation_api:
|
||||||
max_idle_conns: 2
|
max_idle_conns: 2
|
||||||
conn_max_lifetime: -1
|
conn_max_lifetime: -1
|
||||||
|
|
||||||
# List of paths to X.509 certificates to be used by the external federation listeners.
|
|
||||||
# These certificates will be used to calculate the TLS fingerprints and other servers
|
|
||||||
# will expect the certificate to match these fingerprints. Certificates must be in PEM
|
|
||||||
# format.
|
|
||||||
federation_certificates: []
|
|
||||||
|
|
||||||
# How many times we will try to resend a failed transaction to a specific server. The
|
# How many times we will try to resend a failed transaction to a specific server. The
|
||||||
# backoff is 2**x seconds, so 1 = 2 seconds, 2 = 4 seconds, 3 = 8 seconds etc.
|
# backoff is 2**x seconds, so 1 = 2 seconds, 2 = 4 seconds, 3 = 8 seconds etc.
|
||||||
send_max_retries: 16
|
send_max_retries: 16
|
||||||
|
|
|
@ -84,18 +84,6 @@ services:
|
||||||
- internal
|
- internal
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
edu_server:
|
|
||||||
hostname: edu_server
|
|
||||||
image: matrixdotorg/dendrite-polylith:latest
|
|
||||||
command: eduserver
|
|
||||||
volumes:
|
|
||||||
- ./config:/etc/dendrite
|
|
||||||
depends_on:
|
|
||||||
- jetstream
|
|
||||||
networks:
|
|
||||||
- internal
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
federation_api:
|
federation_api:
|
||||||
hostname: federation_api
|
hostname: federation_api
|
||||||
image: matrixdotorg/dendrite-polylith:latest
|
image: matrixdotorg/dendrite-polylith:latest
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package gobind
|
package gobind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -9,7 +23,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -22,11 +35,9 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
|
||||||
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/users"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi"
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
"github.com/matrix-org/dendrite/federationapi/api"
|
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/keyserver"
|
"github.com/matrix-org/dendrite/keyserver"
|
||||||
"github.com/matrix-org/dendrite/roomserver"
|
"github.com/matrix-org/dendrite/roomserver"
|
||||||
|
@ -41,6 +52,7 @@ import (
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/h2c"
|
"golang.org/x/net/http2/h2c"
|
||||||
|
|
||||||
|
pineconeConnections "github.com/matrix-org/pinecone/connections"
|
||||||
pineconeMulticast "github.com/matrix-org/pinecone/multicast"
|
pineconeMulticast "github.com/matrix-org/pinecone/multicast"
|
||||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||||
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||||
|
@ -60,11 +72,9 @@ type DendriteMonolith struct {
|
||||||
PineconeRouter *pineconeRouter.Router
|
PineconeRouter *pineconeRouter.Router
|
||||||
PineconeMulticast *pineconeMulticast.Multicast
|
PineconeMulticast *pineconeMulticast.Multicast
|
||||||
PineconeQUIC *pineconeSessions.Sessions
|
PineconeQUIC *pineconeSessions.Sessions
|
||||||
|
PineconeManager *pineconeConnections.ConnectionManager
|
||||||
StorageDirectory string
|
StorageDirectory string
|
||||||
CacheDirectory string
|
CacheDirectory string
|
||||||
staticPeerURI string
|
|
||||||
staticPeerMutex sync.RWMutex
|
|
||||||
staticPeerAttempt chan struct{}
|
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
processContext *process.ProcessContext
|
processContext *process.ProcessContext
|
||||||
|
@ -80,7 +90,7 @@ func (m *DendriteMonolith) PeerCount(peertype int) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DendriteMonolith) SessionCount() int {
|
func (m *DendriteMonolith) SessionCount() int {
|
||||||
return len(m.PineconeQUIC.Sessions())
|
return len(m.PineconeQUIC.Protocol("matrix").Sessions())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DendriteMonolith) SetMulticastEnabled(enabled bool) {
|
func (m *DendriteMonolith) SetMulticastEnabled(enabled bool) {
|
||||||
|
@ -93,15 +103,8 @@ func (m *DendriteMonolith) SetMulticastEnabled(enabled bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DendriteMonolith) SetStaticPeer(uri string) {
|
func (m *DendriteMonolith) SetStaticPeer(uri string) {
|
||||||
m.staticPeerMutex.Lock()
|
m.PineconeManager.RemovePeers()
|
||||||
m.staticPeerURI = strings.TrimSpace(uri)
|
m.PineconeManager.AddPeer(strings.TrimSpace(uri))
|
||||||
m.staticPeerMutex.Unlock()
|
|
||||||
m.DisconnectType(int(pineconeRouter.PeerTypeRemote))
|
|
||||||
if uri != "" {
|
|
||||||
go func() {
|
|
||||||
m.staticPeerAttempt <- struct{}{}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DendriteMonolith) DisconnectType(peertype int) {
|
func (m *DendriteMonolith) DisconnectType(peertype int) {
|
||||||
|
@ -199,43 +202,6 @@ func (m *DendriteMonolith) RegisterDevice(localpart, deviceID string) (string, e
|
||||||
return loginRes.Device.AccessToken, nil
|
return loginRes.Device.AccessToken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DendriteMonolith) staticPeerConnect() {
|
|
||||||
connected := map[string]bool{} // URI -> connected?
|
|
||||||
attempt := func() {
|
|
||||||
m.staticPeerMutex.RLock()
|
|
||||||
uri := m.staticPeerURI
|
|
||||||
m.staticPeerMutex.RUnlock()
|
|
||||||
if uri == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for k := range connected {
|
|
||||||
delete(connected, k)
|
|
||||||
}
|
|
||||||
for _, uri := range strings.Split(uri, ",") {
|
|
||||||
connected[strings.TrimSpace(uri)] = false
|
|
||||||
}
|
|
||||||
for _, info := range m.PineconeRouter.Peers() {
|
|
||||||
connected[info.URI] = true
|
|
||||||
}
|
|
||||||
for k, online := range connected {
|
|
||||||
if !online {
|
|
||||||
if err := conn.ConnectToPeer(m.PineconeRouter, k); err != nil {
|
|
||||||
logrus.WithError(err).Error("Failed to connect to static peer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-m.processContext.Context().Done():
|
|
||||||
case <-m.staticPeerAttempt:
|
|
||||||
attempt()
|
|
||||||
case <-time.After(time.Second * 5):
|
|
||||||
attempt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:gocyclo
|
// nolint:gocyclo
|
||||||
func (m *DendriteMonolith) Start() {
|
func (m *DendriteMonolith) Start() {
|
||||||
var err error
|
var err error
|
||||||
|
@ -259,7 +225,7 @@ func (m *DendriteMonolith) Start() {
|
||||||
pk = sk.Public().(ed25519.PublicKey)
|
pk = sk.Public().(ed25519.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.listener, err = net.Listen("tcp", "localhost:65432")
|
m.listener, err = net.Listen("tcp", ":65432")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -270,10 +236,10 @@ func (m *DendriteMonolith) Start() {
|
||||||
m.logger.SetOutput(BindLogger{})
|
m.logger.SetOutput(BindLogger{})
|
||||||
logrus.SetOutput(BindLogger{})
|
logrus.SetOutput(BindLogger{})
|
||||||
|
|
||||||
logger := log.New(os.Stdout, "PINECONE: ", 0)
|
m.PineconeRouter = pineconeRouter.NewRouter(logrus.WithField("pinecone", "router"), sk, false)
|
||||||
m.PineconeRouter = pineconeRouter.NewRouter(logger, sk, false)
|
m.PineconeQUIC = pineconeSessions.NewSessions(logrus.WithField("pinecone", "sessions"), m.PineconeRouter, []string{"matrix"})
|
||||||
m.PineconeQUIC = pineconeSessions.NewSessions(logger, m.PineconeRouter)
|
m.PineconeMulticast = pineconeMulticast.NewMulticast(logrus.WithField("pinecone", "multicast"), m.PineconeRouter)
|
||||||
m.PineconeMulticast = pineconeMulticast.NewMulticast(logger, m.PineconeRouter)
|
m.PineconeManager = pineconeConnections.NewConnectionManager(m.PineconeRouter)
|
||||||
|
|
||||||
prefix := hex.EncodeToString(pk)
|
prefix := hex.EncodeToString(pk)
|
||||||
cfg := &config.Dendrite{}
|
cfg := &config.Dendrite{}
|
||||||
|
@ -281,6 +247,7 @@ func (m *DendriteMonolith) Start() {
|
||||||
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
||||||
cfg.Global.PrivateKey = sk
|
cfg.Global.PrivateKey = sk
|
||||||
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
|
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
|
||||||
|
cfg.Global.JetStream.InMemory = true
|
||||||
cfg.Global.JetStream.StoragePath = config.Path(fmt.Sprintf("%s/%s", m.StorageDirectory, prefix))
|
cfg.Global.JetStream.StoragePath = config.Path(fmt.Sprintf("%s/%s", m.StorageDirectory, prefix))
|
||||||
cfg.UserAPI.AccountDatabase.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-account.db", m.StorageDirectory, prefix))
|
cfg.UserAPI.AccountDatabase.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-account.db", m.StorageDirectory, prefix))
|
||||||
cfg.MediaAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-mediaapi.db", m.StorageDirectory))
|
cfg.MediaAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-mediaapi.db", m.StorageDirectory))
|
||||||
|
@ -315,16 +282,15 @@ func (m *DendriteMonolith) Start() {
|
||||||
m.userAPI = userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
m.userAPI = userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
||||||
keyAPI.SetUserAPI(m.userAPI)
|
keyAPI.SetUserAPI(m.userAPI)
|
||||||
|
|
||||||
eduInputAPI := eduserver.NewInternalAPI(
|
|
||||||
base, cache.New(), m.userAPI,
|
|
||||||
)
|
|
||||||
|
|
||||||
asAPI := appservice.NewInternalAPI(base, m.userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(base, m.userAPI, rsAPI)
|
||||||
|
|
||||||
// The underlying roomserver implementation needs to be able to call the fedsender.
|
// The underlying roomserver implementation needs to be able to call the fedsender.
|
||||||
// This is different to rsAPI which can be the http client which doesn't need this dependency
|
// This is different to rsAPI which can be the http client which doesn't need this dependency
|
||||||
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||||
|
|
||||||
|
userProvider := users.NewPineconeUserProvider(m.PineconeRouter, m.PineconeQUIC, m.userAPI, federation)
|
||||||
|
roomProvider := rooms.NewPineconeRoomProvider(m.PineconeRouter, m.PineconeQUIC, fsAPI, federation)
|
||||||
|
|
||||||
monolith := setup.Monolith{
|
monolith := setup.Monolith{
|
||||||
Config: base.Cfg,
|
Config: base.Cfg,
|
||||||
AccountDB: accountDB,
|
AccountDB: accountDB,
|
||||||
|
@ -332,13 +298,13 @@ func (m *DendriteMonolith) Start() {
|
||||||
FedClient: federation,
|
FedClient: federation,
|
||||||
KeyRing: keyRing,
|
KeyRing: keyRing,
|
||||||
|
|
||||||
AppserviceAPI: asAPI,
|
AppserviceAPI: asAPI,
|
||||||
EDUInternalAPI: eduInputAPI,
|
FederationAPI: fsAPI,
|
||||||
FederationAPI: fsAPI,
|
RoomserverAPI: rsAPI,
|
||||||
RoomserverAPI: rsAPI,
|
UserAPI: m.userAPI,
|
||||||
UserAPI: m.userAPI,
|
KeyAPI: keyAPI,
|
||||||
KeyAPI: keyAPI,
|
ExtPublicRoomsProvider: roomProvider,
|
||||||
ExtPublicRoomsProvider: rooms.NewPineconeRoomProvider(m.PineconeRouter, m.PineconeQUIC, fsAPI, federation),
|
ExtUserDirectoryProvider: userProvider,
|
||||||
}
|
}
|
||||||
monolith.AddAllPublicRoutes(
|
monolith.AddAllPublicRoutes(
|
||||||
base.ProcessContext,
|
base.ProcessContext,
|
||||||
|
@ -354,12 +320,15 @@ func (m *DendriteMonolith) Start() {
|
||||||
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux)
|
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux)
|
||||||
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
|
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
|
||||||
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
||||||
|
httpRouter.HandleFunc("/pinecone", m.PineconeRouter.ManholeHandler)
|
||||||
|
|
||||||
pMux := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
pMux := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||||
|
pMux.PathPrefix(users.PublicURL).HandlerFunc(userProvider.FederatedUserProfiles)
|
||||||
pMux.PathPrefix(httputil.PublicFederationPathPrefix).Handler(base.PublicFederationAPIMux)
|
pMux.PathPrefix(httputil.PublicFederationPathPrefix).Handler(base.PublicFederationAPIMux)
|
||||||
pMux.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
pMux.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
||||||
|
|
||||||
pHTTP := m.PineconeQUIC.HTTP()
|
pHTTP := m.PineconeQUIC.Protocol("matrix").HTTP()
|
||||||
|
pHTTP.Mux().Handle(users.PublicURL, pMux)
|
||||||
pHTTP.Mux().Handle(httputil.PublicFederationPathPrefix, pMux)
|
pHTTP.Mux().Handle(httputil.PublicFederationPathPrefix, pMux)
|
||||||
pHTTP.Mux().Handle(httputil.PublicMediaPathPrefix, pMux)
|
pHTTP.Mux().Handle(httputil.PublicMediaPathPrefix, pMux)
|
||||||
|
|
||||||
|
@ -379,35 +348,27 @@ func (m *DendriteMonolith) Start() {
|
||||||
|
|
||||||
m.processContext = base.ProcessContext
|
m.processContext = base.ProcessContext
|
||||||
|
|
||||||
m.staticPeerAttempt = make(chan struct{}, 1)
|
|
||||||
go m.staticPeerConnect()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
m.logger.Info("Listening on ", cfg.Global.ServerName)
|
m.logger.Info("Listening on ", cfg.Global.ServerName)
|
||||||
m.logger.Fatal(m.httpServer.Serve(m.PineconeQUIC))
|
m.logger.Fatal(m.httpServer.Serve(m.PineconeQUIC.Protocol("matrix")))
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
logrus.Info("Listening on ", m.listener.Addr())
|
logrus.Info("Listening on ", m.listener.Addr())
|
||||||
logrus.Fatal(http.Serve(m.listener, httpRouter))
|
logrus.Fatal(http.Serve(m.listener, httpRouter))
|
||||||
}()
|
}()
|
||||||
go func() {
|
|
||||||
logrus.Info("Sending wake-up message to known nodes")
|
|
||||||
req := &api.PerformBroadcastEDURequest{}
|
|
||||||
res := &api.PerformBroadcastEDUResponse{}
|
|
||||||
if err := fsAPI.PerformBroadcastEDU(context.TODO(), req, res); err != nil {
|
|
||||||
logrus.WithError(err).Error("Failed to send wake-up message to known nodes")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DendriteMonolith) Stop() {
|
func (m *DendriteMonolith) Stop() {
|
||||||
|
m.processContext.ShutdownDendrite()
|
||||||
_ = m.listener.Close()
|
_ = m.listener.Close()
|
||||||
m.PineconeMulticast.Stop()
|
m.PineconeMulticast.Stop()
|
||||||
_ = m.PineconeQUIC.Close()
|
_ = m.PineconeQUIC.Close()
|
||||||
m.processContext.ShutdownDendrite()
|
|
||||||
_ = m.PineconeRouter.Close()
|
_ = m.PineconeRouter.Close()
|
||||||
|
m.processContext.WaitForComponentsToFinish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MaxFrameSize = types.MaxFrameSize
|
||||||
|
|
||||||
type Conduit struct {
|
type Conduit struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
port types.SwitchPortID
|
port types.SwitchPortID
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
//go:build ios
|
//go:build ios
|
||||||
// +build ios
|
// +build ios
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
//go:build !ios
|
//go:build !ios
|
||||||
// +build !ios
|
// +build !ios
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggrooms"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggrooms"
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi"
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
"github.com/matrix-org/dendrite/federationapi/api"
|
"github.com/matrix-org/dendrite/federationapi/api"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
|
@ -23,6 +21,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/setup"
|
"github.com/matrix-org/dendrite/setup"
|
||||||
"github.com/matrix-org/dendrite/setup/base"
|
"github.com/matrix-org/dendrite/setup/base"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
"github.com/matrix-org/dendrite/userapi"
|
"github.com/matrix-org/dendrite/userapi"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -36,6 +35,7 @@ type DendriteMonolith struct {
|
||||||
StorageDirectory string
|
StorageDirectory string
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
|
processContext *process.ProcessContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DendriteMonolith) BaseURL() string {
|
func (m *DendriteMonolith) BaseURL() string {
|
||||||
|
@ -87,6 +87,7 @@ func (m *DendriteMonolith) Start() {
|
||||||
cfg.Global.PrivateKey = ygg.PrivateKey()
|
cfg.Global.PrivateKey = ygg.PrivateKey()
|
||||||
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
|
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
|
||||||
cfg.Global.JetStream.StoragePath = config.Path(fmt.Sprintf("%s/", m.StorageDirectory))
|
cfg.Global.JetStream.StoragePath = config.Path(fmt.Sprintf("%s/", m.StorageDirectory))
|
||||||
|
cfg.Global.JetStream.InMemory = true
|
||||||
cfg.UserAPI.AccountDatabase.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-account.db", m.StorageDirectory))
|
cfg.UserAPI.AccountDatabase.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-account.db", m.StorageDirectory))
|
||||||
cfg.MediaAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-mediaapi.db", m.StorageDirectory))
|
cfg.MediaAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-mediaapi.db", m.StorageDirectory))
|
||||||
cfg.SyncAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-syncapi.db", m.StorageDirectory))
|
cfg.SyncAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-syncapi.db", m.StorageDirectory))
|
||||||
|
@ -101,6 +102,7 @@ func (m *DendriteMonolith) Start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
base := base.NewBaseDendrite(cfg, "Monolith")
|
base := base.NewBaseDendrite(cfg, "Monolith")
|
||||||
|
m.processContext = base.ProcessContext
|
||||||
defer base.Close() // nolint: errcheck
|
defer base.Close() // nolint: errcheck
|
||||||
|
|
||||||
accountDB := base.CreateAccountsDB()
|
accountDB := base.CreateAccountsDB()
|
||||||
|
@ -119,10 +121,6 @@ func (m *DendriteMonolith) Start() {
|
||||||
userAPI := userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
userAPI := userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
eduInputAPI := eduserver.NewInternalAPI(
|
|
||||||
base, cache.New(), userAPI,
|
|
||||||
)
|
|
||||||
|
|
||||||
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
|
||||||
rsAPI.SetAppserviceAPI(asAPI)
|
rsAPI.SetAppserviceAPI(asAPI)
|
||||||
|
|
||||||
|
@ -137,12 +135,11 @@ func (m *DendriteMonolith) Start() {
|
||||||
FedClient: federation,
|
FedClient: federation,
|
||||||
KeyRing: keyRing,
|
KeyRing: keyRing,
|
||||||
|
|
||||||
AppserviceAPI: asAPI,
|
AppserviceAPI: asAPI,
|
||||||
EDUInternalAPI: eduInputAPI,
|
FederationAPI: fsAPI,
|
||||||
FederationAPI: fsAPI,
|
RoomserverAPI: rsAPI,
|
||||||
RoomserverAPI: rsAPI,
|
UserAPI: userAPI,
|
||||||
UserAPI: userAPI,
|
KeyAPI: keyAPI,
|
||||||
KeyAPI: keyAPI,
|
|
||||||
ExtPublicRoomsProvider: yggrooms.NewYggdrasilRoomProvider(
|
ExtPublicRoomsProvider: yggrooms.NewYggdrasilRoomProvider(
|
||||||
ygg, fsAPI, federation,
|
ygg, fsAPI, federation,
|
||||||
),
|
),
|
||||||
|
@ -197,9 +194,12 @@ func (m *DendriteMonolith) Start() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DendriteMonolith) Suspend() {
|
func (m *DendriteMonolith) Stop() {
|
||||||
m.logger.Info("Suspending monolith")
|
|
||||||
if err := m.httpServer.Close(); err != nil {
|
if err := m.httpServer.Close(); err != nil {
|
||||||
m.logger.Warn("Error stopping HTTP server:", err)
|
m.logger.Warn("Error stopping HTTP server:", err)
|
||||||
}
|
}
|
||||||
|
if m.processContext != nil {
|
||||||
|
m.processContext.ShutdownDendrite()
|
||||||
|
m.processContext.WaitForComponentsToFinish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ WORKDIR /dendrite
|
||||||
RUN ./generate-keys --private-key matrix_key.pem
|
RUN ./generate-keys --private-key matrix_key.pem
|
||||||
|
|
||||||
ENV SERVER_NAME=localhost
|
ENV SERVER_NAME=localhost
|
||||||
|
ENV API=0
|
||||||
EXPOSE 8008 8448
|
EXPOSE 8008 8448
|
||||||
|
|
||||||
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
||||||
|
@ -28,4 +29,4 @@ EXPOSE 8008 8448
|
||||||
CMD ./generate-keys --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key && \
|
CMD ./generate-keys --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key && \
|
||||||
./generate-config -server $SERVER_NAME --ci > dendrite.yaml && \
|
./generate-config -server $SERVER_NAME --ci > dendrite.yaml && \
|
||||||
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
||||||
./dendrite-monolith-server --tls-cert server.crt --tls-key server.key --config dendrite.yaml
|
./dendrite-monolith-server --tls-cert server.crt --tls-key server.key --config dendrite.yaml -api=${API:-0}
|
||||||
|
|
|
@ -39,6 +39,7 @@ WORKDIR /dendrite
|
||||||
RUN ./generate-keys --private-key matrix_key.pem
|
RUN ./generate-keys --private-key matrix_key.pem
|
||||||
|
|
||||||
ENV SERVER_NAME=localhost
|
ENV SERVER_NAME=localhost
|
||||||
|
ENV API=0
|
||||||
EXPOSE 8008 8448
|
EXPOSE 8008 8448
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,4 +51,4 @@ CMD /build/run_postgres.sh && ./generate-keys --server $SERVER_NAME --tls-cert s
|
||||||
sed -i "s%connection_string:.*$%connection_string: postgresql://postgres@localhost/postgres?sslmode=disable%g" dendrite.yaml && \
|
sed -i "s%connection_string:.*$%connection_string: postgresql://postgres@localhost/postgres?sslmode=disable%g" dendrite.yaml && \
|
||||||
sed -i 's/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml && \
|
sed -i 's/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml && \
|
||||||
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
||||||
./dendrite-monolith-server --tls-cert server.crt --tls-key server.key --config dendrite.yaml
|
./dendrite-monolith-server --tls-cert server.crt --tls-key server.key --config dendrite.yaml -api=${API:-0}
|
|
@ -17,6 +17,7 @@ package authtypes
|
||||||
// Profile represents the profile for a Matrix account.
|
// Profile represents the profile for a Matrix account.
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
Localpart string `json:"local_part"`
|
Localpart string `json:"local_part"`
|
||||||
|
ServerName string `json:"server_name,omitempty"` // NOTSPEC: only set by Pinecone user provider
|
||||||
DisplayName string `json:"display_name"`
|
DisplayName string `json:"display_name"`
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ import (
|
||||||
// called after authorization has completed, with the result of the authorization.
|
// 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
|
// If the final return value is non-nil, an error occurred and the cleanup function
|
||||||
// is nil.
|
// is nil.
|
||||||
func LoginFromJSONReader(ctx context.Context, r io.Reader, accountDB AccountDatabase, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.UserAccountAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||||
reqBytes, err := ioutil.ReadAll(r)
|
reqBytes, err := ioutil.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := &util.JSONResponse{
|
err := &util.JSONResponse{
|
||||||
|
@ -58,7 +58,7 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, accountDB AccountData
|
||||||
switch header.Type {
|
switch header.Type {
|
||||||
case authtypes.LoginTypePassword:
|
case authtypes.LoginTypePassword:
|
||||||
typ = &LoginTypePassword{
|
typ = &LoginTypePassword{
|
||||||
GetAccountByPassword: accountDB.GetAccountByPassword,
|
GetAccountByPassword: useraccountAPI.QueryAccountByPassword,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
}
|
}
|
||||||
case authtypes.LoginTypeToken:
|
case authtypes.LoginTypeToken:
|
||||||
|
|
|
@ -16,7 +16,6 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -64,14 +63,13 @@ func TestLoginFromJSONReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tst := range tsts {
|
for _, tst := range tsts {
|
||||||
t.Run(tst.Name, func(t *testing.T) {
|
t.Run(tst.Name, func(t *testing.T) {
|
||||||
var accountDB fakeAccountDB
|
|
||||||
var userAPI fakeUserInternalAPI
|
var userAPI fakeUserInternalAPI
|
||||||
cfg := &config.ClientAPI{
|
cfg := &config.ClientAPI{
|
||||||
Matrix: &config.Global{
|
Matrix: &config.Global{
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
login, cleanup, err := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &accountDB, &userAPI, cfg)
|
login, cleanup, err := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, &userAPI, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("LoginFromJSONReader failed: %+v", err)
|
t.Fatalf("LoginFromJSONReader failed: %+v", err)
|
||||||
}
|
}
|
||||||
|
@ -143,14 +141,13 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tst := range tsts {
|
for _, tst := range tsts {
|
||||||
t.Run(tst.Name, func(t *testing.T) {
|
t.Run(tst.Name, func(t *testing.T) {
|
||||||
var accountDB fakeAccountDB
|
|
||||||
var userAPI fakeUserInternalAPI
|
var userAPI fakeUserInternalAPI
|
||||||
cfg := &config.ClientAPI{
|
cfg := &config.ClientAPI{
|
||||||
Matrix: &config.Global{
|
Matrix: &config.Global{
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, cleanup, errRes := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &accountDB, &userAPI, cfg)
|
_, cleanup, errRes := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, &userAPI, cfg)
|
||||||
if errRes == nil {
|
if errRes == nil {
|
||||||
cleanup(ctx, nil)
|
cleanup(ctx, nil)
|
||||||
t.Fatalf("LoginFromJSONReader err: got %+v, want code %q", errRes, tst.WantErrCode)
|
t.Fatalf("LoginFromJSONReader err: got %+v, want code %q", errRes, tst.WantErrCode)
|
||||||
|
@ -161,24 +158,22 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeAccountDB struct {
|
|
||||||
AccountDatabase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*fakeAccountDB) GetAccountByPassword(ctx context.Context, localpart, password string) (*uapi.Account, error) {
|
|
||||||
if password == "invalidpassword" {
|
|
||||||
return nil, sql.ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
return &uapi.Account{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type fakeUserInternalAPI struct {
|
type fakeUserInternalAPI struct {
|
||||||
UserInternalAPIForLogin
|
UserInternalAPIForLogin
|
||||||
|
uapi.UserAccountAPI
|
||||||
DeletedTokens []string
|
DeletedTokens []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ua *fakeUserInternalAPI) QueryAccountByPassword(ctx context.Context, req *uapi.QueryAccountByPasswordRequest, res *uapi.QueryAccountByPasswordResponse) error {
|
||||||
|
if req.PlaintextPassword == "invalidpassword" {
|
||||||
|
res.Account = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res.Exists = true
|
||||||
|
res.Account = &uapi.Account{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ua *fakeUserInternalAPI) PerformLoginTokenDeletion(ctx context.Context, req *uapi.PerformLoginTokenDeletionRequest, res *uapi.PerformLoginTokenDeletionResponse) error {
|
func (ua *fakeUserInternalAPI) PerformLoginTokenDeletion(ctx context.Context, req *uapi.PerformLoginTokenDeletionRequest, res *uapi.PerformLoginTokenDeletionResponse) error {
|
||||||
ua.DeletedTokens = append(ua.DeletedTokens, req.Token)
|
ua.DeletedTokens = append(ua.DeletedTokens, req.Token)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -16,7 +16,6 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ import (
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GetAccountByPassword func(ctx context.Context, localpart, password string) (*api.Account, error)
|
type GetAccountByPassword func(ctx context.Context, req *api.QueryAccountByPasswordRequest, res *api.QueryAccountByPasswordResponse) error
|
||||||
|
|
||||||
type PasswordRequest struct {
|
type PasswordRequest struct {
|
||||||
Login
|
Login
|
||||||
|
@ -62,7 +61,7 @@ func (t *LoginTypePassword) LoginFromJSON(ctx context.Context, reqBytes []byte)
|
||||||
|
|
||||||
func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, *util.JSONResponse) {
|
func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, *util.JSONResponse) {
|
||||||
r := req.(*PasswordRequest)
|
r := req.(*PasswordRequest)
|
||||||
username := strings.ToLower(r.Username())
|
username := strings.ToLower(r.Username())
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return nil, &util.JSONResponse{
|
return nil, &util.JSONResponse{
|
||||||
Code: http.StatusUnauthorized,
|
Code: http.StatusUnauthorized,
|
||||||
|
@ -77,19 +76,33 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Squash username to all lowercase letters
|
// Squash username to all lowercase letters
|
||||||
_, err = t.GetAccountByPassword(ctx, strings.ToLower(localpart), r.Password)
|
res := &api.QueryAccountByPasswordResponse{}
|
||||||
|
err = t.GetAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{Localpart: strings.ToLower(localpart), PlaintextPassword: r.Password}, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
return nil, &util.JSONResponse{
|
||||||
_, err = t.GetAccountByPassword(ctx, localpart, r.Password)
|
Code: http.StatusInternalServerError,
|
||||||
if err == nil {
|
JSON: jsonerror.Unknown("unable to fetch account by password"),
|
||||||
return &r.Login, nil
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !res.Exists {
|
||||||
|
err = t.GetAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{
|
||||||
|
Localpart: localpart,
|
||||||
|
PlaintextPassword: r.Password,
|
||||||
|
}, res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: jsonerror.Unknown("unable to fetch account by password"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Technically we could tell them if the user does not exist by checking if err == sql.ErrNoRows
|
// Technically we could tell them if the user does not exist by checking if err == sql.ErrNoRows
|
||||||
// but that would leak the existence of the user.
|
// but that would leak the existence of the user.
|
||||||
return nil, &util.JSONResponse{
|
if !res.Exists {
|
||||||
Code: http.StatusForbidden,
|
return nil, &util.JSONResponse{
|
||||||
JSON: jsonerror.Forbidden("The username or password was incorrect or the account does not exist."),
|
Code: http.StatusForbidden,
|
||||||
|
JSON: jsonerror.Forbidden("The username or password was incorrect or the account does not exist."),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &r.Login, nil
|
return &r.Login, nil
|
||||||
|
|
|
@ -110,9 +110,9 @@ type UserInteractive struct {
|
||||||
Sessions map[string][]string
|
Sessions map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserInteractive(accountDB AccountDatabase, cfg *config.ClientAPI) *UserInteractive {
|
func NewUserInteractive(userAccountAPI api.UserAccountAPI, cfg *config.ClientAPI) *UserInteractive {
|
||||||
typePassword := &LoginTypePassword{
|
typePassword := &LoginTypePassword{
|
||||||
GetAccountByPassword: accountDB.GetAccountByPassword,
|
GetAccountByPassword: userAccountAPI.QueryAccountByPassword,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
}
|
}
|
||||||
return &UserInteractive{
|
return &UserInteractive{
|
||||||
|
|
|
@ -25,15 +25,25 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeAccountDatabase struct {
|
type fakeAccountDatabase struct {
|
||||||
AccountDatabase
|
api.UserAccountAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*fakeAccountDatabase) GetAccountByPassword(ctx context.Context, localpart, plaintextPassword string) (*api.Account, error) {
|
func (d *fakeAccountDatabase) PerformPasswordUpdate(ctx context.Context, req *api.PerformPasswordUpdateRequest, res *api.PerformPasswordUpdateResponse) error {
|
||||||
acc, ok := lookup[localpart+" "+plaintextPassword]
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *fakeAccountDatabase) PerformAccountDeactivation(ctx context.Context, req *api.PerformAccountDeactivationRequest, res *api.PerformAccountDeactivationResponse) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *fakeAccountDatabase) QueryAccountByPassword(ctx context.Context, req *api.QueryAccountByPasswordRequest, res *api.QueryAccountByPasswordResponse) error {
|
||||||
|
acc, ok := lookup[req.Localpart+" "+req.PlaintextPassword]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown user/password")
|
return fmt.Errorf("unknown user/password")
|
||||||
}
|
}
|
||||||
return acc, nil
|
res.Account = acc
|
||||||
|
res.Exists = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup() *UserInteractive {
|
func setup() *UserInteractive {
|
||||||
|
|
|
@ -20,47 +20,52 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/api"
|
"github.com/matrix-org/dendrite/clientapi/api"
|
||||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
"github.com/matrix-org/dendrite/clientapi/routing"
|
"github.com/matrix-org/dendrite/clientapi/routing"
|
||||||
eduServerAPI "github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
"github.com/matrix-org/dendrite/internal/transactions"
|
"github.com/matrix-org/dendrite/internal/transactions"
|
||||||
keyserverAPI "github.com/matrix-org/dendrite/keyserver/api"
|
keyserverAPI "github.com/matrix-org/dendrite/keyserver/api"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddPublicRoutes sets up and registers HTTP handlers for the ClientAPI component.
|
// AddPublicRoutes sets up and registers HTTP handlers for the ClientAPI component.
|
||||||
func AddPublicRoutes(
|
func AddPublicRoutes(
|
||||||
|
process *process.ProcessContext,
|
||||||
router *mux.Router,
|
router *mux.Router,
|
||||||
synapseAdminRouter *mux.Router,
|
synapseAdminRouter *mux.Router,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
accountsDB userdb.Database,
|
|
||||||
federation *gomatrixserverlib.FederationClient,
|
federation *gomatrixserverlib.FederationClient,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
eduInputAPI eduServerAPI.EDUServerInputAPI,
|
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
transactionsCache *transactions.Cache,
|
transactionsCache *transactions.Cache,
|
||||||
fsAPI federationAPI.FederationInternalAPI,
|
fsAPI federationAPI.FederationInternalAPI,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserInternalAPI,
|
||||||
|
userDirectoryProvider userapi.UserDirectoryProvider,
|
||||||
keyAPI keyserverAPI.KeyInternalAPI,
|
keyAPI keyserverAPI.KeyInternalAPI,
|
||||||
extRoomsProvider api.ExtraPublicRoomsProvider,
|
extRoomsProvider api.ExtraPublicRoomsProvider,
|
||||||
mscCfg *config.MSCs,
|
mscCfg *config.MSCs,
|
||||||
) {
|
) {
|
||||||
js := jetstream.Prepare(&cfg.Matrix.JetStream)
|
js, natsClient := jetstream.Prepare(process, &cfg.Matrix.JetStream)
|
||||||
|
|
||||||
syncProducer := &producers.SyncAPIProducer{
|
syncProducer := &producers.SyncAPIProducer{
|
||||||
JetStream: js,
|
JetStream: js,
|
||||||
Topic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputClientData),
|
TopicClientData: cfg.Matrix.JetStream.Prefixed(jetstream.OutputClientData),
|
||||||
|
TopicReceiptEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputReceiptEvent),
|
||||||
|
TopicSendToDeviceEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputSendToDeviceEvent),
|
||||||
|
TopicTypingEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputTypingEvent),
|
||||||
|
TopicPresenceEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputPresenceEvent),
|
||||||
|
UserAPI: userAPI,
|
||||||
|
ServerName: cfg.Matrix.ServerName,
|
||||||
}
|
}
|
||||||
|
|
||||||
routing.Setup(
|
routing.Setup(
|
||||||
router, synapseAdminRouter, cfg, eduInputAPI, rsAPI, asAPI,
|
router, synapseAdminRouter, cfg, rsAPI, asAPI,
|
||||||
accountsDB, userAPI, federation,
|
userAPI, userDirectoryProvider, federation,
|
||||||
syncProducer, transactionsCache, fsAPI, keyAPI,
|
syncProducer, transactionsCache, fsAPI, keyAPI,
|
||||||
extRoomsProvider, mscCfg,
|
extRoomsProvider, mscCfg, natsClient,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,32 +15,45 @@
|
||||||
package producers
|
package producers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
|
"github.com/matrix-org/dendrite/syncapi/types"
|
||||||
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SyncAPIProducer produces events for the sync API server to consume
|
// SyncAPIProducer produces events for the sync API server to consume
|
||||||
type SyncAPIProducer struct {
|
type SyncAPIProducer struct {
|
||||||
Topic string
|
TopicClientData string
|
||||||
JetStream nats.JetStreamContext
|
TopicReceiptEvent string
|
||||||
|
TopicSendToDeviceEvent string
|
||||||
|
TopicTypingEvent string
|
||||||
|
TopicPresenceEvent string
|
||||||
|
JetStream nats.JetStreamContext
|
||||||
|
ServerName gomatrixserverlib.ServerName
|
||||||
|
UserAPI userapi.UserInternalAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendData sends account data to the sync API server
|
// SendData sends account data to the sync API server
|
||||||
func (p *SyncAPIProducer) SendData(userID string, roomID string, dataType string, readMarker *eventutil.ReadMarkerJSON) error {
|
func (p *SyncAPIProducer) SendData(userID string, roomID string, dataType string, readMarker *eventutil.ReadMarkerJSON, ignoredUsers *types.IgnoredUsers) error {
|
||||||
m := &nats.Msg{
|
m := &nats.Msg{
|
||||||
Subject: p.Topic,
|
Subject: p.TopicClientData,
|
||||||
Header: nats.Header{},
|
Header: nats.Header{},
|
||||||
}
|
}
|
||||||
m.Header.Set(jetstream.UserID, userID)
|
m.Header.Set(jetstream.UserID, userID)
|
||||||
|
|
||||||
data := eventutil.AccountData{
|
data := eventutil.AccountData{
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
Type: dataType,
|
Type: dataType,
|
||||||
ReadMarker: readMarker,
|
ReadMarker: readMarker,
|
||||||
|
IgnoredUsers: ignoredUsers,
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
m.Data, err = json.Marshal(data)
|
m.Data, err = json.Marshal(data)
|
||||||
|
@ -52,8 +65,130 @@ func (p *SyncAPIProducer) SendData(userID string, roomID string, dataType string
|
||||||
"user_id": userID,
|
"user_id": userID,
|
||||||
"room_id": roomID,
|
"room_id": roomID,
|
||||||
"data_type": dataType,
|
"data_type": dataType,
|
||||||
}).Tracef("Producing to topic '%s'", p.Topic)
|
}).Tracef("Producing to topic '%s'", p.TopicClientData)
|
||||||
|
|
||||||
_, err = p.JetStream.PublishMsg(m)
|
_, err = p.JetStream.PublishMsg(m)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *SyncAPIProducer) SendReceipt(
|
||||||
|
ctx context.Context,
|
||||||
|
userID, roomID, eventID, receiptType string, timestamp gomatrixserverlib.Timestamp,
|
||||||
|
) error {
|
||||||
|
m := &nats.Msg{
|
||||||
|
Subject: p.TopicReceiptEvent,
|
||||||
|
Header: nats.Header{},
|
||||||
|
}
|
||||||
|
m.Header.Set(jetstream.UserID, userID)
|
||||||
|
m.Header.Set(jetstream.RoomID, roomID)
|
||||||
|
m.Header.Set(jetstream.EventID, eventID)
|
||||||
|
m.Header.Set("type", receiptType)
|
||||||
|
m.Header.Set("timestamp", strconv.Itoa(int(timestamp)))
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{}).Tracef("Producing to topic '%s'", p.TopicReceiptEvent)
|
||||||
|
_, err := p.JetStream.PublishMsg(m, nats.Context(ctx))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SyncAPIProducer) SendToDevice(
|
||||||
|
ctx context.Context, sender, userID, deviceID, eventType string,
|
||||||
|
message interface{},
|
||||||
|
) error {
|
||||||
|
devices := []string{}
|
||||||
|
_, domain, err := gomatrixserverlib.SplitID('@', userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the event is targeted locally then we want to expand the wildcard
|
||||||
|
// out into individual device IDs so that we can send them to each respective
|
||||||
|
// device. If the event isn't targeted locally then we can't expand the
|
||||||
|
// wildcard as we don't know about the remote devices, so instead we leave it
|
||||||
|
// as-is, so that the federation sender can send it on with the wildcard intact.
|
||||||
|
if domain == p.ServerName && deviceID == "*" {
|
||||||
|
var res userapi.QueryDevicesResponse
|
||||||
|
err = p.UserAPI.QueryDevices(context.TODO(), &userapi.QueryDevicesRequest{
|
||||||
|
UserID: userID,
|
||||||
|
}, &res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, dev := range res.Devices {
|
||||||
|
devices = append(devices, dev.ID)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
devices = append(devices, deviceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
js, err := json.Marshal(message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"user_id": userID,
|
||||||
|
"num_devices": len(devices),
|
||||||
|
"type": eventType,
|
||||||
|
}).Tracef("Producing to topic '%s'", p.TopicSendToDeviceEvent)
|
||||||
|
for _, device := range devices {
|
||||||
|
ote := &types.OutputSendToDeviceEvent{
|
||||||
|
UserID: userID,
|
||||||
|
DeviceID: device,
|
||||||
|
SendToDeviceEvent: gomatrixserverlib.SendToDeviceEvent{
|
||||||
|
Sender: sender,
|
||||||
|
Type: eventType,
|
||||||
|
Content: js,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
eventJSON, err := json.Marshal(ote)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("sendToDevice failed json.Marshal")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m := &nats.Msg{
|
||||||
|
Subject: p.TopicSendToDeviceEvent,
|
||||||
|
Data: eventJSON,
|
||||||
|
Header: nats.Header{},
|
||||||
|
}
|
||||||
|
m.Header.Set("sender", sender)
|
||||||
|
m.Header.Set(jetstream.UserID, userID)
|
||||||
|
if _, err = p.JetStream.PublishMsg(m, nats.Context(ctx)); err != nil {
|
||||||
|
log.WithError(err).Error("sendToDevice failed t.Producer.SendMessage")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SyncAPIProducer) SendTyping(
|
||||||
|
ctx context.Context, userID, roomID string, typing bool, timeoutMS int64,
|
||||||
|
) error {
|
||||||
|
m := &nats.Msg{
|
||||||
|
Subject: p.TopicTypingEvent,
|
||||||
|
Header: nats.Header{},
|
||||||
|
}
|
||||||
|
m.Header.Set(jetstream.UserID, userID)
|
||||||
|
m.Header.Set(jetstream.RoomID, roomID)
|
||||||
|
m.Header.Set("typing", strconv.FormatBool(typing))
|
||||||
|
m.Header.Set("timeout_ms", strconv.Itoa(int(timeoutMS)))
|
||||||
|
|
||||||
|
_, err := p.JetStream.PublishMsg(m, nats.Context(ctx))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SyncAPIProducer) SendPresence(
|
||||||
|
ctx context.Context, userID string, presence types.Presence, statusMsg *string,
|
||||||
|
) error {
|
||||||
|
m := nats.NewMsg(p.TopicPresenceEvent)
|
||||||
|
m.Header.Set(jetstream.UserID, userID)
|
||||||
|
m.Header.Set("presence", presence.String())
|
||||||
|
if statusMsg != nil {
|
||||||
|
m.Header.Set("status_msg", *statusMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Header.Set("last_active_ts", strconv.Itoa(int(gomatrixserverlib.AsTimestamp(time.Now()))))
|
||||||
|
|
||||||
|
_, err := p.JetStream.PublishMsg(m, nats.Context(ctx))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
eduserverAPI "github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/syncapi/types"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
@ -95,10 +95,10 @@ func SaveAccountData(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dataType == "m.fully_read" {
|
if dataType == "m.fully_read" || dataType == "m.push_rules" {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusForbidden,
|
Code: http.StatusForbidden,
|
||||||
JSON: jsonerror.Forbidden("Unable to set read marker"),
|
JSON: jsonerror.Forbidden(fmt.Sprintf("Unable to modify %q using this API", dataType)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,8 +127,14 @@ func SaveAccountData(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ignoredUsers *types.IgnoredUsers
|
||||||
|
if dataType == "m.ignored_user_list" {
|
||||||
|
ignoredUsers = &types.IgnoredUsers{}
|
||||||
|
_ = json.Unmarshal(body, ignoredUsers)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: user API should do this since it's account data
|
// TODO: user API should do this since it's account data
|
||||||
if err := syncProducer.SendData(userID, roomID, dataType, nil); err != nil {
|
if err := syncProducer.SendData(userID, roomID, dataType, nil, ignoredUsers); err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("syncProducer.SendData failed")
|
util.GetLogger(req.Context()).WithError(err).Error("syncProducer.SendData failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
@ -146,7 +152,7 @@ type fullyReadEvent struct {
|
||||||
// SaveReadMarker implements POST /rooms/{roomId}/read_markers
|
// SaveReadMarker implements POST /rooms/{roomId}/read_markers
|
||||||
func SaveReadMarker(
|
func SaveReadMarker(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
userAPI api.UserInternalAPI, rsAPI roomserverAPI.RoomserverInternalAPI, eduAPI eduserverAPI.EDUServerInputAPI,
|
userAPI api.UserInternalAPI, rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
syncProducer *producers.SyncAPIProducer, device *api.Device, roomID string,
|
syncProducer *producers.SyncAPIProducer, device *api.Device, roomID string,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
// Verify that the user is a member of this room
|
// Verify that the user is a member of this room
|
||||||
|
@ -185,14 +191,14 @@ func SaveReadMarker(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := syncProducer.SendData(device.UserID, roomID, "m.fully_read", &r); err != nil {
|
if err := syncProducer.SendData(device.UserID, roomID, "m.fully_read", &r, nil); err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("syncProducer.SendData failed")
|
util.GetLogger(req.Context()).WithError(err).Error("syncProducer.SendData failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the read receipt that may be included in the read marker
|
// Handle the read receipt that may be included in the read marker
|
||||||
if r.Read != "" {
|
if r.Read != "" {
|
||||||
return SetReceipt(req, eduAPI, device, roomID, "m.read", r.Read)
|
return SetReceipt(req, syncProducer, device, roomID, "m.read", r.Read)
|
||||||
}
|
}
|
||||||
|
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -97,7 +96,7 @@ func consent(writer http.ResponseWriter, req *http.Request, userAPI userapi.User
|
||||||
}
|
}
|
||||||
return &internalError
|
return &internalError
|
||||||
}
|
}
|
||||||
if err := userAPI.PerformUpdatePolicyVersion(
|
if err = userAPI.PerformUpdatePolicyVersion(
|
||||||
req.Context(),
|
req.Context(),
|
||||||
&userapi.UpdatePolicyVersionRequest{
|
&userapi.UpdatePolicyVersionRequest{
|
||||||
PolicyVersion: data.Version,
|
PolicyVersion: data.Version,
|
||||||
|
@ -129,7 +128,6 @@ func sendServerNoticeForConsent(userAPI userapi.UserInternalAPI, rsAPI api.Rooms
|
||||||
cfgNotices *config.ServerNotices,
|
cfgNotices *config.ServerNotices,
|
||||||
cfgClient *config.ClientAPI,
|
cfgClient *config.ClientAPI,
|
||||||
senderDevice *userapi.Device,
|
senderDevice *userapi.Device,
|
||||||
accountsDB userdb.Database,
|
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
) {
|
) {
|
||||||
res := &userapi.QueryOutdatedPolicyResponse{}
|
res := &userapi.QueryOutdatedPolicyResponse{}
|
||||||
|
@ -180,7 +178,7 @@ func sendServerNoticeForConsent(userAPI userapi.UserInternalAPI, rsAPI api.Rooms
|
||||||
Body: msgBody.String(),
|
Body: msgBody.String(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err = sendServerNotice(context.Background(), req, rsAPI, cfgNotices, cfgClient, senderDevice, accountsDB, asAPI, userAPI, nil, nil, nil)
|
_, err = sendServerNotice(context.Background(), req, rsAPI, cfgNotices, cfgClient, senderDevice, asAPI, userAPI, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).WithField("userID", userID).Error("failed to send server notice for consent to user")
|
logrus.WithError(err).WithField("userID", userID).Error("failed to send server notice for consent to user")
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -31,7 +31,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
@ -138,7 +137,7 @@ type fledglingEvent struct {
|
||||||
func CreateRoom(
|
func CreateRoom(
|
||||||
req *http.Request, device *api.Device,
|
req *http.Request, device *api.Device,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
accountDB userdb.Database, rsAPI roomserverAPI.RoomserverInternalAPI,
|
profileAPI api.UserProfileAPI, rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
var r createRoomRequest
|
var r createRoomRequest
|
||||||
|
@ -156,7 +155,7 @@ func CreateRoom(
|
||||||
JSON: jsonerror.InvalidArgumentValue(err.Error()),
|
JSON: jsonerror.InvalidArgumentValue(err.Error()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return createRoom(req.Context(), r, device, cfg, accountDB, rsAPI, asAPI, evTime)
|
return createRoom(req.Context(), r, device, cfg, profileAPI, rsAPI, asAPI, evTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createRoom implements /createRoom
|
// createRoom implements /createRoom
|
||||||
|
@ -165,7 +164,7 @@ func createRoom(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
r createRoomRequest, device *api.Device,
|
r createRoomRequest, device *api.Device,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
accountDB userdb.Database, rsAPI roomserverAPI.RoomserverInternalAPI,
|
profileAPI api.UserProfileAPI, rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
evTime time.Time,
|
evTime time.Time,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
@ -201,7 +200,7 @@ func createRoom(
|
||||||
"roomVersion": roomVersion,
|
"roomVersion": roomVersion,
|
||||||
}).Info("Creating new room")
|
}).Info("Creating new room")
|
||||||
|
|
||||||
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, accountDB)
|
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, profileAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("appserviceAPI.RetrieveUserProfile failed")
|
util.GetLogger(ctx).WithError(err).Error("appserviceAPI.RetrieveUserProfile failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
@ -520,7 +519,7 @@ func createRoom(
|
||||||
for _, invitee := range r.Invite {
|
for _, invitee := range r.Invite {
|
||||||
// Build the invite event.
|
// Build the invite event.
|
||||||
inviteEvent, err := buildMembershipEvent(
|
inviteEvent, err := buildMembershipEvent(
|
||||||
ctx, invitee, "", accountDB, device, gomatrixserverlib.Invite,
|
ctx, invitee, "", profileAPI, device, gomatrixserverlib.Invite,
|
||||||
roomID, true, cfg, evTime, rsAPI, asAPI,
|
roomID, true, cfg, evTime, rsAPI, asAPI,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func Deactivate(
|
func Deactivate(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
userInteractiveAuth *auth.UserInteractive,
|
userInteractiveAuth *auth.UserInteractive,
|
||||||
userAPI api.UserInternalAPI,
|
accountAPI api.UserAccountAPI,
|
||||||
deviceAPI *api.Device,
|
deviceAPI *api.Device,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
ctx := req.Context()
|
ctx := req.Context()
|
||||||
|
@ -40,7 +40,7 @@ func Deactivate(
|
||||||
}
|
}
|
||||||
|
|
||||||
var res api.PerformAccountDeactivationResponse
|
var res api.PerformAccountDeactivationResponse
|
||||||
err = userAPI.PerformAccountDeactivation(ctx, &api.PerformAccountDeactivationRequest{
|
err = accountAPI.PerformAccountDeactivation(ctx, &api.PerformAccountDeactivationRequest{
|
||||||
Localpart: localpart,
|
Localpart: localpart,
|
||||||
}, &res)
|
}, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -18,12 +18,10 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
@ -32,7 +30,7 @@ func JoinRoomByIDOrAlias(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
device *api.Device,
|
device *api.Device,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
accountDB userdb.Database,
|
profileAPI api.UserProfileAPI,
|
||||||
roomIDOrAlias string,
|
roomIDOrAlias string,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
// Prepare to ask the roomserver to perform the room join.
|
// Prepare to ask the roomserver to perform the room join.
|
||||||
|
@ -60,19 +58,23 @@ func JoinRoomByIDOrAlias(
|
||||||
_ = httputil.UnmarshalJSONRequest(req, &joinReq.Content)
|
_ = httputil.UnmarshalJSONRequest(req, &joinReq.Content)
|
||||||
|
|
||||||
// Work out our localpart for the client profile request.
|
// Work out our localpart for the client profile request.
|
||||||
localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID)
|
|
||||||
if err != nil {
|
// Request our profile content to populate the request content with.
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
|
res := &api.QueryProfileResponse{}
|
||||||
} else {
|
err := profileAPI.QueryProfile(req.Context(), &api.QueryProfileRequest{UserID: device.UserID}, res)
|
||||||
// Request our profile content to populate the request content with.
|
if err != nil || !res.UserExists {
|
||||||
var profile *authtypes.Profile
|
if !res.UserExists {
|
||||||
profile, err = accountDB.GetProfileByLocalpart(req.Context(), localpart)
|
util.GetLogger(req.Context()).Error("Unable to query user profile, no profile found.")
|
||||||
if err != nil {
|
return util.JSONResponse{
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.GetProfileByLocalpart failed")
|
Code: http.StatusInternalServerError,
|
||||||
} else {
|
JSON: jsonerror.Unknown("Unable to query user profile, no profile found."),
|
||||||
joinReq.Content["displayname"] = profile.DisplayName
|
}
|
||||||
joinReq.Content["avatar_url"] = profile.AvatarURL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
util.GetLogger(req.Context()).WithError(err).Error("UserProfileAPI.QueryProfile failed")
|
||||||
|
} else {
|
||||||
|
joinReq.Content["displayname"] = res.DisplayName
|
||||||
|
joinReq.Content["avatar_url"] = res.AvatarURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the roomserver to perform the join.
|
// Ask the roomserver to perform the join.
|
||||||
|
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ type crossSigningRequest struct {
|
||||||
func UploadCrossSigningDeviceKeys(
|
func UploadCrossSigningDeviceKeys(
|
||||||
req *http.Request, userInteractiveAuth *auth.UserInteractive,
|
req *http.Request, userInteractiveAuth *auth.UserInteractive,
|
||||||
keyserverAPI api.KeyInternalAPI, device *userapi.Device,
|
keyserverAPI api.KeyInternalAPI, device *userapi.Device,
|
||||||
accountDB userdb.Database, cfg *config.ClientAPI,
|
accountAPI userapi.UserAccountAPI, cfg *config.ClientAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
uploadReq := &crossSigningRequest{}
|
uploadReq := &crossSigningRequest{}
|
||||||
uploadRes := &api.PerformUploadDeviceKeysResponse{}
|
uploadRes := &api.PerformUploadDeviceKeysResponse{}
|
||||||
|
@ -64,7 +63,7 @@ func UploadCrossSigningDeviceKeys(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typePassword := auth.LoginTypePassword{
|
typePassword := auth.LoginTypePassword{
|
||||||
GetAccountByPassword: accountDB.GetAccountByPassword,
|
GetAccountByPassword: accountAPI.QueryAccountByPassword,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
}
|
}
|
||||||
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
|
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
@ -54,7 +53,7 @@ func passwordLogin() flows {
|
||||||
|
|
||||||
// Login implements GET and POST /login
|
// Login implements GET and POST /login
|
||||||
func Login(
|
func Login(
|
||||||
req *http.Request, accountDB userdb.Database, userAPI userapi.UserInternalAPI,
|
req *http.Request, userAPI userapi.UserInternalAPI,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if req.Method == http.MethodGet {
|
if req.Method == http.MethodGet {
|
||||||
|
@ -64,7 +63,7 @@ func Login(
|
||||||
JSON: passwordLogin(),
|
JSON: passwordLogin(),
|
||||||
}
|
}
|
||||||
} else if req.Method == http.MethodPost {
|
} else if req.Method == http.MethodPost {
|
||||||
login, cleanup, authErr := auth.LoginFromJSONReader(req.Context(), req.Body, accountDB, userAPI, cfg)
|
login, cleanup, authErr := auth.LoginFromJSONReader(req.Context(), req.Body, userAPI, userAPI, cfg)
|
||||||
if authErr != nil {
|
if authErr != nil {
|
||||||
return *authErr
|
return *authErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import (
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
@ -39,7 +38,7 @@ import (
|
||||||
var errMissingUserID = errors.New("'user_id' must be supplied")
|
var errMissingUserID = errors.New("'user_id' must be supplied")
|
||||||
|
|
||||||
func SendBan(
|
func SendBan(
|
||||||
req *http.Request, accountDB userdb.Database, device *userapi.Device,
|
req *http.Request, profileAPI userapi.UserProfileAPI, device *userapi.Device,
|
||||||
roomID string, cfg *config.ClientAPI,
|
roomID string, cfg *config.ClientAPI,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
@ -78,16 +77,16 @@ func SendBan(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendMembership(req.Context(), accountDB, device, roomID, "ban", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI)
|
return sendMembership(req.Context(), profileAPI, device, roomID, "ban", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendMembership(ctx context.Context, accountDB userdb.Database, device *userapi.Device,
|
func sendMembership(ctx context.Context, profileAPI userapi.UserProfileAPI, device *userapi.Device,
|
||||||
roomID, membership, reason string, cfg *config.ClientAPI, targetUserID string, evTime time.Time,
|
roomID, membership, reason string, cfg *config.ClientAPI, targetUserID string, evTime time.Time,
|
||||||
roomVer gomatrixserverlib.RoomVersion,
|
roomVer gomatrixserverlib.RoomVersion,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI) util.JSONResponse {
|
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI) util.JSONResponse {
|
||||||
|
|
||||||
event, err := buildMembershipEvent(
|
event, err := buildMembershipEvent(
|
||||||
ctx, targetUserID, reason, accountDB, device, membership,
|
ctx, targetUserID, reason, profileAPI, device, membership,
|
||||||
roomID, false, cfg, evTime, rsAPI, asAPI,
|
roomID, false, cfg, evTime, rsAPI, asAPI,
|
||||||
)
|
)
|
||||||
if err == errMissingUserID {
|
if err == errMissingUserID {
|
||||||
|
@ -125,7 +124,7 @@ func sendMembership(ctx context.Context, accountDB userdb.Database, device *user
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendKick(
|
func SendKick(
|
||||||
req *http.Request, accountDB userdb.Database, device *userapi.Device,
|
req *http.Request, profileAPI userapi.UserProfileAPI, device *userapi.Device,
|
||||||
roomID string, cfg *config.ClientAPI,
|
roomID string, cfg *config.ClientAPI,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
@ -161,11 +160,11 @@ func SendKick(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: should we be using SendLeave instead?
|
// TODO: should we be using SendLeave instead?
|
||||||
return sendMembership(req.Context(), accountDB, device, roomID, "leave", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI)
|
return sendMembership(req.Context(), profileAPI, device, roomID, "leave", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendUnban(
|
func SendUnban(
|
||||||
req *http.Request, accountDB userdb.Database, device *userapi.Device,
|
req *http.Request, profileAPI userapi.UserProfileAPI, device *userapi.Device,
|
||||||
roomID string, cfg *config.ClientAPI,
|
roomID string, cfg *config.ClientAPI,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
@ -196,11 +195,11 @@ func SendUnban(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: should we be using SendLeave instead?
|
// TODO: should we be using SendLeave instead?
|
||||||
return sendMembership(req.Context(), accountDB, device, roomID, "leave", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI)
|
return sendMembership(req.Context(), profileAPI, device, roomID, "leave", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendInvite(
|
func SendInvite(
|
||||||
req *http.Request, accountDB userdb.Database, device *userapi.Device,
|
req *http.Request, profileAPI userapi.UserProfileAPI, device *userapi.Device,
|
||||||
roomID string, cfg *config.ClientAPI,
|
roomID string, cfg *config.ClientAPI,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
@ -210,7 +209,7 @@ func SendInvite(
|
||||||
}
|
}
|
||||||
|
|
||||||
inviteStored, jsonErrResp := checkAndProcessThreepid(
|
inviteStored, jsonErrResp := checkAndProcessThreepid(
|
||||||
req, device, body, cfg, rsAPI, accountDB, roomID, evTime,
|
req, device, body, cfg, rsAPI, profileAPI, roomID, evTime,
|
||||||
)
|
)
|
||||||
if jsonErrResp != nil {
|
if jsonErrResp != nil {
|
||||||
return *jsonErrResp
|
return *jsonErrResp
|
||||||
|
@ -227,14 +226,14 @@ func SendInvite(
|
||||||
}
|
}
|
||||||
|
|
||||||
// We already received the return value, so no need to check for an error here.
|
// We already received the return value, so no need to check for an error here.
|
||||||
response, _ := sendInvite(req.Context(), accountDB, device, roomID, body.UserID, body.Reason, cfg, rsAPI, asAPI, evTime)
|
response, _ := sendInvite(req.Context(), profileAPI, device, roomID, body.UserID, body.Reason, cfg, rsAPI, asAPI, evTime)
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendInvite sends an invitation to a user. Returns a JSONResponse and an error
|
// sendInvite sends an invitation to a user. Returns a JSONResponse and an error
|
||||||
func sendInvite(
|
func sendInvite(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
accountDB userdb.Database,
|
profileAPI userapi.UserProfileAPI,
|
||||||
device *userapi.Device,
|
device *userapi.Device,
|
||||||
roomID, userID, reason string,
|
roomID, userID, reason string,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
|
@ -242,7 +241,7 @@ func sendInvite(
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI, evTime time.Time,
|
asAPI appserviceAPI.AppServiceQueryAPI, evTime time.Time,
|
||||||
) (util.JSONResponse, error) {
|
) (util.JSONResponse, error) {
|
||||||
event, err := buildMembershipEvent(
|
event, err := buildMembershipEvent(
|
||||||
ctx, userID, reason, accountDB, device, "invite",
|
ctx, userID, reason, profileAPI, device, "invite",
|
||||||
roomID, false, cfg, evTime, rsAPI, asAPI,
|
roomID, false, cfg, evTime, rsAPI, asAPI,
|
||||||
)
|
)
|
||||||
if err == errMissingUserID {
|
if err == errMissingUserID {
|
||||||
|
@ -286,13 +285,13 @@ func sendInvite(
|
||||||
|
|
||||||
func buildMembershipEvent(
|
func buildMembershipEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
targetUserID, reason string, accountDB userdb.Database,
|
targetUserID, reason string, profileAPI userapi.UserProfileAPI,
|
||||||
device *userapi.Device,
|
device *userapi.Device,
|
||||||
membership, roomID string, isDirect bool,
|
membership, roomID string, isDirect bool,
|
||||||
cfg *config.ClientAPI, evTime time.Time,
|
cfg *config.ClientAPI, evTime time.Time,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
) (*gomatrixserverlib.HeaderedEvent, error) {
|
) (*gomatrixserverlib.HeaderedEvent, error) {
|
||||||
profile, err := loadProfile(ctx, targetUserID, cfg, accountDB, asAPI)
|
profile, err := loadProfile(ctx, targetUserID, cfg, profileAPI, asAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -327,7 +326,7 @@ func loadProfile(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID string,
|
userID string,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
accountDB userdb.Database,
|
profileAPI userapi.UserProfileAPI,
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
) (*authtypes.Profile, error) {
|
) (*authtypes.Profile, error) {
|
||||||
_, serverName, err := gomatrixserverlib.SplitID('@', userID)
|
_, serverName, err := gomatrixserverlib.SplitID('@', userID)
|
||||||
|
@ -337,7 +336,7 @@ func loadProfile(
|
||||||
|
|
||||||
var profile *authtypes.Profile
|
var profile *authtypes.Profile
|
||||||
if serverName == cfg.Matrix.ServerName {
|
if serverName == cfg.Matrix.ServerName {
|
||||||
profile, err = appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, accountDB)
|
profile, err = appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, profileAPI)
|
||||||
} else {
|
} else {
|
||||||
profile = &authtypes.Profile{}
|
profile = &authtypes.Profile{}
|
||||||
}
|
}
|
||||||
|
@ -381,13 +380,13 @@ func checkAndProcessThreepid(
|
||||||
body *threepid.MembershipRequest,
|
body *threepid.MembershipRequest,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
accountDB userdb.Database,
|
profileAPI userapi.UserProfileAPI,
|
||||||
roomID string,
|
roomID string,
|
||||||
evTime time.Time,
|
evTime time.Time,
|
||||||
) (inviteStored bool, errRes *util.JSONResponse) {
|
) (inviteStored bool, errRes *util.JSONResponse) {
|
||||||
|
|
||||||
inviteStored, err := threepid.CheckAndProcessInvite(
|
inviteStored, err := threepid.CheckAndProcessInvite(
|
||||||
req.Context(), device, body, cfg, rsAPI, accountDB,
|
req.Context(), device, body, cfg, rsAPI, profileAPI,
|
||||||
roomID, evTime,
|
roomID, evTime,
|
||||||
)
|
)
|
||||||
if err == threepid.ErrMissingParameter {
|
if err == threepid.ErrMissingParameter {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -30,7 +29,6 @@ type newPasswordAuth struct {
|
||||||
func Password(
|
func Password(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
userAPI api.UserInternalAPI,
|
userAPI api.UserInternalAPI,
|
||||||
accountDB userdb.Database,
|
|
||||||
device *api.Device,
|
device *api.Device,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
@ -74,7 +72,7 @@ func Password(
|
||||||
|
|
||||||
// Check if the existing password is correct.
|
// Check if the existing password is correct.
|
||||||
typePassword := auth.LoginTypePassword{
|
typePassword := auth.LoginTypePassword{
|
||||||
GetAccountByPassword: accountDB.GetAccountByPassword,
|
GetAccountByPassword: userAPI.QueryAccountByPassword,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
}
|
}
|
||||||
if _, authErr := typePassword.Login(req.Context(), &r.Auth.PasswordRequest); authErr != nil {
|
if _, authErr := typePassword.Login(req.Context(), &r.Auth.PasswordRequest); authErr != nil {
|
||||||
|
|
|
@ -19,7 +19,6 @@ import (
|
||||||
|
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
@ -28,7 +27,6 @@ func PeekRoomByIDOrAlias(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
device *api.Device,
|
device *api.Device,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
accountDB userdb.Database,
|
|
||||||
roomIDOrAlias string,
|
roomIDOrAlias string,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
// if this is a remote roomIDOrAlias, we have to ask the roomserver (or federation sender?) to
|
// if this is a remote roomIDOrAlias, we have to ask the roomserver (or federation sender?) to
|
||||||
|
@ -82,7 +80,6 @@ func UnpeekRoomByID(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
device *api.Device,
|
device *api.Device,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
accountDB userdb.Database,
|
|
||||||
roomID string,
|
roomID string,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
unpeekReq := roomserverAPI.PerformUnpeekRequest{
|
unpeekReq := roomserverAPI.PerformUnpeekRequest{
|
||||||
|
|
137
clientapi/routing/presence.go
Normal file
137
clientapi/routing/presence.go
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
|
"github.com/matrix-org/dendrite/syncapi/types"
|
||||||
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type presenceReq struct {
|
||||||
|
Presence string `json:"presence"`
|
||||||
|
StatusMsg *string `json:"status_msg,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetPresence(
|
||||||
|
req *http.Request,
|
||||||
|
cfg *config.ClientAPI,
|
||||||
|
device *api.Device,
|
||||||
|
producer *producers.SyncAPIProducer,
|
||||||
|
userID string,
|
||||||
|
) util.JSONResponse {
|
||||||
|
if !cfg.Matrix.Presence.EnableOutbound {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if device.UserID != userID {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: jsonerror.Forbidden("Unable to set presence for other user."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var presence presenceReq
|
||||||
|
parseErr := httputil.UnmarshalJSONRequest(req, &presence)
|
||||||
|
if parseErr != nil {
|
||||||
|
return *parseErr
|
||||||
|
}
|
||||||
|
|
||||||
|
presenceStatus, ok := types.PresenceFromString(presence.Presence)
|
||||||
|
if !ok {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.Unknown(fmt.Sprintf("Unknown presence '%s'.", presence.Presence)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := producer.SendPresence(req.Context(), userID, presenceStatus, presence.StatusMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Errorf("failed to update presence")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: jsonerror.InternalServerError(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPresence(
|
||||||
|
req *http.Request,
|
||||||
|
device *api.Device,
|
||||||
|
natsClient *nats.Conn,
|
||||||
|
presenceTopic string,
|
||||||
|
userID string,
|
||||||
|
) util.JSONResponse {
|
||||||
|
msg := nats.NewMsg(presenceTopic)
|
||||||
|
msg.Header.Set(jetstream.UserID, userID)
|
||||||
|
|
||||||
|
presence, err := natsClient.RequestMsg(msg, time.Second*10)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Errorf("unable to get presence")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: jsonerror.InternalServerError(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statusMsg := presence.Header.Get("status_msg")
|
||||||
|
e := presence.Header.Get("error")
|
||||||
|
if e != "" {
|
||||||
|
log.Errorf("received error msg from nats: %s", e)
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: types.PresenceClientResponse{
|
||||||
|
Presence: types.PresenceUnavailable.String(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastActive, err := strconv.Atoi(presence.Header.Get("last_active_ts"))
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: jsonerror.InternalServerError(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p := types.PresenceInternal{LastActiveTS: gomatrixserverlib.Timestamp(lastActive)}
|
||||||
|
currentlyActive := p.CurrentlyActive()
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: types.PresenceClientResponse{
|
||||||
|
CurrentlyActive: ¤tlyActive,
|
||||||
|
LastActiveAgo: p.LastActiveAgo(),
|
||||||
|
Presence: presence.Header.Get("presence"),
|
||||||
|
StatusMsg: &statusMsg,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrix"
|
"github.com/matrix-org/gomatrix"
|
||||||
|
@ -36,12 +35,12 @@ import (
|
||||||
|
|
||||||
// GetProfile implements GET /profile/{userID}
|
// GetProfile implements GET /profile/{userID}
|
||||||
func GetProfile(
|
func GetProfile(
|
||||||
req *http.Request, accountDB userdb.Database, cfg *config.ClientAPI,
|
req *http.Request, profileAPI userapi.UserProfileAPI, cfg *config.ClientAPI,
|
||||||
userID string,
|
userID string,
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
federation *gomatrixserverlib.FederationClient,
|
federation *gomatrixserverlib.FederationClient,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
profile, err := getProfile(req.Context(), accountDB, cfg, userID, asAPI, federation)
|
profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == eventutil.ErrProfileNoExists {
|
if err == eventutil.ErrProfileNoExists {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -65,11 +64,11 @@ func GetProfile(
|
||||||
|
|
||||||
// GetAvatarURL implements GET /profile/{userID}/avatar_url
|
// GetAvatarURL implements GET /profile/{userID}/avatar_url
|
||||||
func GetAvatarURL(
|
func GetAvatarURL(
|
||||||
req *http.Request, accountDB userdb.Database, cfg *config.ClientAPI,
|
req *http.Request, profileAPI userapi.UserProfileAPI, cfg *config.ClientAPI,
|
||||||
userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
federation *gomatrixserverlib.FederationClient,
|
federation *gomatrixserverlib.FederationClient,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
profile, err := getProfile(req.Context(), accountDB, cfg, userID, asAPI, federation)
|
profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == eventutil.ErrProfileNoExists {
|
if err == eventutil.ErrProfileNoExists {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -92,7 +91,7 @@ func GetAvatarURL(
|
||||||
|
|
||||||
// SetAvatarURL implements PUT /profile/{userID}/avatar_url
|
// SetAvatarURL implements PUT /profile/{userID}/avatar_url
|
||||||
func SetAvatarURL(
|
func SetAvatarURL(
|
||||||
req *http.Request, accountDB userdb.Database,
|
req *http.Request, profileAPI userapi.UserProfileAPI,
|
||||||
device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.RoomserverInternalAPI,
|
device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.RoomserverInternalAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if userID != device.UserID {
|
if userID != device.UserID {
|
||||||
|
@ -127,22 +126,34 @@ func SetAvatarURL(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oldProfile, err := accountDB.GetProfileByLocalpart(req.Context(), localpart)
|
res := &userapi.QueryProfileResponse{}
|
||||||
|
err = profileAPI.QueryProfile(req.Context(), &userapi.QueryProfileRequest{
|
||||||
|
UserID: userID,
|
||||||
|
}, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.GetProfileByLocalpart failed")
|
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.QueryProfile failed")
|
||||||
|
return jsonerror.InternalServerError()
|
||||||
|
}
|
||||||
|
oldProfile := &authtypes.Profile{
|
||||||
|
Localpart: localpart,
|
||||||
|
DisplayName: res.DisplayName,
|
||||||
|
AvatarURL: res.AvatarURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
setRes := &userapi.PerformSetAvatarURLResponse{}
|
||||||
|
if err = profileAPI.SetAvatarURL(req.Context(), &userapi.PerformSetAvatarURLRequest{
|
||||||
|
Localpart: localpart,
|
||||||
|
AvatarURL: r.AvatarURL,
|
||||||
|
}, setRes); err != nil {
|
||||||
|
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetAvatarURL failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = accountDB.SetAvatarURL(req.Context(), localpart, r.AvatarURL); err != nil {
|
var roomsRes api.QueryRoomsForUserResponse
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.SetAvatarURL failed")
|
|
||||||
return jsonerror.InternalServerError()
|
|
||||||
}
|
|
||||||
|
|
||||||
var res api.QueryRoomsForUserResponse
|
|
||||||
err = rsAPI.QueryRoomsForUser(req.Context(), &api.QueryRoomsForUserRequest{
|
err = rsAPI.QueryRoomsForUser(req.Context(), &api.QueryRoomsForUserRequest{
|
||||||
UserID: device.UserID,
|
UserID: device.UserID,
|
||||||
WantMembership: "join",
|
WantMembership: "join",
|
||||||
}, &res)
|
}, &roomsRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("QueryRoomsForUser failed")
|
util.GetLogger(req.Context()).WithError(err).Error("QueryRoomsForUser failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
@ -155,7 +166,7 @@ func SetAvatarURL(
|
||||||
}
|
}
|
||||||
|
|
||||||
events, err := buildMembershipEvents(
|
events, err := buildMembershipEvents(
|
||||||
req.Context(), res.RoomIDs, newProfile, userID, cfg, evTime, rsAPI,
|
req.Context(), roomsRes.RoomIDs, newProfile, userID, cfg, evTime, rsAPI,
|
||||||
)
|
)
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
|
@ -169,7 +180,7 @@ func SetAvatarURL(
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := api.SendEvents(req.Context(), rsAPI, api.KindNew, events, cfg.Matrix.ServerName, cfg.Matrix.ServerName, nil, false); err != nil {
|
if err := api.SendEvents(req.Context(), rsAPI, api.KindNew, events, cfg.Matrix.ServerName, cfg.Matrix.ServerName, nil, true); err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("SendEvents failed")
|
util.GetLogger(req.Context()).WithError(err).Error("SendEvents failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
@ -182,11 +193,11 @@ func SetAvatarURL(
|
||||||
|
|
||||||
// GetDisplayName implements GET /profile/{userID}/displayname
|
// GetDisplayName implements GET /profile/{userID}/displayname
|
||||||
func GetDisplayName(
|
func GetDisplayName(
|
||||||
req *http.Request, accountDB userdb.Database, cfg *config.ClientAPI,
|
req *http.Request, profileAPI userapi.UserProfileAPI, cfg *config.ClientAPI,
|
||||||
userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
federation *gomatrixserverlib.FederationClient,
|
federation *gomatrixserverlib.FederationClient,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
profile, err := getProfile(req.Context(), accountDB, cfg, userID, asAPI, federation)
|
profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == eventutil.ErrProfileNoExists {
|
if err == eventutil.ErrProfileNoExists {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -209,7 +220,7 @@ func GetDisplayName(
|
||||||
|
|
||||||
// SetDisplayName implements PUT /profile/{userID}/displayname
|
// SetDisplayName implements PUT /profile/{userID}/displayname
|
||||||
func SetDisplayName(
|
func SetDisplayName(
|
||||||
req *http.Request, accountDB userdb.Database,
|
req *http.Request, profileAPI userapi.UserProfileAPI,
|
||||||
device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.RoomserverInternalAPI,
|
device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.RoomserverInternalAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if userID != device.UserID {
|
if userID != device.UserID {
|
||||||
|
@ -244,14 +255,26 @@ func SetDisplayName(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oldProfile, err := accountDB.GetProfileByLocalpart(req.Context(), localpart)
|
pRes := &userapi.QueryProfileResponse{}
|
||||||
|
err = profileAPI.QueryProfile(req.Context(), &userapi.QueryProfileRequest{
|
||||||
|
UserID: userID,
|
||||||
|
}, pRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.GetProfileByLocalpart failed")
|
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.QueryProfile failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
oldProfile := &authtypes.Profile{
|
||||||
|
Localpart: localpart,
|
||||||
|
DisplayName: pRes.DisplayName,
|
||||||
|
AvatarURL: pRes.AvatarURL,
|
||||||
|
}
|
||||||
|
|
||||||
if err = accountDB.SetDisplayName(req.Context(), localpart, r.DisplayName); err != nil {
|
err = profileAPI.SetDisplayName(req.Context(), &userapi.PerformUpdateDisplayNameRequest{
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.SetDisplayName failed")
|
Localpart: localpart,
|
||||||
|
DisplayName: r.DisplayName,
|
||||||
|
}, &struct{}{})
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetDisplayName failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +325,7 @@ func SetDisplayName(
|
||||||
// Returns an error when something goes wrong or specifically
|
// Returns an error when something goes wrong or specifically
|
||||||
// eventutil.ErrProfileNoExists when the profile doesn't exist.
|
// eventutil.ErrProfileNoExists when the profile doesn't exist.
|
||||||
func getProfile(
|
func getProfile(
|
||||||
ctx context.Context, accountDB userdb.Database, cfg *config.ClientAPI,
|
ctx context.Context, profileAPI userapi.UserProfileAPI, cfg *config.ClientAPI,
|
||||||
userID string,
|
userID string,
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
federation *gomatrixserverlib.FederationClient,
|
federation *gomatrixserverlib.FederationClient,
|
||||||
|
@ -331,7 +354,7 @@ func getProfile(
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, accountDB)
|
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, profileAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,21 +19,20 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
|
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetReceipt(req *http.Request, eduAPI api.EDUServerInputAPI, device *userapi.Device, roomId, receiptType, eventId string) util.JSONResponse {
|
func SetReceipt(req *http.Request, syncProducer *producers.SyncAPIProducer, device *userapi.Device, roomID, receiptType, eventID string) util.JSONResponse {
|
||||||
timestamp := gomatrixserverlib.AsTimestamp(time.Now())
|
timestamp := gomatrixserverlib.AsTimestamp(time.Now())
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"roomId": roomId,
|
"roomID": roomID,
|
||||||
"receiptType": receiptType,
|
"receiptType": receiptType,
|
||||||
"eventId": eventId,
|
"eventID": eventID,
|
||||||
"userId": device.UserID,
|
"userId": device.UserID,
|
||||||
"timestamp": timestamp,
|
"timestamp": timestamp,
|
||||||
}).Debug("Setting receipt")
|
}).Debug("Setting receipt")
|
||||||
|
@ -43,7 +42,7 @@ func SetReceipt(req *http.Request, eduAPI api.EDUServerInputAPI, device *userapi
|
||||||
return util.MessageResponse(400, fmt.Sprintf("receipt type must be m.read not '%s'", receiptType))
|
return util.MessageResponse(400, fmt.Sprintf("receipt type must be m.read not '%s'", receiptType))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := api.SendReceipt(req.Context(), eduAPI, device.UserID, roomId, eventId, receiptType, timestamp); err != nil {
|
if err := syncProducer.SendReceipt(req.Context(), device.UserID, roomID, eventID, receiptType, timestamp); err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,12 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/gomatrixserverlib/tokens"
|
"github.com/matrix-org/gomatrixserverlib/tokens"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
@ -44,7 +44,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -64,11 +63,6 @@ const (
|
||||||
sessionIDLength = 24
|
sessionIDLength = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Register prometheus metrics. They must be registered to be exposed.
|
|
||||||
prometheus.MustRegister(amtRegUsers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sessionsDict keeps track of completed auth stages for each session.
|
// sessionsDict keeps track of completed auth stages for each session.
|
||||||
// It shouldn't be passed by value because it contains a mutex.
|
// It shouldn't be passed by value because it contains a mutex.
|
||||||
type sessionsDict struct {
|
type sessionsDict struct {
|
||||||
|
@ -523,26 +517,40 @@ func validateApplicationService(
|
||||||
// http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#post-matrix-client-unstable-register
|
// http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#post-matrix-client-unstable-register
|
||||||
func Register(
|
func Register(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserRegisterAPI,
|
||||||
accountDB userdb.Database,
|
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
defer req.Body.Close() // nolint: errcheck
|
||||||
|
reqBody, err := ioutil.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.NotJSON("Unable to read request body"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var r registerRequest
|
var r registerRequest
|
||||||
resErr := httputil.UnmarshalJSONRequest(req, &r)
|
sessionID := gjson.GetBytes(reqBody, "auth.session").String()
|
||||||
if resErr != nil {
|
if sessionID == "" {
|
||||||
|
// Generate a new, random session ID
|
||||||
|
sessionID = util.RandomString(sessionIDLength)
|
||||||
|
} else if data, ok := sessions.getParams(sessionID); ok {
|
||||||
|
// Use the parameters from the session as our defaults.
|
||||||
|
// Some of these might end up being overwritten if the
|
||||||
|
// values are specified again in the request body.
|
||||||
|
r.Username = data.Username
|
||||||
|
r.Password = data.Password
|
||||||
|
r.DeviceID = data.DeviceID
|
||||||
|
r.InitialDisplayName = data.InitialDisplayName
|
||||||
|
r.InhibitLogin = data.InhibitLogin
|
||||||
|
}
|
||||||
|
if resErr := httputil.UnmarshalJSON(reqBody, &r); resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
if req.URL.Query().Get("kind") == "guest" {
|
if req.URL.Query().Get("kind") == "guest" {
|
||||||
return handleGuestRegistration(req, r, cfg, userAPI)
|
return handleGuestRegistration(req, r, cfg, userAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve or generate the sessionID
|
|
||||||
sessionID := r.Auth.Session
|
|
||||||
if sessionID == "" {
|
|
||||||
// Generate a new, random session ID
|
|
||||||
sessionID = util.RandomString(sessionIDLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow numeric usernames less than MAX_INT64.
|
// Don't allow numeric usernames less than MAX_INT64.
|
||||||
if _, err := strconv.ParseInt(r.Username, 10, 64); err == nil {
|
if _, err := strconv.ParseInt(r.Username, 10, 64); err == nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -552,13 +560,12 @@ func Register(
|
||||||
}
|
}
|
||||||
// Auto generate a numeric username if r.Username is empty
|
// Auto generate a numeric username if r.Username is empty
|
||||||
if r.Username == "" {
|
if r.Username == "" {
|
||||||
id, err := accountDB.GetNewNumericLocalpart(req.Context())
|
res := &userapi.QueryNumericLocalpartResponse{}
|
||||||
if err != nil {
|
if err := userAPI.QueryNumericLocalpart(req.Context(), res); err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.GetNewNumericLocalpart failed")
|
util.GetLogger(req.Context()).WithError(err).Error("userAPI.QueryNumericLocalpart failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
r.Username = strconv.FormatInt(res.ID, 10)
|
||||||
r.Username = strconv.FormatInt(id, 10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this an appservice registration? It will be if the access
|
// Is this an appservice registration? It will be if the access
|
||||||
|
@ -571,7 +578,7 @@ func Register(
|
||||||
case r.Type == authtypes.LoginTypeApplicationService && accessTokenErr == nil:
|
case r.Type == authtypes.LoginTypeApplicationService && accessTokenErr == nil:
|
||||||
// Spec-compliant case (the access_token is specified and the login type
|
// Spec-compliant case (the access_token is specified and the login type
|
||||||
// is correctly set, so it's an appservice registration)
|
// is correctly set, so it's an appservice registration)
|
||||||
if resErr = validateApplicationServiceUsername(r.Username); resErr != nil {
|
if resErr := validateApplicationServiceUsername(r.Username); resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
case accessTokenErr == nil:
|
case accessTokenErr == nil:
|
||||||
|
@ -584,11 +591,11 @@ func Register(
|
||||||
default:
|
default:
|
||||||
// Spec-compliant case (neither the access_token nor the login type are
|
// Spec-compliant case (neither the access_token nor the login type are
|
||||||
// specified, so it's a normal user registration)
|
// specified, so it's a normal user registration)
|
||||||
if resErr = validateUsername(r.Username); resErr != nil {
|
if resErr := validateUsername(r.Username); resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if resErr = validatePassword(r.Password); resErr != nil {
|
if resErr := validatePassword(r.Password); resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,7 +613,7 @@ func handleGuestRegistration(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
r registerRequest,
|
r registerRequest,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserRegisterAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if cfg.RegistrationDisabled || cfg.GuestsDisabled {
|
if cfg.RegistrationDisabled || cfg.GuestsDisabled {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -671,7 +678,7 @@ func handleRegistrationFlow(
|
||||||
r registerRequest,
|
r registerRequest,
|
||||||
sessionID string,
|
sessionID string,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserRegisterAPI,
|
||||||
accessToken string,
|
accessToken string,
|
||||||
accessTokenErr error,
|
accessTokenErr error,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
@ -762,7 +769,7 @@ func handleApplicationServiceRegistration(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
r registerRequest,
|
r registerRequest,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserRegisterAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
// Check if we previously had issues extracting the access token from the
|
// Check if we previously had issues extracting the access token from the
|
||||||
// request.
|
// request.
|
||||||
|
@ -805,7 +812,7 @@ func checkAndCompleteFlow(
|
||||||
r registerRequest,
|
r registerRequest,
|
||||||
sessionID string,
|
sessionID string,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserRegisterAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if checkFlowCompleted(flow, cfg.Derived.Registration.Flows) {
|
if checkFlowCompleted(flow, cfg.Derived.Registration.Flows) {
|
||||||
policyVersion := ""
|
policyVersion := ""
|
||||||
|
@ -836,7 +843,7 @@ func checkAndCompleteFlow(
|
||||||
// not all
|
// not all
|
||||||
func completeRegistration(
|
func completeRegistration(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserRegisterAPI,
|
||||||
username, password, appserviceID, ipAddr, userAgent, sessionID, policyVersion string,
|
username, password, appserviceID, ipAddr, userAgent, sessionID, policyVersion string,
|
||||||
inhibitLogin eventutil.WeakBoolean,
|
inhibitLogin eventutil.WeakBoolean,
|
||||||
displayName, deviceID *string,
|
displayName, deviceID *string,
|
||||||
|
@ -849,24 +856,17 @@ func completeRegistration(
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if data, ok := sessions.getParams(sessionID); ok {
|
|
||||||
username = data.Username
|
|
||||||
password = data.Password
|
|
||||||
deviceID = data.DeviceID
|
|
||||||
displayName = data.InitialDisplayName
|
|
||||||
inhibitLogin = data.InhibitLogin
|
|
||||||
}
|
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.BadJSON("missing username"),
|
JSON: jsonerror.MissingArgument("Missing username"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Blank passwords are only allowed by registered application services
|
// Blank passwords are only allowed by registered application services
|
||||||
if password == "" && appserviceID == "" {
|
if password == "" && appserviceID == "" {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.BadJSON("missing password"),
|
JSON: jsonerror.MissingArgument("Missing password"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var accRes userapi.PerformAccountCreationResponse
|
var accRes userapi.PerformAccountCreationResponse
|
||||||
|
@ -1003,7 +1003,7 @@ type availableResponse struct {
|
||||||
func RegisterAvailable(
|
func RegisterAvailable(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
accountDB userdb.Database,
|
registerAPI userapi.UserRegisterAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
username := req.URL.Query().Get("username")
|
username := req.URL.Query().Get("username")
|
||||||
|
|
||||||
|
@ -1025,14 +1025,18 @@ func RegisterAvailable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
availability, availabilityErr := accountDB.CheckAccountAvailability(req.Context(), username)
|
res := &userapi.QueryAccountAvailabilityResponse{}
|
||||||
if availabilityErr != nil {
|
err := registerAPI.QueryAccountAvailability(req.Context(), &userapi.QueryAccountAvailabilityRequest{
|
||||||
|
Localpart: username,
|
||||||
|
}, res)
|
||||||
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusInternalServerError,
|
Code: http.StatusInternalServerError,
|
||||||
JSON: jsonerror.Unknown("failed to check availability: " + availabilityErr.Error()),
|
JSON: jsonerror.Unknown("failed to check availability:" + err.Error()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !availability {
|
|
||||||
|
if !res.Available {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.UserInUse("Desired User ID is already taken."),
|
JSON: jsonerror.UserInUse("Desired User ID is already taken."),
|
||||||
|
|
|
@ -99,7 +99,7 @@ func PutTag(
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syncProducer.SendData(userID, roomID, "m.tag", nil); err != nil {
|
if err = syncProducer.SendData(userID, roomID, "m.tag", nil, nil); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to send m.tag account data update to syncapi")
|
logrus.WithError(err).Error("Failed to send m.tag account data update to syncapi")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ func DeleteTag(
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: user API should do this since it's account data
|
// TODO: user API should do this since it's account data
|
||||||
if err := syncProducer.SendData(userID, roomID, "m.tag", nil); err != nil {
|
if err := syncProducer.SendData(userID, roomID, "m.tag", nil, nil); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to send m.tag account data update to syncapi")
|
logrus.WithError(err).Error("Failed to send m.tag account data update to syncapi")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,17 +26,18 @@ import (
|
||||||
clientutil "github.com/matrix-org/dendrite/clientapi/httputil"
|
clientutil "github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
eduServerAPI "github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/internal/transactions"
|
"github.com/matrix-org/dendrite/internal/transactions"
|
||||||
keyserverAPI "github.com/matrix-org/dendrite/keyserver/api"
|
keyserverAPI "github.com/matrix-org/dendrite/keyserver/api"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,21 +49,22 @@ import (
|
||||||
// nolint: gocyclo
|
// nolint: gocyclo
|
||||||
func Setup(
|
func Setup(
|
||||||
publicAPIMux, synapseAdminRouter *mux.Router, cfg *config.ClientAPI,
|
publicAPIMux, synapseAdminRouter *mux.Router, cfg *config.ClientAPI,
|
||||||
eduAPI eduServerAPI.EDUServerInputAPI,
|
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI,
|
rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
accountDB userdb.Database,
|
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserInternalAPI,
|
||||||
|
userDirectoryProvider userapi.UserDirectoryProvider,
|
||||||
federation *gomatrixserverlib.FederationClient,
|
federation *gomatrixserverlib.FederationClient,
|
||||||
syncProducer *producers.SyncAPIProducer,
|
syncProducer *producers.SyncAPIProducer,
|
||||||
transactionsCache *transactions.Cache,
|
transactionsCache *transactions.Cache,
|
||||||
federationSender federationAPI.FederationInternalAPI,
|
federationSender federationAPI.FederationInternalAPI,
|
||||||
keyAPI keyserverAPI.KeyInternalAPI,
|
keyAPI keyserverAPI.KeyInternalAPI,
|
||||||
extRoomsProvider api.ExtraPublicRoomsProvider,
|
extRoomsProvider api.ExtraPublicRoomsProvider,
|
||||||
mscCfg *config.MSCs,
|
mscCfg *config.MSCs, natsClient *nats.Conn,
|
||||||
) {
|
) {
|
||||||
|
prometheus.MustRegister(amtRegUsers, sendEventDuration)
|
||||||
|
|
||||||
rateLimits := httputil.NewRateLimits(&cfg.RateLimiting)
|
rateLimits := httputil.NewRateLimits(&cfg.RateLimiting)
|
||||||
userInteractiveAuth := auth.NewUserInteractive(accountDB, cfg)
|
userInteractiveAuth := auth.NewUserInteractive(userAPI, cfg)
|
||||||
|
|
||||||
unstableFeatures := map[string]bool{
|
unstableFeatures := map[string]bool{
|
||||||
"org.matrix.e2e_cross_signing": true,
|
"org.matrix.e2e_cross_signing": true,
|
||||||
|
@ -124,7 +126,7 @@ func Setup(
|
||||||
)
|
)
|
||||||
if cfg.Matrix.ServerNotices.Enabled {
|
if cfg.Matrix.ServerNotices.Enabled {
|
||||||
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
|
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
|
||||||
serverNotificationSender, err = getSenderDevice(context.Background(), userAPI, accountDB, cfg)
|
serverNotificationSender, err = getSenderDevice(context.Background(), userAPI, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).Fatal("unable to get account for sending sending server notices")
|
logrus.WithError(err).Fatal("unable to get account for sending sending server notices")
|
||||||
}
|
}
|
||||||
|
@ -142,7 +144,7 @@ func Setup(
|
||||||
txnID := vars["txnID"]
|
txnID := vars["txnID"]
|
||||||
return SendServerNotice(
|
return SendServerNotice(
|
||||||
req, &cfg.Matrix.ServerNotices,
|
req, &cfg.Matrix.ServerNotices,
|
||||||
cfg, userAPI, rsAPI, accountDB, asAPI,
|
cfg, userAPI, rsAPI, asAPI,
|
||||||
device, serverNotificationSender,
|
device, serverNotificationSender,
|
||||||
&txnID, transactionsCache,
|
&txnID, transactionsCache,
|
||||||
)
|
)
|
||||||
|
@ -157,7 +159,7 @@ func Setup(
|
||||||
}
|
}
|
||||||
return SendServerNotice(
|
return SendServerNotice(
|
||||||
req, &cfg.Matrix.ServerNotices,
|
req, &cfg.Matrix.ServerNotices,
|
||||||
cfg, userAPI, rsAPI, accountDB, asAPI,
|
cfg, userAPI, rsAPI, asAPI,
|
||||||
device, serverNotificationSender,
|
device, serverNotificationSender,
|
||||||
nil, transactionsCache,
|
nil, transactionsCache,
|
||||||
)
|
)
|
||||||
|
@ -180,7 +182,7 @@ func Setup(
|
||||||
logrus.Warnf("Consent tracking is enabled, but server notes are not. No server notice will be sent to users")
|
logrus.Warnf("Consent tracking is enabled, but server notes are not. No server notice will be sent to users")
|
||||||
} else {
|
} else {
|
||||||
// start a new go routine to send messages about consent
|
// start a new go routine to send messages about consent
|
||||||
go sendServerNoticeForConsent(userAPI, rsAPI, &cfg.Matrix.ServerNotices, cfg, serverNotificationSender, accountDB, asAPI)
|
go sendServerNoticeForConsent(userAPI, rsAPI, &cfg.Matrix.ServerNotices, cfg, serverNotificationSender, asAPI)
|
||||||
}
|
}
|
||||||
publicAPIMux.Handle("/consent",
|
publicAPIMux.Handle("/consent",
|
||||||
httputil.MakeHTMLAPI("consent", func(writer http.ResponseWriter, request *http.Request) *util.JSONResponse {
|
httputil.MakeHTMLAPI("consent", func(writer http.ResponseWriter, request *http.Request) *util.JSONResponse {
|
||||||
|
@ -192,9 +194,8 @@ func Setup(
|
||||||
consentRequiredCheck := httputil.WithConsentCheck(cfg.Matrix.UserConsentOptions, userAPI)
|
consentRequiredCheck := httputil.WithConsentCheck(cfg.Matrix.UserConsentOptions, userAPI)
|
||||||
|
|
||||||
v3mux.Handle("/createRoom",
|
v3mux.Handle("/createRoom",
|
||||||
|
|
||||||
httputil.MakeAuthAPI("createRoom", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAuthAPI("createRoom", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return CreateRoom(req, device, cfg, accountDB, rsAPI, asAPI)
|
return CreateRoom(req, device, cfg, userAPI, rsAPI, asAPI)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
v3mux.Handle("/join/{roomIDOrAlias}",
|
v3mux.Handle("/join/{roomIDOrAlias}",
|
||||||
|
@ -207,7 +208,7 @@ func Setup(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return JoinRoomByIDOrAlias(
|
return JoinRoomByIDOrAlias(
|
||||||
req, device, rsAPI, accountDB, vars["roomIDOrAlias"],
|
req, device, rsAPI, userAPI, vars["roomIDOrAlias"],
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
@ -223,7 +224,7 @@ func Setup(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return PeekRoomByIDOrAlias(
|
return PeekRoomByIDOrAlias(
|
||||||
req, device, rsAPI, accountDB, vars["roomIDOrAlias"],
|
req, device, rsAPI, vars["roomIDOrAlias"],
|
||||||
)
|
)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
@ -243,7 +244,7 @@ func Setup(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return JoinRoomByIDOrAlias(
|
return JoinRoomByIDOrAlias(
|
||||||
req, device, rsAPI, accountDB, vars["roomID"],
|
req, device, rsAPI, userAPI, vars["roomID"],
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
@ -268,7 +269,7 @@ func Setup(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return UnpeekRoomByID(
|
return UnpeekRoomByID(
|
||||||
req, device, rsAPI, accountDB, vars["roomID"],
|
req, device, rsAPI, vars["roomID"],
|
||||||
)
|
)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
@ -278,7 +279,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return SendBan(req, accountDB, device, vars["roomID"], cfg, rsAPI, asAPI)
|
return SendBan(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
v3mux.Handle("/rooms/{roomID}/invite",
|
v3mux.Handle("/rooms/{roomID}/invite",
|
||||||
|
@ -290,7 +291,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return SendInvite(req, accountDB, device, vars["roomID"], cfg, rsAPI, asAPI)
|
return SendInvite(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
v3mux.Handle("/rooms/{roomID}/kick",
|
v3mux.Handle("/rooms/{roomID}/kick",
|
||||||
|
@ -299,7 +300,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return SendKick(req, accountDB, device, vars["roomID"], cfg, rsAPI, asAPI)
|
return SendKick(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
v3mux.Handle("/rooms/{roomID}/unban",
|
v3mux.Handle("/rooms/{roomID}/unban",
|
||||||
|
@ -308,7 +309,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return SendUnban(req, accountDB, device, vars["roomID"], cfg, rsAPI, asAPI)
|
return SendUnban(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
v3mux.Handle("/rooms/{roomID}/send/{eventType}",
|
v3mux.Handle("/rooms/{roomID}/send/{eventType}",
|
||||||
|
@ -404,14 +405,14 @@ func Setup(
|
||||||
if r := rateLimits.Limit(req); r != nil {
|
if r := rateLimits.Limit(req); r != nil {
|
||||||
return *r
|
return *r
|
||||||
}
|
}
|
||||||
return Register(req, userAPI, accountDB, cfg)
|
return Register(req, userAPI, cfg)
|
||||||
})).Methods(http.MethodPost, http.MethodOptions)
|
})).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
v3mux.Handle("/register/available", httputil.MakeExternalAPI("registerAvailable", func(req *http.Request) util.JSONResponse {
|
v3mux.Handle("/register/available", httputil.MakeExternalAPI("registerAvailable", func(req *http.Request) util.JSONResponse {
|
||||||
if r := rateLimits.Limit(req); r != nil {
|
if r := rateLimits.Limit(req); r != nil {
|
||||||
return *r
|
return *r
|
||||||
}
|
}
|
||||||
return RegisterAvailable(req, cfg, accountDB)
|
return RegisterAvailable(req, cfg, userAPI)
|
||||||
})).Methods(http.MethodGet, http.MethodOptions)
|
})).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
v3mux.Handle("/directory/room/{roomAlias}",
|
v3mux.Handle("/directory/room/{roomAlias}",
|
||||||
|
@ -489,7 +490,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return SendTyping(req, device, vars["roomID"], vars["userID"], accountDB, eduAPI, rsAPI)
|
return SendTyping(req, device, vars["roomID"], vars["userID"], rsAPI, syncProducer)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPut, http.MethodOptions)
|
).Methods(http.MethodPut, http.MethodOptions)
|
||||||
v3mux.Handle("/rooms/{roomID}/redact/{eventID}",
|
v3mux.Handle("/rooms/{roomID}/redact/{eventID}",
|
||||||
|
@ -518,7 +519,7 @@ func Setup(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
txnID := vars["txnID"]
|
txnID := vars["txnID"]
|
||||||
return SendToDevice(req, device, eduAPI, transactionsCache, vars["eventType"], &txnID)
|
return SendToDevice(req, device, syncProducer, transactionsCache, vars["eventType"], &txnID)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPut, http.MethodOptions)
|
).Methods(http.MethodPut, http.MethodOptions)
|
||||||
|
|
||||||
|
@ -532,7 +533,7 @@ func Setup(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
txnID := vars["txnID"]
|
txnID := vars["txnID"]
|
||||||
return SendToDevice(req, device, eduAPI, transactionsCache, vars["eventType"], &txnID)
|
return SendToDevice(req, device, syncProducer, transactionsCache, vars["eventType"], &txnID)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPut, http.MethodOptions)
|
).Methods(http.MethodPut, http.MethodOptions)
|
||||||
|
|
||||||
|
@ -550,7 +551,7 @@ func Setup(
|
||||||
if r := rateLimits.Limit(req); r != nil {
|
if r := rateLimits.Limit(req); r != nil {
|
||||||
return *r
|
return *r
|
||||||
}
|
}
|
||||||
return Password(req, userAPI, accountDB, device, cfg)
|
return Password(req, userAPI, device, cfg)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
@ -570,7 +571,7 @@ func Setup(
|
||||||
if r := rateLimits.Limit(req); r != nil {
|
if r := rateLimits.Limit(req); r != nil {
|
||||||
return *r
|
return *r
|
||||||
}
|
}
|
||||||
return Login(req, accountDB, userAPI, cfg)
|
return Login(req, userAPI, cfg)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
@ -725,7 +726,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return GetProfile(req, accountDB, cfg, vars["userID"], asAPI, federation)
|
return GetProfile(req, userAPI, cfg, vars["userID"], asAPI, federation)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
@ -735,7 +736,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return GetAvatarURL(req, accountDB, cfg, vars["userID"], asAPI, federation)
|
return GetAvatarURL(req, userAPI, cfg, vars["userID"], asAPI, federation)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
@ -748,7 +749,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return SetAvatarURL(req, accountDB, device, vars["userID"], cfg, rsAPI)
|
return SetAvatarURL(req, userAPI, device, vars["userID"], cfg, rsAPI)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPut, http.MethodOptions)
|
).Methods(http.MethodPut, http.MethodOptions)
|
||||||
// Browsers use the OPTIONS HTTP method to check if the CORS policy allows
|
// Browsers use the OPTIONS HTTP method to check if the CORS policy allows
|
||||||
|
@ -760,7 +761,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return GetDisplayName(req, accountDB, cfg, vars["userID"], asAPI, federation)
|
return GetDisplayName(req, userAPI, cfg, vars["userID"], asAPI, federation)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
@ -773,7 +774,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return SetDisplayName(req, accountDB, device, vars["userID"], cfg, rsAPI)
|
return SetDisplayName(req, userAPI, device, vars["userID"], cfg, rsAPI)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPut, http.MethodOptions)
|
).Methods(http.MethodPut, http.MethodOptions)
|
||||||
// Browsers use the OPTIONS HTTP method to check if the CORS policy allows
|
// Browsers use the OPTIONS HTTP method to check if the CORS policy allows
|
||||||
|
@ -781,42 +782,28 @@ func Setup(
|
||||||
|
|
||||||
v3mux.Handle("/account/3pid",
|
v3mux.Handle("/account/3pid",
|
||||||
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return GetAssociated3PIDs(req, accountDB, device)
|
return GetAssociated3PIDs(req, userAPI, device)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
v3mux.Handle("/account/3pid",
|
v3mux.Handle("/account/3pid",
|
||||||
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return CheckAndSave3PIDAssociation(req, accountDB, device, cfg)
|
return CheckAndSave3PIDAssociation(req, userAPI, device, cfg)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
unstableMux.Handle("/account/3pid/delete",
|
unstableMux.Handle("/account/3pid/delete",
|
||||||
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return Forget3PID(req, accountDB)
|
return Forget3PID(req, userAPI)
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
v3mux.Handle("/{path:(?:account/3pid|register)}/email/requestToken",
|
v3mux.Handle("/{path:(?:account/3pid|register)}/email/requestToken",
|
||||||
httputil.MakeExternalAPI("account_3pid_request_token", func(req *http.Request) util.JSONResponse {
|
httputil.MakeExternalAPI("account_3pid_request_token", func(req *http.Request) util.JSONResponse {
|
||||||
return RequestEmailToken(req, accountDB, cfg)
|
return RequestEmailToken(req, userAPI, cfg)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
// Element logs get flooded unless this is handled
|
|
||||||
v3mux.Handle("/presence/{userID}/status",
|
|
||||||
httputil.MakeExternalAPI("presence", func(req *http.Request) util.JSONResponse {
|
|
||||||
if r := rateLimits.Limit(req); r != nil {
|
|
||||||
return *r
|
|
||||||
}
|
|
||||||
// TODO: Set presence (probably the responsibility of a presence server not clientapi)
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusOK,
|
|
||||||
JSON: struct{}{},
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
).Methods(http.MethodPut, http.MethodOptions)
|
|
||||||
|
|
||||||
v3mux.Handle("/voip/turnServer",
|
v3mux.Handle("/voip/turnServer",
|
||||||
httputil.MakeAuthAPI("turn_server", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAuthAPI("turn_server", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
if r := rateLimits.Limit(req); r != nil {
|
if r := rateLimits.Limit(req); r != nil {
|
||||||
|
@ -927,6 +914,7 @@ func Setup(
|
||||||
device,
|
device,
|
||||||
userAPI,
|
userAPI,
|
||||||
rsAPI,
|
rsAPI,
|
||||||
|
userDirectoryProvider,
|
||||||
cfg.Matrix.ServerName,
|
cfg.Matrix.ServerName,
|
||||||
postContent.SearchString,
|
postContent.SearchString,
|
||||||
postContent.Limit,
|
postContent.Limit,
|
||||||
|
@ -963,7 +951,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return SaveReadMarker(req, userAPI, rsAPI, eduAPI, syncProducer, device, vars["roomID"])
|
return SaveReadMarker(req, userAPI, rsAPI, syncProducer, device, vars["roomID"])
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
@ -980,6 +968,16 @@ func Setup(
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
v3mux.Handle("/rooms/{roomID}/upgrade",
|
||||||
|
httputil.MakeAuthAPI("rooms_upgrade", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
return UpgradeRoom(req, device, cfg, vars["roomID"], userAPI, rsAPI, asAPI)
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
v3mux.Handle("/devices",
|
v3mux.Handle("/devices",
|
||||||
httputil.MakeAuthAPI("get_devices", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAuthAPI("get_devices", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return GetDevicesByLocalpart(req, userAPI, device)
|
return GetDevicesByLocalpart(req, userAPI, device)
|
||||||
|
@ -1274,7 +1272,7 @@ func Setup(
|
||||||
// Cross-signing device keys
|
// Cross-signing device keys
|
||||||
|
|
||||||
postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, keyAPI, device, accountDB, cfg)
|
return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, keyAPI, device, userAPI, cfg)
|
||||||
}, consentRequiredCheck)
|
}, consentRequiredCheck)
|
||||||
|
|
||||||
postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
@ -1318,7 +1316,25 @@ func Setup(
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return SetReceipt(req, eduAPI, device, vars["roomId"], vars["receiptType"], vars["eventId"])
|
return SetReceipt(req, syncProducer, device, vars["roomId"], vars["receiptType"], vars["eventId"])
|
||||||
}, consentRequiredCheck),
|
}, consentRequiredCheck),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
v3mux.Handle("/presence/{userId}/status",
|
||||||
|
httputil.MakeAuthAPI("set_presence", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
return SetPresence(req, cfg, device, syncProducer, vars["userId"])
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodPut, http.MethodOptions)
|
||||||
|
v3mux.Handle("/presence/{userId}/status",
|
||||||
|
httputil.MakeAuthAPI("get_presence", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
return GetPresence(req, device, natsClient, cfg.Matrix.JetStream.Prefixed(jetstream.RequestPresence), vars["userId"])
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,10 +46,6 @@ var (
|
||||||
userRoomSendMutexes sync.Map // (roomID+userID) -> mutex. mutexes to ensure correct ordering of sendEvents
|
userRoomSendMutexes sync.Map // (roomID+userID) -> mutex. mutexes to ensure correct ordering of sendEvents
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
prometheus.MustRegister(sendEventDuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
var sendEventDuration = prometheus.NewHistogramVec(
|
var sendEventDuration = prometheus.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
prometheus.HistogramOpts{
|
||||||
Namespace: "dendrite",
|
Namespace: "dendrite",
|
||||||
|
@ -272,5 +268,24 @@ func generateSendEvent(
|
||||||
JSON: jsonerror.Forbidden(err.Error()), // TODO: Is this error string comprehensible to the client?
|
JSON: jsonerror.Forbidden(err.Error()), // TODO: Is this error string comprehensible to the client?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User should not be able to send a tombstone event to the same room.
|
||||||
|
if e.Type() == "m.room.tombstone" {
|
||||||
|
content := make(map[string]interface{})
|
||||||
|
if err = json.Unmarshal(e.Content(), &content); err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("Cannot unmarshal the event content.")
|
||||||
|
return nil, &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.BadJSON("Cannot unmarshal the event content."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if content["replacement_room"] == e.RoomID() {
|
||||||
|
return nil, &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.InvalidParam("Cannot send tombstone event that points to the same room."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return e.Event, nil
|
return e.Event, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,17 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
"github.com/matrix-org/dendrite/internal/transactions"
|
"github.com/matrix-org/dendrite/internal/transactions"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendToDevice handles PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}
|
// SendToDevice handles PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}
|
||||||
// sends the device events to the EDU Server
|
// sends the device events to the syncapi & federationsender
|
||||||
func SendToDevice(
|
func SendToDevice(
|
||||||
req *http.Request, device *userapi.Device,
|
req *http.Request, device *userapi.Device,
|
||||||
eduAPI api.EDUServerInputAPI,
|
syncProducer *producers.SyncAPIProducer,
|
||||||
txnCache *transactions.Cache,
|
txnCache *transactions.Cache,
|
||||||
eventType string, txnID *string,
|
eventType string, txnID *string,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
@ -48,8 +48,8 @@ func SendToDevice(
|
||||||
|
|
||||||
for userID, byUser := range httpReq.Messages {
|
for userID, byUser := range httpReq.Messages {
|
||||||
for deviceID, message := range byUser {
|
for deviceID, message := range byUser {
|
||||||
if err := api.SendToDevice(
|
if err := syncProducer.SendToDevice(
|
||||||
req.Context(), eduAPI, device.UserID, userID, deviceID, eventType, message,
|
req.Context(), device.UserID, userID, deviceID, eventType, message,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("eduProducer.SendToDevice failed")
|
util.GetLogger(req.Context()).WithError(err).Error("eduProducer.SendToDevice failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
|
|
@ -17,10 +17,9 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,9 +32,8 @@ type typingContentJSON struct {
|
||||||
// sends the typing events to client API typingProducer
|
// sends the typing events to client API typingProducer
|
||||||
func SendTyping(
|
func SendTyping(
|
||||||
req *http.Request, device *userapi.Device, roomID string,
|
req *http.Request, device *userapi.Device, roomID string,
|
||||||
userID string, accountDB userdb.Database,
|
userID string, rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
eduAPI api.EDUServerInputAPI,
|
syncProducer *producers.SyncAPIProducer,
|
||||||
rsAPI roomserverAPI.RoomserverInternalAPI,
|
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if device.UserID != userID {
|
if device.UserID != userID {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -57,9 +55,7 @@ func SendTyping(
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := api.SendTyping(
|
if err := syncProducer.SendTyping(req.Context(), userID, roomID, r.Typing, r.Timeout); err != nil {
|
||||||
req.Context(), eduAPI, userID, roomID, r.Typing, r.Timeout,
|
|
||||||
); err != nil {
|
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("eduProducer.Send failed")
|
util.GetLogger(req.Context()).WithError(err).Error("eduProducer.Send failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/roomserver/version"
|
"github.com/matrix-org/dendrite/roomserver/version"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrix"
|
"github.com/matrix-org/gomatrix"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/gomatrixserverlib/tokens"
|
"github.com/matrix-org/gomatrixserverlib/tokens"
|
||||||
|
@ -59,7 +58,6 @@ func SendServerNotice(
|
||||||
cfgClient *config.ClientAPI,
|
cfgClient *config.ClientAPI,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserInternalAPI,
|
||||||
rsAPI api.RoomserverInternalAPI,
|
rsAPI api.RoomserverInternalAPI,
|
||||||
accountsDB userdb.Database,
|
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
device *userapi.Device,
|
device *userapi.Device,
|
||||||
senderDevice *userapi.Device,
|
senderDevice *userapi.Device,
|
||||||
|
@ -86,7 +84,7 @@ func SendServerNotice(
|
||||||
if resErr != nil {
|
if resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
res, _ := sendServerNotice(ctx, r, rsAPI, cfgNotices, cfgClient, senderDevice, accountsDB, asAPI, userAPI, txnID, device, txnCache)
|
res, _ := sendServerNotice(ctx, r, rsAPI, cfgNotices, cfgClient, senderDevice, asAPI, userAPI, txnID, device, txnCache)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +95,6 @@ func sendServerNotice(
|
||||||
cfgNotices *config.ServerNotices,
|
cfgNotices *config.ServerNotices,
|
||||||
cfgClient *config.ClientAPI,
|
cfgClient *config.ClientAPI,
|
||||||
senderDevice *userapi.Device,
|
senderDevice *userapi.Device,
|
||||||
accountsDB userdb.Database,
|
|
||||||
asAPI appserviceAPI.AppServiceQueryAPI,
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserInternalAPI,
|
||||||
txnID *string,
|
txnID *string,
|
||||||
|
@ -110,7 +107,7 @@ func sendServerNotice(
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.BadJSON("Invalid request"),
|
JSON: jsonerror.BadJSON("Invalid request"),
|
||||||
}, nil
|
}, fmt.Errorf("invalid server notice request")
|
||||||
}
|
}
|
||||||
|
|
||||||
qryServerNoticeRoom := &userapi.QueryServerNoticeRoomResponse{}
|
qryServerNoticeRoom := &userapi.QueryServerNoticeRoomResponse{}
|
||||||
|
@ -119,11 +116,11 @@ func sendServerNotice(
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.BadJSON("Invalid request"),
|
JSON: jsonerror.BadJSON("Invalid request"),
|
||||||
}, nil
|
}, err
|
||||||
}
|
}
|
||||||
err = userAPI.SelectServerNoticeRoomID(ctx, &userapi.QueryServerNoticeRoomRequest{Localpart: localpart}, qryServerNoticeRoom)
|
err = userAPI.SelectServerNoticeRoomID(ctx, &userapi.QueryServerNoticeRoomRequest{Localpart: localpart}, qryServerNoticeRoom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err), nil
|
return util.ErrorResponse(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
senderUserID := fmt.Sprintf("@%s:%s", cfgNotices.LocalPart, cfgClient.Matrix.ServerName)
|
senderUserID := fmt.Sprintf("@%s:%s", cfgNotices.LocalPart, cfgClient.Matrix.ServerName)
|
||||||
|
@ -132,17 +129,18 @@ func sendServerNotice(
|
||||||
|
|
||||||
// create a new room for the user
|
// create a new room for the user
|
||||||
if qryServerNoticeRoom.RoomID == "" {
|
if qryServerNoticeRoom.RoomID == "" {
|
||||||
|
var pl, cc []byte
|
||||||
powerLevelContent := eventutil.InitialPowerLevelsContent(senderUserID)
|
powerLevelContent := eventutil.InitialPowerLevelsContent(senderUserID)
|
||||||
powerLevelContent.Users[serverNoticeRequest.UserID] = -10 // taken from Synapse
|
powerLevelContent.Users[serverNoticeRequest.UserID] = -10 // taken from Synapse
|
||||||
pl, err := json.Marshal(powerLevelContent)
|
pl, err = json.Marshal(powerLevelContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err), nil
|
return util.ErrorResponse(err), err
|
||||||
}
|
}
|
||||||
createContent := map[string]interface{}{}
|
createContent := map[string]interface{}{}
|
||||||
createContent["m.federate"] = false
|
createContent["m.federate"] = false
|
||||||
cc, err := json.Marshal(createContent)
|
cc, err = json.Marshal(createContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err), nil
|
return util.ErrorResponse(err), err
|
||||||
}
|
}
|
||||||
crReq := createRoomRequest{
|
crReq := createRoomRequest{
|
||||||
Invite: []string{serverNoticeRequest.UserID},
|
Invite: []string{serverNoticeRequest.UserID},
|
||||||
|
@ -155,16 +153,16 @@ func sendServerNotice(
|
||||||
PowerLevelContentOverride: pl,
|
PowerLevelContentOverride: pl,
|
||||||
}
|
}
|
||||||
|
|
||||||
roomRes := createRoom(ctx, crReq, senderDevice, cfgClient, accountsDB, rsAPI, asAPI, time.Now())
|
roomRes := createRoom(ctx, crReq, senderDevice, cfgClient, userAPI, rsAPI, asAPI, time.Now())
|
||||||
|
|
||||||
switch data := roomRes.JSON.(type) {
|
switch data := roomRes.JSON.(type) {
|
||||||
case createRoomResponse:
|
case createRoomResponse:
|
||||||
roomID = data.RoomID
|
roomID = data.RoomID
|
||||||
res := &userapi.UpdateServerNoticeRoomResponse{}
|
res := &userapi.UpdateServerNoticeRoomResponse{}
|
||||||
err := userAPI.UpdateServerNoticeRoomID(ctx, &userapi.UpdateServerNoticeRoomRequest{RoomID: roomID, Localpart: localpart}, res)
|
err = userAPI.UpdateServerNoticeRoomID(ctx, &userapi.UpdateServerNoticeRoomRequest{RoomID: roomID, Localpart: localpart}, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("UpdateServerNoticeRoomID failed")
|
util.GetLogger(ctx).WithError(err).Error("UpdateServerNoticeRoomID failed")
|
||||||
return jsonerror.InternalServerError(), nil
|
return jsonerror.InternalServerError(), err
|
||||||
}
|
}
|
||||||
// tag the room, so we can later check if the user tries to reject an invite
|
// tag the room, so we can later check if the user tries to reject an invite
|
||||||
serverAlertTag := gomatrix.TagContent{Tags: map[string]gomatrix.TagProperties{
|
serverAlertTag := gomatrix.TagContent{Tags: map[string]gomatrix.TagProperties{
|
||||||
|
@ -174,25 +172,26 @@ func sendServerNotice(
|
||||||
}}
|
}}
|
||||||
if err = saveTagData(ctx, serverNoticeRequest.UserID, roomID, userAPI, serverAlertTag); err != nil {
|
if err = saveTagData(ctx, serverNoticeRequest.UserID, roomID, userAPI, serverAlertTag); err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("saveTagData failed")
|
util.GetLogger(ctx).WithError(err).Error("saveTagData failed")
|
||||||
return jsonerror.InternalServerError(), nil
|
return jsonerror.InternalServerError(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// if we didn't get a createRoomResponse, we probably received an error, so return that.
|
// if we didn't get a createRoomResponse, we probably received an error, so return that.
|
||||||
return roomRes, nil
|
return roomRes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
res := &api.QueryMembershipForUserResponse{}
|
res := &api.QueryMembershipForUserResponse{}
|
||||||
err := rsAPI.QueryMembershipForUser(ctx, &api.QueryMembershipForUserRequest{UserID: serverNoticeRequest.UserID, RoomID: roomID}, res)
|
err = rsAPI.QueryMembershipForUser(ctx, &api.QueryMembershipForUserRequest{UserID: serverNoticeRequest.UserID, RoomID: roomID}, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err), nil
|
return util.ErrorResponse(err), err
|
||||||
}
|
}
|
||||||
// re-invite the user
|
// re-invite the user
|
||||||
if res.Membership != gomatrixserverlib.Join {
|
if res.Membership != gomatrixserverlib.Join {
|
||||||
res, err := sendInvite(ctx, accountsDB, senderDevice, roomID, serverNoticeRequest.UserID, "Server notice room", cfgClient, rsAPI, asAPI, time.Now())
|
var sendInviteRes util.JSONResponse
|
||||||
|
sendInviteRes, err = sendInvite(ctx, userAPI, senderDevice, roomID, serverNoticeRequest.UserID, "Server notice room", cfgClient, rsAPI, asAPI, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, nil
|
return sendInviteRes, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,7 +205,7 @@ func sendServerNotice(
|
||||||
e, resErr := generateSendEvent(ctx, request, senderDevice, roomID, "m.room.message", nil, cfgClient, rsAPI, time.Now())
|
e, resErr := generateSendEvent(ctx, request, senderDevice, roomID, "m.room.message", nil, cfgClient, rsAPI, time.Now())
|
||||||
if resErr != nil {
|
if resErr != nil {
|
||||||
logrus.Errorf("failed to send message: %+v", resErr)
|
logrus.Errorf("failed to send message: %+v", resErr)
|
||||||
return *resErr, nil
|
return *resErr, err
|
||||||
}
|
}
|
||||||
timeToGenerateEvent := time.Since(startedGeneratingEvent)
|
timeToGenerateEvent := time.Since(startedGeneratingEvent)
|
||||||
|
|
||||||
|
@ -233,7 +232,7 @@ func sendServerNotice(
|
||||||
false,
|
false,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("SendEvents failed")
|
util.GetLogger(ctx).WithError(err).Error("SendEvents failed")
|
||||||
return jsonerror.InternalServerError(), nil
|
return jsonerror.InternalServerError(), err
|
||||||
}
|
}
|
||||||
util.GetLogger(ctx).WithFields(logrus.Fields{
|
util.GetLogger(ctx).WithFields(logrus.Fields{
|
||||||
"event_id": e.EventID(),
|
"event_id": e.EventID(),
|
||||||
|
@ -274,7 +273,6 @@ func (r sendServerNoticeRequest) valid() (ok bool) {
|
||||||
func getSenderDevice(
|
func getSenderDevice(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserInternalAPI,
|
||||||
accountDB userdb.Database,
|
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
) (*userapi.Device, error) {
|
) (*userapi.Device, error) {
|
||||||
var accRes userapi.PerformAccountCreationResponse
|
var accRes userapi.PerformAccountCreationResponse
|
||||||
|
@ -289,8 +287,12 @@ func getSenderDevice(
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the avatarurl for the user
|
// set the avatarurl for the user
|
||||||
if err = accountDB.SetAvatarURL(ctx, cfg.Matrix.ServerNotices.LocalPart, cfg.Matrix.ServerNotices.AvatarURL); err != nil {
|
res := &userapi.PerformSetAvatarURLResponse{}
|
||||||
util.GetLogger(ctx).WithError(err).Error("accountDB.SetAvatarURL failed")
|
if err = userAPI.SetAvatarURL(ctx, &userapi.PerformSetAvatarURLRequest{
|
||||||
|
Localpart: cfg.Matrix.ServerNotices.LocalPart,
|
||||||
|
AvatarURL: cfg.Matrix.ServerNotices.AvatarURL,
|
||||||
|
}, res); err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("userAPI.SetAvatarURL failed")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ type threePIDsResponse struct {
|
||||||
// RequestEmailToken implements:
|
// RequestEmailToken implements:
|
||||||
// POST /account/3pid/email/requestToken
|
// POST /account/3pid/email/requestToken
|
||||||
// POST /register/email/requestToken
|
// POST /register/email/requestToken
|
||||||
func RequestEmailToken(req *http.Request, accountDB userdb.Database, cfg *config.ClientAPI) util.JSONResponse {
|
func RequestEmailToken(req *http.Request, threePIDAPI api.UserThreePIDAPI, cfg *config.ClientAPI) util.JSONResponse {
|
||||||
var body threepid.EmailAssociationRequest
|
var body threepid.EmailAssociationRequest
|
||||||
if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil {
|
if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil {
|
||||||
return *reqErr
|
return *reqErr
|
||||||
|
@ -50,13 +50,18 @@ func RequestEmailToken(req *http.Request, accountDB userdb.Database, cfg *config
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Check if the 3PID is already in use locally
|
// Check if the 3PID is already in use locally
|
||||||
localpart, err := accountDB.GetLocalpartForThreePID(req.Context(), body.Email, "email")
|
res := &api.QueryLocalpartForThreePIDResponse{}
|
||||||
|
err = threePIDAPI.QueryLocalpartForThreePID(req.Context(), &api.QueryLocalpartForThreePIDRequest{
|
||||||
|
ThreePID: body.Email,
|
||||||
|
Medium: "email",
|
||||||
|
}, res)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.GetLocalpartForThreePID failed")
|
util.GetLogger(req.Context()).WithError(err).Error("threePIDAPI.QueryLocalpartForThreePID failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(localpart) > 0 {
|
if len(res.Localpart) > 0 {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.MatrixError{
|
JSON: jsonerror.MatrixError{
|
||||||
|
@ -85,7 +90,7 @@ func RequestEmailToken(req *http.Request, accountDB userdb.Database, cfg *config
|
||||||
|
|
||||||
// CheckAndSave3PIDAssociation implements POST /account/3pid
|
// CheckAndSave3PIDAssociation implements POST /account/3pid
|
||||||
func CheckAndSave3PIDAssociation(
|
func CheckAndSave3PIDAssociation(
|
||||||
req *http.Request, accountDB userdb.Database, device *api.Device,
|
req *http.Request, threePIDAPI api.UserThreePIDAPI, device *api.Device,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
var body threepid.EmailAssociationCheckRequest
|
var body threepid.EmailAssociationCheckRequest
|
||||||
|
@ -136,8 +141,12 @@ func CheckAndSave3PIDAssociation(
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = accountDB.SaveThreePIDAssociation(req.Context(), address, localpart, medium); err != nil {
|
if err = threePIDAPI.PerformSaveThreePIDAssociation(req.Context(), &api.PerformSaveThreePIDAssociationRequest{
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountsDB.SaveThreePIDAssociation failed")
|
ThreePID: address,
|
||||||
|
Localpart: localpart,
|
||||||
|
Medium: medium,
|
||||||
|
}, &struct{}{}); err != nil {
|
||||||
|
util.GetLogger(req.Context()).WithError(err).Error("threePIDAPI.PerformSaveThreePIDAssociation failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +158,7 @@ func CheckAndSave3PIDAssociation(
|
||||||
|
|
||||||
// GetAssociated3PIDs implements GET /account/3pid
|
// GetAssociated3PIDs implements GET /account/3pid
|
||||||
func GetAssociated3PIDs(
|
func GetAssociated3PIDs(
|
||||||
req *http.Request, accountDB userdb.Database, device *api.Device,
|
req *http.Request, threepidAPI api.UserThreePIDAPI, device *api.Device,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID)
|
localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -157,27 +166,30 @@ func GetAssociated3PIDs(
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
threepids, err := accountDB.GetThreePIDsForLocalpart(req.Context(), localpart)
|
res := &api.QueryThreePIDsForLocalpartResponse{}
|
||||||
|
err = threepidAPI.QueryThreePIDsForLocalpart(req.Context(), &api.QueryThreePIDsForLocalpartRequest{
|
||||||
|
Localpart: localpart,
|
||||||
|
}, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.GetThreePIDsForLocalpart failed")
|
util.GetLogger(req.Context()).WithError(err).Error("threepidAPI.QueryThreePIDsForLocalpart failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
JSON: threePIDsResponse{threepids},
|
JSON: threePIDsResponse{res.ThreePIDs},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forget3PID implements POST /account/3pid/delete
|
// Forget3PID implements POST /account/3pid/delete
|
||||||
func Forget3PID(req *http.Request, accountDB userdb.Database) util.JSONResponse {
|
func Forget3PID(req *http.Request, threepidAPI api.UserThreePIDAPI) util.JSONResponse {
|
||||||
var body authtypes.ThreePID
|
var body authtypes.ThreePID
|
||||||
if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil {
|
if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil {
|
||||||
return *reqErr
|
return *reqErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := accountDB.RemoveThreePIDAssociation(req.Context(), body.Address, body.Medium); err != nil {
|
if err := threepidAPI.PerformForgetThreePID(req.Context(), &api.PerformForgetThreePIDRequest{}, &struct{}{}); err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("accountDB.RemoveThreePIDAssociation failed")
|
util.GetLogger(req.Context()).WithError(err).Error("threepidAPI.PerformForgetThreePID failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
92
clientapi/routing/upgrade_room.go
Normal file
92
clientapi/routing/upgrade_room.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/version"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type upgradeRoomRequest struct {
|
||||||
|
NewVersion string `json:"new_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type upgradeRoomResponse struct {
|
||||||
|
ReplacementRoom string `json:"replacement_room"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpgradeRoom implements /upgrade
|
||||||
|
func UpgradeRoom(
|
||||||
|
req *http.Request, device *userapi.Device,
|
||||||
|
cfg *config.ClientAPI,
|
||||||
|
roomID string, profileAPI userapi.UserProfileAPI,
|
||||||
|
rsAPI roomserverAPI.RoomserverInternalAPI,
|
||||||
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
|
) util.JSONResponse {
|
||||||
|
var r upgradeRoomRequest
|
||||||
|
if rErr := httputil.UnmarshalJSONRequest(req, &r); rErr != nil {
|
||||||
|
return *rErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the room version is supported
|
||||||
|
if _, err := version.SupportedRoomVersion(gomatrixserverlib.RoomVersion(r.NewVersion)); err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.UnsupportedRoomVersion("This server does not support that room version"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
upgradeReq := roomserverAPI.PerformRoomUpgradeRequest{
|
||||||
|
UserID: device.UserID,
|
||||||
|
RoomID: roomID,
|
||||||
|
RoomVersion: gomatrixserverlib.RoomVersion(r.NewVersion),
|
||||||
|
}
|
||||||
|
upgradeResp := roomserverAPI.PerformRoomUpgradeResponse{}
|
||||||
|
|
||||||
|
rsAPI.PerformRoomUpgrade(req.Context(), &upgradeReq, &upgradeResp)
|
||||||
|
|
||||||
|
if upgradeResp.Error != nil {
|
||||||
|
if upgradeResp.Error.Code == roomserverAPI.PerformErrorNoRoom {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("Room does not exist"),
|
||||||
|
}
|
||||||
|
} else if upgradeResp.Error.Code == roomserverAPI.PerformErrorNotAllowed {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: jsonerror.Forbidden(upgradeResp.Error.Msg),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return jsonerror.InternalServerError()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: upgradeRoomResponse{
|
||||||
|
ReplacementRoom: upgradeResp.NewRoomID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
@ -35,6 +36,7 @@ func SearchUserDirectory(
|
||||||
device *userapi.Device,
|
device *userapi.Device,
|
||||||
userAPI userapi.UserInternalAPI,
|
userAPI userapi.UserInternalAPI,
|
||||||
rsAPI api.RoomserverInternalAPI,
|
rsAPI api.RoomserverInternalAPI,
|
||||||
|
provider userapi.UserDirectoryProvider,
|
||||||
serverName gomatrixserverlib.ServerName,
|
serverName gomatrixserverlib.ServerName,
|
||||||
searchString string,
|
searchString string,
|
||||||
limit int,
|
limit int,
|
||||||
|
@ -50,13 +52,12 @@ func SearchUserDirectory(
|
||||||
}
|
}
|
||||||
|
|
||||||
// First start searching local users.
|
// First start searching local users.
|
||||||
|
|
||||||
userReq := &userapi.QuerySearchProfilesRequest{
|
userReq := &userapi.QuerySearchProfilesRequest{
|
||||||
SearchString: searchString,
|
SearchString: searchString,
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
}
|
}
|
||||||
userRes := &userapi.QuerySearchProfilesResponse{}
|
userRes := &userapi.QuerySearchProfilesResponse{}
|
||||||
if err := userAPI.QuerySearchProfiles(ctx, userReq, userRes); err != nil {
|
if err := provider.QuerySearchProfiles(ctx, userReq, userRes); err != nil {
|
||||||
errRes := util.ErrorResponse(fmt.Errorf("userAPI.QuerySearchProfiles: %w", err))
|
errRes := util.ErrorResponse(fmt.Errorf("userAPI.QuerySearchProfiles: %w", err))
|
||||||
return &errRes
|
return &errRes
|
||||||
}
|
}
|
||||||
|
@ -67,7 +68,12 @@ func SearchUserDirectory(
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
userID := fmt.Sprintf("@%s:%s", user.Localpart, serverName)
|
var userID string
|
||||||
|
if user.ServerName != "" {
|
||||||
|
userID = fmt.Sprintf("@%s:%s", user.Localpart, user.ServerName)
|
||||||
|
} else {
|
||||||
|
userID = fmt.Sprintf("@%s:%s", user.Localpart, serverName)
|
||||||
|
}
|
||||||
if _, ok := results[userID]; !ok {
|
if _, ok := results[userID]; !ok {
|
||||||
results[userID] = authtypes.FullyQualifiedProfile{
|
results[userID] = authtypes.FullyQualifiedProfile{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
|
@ -87,7 +93,7 @@ func SearchUserDirectory(
|
||||||
Limit: limit - len(results),
|
Limit: limit - len(results),
|
||||||
}
|
}
|
||||||
stateRes := &api.QueryKnownUsersResponse{}
|
stateRes := &api.QueryKnownUsersResponse{}
|
||||||
if err := rsAPI.QueryKnownUsers(ctx, stateReq, stateRes); err != nil {
|
if err := rsAPI.QueryKnownUsers(ctx, stateReq, stateRes); err != nil && err != sql.ErrNoRows {
|
||||||
errRes := util.ErrorResponse(fmt.Errorf("rsAPI.QueryKnownUsers: %w", err))
|
errRes := util.ErrorResponse(fmt.Errorf("rsAPI.QueryKnownUsers: %w", err))
|
||||||
return &errRes
|
return &errRes
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ func RequestTurnServer(req *http.Request, device *api.Device, cfg *config.Client
|
||||||
|
|
||||||
if turnConfig.SharedSecret != "" {
|
if turnConfig.SharedSecret != "" {
|
||||||
expiry := time.Now().Add(duration).Unix()
|
expiry := time.Now().Add(duration).Unix()
|
||||||
|
resp.Username = fmt.Sprintf("%d:%s", expiry, device.UserID)
|
||||||
mac := hmac.New(sha1.New, []byte(turnConfig.SharedSecret))
|
mac := hmac.New(sha1.New, []byte(turnConfig.SharedSecret))
|
||||||
_, err := mac.Write([]byte(resp.Username))
|
_, err := mac.Write([]byte(resp.Username))
|
||||||
|
|
||||||
|
@ -60,7 +61,6 @@ func RequestTurnServer(req *http.Request, device *api.Device, cfg *config.Client
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.Username = fmt.Sprintf("%d:%s", expiry, device.UserID)
|
|
||||||
resp.Password = base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
resp.Password = base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||||
} else if turnConfig.Username != "" && turnConfig.Password != "" {
|
} else if turnConfig.Username != "" && turnConfig.Password != "" {
|
||||||
resp.Username = turnConfig.Username
|
resp.Username = turnConfig.Username
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
userdb "github.com/matrix-org/dendrite/userapi/storage"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,7 +86,7 @@ var (
|
||||||
func CheckAndProcessInvite(
|
func CheckAndProcessInvite(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
device *userapi.Device, body *MembershipRequest, cfg *config.ClientAPI,
|
device *userapi.Device, body *MembershipRequest, cfg *config.ClientAPI,
|
||||||
rsAPI api.RoomserverInternalAPI, db userdb.Database,
|
rsAPI api.RoomserverInternalAPI, db userapi.UserProfileAPI,
|
||||||
roomID string,
|
roomID string,
|
||||||
evTime time.Time,
|
evTime time.Time,
|
||||||
) (inviteStoredOnIDServer bool, err error) {
|
) (inviteStoredOnIDServer bool, err error) {
|
||||||
|
@ -137,7 +136,7 @@ func CheckAndProcessInvite(
|
||||||
// Returns an error if a check or a request failed.
|
// Returns an error if a check or a request failed.
|
||||||
func queryIDServer(
|
func queryIDServer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
db userdb.Database, cfg *config.ClientAPI, device *userapi.Device,
|
db userapi.UserProfileAPI, cfg *config.ClientAPI, device *userapi.Device,
|
||||||
body *MembershipRequest, roomID string,
|
body *MembershipRequest, roomID string,
|
||||||
) (lookupRes *idServerLookupResponse, storeInviteRes *idServerStoreInviteResponse, err error) {
|
) (lookupRes *idServerLookupResponse, storeInviteRes *idServerStoreInviteResponse, err error) {
|
||||||
if err = isTrusted(body.IDServer, cfg); err != nil {
|
if err = isTrusted(body.IDServer, cfg); err != nil {
|
||||||
|
@ -206,7 +205,7 @@ func queryIDServerLookup(ctx context.Context, body *MembershipRequest) (*idServe
|
||||||
// Returns an error if the request failed to send or if the response couldn't be parsed.
|
// Returns an error if the request failed to send or if the response couldn't be parsed.
|
||||||
func queryIDServerStoreInvite(
|
func queryIDServerStoreInvite(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
db userdb.Database, cfg *config.ClientAPI, device *userapi.Device,
|
db userapi.UserProfileAPI, cfg *config.ClientAPI, device *userapi.Device,
|
||||||
body *MembershipRequest, roomID string,
|
body *MembershipRequest, roomID string,
|
||||||
) (*idServerStoreInviteResponse, error) {
|
) (*idServerStoreInviteResponse, error) {
|
||||||
// Retrieve the sender's profile to get their display name
|
// Retrieve the sender's profile to get their display name
|
||||||
|
@ -217,10 +216,17 @@ func queryIDServerStoreInvite(
|
||||||
|
|
||||||
var profile *authtypes.Profile
|
var profile *authtypes.Profile
|
||||||
if serverName == cfg.Matrix.ServerName {
|
if serverName == cfg.Matrix.ServerName {
|
||||||
profile, err = db.GetProfileByLocalpart(ctx, localpart)
|
res := &userapi.QueryProfileResponse{}
|
||||||
|
err = db.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: device.UserID}, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
profile = &authtypes.Profile{
|
||||||
|
Localpart: localpart,
|
||||||
|
DisplayName: res.DisplayName,
|
||||||
|
AvatarURL: res.AvatarURL,
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
profile = &authtypes.Profile{}
|
profile = &authtypes.Profile{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
// Copyright 2017 Vector Creations Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const usage = `Usage: %s
|
|
||||||
|
|
||||||
Create a single endpoint URL which clients can be pointed at.
|
|
||||||
|
|
||||||
The client-server API in Dendrite is split across multiple processes
|
|
||||||
which listen on multiple ports. You cannot point a Matrix client at
|
|
||||||
any of those ports, as there will be unimplemented functionality.
|
|
||||||
In addition, all client-server API processes start with the additional
|
|
||||||
path prefix '/api', which Matrix clients will be unaware of.
|
|
||||||
|
|
||||||
This tool will proxy requests for all client-server URLs and forward
|
|
||||||
them to their respective process. It will also add the '/api' path
|
|
||||||
prefix to incoming requests.
|
|
||||||
|
|
||||||
THIS TOOL IS FOR TESTING AND NOT INTENDED FOR PRODUCTION USE.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
var (
|
|
||||||
syncServerURL = flag.String("sync-api-server-url", "", "The base URL of the listening 'dendrite-sync-api-server' process. E.g. 'http://localhost:4200'")
|
|
||||||
clientAPIURL = flag.String("client-api-server-url", "", "The base URL of the listening 'dendrite-client-api-server' process. E.g. 'http://localhost:4321'")
|
|
||||||
mediaAPIURL = flag.String("media-api-server-url", "", "The base URL of the listening 'dendrite-media-api-server' process. E.g. 'http://localhost:7779'")
|
|
||||||
bindAddress = flag.String("bind-address", ":8008", "The listening port for the proxy.")
|
|
||||||
certFile = flag.String("tls-cert", "", "The PEM formatted X509 certificate to use for TLS")
|
|
||||||
keyFile = flag.String("tls-key", "", "The PEM private key to use for TLS")
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeProxy(targetURL string) (*httputil.ReverseProxy, error) {
|
|
||||||
targetURL = strings.TrimSuffix(targetURL, "/")
|
|
||||||
|
|
||||||
// Check that we can parse the URL.
|
|
||||||
_, err := url.Parse(targetURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &httputil.ReverseProxy{
|
|
||||||
Director: func(req *http.Request) {
|
|
||||||
// URL.Path() removes the % escaping from the path.
|
|
||||||
// The % encoding will be added back when the url is encoded
|
|
||||||
// when the request is forwarded.
|
|
||||||
// This means that we will lose any unessecary escaping from the URL.
|
|
||||||
// Pratically this means that any distinction between '%2F' and '/'
|
|
||||||
// in the URL will be lost by the time it reaches the target.
|
|
||||||
path := req.URL.Path
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"path": path,
|
|
||||||
"url": targetURL,
|
|
||||||
"method": req.Method,
|
|
||||||
}).Print("proxying request")
|
|
||||||
newURL, err := url.Parse(targetURL)
|
|
||||||
// Set the path separately as we need to preserve '#' characters
|
|
||||||
// that would otherwise be interpreted as being the start of a URL
|
|
||||||
// fragment.
|
|
||||||
newURL.Path += path
|
|
||||||
if err != nil {
|
|
||||||
// We already checked that we can parse the URL
|
|
||||||
// So this shouldn't ever get hit.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// Copy the query parameters from the request.
|
|
||||||
newURL.RawQuery = req.URL.RawQuery
|
|
||||||
req.URL = newURL
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Usage = func() {
|
|
||||||
fmt.Fprintf(os.Stderr, usage, os.Args[0])
|
|
||||||
flag.PrintDefaults()
|
|
||||||
}
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *syncServerURL == "" {
|
|
||||||
flag.Usage()
|
|
||||||
fmt.Fprintln(os.Stderr, "no --sync-api-server-url specified.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *clientAPIURL == "" {
|
|
||||||
flag.Usage()
|
|
||||||
fmt.Fprintln(os.Stderr, "no --client-api-server-url specified.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *mediaAPIURL == "" {
|
|
||||||
flag.Usage()
|
|
||||||
fmt.Fprintln(os.Stderr, "no --media-api-server-url specified.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
syncProxy, err := makeProxy(*syncServerURL)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
clientProxy, err := makeProxy(*clientAPIURL)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
mediaProxy, err := makeProxy(*mediaAPIURL)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
http.Handle("/_matrix/client/r0/sync", syncProxy)
|
|
||||||
http.Handle("/_matrix/media/v1/", mediaProxy)
|
|
||||||
http.Handle("/", clientProxy)
|
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Addr: *bindAddress,
|
|
||||||
ReadTimeout: 1 * time.Minute, // how long we wait for the client to send the entire request (after connection accept)
|
|
||||||
WriteTimeout: 5 * time.Minute, // how long the proxy has to write the full response
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Proxying requests to:")
|
|
||||||
fmt.Println(" /_matrix/client/r0/sync => ", *syncServerURL+"/api/_matrix/client/r0/sync")
|
|
||||||
fmt.Println(" /_matrix/media/v1 => ", *mediaAPIURL+"/api/_matrix/media/v1")
|
|
||||||
fmt.Println(" /* => ", *clientAPIURL+"/api/*")
|
|
||||||
fmt.Println("Listening on ", *bindAddress)
|
|
||||||
if *certFile != "" && *keyFile != "" {
|
|
||||||
panic(srv.ListenAndServeTLS(*certFile, *keyFile))
|
|
||||||
} else {
|
|
||||||
panic(srv.ListenAndServe())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -24,12 +24,11 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/setup"
|
||||||
"github.com/matrix-org/dendrite/setup/base"
|
"github.com/matrix-org/dendrite/setup/base"
|
||||||
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/setup"
|
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `Usage: %s
|
const usage = `Usage: %s
|
||||||
|
@ -43,7 +42,7 @@ Example:
|
||||||
# use password from file
|
# use password from file
|
||||||
%s --config dendrite.yaml -username alice -passwordfile my.pass
|
%s --config dendrite.yaml -username alice -passwordfile my.pass
|
||||||
# ask user to provide password
|
# ask user to provide password
|
||||||
%s --config dendrite.yaml -username alice -ask-pass
|
%s --config dendrite.yaml -username alice
|
||||||
# read password from stdin
|
# read password from stdin
|
||||||
%s --config dendrite.yaml -username alice -passwordstdin < my.pass
|
%s --config dendrite.yaml -username alice -passwordstdin < my.pass
|
||||||
cat my.pass | %s --config dendrite.yaml -username alice -passwordstdin
|
cat my.pass | %s --config dendrite.yaml -username alice -passwordstdin
|
||||||
|
@ -56,10 +55,10 @@ Arguments:
|
||||||
|
|
||||||
var (
|
var (
|
||||||
username = flag.String("username", "", "The username of the account to register (specify the localpart only, e.g. 'alice' for '@alice:domain.com')")
|
username = flag.String("username", "", "The username of the account to register (specify the localpart only, e.g. 'alice' for '@alice:domain.com')")
|
||||||
password = flag.String("password", "", "The password to associate with the account (optional, account will be password-less if not specified)")
|
password = flag.String("password", "", "The password to associate with the account")
|
||||||
pwdFile = flag.String("passwordfile", "", "The file to use for the password (e.g. for automated account creation)")
|
pwdFile = flag.String("passwordfile", "", "The file to use for the password (e.g. for automated account creation)")
|
||||||
pwdStdin = flag.Bool("passwordstdin", false, "Reads the password from stdin")
|
pwdStdin = flag.Bool("passwordstdin", false, "Reads the password from stdin")
|
||||||
askPass = flag.Bool("ask-pass", false, "Ask for the password to use")
|
pwdLess = flag.Bool("passwordless", false, "Create a passwordless account, e.g. if only an accesstoken is required")
|
||||||
isAdmin = flag.Bool("admin", false, "Create an admin account")
|
isAdmin = flag.Bool("admin", false, "Create an admin account")
|
||||||
resetPassword = flag.Bool("reset-password", false, "Resets the password for the given username")
|
resetPassword = flag.Bool("reset-password", false, "Resets the password for the given username")
|
||||||
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-=./]+$`)
|
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-=./]+$`)
|
||||||
|
@ -78,22 +77,44 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *pwdLess && *resetPassword {
|
||||||
|
logrus.Fatalf("Can not reset to an empty password, unable to login afterwards.")
|
||||||
|
}
|
||||||
|
|
||||||
if !validUsernameRegex.MatchString(*username) {
|
if !validUsernameRegex.MatchString(*username) {
|
||||||
logrus.Warn("Username can only contain characters a-z, 0-9, or '_-./='")
|
logrus.Warn("Username can only contain characters a-z, 0-9, or '_-./='")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pass := getPassword(password, pwdFile, pwdStdin, askPass, os.Stdin)
|
if len(fmt.Sprintf("@%s:%s", *username, cfg.Global.ServerName)) > 255 {
|
||||||
|
logrus.Fatalf("Username can not be longer than 255 characters: %s", fmt.Sprintf("@%s:%s", *username, cfg.Global.ServerName))
|
||||||
|
}
|
||||||
|
|
||||||
b := base.NewBaseDendrite(cfg, "create-account")
|
var pass string
|
||||||
|
var err error
|
||||||
|
if !*pwdLess {
|
||||||
|
pass, err = getPassword(*password, *pwdFile, *pwdStdin, os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b := base.NewBaseDendrite(cfg, "Monolith")
|
||||||
accountDB := b.CreateAccountsDB()
|
accountDB := b.CreateAccountsDB()
|
||||||
|
|
||||||
accType := api.AccountTypeUser
|
accType := api.AccountTypeUser
|
||||||
if *isAdmin {
|
if *isAdmin {
|
||||||
accType = api.AccountTypeAdmin
|
accType = api.AccountTypeAdmin
|
||||||
}
|
}
|
||||||
var err error
|
|
||||||
|
available, err := accountDB.CheckAccountAvailability(context.Background(), *username)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalln("Unable check username existence.")
|
||||||
|
}
|
||||||
if *resetPassword {
|
if *resetPassword {
|
||||||
|
if available {
|
||||||
|
logrus.Fatalln("Username could not be found.")
|
||||||
|
}
|
||||||
err = accountDB.SetPassword(context.Background(), *username, pass)
|
err = accountDB.SetPassword(context.Background(), *username, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalf("Failed to update password for user %s: %s", *username, err.Error())
|
logrus.Fatalf("Failed to update password for user %s: %s", *username, err.Error())
|
||||||
|
@ -104,6 +125,9 @@ func main() {
|
||||||
logrus.Infof("Updated password for user %s and invalidated all logins\n", *username)
|
logrus.Infof("Updated password for user %s and invalidated all logins\n", *username)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !available {
|
||||||
|
logrus.Fatalln("Username is already in use.")
|
||||||
|
}
|
||||||
|
|
||||||
policyVersion := ""
|
policyVersion := ""
|
||||||
if cfg.Global.UserConsentOptions.Enabled {
|
if cfg.Global.UserConsentOptions.Enabled {
|
||||||
|
@ -118,53 +142,44 @@ func main() {
|
||||||
logrus.Infoln("Created account", *username)
|
logrus.Infoln("Created account", *username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPassword(password, pwdFile *string, pwdStdin, askPass *bool, r io.Reader) string {
|
func getPassword(password, pwdFile string, pwdStdin bool, r io.Reader) (string, error) {
|
||||||
// no password option set, use empty password
|
|
||||||
if password == nil && pwdFile == nil && pwdStdin == nil && askPass == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
// password defined as parameter
|
|
||||||
if password != nil && *password != "" {
|
|
||||||
return *password
|
|
||||||
}
|
|
||||||
|
|
||||||
// read password from file
|
// read password from file
|
||||||
if pwdFile != nil && *pwdFile != "" {
|
if pwdFile != "" {
|
||||||
pw, err := ioutil.ReadFile(*pwdFile)
|
pw, err := ioutil.ReadFile(pwdFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalln("Unable to read password from file:", err)
|
return "", fmt.Errorf("Unable to read password from file: %v", err)
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(pw))
|
return strings.TrimSpace(string(pw)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// read password from stdin
|
// read password from stdin
|
||||||
if pwdStdin != nil && *pwdStdin {
|
if pwdStdin {
|
||||||
data, err := ioutil.ReadAll(r)
|
data, err := ioutil.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalln("Unable to read password from stdin:", err)
|
return "", fmt.Errorf("Unable to read password from stdin: %v", err)
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(data))
|
return strings.TrimSpace(string(data)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ask the user to provide the password
|
// If no parameter was set, ask the user to provide the password
|
||||||
if *askPass {
|
if password == "" {
|
||||||
fmt.Print("Enter Password: ")
|
fmt.Print("Enter Password: ")
|
||||||
bytePassword, err := term.ReadPassword(int(os.Stdin.Fd()))
|
bytePassword, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalln("Unable to read password:", err)
|
return "", fmt.Errorf("Unable to read password: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Print("Confirm Password: ")
|
fmt.Print("Confirm Password: ")
|
||||||
bytePassword2, err := term.ReadPassword(int(os.Stdin.Fd()))
|
bytePassword2, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalln("Unable to read password:", err)
|
return "", fmt.Errorf("Unable to read password: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
if strings.TrimSpace(string(bytePassword)) != strings.TrimSpace(string(bytePassword2)) {
|
if strings.TrimSpace(string(bytePassword)) != strings.TrimSpace(string(bytePassword2)) {
|
||||||
logrus.Fatalln("Entered passwords don't match")
|
return "", fmt.Errorf("Entered passwords don't match")
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(bytePassword))
|
return strings.TrimSpace(string(bytePassword)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return password, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,45 +8,48 @@ import (
|
||||||
|
|
||||||
func Test_getPassword(t *testing.T) {
|
func Test_getPassword(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
password *string
|
password string
|
||||||
pwdFile *string
|
pwdFile string
|
||||||
pwdStdin *bool
|
pwdStdin bool
|
||||||
askPass *bool
|
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
pass := "mySecretPass"
|
pass := "mySecretPass"
|
||||||
passwordFile := "testdata/my.pass"
|
passwordFile := "testdata/my.pass"
|
||||||
passwordStdin := true
|
|
||||||
reader := &bytes.Buffer{}
|
reader := &bytes.Buffer{}
|
||||||
_, err := reader.WriteString(pass)
|
_, err := reader.WriteString(pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unable to write to buffer: %+v", err)
|
t.Errorf("unable to write to buffer: %+v", err)
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want string
|
want string
|
||||||
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
name: "no password defined",
|
|
||||||
args: args{},
|
|
||||||
want: "",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "password defined",
|
name: "password defined",
|
||||||
args: args{password: &pass},
|
args: args{
|
||||||
|
password: pass,
|
||||||
|
},
|
||||||
want: pass,
|
want: pass,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pwdFile defined",
|
name: "pwdFile defined",
|
||||||
args: args{pwdFile: &passwordFile},
|
args: args{
|
||||||
|
pwdFile: passwordFile,
|
||||||
|
},
|
||||||
want: pass,
|
want: pass,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "pwdFile does not exist",
|
||||||
|
args: args{pwdFile: "iDontExist"},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "read pass from stdin defined",
|
name: "read pass from stdin defined",
|
||||||
args: args{
|
args: args{
|
||||||
pwdStdin: &passwordStdin,
|
pwdStdin: true,
|
||||||
reader: reader,
|
reader: reader,
|
||||||
},
|
},
|
||||||
want: pass,
|
want: pass,
|
||||||
|
@ -54,7 +57,11 @@ func Test_getPassword(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := getPassword(tt.args.password, tt.args.pwdFile, tt.args.pwdStdin, tt.args.askPass, tt.args.reader); got != tt.want {
|
got, err := getPassword(tt.args.password, tt.args.pwdFile, tt.args.pwdStdin, tt.args.reader)
|
||||||
|
if !tt.wantErr && err != nil {
|
||||||
|
t.Errorf("expected no error, but got %v", err)
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
t.Errorf("getPassword() = '%v', want '%v'", got, tt.want)
|
t.Errorf("getPassword() = '%v', want '%v'", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
p2pdisc "github.com/libp2p/go-libp2p/p2p/discovery"
|
p2pdisc "github.com/libp2p/go-libp2p/p2p/discovery"
|
||||||
"github.com/matrix-org/dendrite/appservice"
|
"github.com/matrix-org/dendrite/appservice"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/embed"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/embed"
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi"
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/keyserver"
|
"github.com/matrix-org/dendrite/keyserver"
|
||||||
|
@ -40,8 +39,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/userapi"
|
"github.com/matrix-org/dendrite/userapi"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
@ -152,9 +149,6 @@ func main() {
|
||||||
userAPI := userapi.NewInternalAPI(&base.Base, accountDB, &cfg.UserAPI, nil, keyAPI, rsAPI, base.Base.PushGatewayHTTPClient())
|
userAPI := userapi.NewInternalAPI(&base.Base, accountDB, &cfg.UserAPI, nil, keyAPI, rsAPI, base.Base.PushGatewayHTTPClient())
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
eduInputAPI := eduserver.NewInternalAPI(
|
|
||||||
&base.Base, cache.New(), userAPI,
|
|
||||||
)
|
|
||||||
asAPI := appservice.NewInternalAPI(&base.Base, userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(&base.Base, userAPI, rsAPI)
|
||||||
rsAPI.SetAppserviceAPI(asAPI)
|
rsAPI.SetAppserviceAPI(asAPI)
|
||||||
fsAPI := federationapi.NewInternalAPI(
|
fsAPI := federationapi.NewInternalAPI(
|
||||||
|
@ -180,7 +174,6 @@ func main() {
|
||||||
KeyRing: keyRing,
|
KeyRing: keyRing,
|
||||||
|
|
||||||
AppserviceAPI: asAPI,
|
AppserviceAPI: asAPI,
|
||||||
EDUInternalAPI: eduInputAPI,
|
|
||||||
FederationAPI: fsAPI,
|
FederationAPI: fsAPI,
|
||||||
RoomserverAPI: rsAPI,
|
RoomserverAPI: rsAPI,
|
||||||
UserAPI: userAPI,
|
UserAPI: userAPI,
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -53,21 +67,22 @@ func (y *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTransport(s *pineconeSessions.Sessions) *http.Transport {
|
func createTransport(s *pineconeSessions.Sessions) *http.Transport {
|
||||||
|
proto := s.Protocol("matrix")
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
DisableKeepAlives: false,
|
DisableKeepAlives: false,
|
||||||
Dial: s.Dial,
|
Dial: proto.Dial,
|
||||||
DialContext: s.DialContext,
|
DialContext: proto.DialContext,
|
||||||
DialTLS: s.DialTLS,
|
DialTLS: proto.DialTLS,
|
||||||
DialTLSContext: s.DialTLSContext,
|
DialTLSContext: proto.DialTLSContext,
|
||||||
}
|
}
|
||||||
tr.RegisterProtocol(
|
tr.RegisterProtocol(
|
||||||
"matrix", &RoundTripper{
|
"matrix", &RoundTripper{
|
||||||
inner: &http.Transport{
|
inner: &http.Transport{
|
||||||
DisableKeepAlives: false,
|
DisableKeepAlives: false,
|
||||||
Dial: s.Dial,
|
Dial: proto.Dial,
|
||||||
DialContext: s.DialContext,
|
DialContext: proto.DialContext,
|
||||||
DialTLS: s.DialTLS,
|
DialTLS: proto.DialTLS,
|
||||||
DialTLSContext: s.DialTLSContext,
|
DialTLSContext: proto.DialTLSContext,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// Copyright 2017-2018 New Vector Ltd
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
|
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -13,26 +12,10 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package sqlite3
|
package defaults
|
||||||
|
|
||||||
import (
|
import "github.com/matrix-org/gomatrixserverlib"
|
||||||
"database/sql"
|
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
var DefaultServerNames = map[gomatrixserverlib.ServerName]struct{}{
|
||||||
)
|
"3bf0258d23c60952639cc4c69c71d1508a7d43a0475d9000ff900a1848411ec7": {},
|
||||||
|
|
||||||
type statements struct {
|
|
||||||
media mediaStatements
|
|
||||||
thumbnail thumbnailStatements
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statements) prepare(db *sql.DB, writer sqlutil.Writer) (err error) {
|
|
||||||
if err = s.media.prepare(db, writer); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = s.thumbnail.prepare(db, writer); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
|
@ -1,3 +1,17 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
//go:build elementweb
|
//go:build elementweb
|
||||||
// +build elementweb
|
// +build elementweb
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
//go:build !elementweb
|
//go:build !elementweb
|
||||||
// +build !elementweb
|
// +build !elementweb
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -22,11 +22,9 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
@ -35,11 +33,9 @@ import (
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/embed"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/embed"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
|
||||||
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/users"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi"
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
"github.com/matrix-org/dendrite/federationapi/api"
|
|
||||||
"github.com/matrix-org/dendrite/internal"
|
"github.com/matrix-org/dendrite/internal"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/keyserver"
|
"github.com/matrix-org/dendrite/keyserver"
|
||||||
|
@ -50,6 +46,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/userapi"
|
"github.com/matrix-org/dendrite/userapi"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
pineconeConnections "github.com/matrix-org/pinecone/connections"
|
||||||
pineconeMulticast "github.com/matrix-org/pinecone/multicast"
|
pineconeMulticast "github.com/matrix-org/pinecone/multicast"
|
||||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||||
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||||
|
@ -92,8 +89,14 @@ func main() {
|
||||||
pk = sk.Public().(ed25519.PublicKey)
|
pk = sk.Public().(ed25519.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := log.New(os.Stdout, "", 0)
|
pRouter := pineconeRouter.NewRouter(logrus.WithField("pinecone", "router"), sk, false)
|
||||||
pRouter := pineconeRouter.NewRouter(logger, sk, false)
|
pQUIC := pineconeSessions.NewSessions(logrus.WithField("pinecone", "sessions"), pRouter, []string{"matrix"})
|
||||||
|
pMulticast := pineconeMulticast.NewMulticast(logrus.WithField("pinecone", "multicast"), pRouter)
|
||||||
|
pManager := pineconeConnections.NewConnectionManager(pRouter)
|
||||||
|
pMulticast.Start()
|
||||||
|
if instancePeer != nil && *instancePeer != "" {
|
||||||
|
pManager.AddPeer(*instancePeer)
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
listener, err := net.Listen("tcp", *instanceListen)
|
listener, err := net.Listen("tcp", *instanceListen)
|
||||||
|
@ -123,36 +126,6 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
pQUIC := pineconeSessions.NewSessions(logger, pRouter)
|
|
||||||
pMulticast := pineconeMulticast.NewMulticast(logger, pRouter)
|
|
||||||
pMulticast.Start()
|
|
||||||
|
|
||||||
connectToStaticPeer := func() {
|
|
||||||
connected := map[string]bool{} // URI -> connected?
|
|
||||||
for _, uri := range strings.Split(*instancePeer, ",") {
|
|
||||||
connected[strings.TrimSpace(uri)] = false
|
|
||||||
}
|
|
||||||
attempt := func() {
|
|
||||||
for k := range connected {
|
|
||||||
connected[k] = false
|
|
||||||
}
|
|
||||||
for _, info := range pRouter.Peers() {
|
|
||||||
connected[info.URI] = true
|
|
||||||
}
|
|
||||||
for k, online := range connected {
|
|
||||||
if !online {
|
|
||||||
if err := conn.ConnectToPeer(pRouter, k); err != nil {
|
|
||||||
logrus.WithError(err).Error("Failed to connect to static peer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
attempt()
|
|
||||||
time.Sleep(time.Second * 5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := &config.Dendrite{}
|
cfg := &config.Dendrite{}
|
||||||
cfg.Defaults(true)
|
cfg.Defaults(true)
|
||||||
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
||||||
|
@ -190,14 +163,13 @@ func main() {
|
||||||
userAPI := userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, nil, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
userAPI := userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, nil, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
eduInputAPI := eduserver.NewInternalAPI(
|
|
||||||
base, cache.New(), userAPI,
|
|
||||||
)
|
|
||||||
|
|
||||||
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
|
||||||
|
|
||||||
rsComponent.SetFederationAPI(fsAPI, keyRing)
|
rsComponent.SetFederationAPI(fsAPI, keyRing)
|
||||||
|
|
||||||
|
userProvider := users.NewPineconeUserProvider(pRouter, pQUIC, userAPI, federation)
|
||||||
|
roomProvider := rooms.NewPineconeRoomProvider(pRouter, pQUIC, fsAPI, federation)
|
||||||
|
|
||||||
monolith := setup.Monolith{
|
monolith := setup.Monolith{
|
||||||
Config: base.Cfg,
|
Config: base.Cfg,
|
||||||
AccountDB: accountDB,
|
AccountDB: accountDB,
|
||||||
|
@ -205,13 +177,13 @@ func main() {
|
||||||
FedClient: federation,
|
FedClient: federation,
|
||||||
KeyRing: keyRing,
|
KeyRing: keyRing,
|
||||||
|
|
||||||
AppserviceAPI: asAPI,
|
AppserviceAPI: asAPI,
|
||||||
EDUInternalAPI: eduInputAPI,
|
FederationAPI: fsAPI,
|
||||||
FederationAPI: fsAPI,
|
RoomserverAPI: rsAPI,
|
||||||
RoomserverAPI: rsAPI,
|
UserAPI: userAPI,
|
||||||
UserAPI: userAPI,
|
KeyAPI: keyAPI,
|
||||||
KeyAPI: keyAPI,
|
ExtPublicRoomsProvider: roomProvider,
|
||||||
ExtPublicRoomsProvider: rooms.NewPineconeRoomProvider(pRouter, pQUIC, fsAPI, federation),
|
ExtUserDirectoryProvider: userProvider,
|
||||||
}
|
}
|
||||||
monolith.AddAllPublicRoutes(
|
monolith.AddAllPublicRoutes(
|
||||||
base.ProcessContext,
|
base.ProcessContext,
|
||||||
|
@ -247,13 +219,16 @@ func main() {
|
||||||
logrus.WithError(err).Error("Failed to connect WebSocket peer to Pinecone switch")
|
logrus.WithError(err).Error("Failed to connect WebSocket peer to Pinecone switch")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
httpRouter.HandleFunc("/pinecone", pRouter.ManholeHandler)
|
||||||
embed.Embed(httpRouter, *instancePort, "Pinecone Demo")
|
embed.Embed(httpRouter, *instancePort, "Pinecone Demo")
|
||||||
|
|
||||||
pMux := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
pMux := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||||
|
pMux.PathPrefix(users.PublicURL).HandlerFunc(userProvider.FederatedUserProfiles)
|
||||||
pMux.PathPrefix(httputil.PublicFederationPathPrefix).Handler(base.PublicFederationAPIMux)
|
pMux.PathPrefix(httputil.PublicFederationPathPrefix).Handler(base.PublicFederationAPIMux)
|
||||||
pMux.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
pMux.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
||||||
|
|
||||||
pHTTP := pQUIC.HTTP()
|
pHTTP := pQUIC.Protocol("matrix").HTTP()
|
||||||
|
pHTTP.Mux().Handle(users.PublicURL, pMux)
|
||||||
pHTTP.Mux().Handle(httputil.PublicFederationPathPrefix, pMux)
|
pHTTP.Mux().Handle(httputil.PublicFederationPathPrefix, pMux)
|
||||||
pHTTP.Mux().Handle(httputil.PublicMediaPathPrefix, pMux)
|
pHTTP.Mux().Handle(httputil.PublicMediaPathPrefix, pMux)
|
||||||
|
|
||||||
|
@ -270,25 +245,16 @@ func main() {
|
||||||
Handler: pMux,
|
Handler: pMux,
|
||||||
}
|
}
|
||||||
|
|
||||||
go connectToStaticPeer()
|
|
||||||
go func() {
|
go func() {
|
||||||
pubkey := pRouter.PublicKey()
|
pubkey := pRouter.PublicKey()
|
||||||
logrus.Info("Listening on ", hex.EncodeToString(pubkey[:]))
|
logrus.Info("Listening on ", hex.EncodeToString(pubkey[:]))
|
||||||
logrus.Fatal(httpServer.Serve(pQUIC))
|
logrus.Fatal(httpServer.Serve(pQUIC.Protocol("matrix")))
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
httpBindAddr := fmt.Sprintf(":%d", *instancePort)
|
httpBindAddr := fmt.Sprintf(":%d", *instancePort)
|
||||||
logrus.Info("Listening on ", httpBindAddr)
|
logrus.Info("Listening on ", httpBindAddr)
|
||||||
logrus.Fatal(http.ListenAndServe(httpBindAddr, httpRouter))
|
logrus.Fatal(http.ListenAndServe(httpBindAddr, httpRouter))
|
||||||
}()
|
}()
|
||||||
go func() {
|
|
||||||
logrus.Info("Sending wake-up message to known nodes")
|
|
||||||
req := &api.PerformBroadcastEDURequest{}
|
|
||||||
res := &api.PerformBroadcastEDUResponse{}
|
|
||||||
if err := fsAPI.PerformBroadcastEDU(context.TODO(), req, res); err != nil {
|
|
||||||
logrus.WithError(err).Error("Failed to send wake-up message to known nodes")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
base.WaitForShutdown()
|
base.WaitForShutdown()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -19,6 +19,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/defaults"
|
||||||
"github.com/matrix-org/dendrite/federationapi/api"
|
"github.com/matrix-org/dendrite/federationapi/api"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
@ -50,9 +51,12 @@ func NewPineconeRoomProvider(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PineconeRoomProvider) Rooms() []gomatrixserverlib.PublicRoom {
|
func (p *PineconeRoomProvider) Rooms() []gomatrixserverlib.PublicRoom {
|
||||||
list := []gomatrixserverlib.ServerName{}
|
list := map[gomatrixserverlib.ServerName]struct{}{}
|
||||||
|
for k := range defaults.DefaultServerNames {
|
||||||
|
list[k] = struct{}{}
|
||||||
|
}
|
||||||
for _, k := range p.r.Peers() {
|
for _, k := range p.r.Peers() {
|
||||||
list = append(list, gomatrixserverlib.ServerName(k.PublicKey))
|
list[gomatrixserverlib.ServerName(k.PublicKey)] = struct{}{}
|
||||||
}
|
}
|
||||||
return bulkFetchPublicRoomsFromServers(context.Background(), p.fedClient, list)
|
return bulkFetchPublicRoomsFromServers(context.Background(), p.fedClient, list)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +65,7 @@ func (p *PineconeRoomProvider) Rooms() []gomatrixserverlib.PublicRoom {
|
||||||
// Returns a list of public rooms.
|
// Returns a list of public rooms.
|
||||||
func bulkFetchPublicRoomsFromServers(
|
func bulkFetchPublicRoomsFromServers(
|
||||||
ctx context.Context, fedClient *gomatrixserverlib.FederationClient,
|
ctx context.Context, fedClient *gomatrixserverlib.FederationClient,
|
||||||
homeservers []gomatrixserverlib.ServerName,
|
homeservers map[gomatrixserverlib.ServerName]struct{},
|
||||||
) (publicRooms []gomatrixserverlib.PublicRoom) {
|
) (publicRooms []gomatrixserverlib.PublicRoom) {
|
||||||
limit := 200
|
limit := 200
|
||||||
// follow pipeline semantics, see https://blog.golang.org/pipelines for more info.
|
// follow pipeline semantics, see https://blog.golang.org/pipelines for more info.
|
||||||
|
@ -74,7 +78,7 @@ func bulkFetchPublicRoomsFromServers(
|
||||||
wg.Add(len(homeservers))
|
wg.Add(len(homeservers))
|
||||||
// concurrently query for public rooms
|
// concurrently query for public rooms
|
||||||
reqctx, reqcancel := context.WithTimeout(ctx, time.Second*5)
|
reqctx, reqcancel := context.WithTimeout(ctx, time.Second*5)
|
||||||
for _, hs := range homeservers {
|
for hs := range homeservers {
|
||||||
go func(homeserverDomain gomatrixserverlib.ServerName) {
|
go func(homeserverDomain gomatrixserverlib.ServerName) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
util.GetLogger(reqctx).WithField("hs", homeserverDomain).Info("Querying HS for public rooms")
|
util.GetLogger(reqctx).WithField("hs", homeserverDomain).Info("Querying HS for public rooms")
|
||||||
|
|
163
cmd/dendrite-demo-pinecone/users/users.go
Normal file
163
cmd/dendrite-demo-pinecone/users/users.go
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
clienthttputil "github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/defaults"
|
||||||
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
|
||||||
|
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||||
|
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PineconeUserProvider struct {
|
||||||
|
r *pineconeRouter.Router
|
||||||
|
s *pineconeSessions.Sessions
|
||||||
|
userAPI userapi.UserProfileAPI
|
||||||
|
fedClient *gomatrixserverlib.FederationClient
|
||||||
|
}
|
||||||
|
|
||||||
|
const PublicURL = "/_matrix/p2p/profiles"
|
||||||
|
|
||||||
|
func NewPineconeUserProvider(
|
||||||
|
r *pineconeRouter.Router,
|
||||||
|
s *pineconeSessions.Sessions,
|
||||||
|
userAPI userapi.UserProfileAPI,
|
||||||
|
fedClient *gomatrixserverlib.FederationClient,
|
||||||
|
) *PineconeUserProvider {
|
||||||
|
p := &PineconeUserProvider{
|
||||||
|
r: r,
|
||||||
|
s: s,
|
||||||
|
userAPI: userAPI,
|
||||||
|
fedClient: fedClient,
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PineconeUserProvider) FederatedUserProfiles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &userapi.QuerySearchProfilesRequest{Limit: 25}
|
||||||
|
res := &userapi.QuerySearchProfilesResponse{}
|
||||||
|
if err := clienthttputil.UnmarshalJSONRequest(r, &req); err != nil {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := p.userAPI.QuerySearchProfiles(r.Context(), req, res); err != nil {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
j, err := json.Marshal(res)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(200)
|
||||||
|
_, _ = w.Write(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PineconeUserProvider) QuerySearchProfiles(ctx context.Context, req *userapi.QuerySearchProfilesRequest, res *userapi.QuerySearchProfilesResponse) error {
|
||||||
|
list := map[gomatrixserverlib.ServerName]struct{}{}
|
||||||
|
for k := range defaults.DefaultServerNames {
|
||||||
|
list[k] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, k := range p.r.Peers() {
|
||||||
|
list[gomatrixserverlib.ServerName(k.PublicKey)] = struct{}{}
|
||||||
|
}
|
||||||
|
res.Profiles = bulkFetchUserDirectoriesFromServers(context.Background(), req, p.fedClient, list)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bulkFetchUserDirectoriesFromServers fetches users from the list of homeservers.
|
||||||
|
// Returns a list of user profiles.
|
||||||
|
func bulkFetchUserDirectoriesFromServers(
|
||||||
|
ctx context.Context, req *userapi.QuerySearchProfilesRequest,
|
||||||
|
fedClient *gomatrixserverlib.FederationClient,
|
||||||
|
homeservers map[gomatrixserverlib.ServerName]struct{},
|
||||||
|
) (profiles []authtypes.Profile) {
|
||||||
|
jsonBody, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
limit := 200
|
||||||
|
// follow pipeline semantics, see https://blog.golang.org/pipelines for more info.
|
||||||
|
// goroutines send rooms to this channel
|
||||||
|
profileCh := make(chan authtypes.Profile, int(limit))
|
||||||
|
// signalling channel to tell goroutines to stop sending rooms and quit
|
||||||
|
done := make(chan bool)
|
||||||
|
// signalling to say when we can close the room channel
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(len(homeservers))
|
||||||
|
// concurrently query for public rooms
|
||||||
|
reqctx, reqcancel := context.WithTimeout(ctx, time.Second*5)
|
||||||
|
for hs := range homeservers {
|
||||||
|
go func(homeserverDomain gomatrixserverlib.ServerName) {
|
||||||
|
defer wg.Done()
|
||||||
|
util.GetLogger(reqctx).WithField("hs", homeserverDomain).Info("Querying HS for users")
|
||||||
|
|
||||||
|
jsonBodyReader := bytes.NewBuffer(jsonBody)
|
||||||
|
httpReq, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("matrix://%s%s", homeserverDomain, PublicURL), jsonBodyReader)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(reqctx).WithError(err).WithField("hs", homeserverDomain).Warn(
|
||||||
|
"bulkFetchUserDirectoriesFromServers: failed to create request",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
res := &userapi.QuerySearchProfilesResponse{}
|
||||||
|
if err = fedClient.DoRequestAndParseResponse(reqctx, httpReq, res); err != nil {
|
||||||
|
util.GetLogger(reqctx).WithError(err).WithField("hs", homeserverDomain).Warn(
|
||||||
|
"bulkFetchUserDirectoriesFromServers: failed to query hs",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, profile := range res.Profiles {
|
||||||
|
profile.ServerName = string(homeserverDomain)
|
||||||
|
// atomically send a room or stop
|
||||||
|
select {
|
||||||
|
case profileCh <- profile:
|
||||||
|
case <-done:
|
||||||
|
case <-reqctx.Done():
|
||||||
|
util.GetLogger(reqctx).WithError(err).WithField("hs", homeserverDomain).Info("Interrupted whilst sending profiles")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(hs)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
default:
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
reqcancel()
|
||||||
|
close(done)
|
||||||
|
close(profileCh)
|
||||||
|
|
||||||
|
for profile := range profileCh {
|
||||||
|
profiles = append(profiles, profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
return profiles
|
||||||
|
}
|
|
@ -32,8 +32,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggrooms"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggrooms"
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi"
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
"github.com/matrix-org/dendrite/federationapi/api"
|
"github.com/matrix-org/dendrite/federationapi/api"
|
||||||
"github.com/matrix-org/dendrite/internal"
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
@ -120,10 +118,6 @@ func main() {
|
||||||
userAPI := userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, nil, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
userAPI := userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, nil, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
eduInputAPI := eduserver.NewInternalAPI(
|
|
||||||
base, cache.New(), userAPI,
|
|
||||||
)
|
|
||||||
|
|
||||||
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
|
||||||
rsAPI.SetAppserviceAPI(asAPI)
|
rsAPI.SetAppserviceAPI(asAPI)
|
||||||
fsAPI := federationapi.NewInternalAPI(
|
fsAPI := federationapi.NewInternalAPI(
|
||||||
|
@ -139,12 +133,11 @@ func main() {
|
||||||
FedClient: federation,
|
FedClient: federation,
|
||||||
KeyRing: keyRing,
|
KeyRing: keyRing,
|
||||||
|
|
||||||
AppserviceAPI: asAPI,
|
AppserviceAPI: asAPI,
|
||||||
EDUInternalAPI: eduInputAPI,
|
FederationAPI: fsAPI,
|
||||||
FederationAPI: fsAPI,
|
RoomserverAPI: rsAPI,
|
||||||
RoomserverAPI: rsAPI,
|
UserAPI: userAPI,
|
||||||
UserAPI: userAPI,
|
KeyAPI: keyAPI,
|
||||||
KeyAPI: keyAPI,
|
|
||||||
ExtPublicRoomsProvider: yggrooms.NewYggdrasilRoomProvider(
|
ExtPublicRoomsProvider: yggrooms.NewYggdrasilRoomProvider(
|
||||||
ygg, fsAPI, federation,
|
ygg, fsAPI, federation,
|
||||||
),
|
),
|
||||||
|
|
|
@ -26,7 +26,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/lucas-clemente/quic-go"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/neilalexander/utp"
|
"github.com/neilalexander/utp"
|
||||||
|
|
||||||
|
@ -44,7 +43,6 @@ type Node struct {
|
||||||
config *yggdrasilconfig.NodeConfig
|
config *yggdrasilconfig.NodeConfig
|
||||||
multicast *yggdrasilmulticast.Multicast
|
multicast *yggdrasilmulticast.Multicast
|
||||||
log *gologme.Logger
|
log *gologme.Logger
|
||||||
listener quic.Listener
|
|
||||||
utpSocket *utp.Socket
|
utpSocket *utp.Socket
|
||||||
incoming chan net.Conn
|
incoming chan net.Conn
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,12 +37,12 @@ func (n *Node) Accept() (net.Conn, error) {
|
||||||
|
|
||||||
// Implements net.Listener
|
// Implements net.Listener
|
||||||
func (n *Node) Close() error {
|
func (n *Node) Close() error {
|
||||||
return n.listener.Close()
|
return n.utpSocket.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements net.Listener
|
// Implements net.Listener
|
||||||
func (n *Node) Addr() net.Addr {
|
func (n *Node) Addr() net.Addr {
|
||||||
return n.listener.Addr()
|
return n.utpSocket.Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements http.Transport.Dial
|
// Implements http.Transport.Dial
|
||||||
|
|
|
@ -19,8 +19,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/appservice"
|
"github.com/matrix-org/dendrite/appservice"
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi"
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
"github.com/matrix-org/dendrite/keyserver"
|
"github.com/matrix-org/dendrite/keyserver"
|
||||||
"github.com/matrix-org/dendrite/roomserver"
|
"github.com/matrix-org/dendrite/roomserver"
|
||||||
|
@ -61,7 +59,6 @@ func main() {
|
||||||
// itself.
|
// itself.
|
||||||
cfg.AppServiceAPI.InternalAPI.Connect = httpAPIAddr
|
cfg.AppServiceAPI.InternalAPI.Connect = httpAPIAddr
|
||||||
cfg.ClientAPI.InternalAPI.Connect = httpAPIAddr
|
cfg.ClientAPI.InternalAPI.Connect = httpAPIAddr
|
||||||
cfg.EDUServer.InternalAPI.Connect = httpAPIAddr
|
|
||||||
cfg.FederationAPI.InternalAPI.Connect = httpAPIAddr
|
cfg.FederationAPI.InternalAPI.Connect = httpAPIAddr
|
||||||
cfg.KeyServer.InternalAPI.Connect = httpAPIAddr
|
cfg.KeyServer.InternalAPI.Connect = httpAPIAddr
|
||||||
cfg.MediaAPI.InternalAPI.Connect = httpAPIAddr
|
cfg.MediaAPI.InternalAPI.Connect = httpAPIAddr
|
||||||
|
@ -136,14 +133,6 @@ func main() {
|
||||||
rsImpl.SetUserAPI(userAPI)
|
rsImpl.SetUserAPI(userAPI)
|
||||||
keyImpl.SetUserAPI(userAPI)
|
keyImpl.SetUserAPI(userAPI)
|
||||||
|
|
||||||
eduInputAPI := eduserver.NewInternalAPI(
|
|
||||||
base, cache.New(), userAPI,
|
|
||||||
)
|
|
||||||
if base.UseHTTPAPIs {
|
|
||||||
eduserver.AddInternalRoutes(base.InternalAPIMux, eduInputAPI)
|
|
||||||
eduInputAPI = base.EDUServerClient()
|
|
||||||
}
|
|
||||||
|
|
||||||
monolith := setup.Monolith{
|
monolith := setup.Monolith{
|
||||||
Config: base.Cfg,
|
Config: base.Cfg,
|
||||||
AccountDB: accountDB,
|
AccountDB: accountDB,
|
||||||
|
@ -151,12 +140,10 @@ func main() {
|
||||||
FedClient: federation,
|
FedClient: federation,
|
||||||
KeyRing: keyRing,
|
KeyRing: keyRing,
|
||||||
|
|
||||||
AppserviceAPI: asAPI,
|
AppserviceAPI: asAPI, FederationAPI: fsAPI,
|
||||||
EDUInternalAPI: eduInputAPI,
|
RoomserverAPI: rsAPI,
|
||||||
FederationAPI: fsAPI,
|
UserAPI: userAPI,
|
||||||
RoomserverAPI: rsAPI,
|
KeyAPI: keyAPI,
|
||||||
UserAPI: userAPI,
|
|
||||||
KeyAPI: keyAPI,
|
|
||||||
}
|
}
|
||||||
monolith.AddAllPublicRoutes(
|
monolith.AddAllPublicRoutes(
|
||||||
base.ProcessContext,
|
base.ProcessContext,
|
||||||
|
|
|
@ -43,7 +43,6 @@ func main() {
|
||||||
components := map[string]entrypoint{
|
components := map[string]entrypoint{
|
||||||
"appservice": personalities.Appservice,
|
"appservice": personalities.Appservice,
|
||||||
"clientapi": personalities.ClientAPI,
|
"clientapi": personalities.ClientAPI,
|
||||||
"eduserver": personalities.EDUServer,
|
|
||||||
"federationapi": personalities.FederationAPI,
|
"federationapi": personalities.FederationAPI,
|
||||||
"keyserver": personalities.KeyServer,
|
"keyserver": personalities.KeyServer,
|
||||||
"mediaapi": personalities.MediaAPI,
|
"mediaapi": personalities.MediaAPI,
|
||||||
|
|
|
@ -22,20 +22,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func ClientAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
|
func ClientAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
|
||||||
accountDB := base.CreateAccountsDB()
|
|
||||||
federation := base.CreateFederationClient()
|
federation := base.CreateFederationClient()
|
||||||
|
|
||||||
asQuery := base.AppserviceHTTPClient()
|
asQuery := base.AppserviceHTTPClient()
|
||||||
rsAPI := base.RoomserverHTTPClient()
|
rsAPI := base.RoomserverHTTPClient()
|
||||||
fsAPI := base.FederationAPIHTTPClient()
|
fsAPI := base.FederationAPIHTTPClient()
|
||||||
eduInputAPI := base.EDUServerClient()
|
|
||||||
userAPI := base.UserAPIClient()
|
userAPI := base.UserAPIClient()
|
||||||
keyAPI := base.KeyServerHTTPClient()
|
keyAPI := base.KeyServerHTTPClient()
|
||||||
|
|
||||||
clientapi.AddPublicRoutes(
|
clientapi.AddPublicRoutes(
|
||||||
base.PublicClientAPIMux, base.SynapseAdminMux, &base.Cfg.ClientAPI, accountDB, federation,
|
base.ProcessContext, base.PublicClientAPIMux, base.SynapseAdminMux, &base.Cfg.ClientAPI,
|
||||||
rsAPI, eduInputAPI, asQuery, transactions.New(), fsAPI, userAPI, keyAPI, nil,
|
federation, rsAPI, asQuery, transactions.New(), fsAPI, userAPI, userAPI,
|
||||||
&cfg.MSCs,
|
keyAPI, nil, &cfg.MSCs,
|
||||||
)
|
)
|
||||||
|
|
||||||
base.SetupAndServeHTTP(
|
base.SetupAndServeHTTP(
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2020 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 personalities
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
basepkg "github.com/matrix-org/dendrite/setup/base"
|
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func EDUServer(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
|
|
||||||
intAPI := eduserver.NewInternalAPI(base, cache.New(), base.UserAPIClient())
|
|
||||||
eduserver.AddInternalRoutes(base.InternalAPIMux, intAPI)
|
|
||||||
|
|
||||||
base.SetupAndServeHTTP(
|
|
||||||
base.Cfg.EDUServer.InternalAPI.Listen, // internal listener
|
|
||||||
basepkg.NoListener, // external listener
|
|
||||||
nil, nil,
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -29,9 +29,9 @@ func FederationAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
|
||||||
keyRing := fsAPI.KeyRing()
|
keyRing := fsAPI.KeyRing()
|
||||||
|
|
||||||
federationapi.AddPublicRoutes(
|
federationapi.AddPublicRoutes(
|
||||||
base.PublicFederationAPIMux, base.PublicKeyAPIMux, base.PublicWellKnownAPIMux,
|
base.ProcessContext, base.PublicFederationAPIMux, base.PublicKeyAPIMux, base.PublicWellKnownAPIMux,
|
||||||
&base.Cfg.FederationAPI, userAPI, federation, keyRing,
|
&base.Cfg.FederationAPI, userAPI, federation, keyRing,
|
||||||
rsAPI, fsAPI, base.EDUServerClient(), keyAPI,
|
rsAPI, fsAPI, keyAPI,
|
||||||
&base.Cfg.MSCs, nil,
|
&base.Cfg.MSCs, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -21,18 +21,13 @@ import (
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/appservice"
|
"github.com/matrix-org/dendrite/appservice"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
|
||||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi"
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/keyserver"
|
"github.com/matrix-org/dendrite/keyserver"
|
||||||
|
@ -48,6 +43,7 @@ import (
|
||||||
|
|
||||||
_ "github.com/matrix-org/go-sqlite3-js"
|
_ "github.com/matrix-org/go-sqlite3-js"
|
||||||
|
|
||||||
|
pineconeConnections "github.com/matrix-org/pinecone/connections"
|
||||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||||
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||||
)
|
)
|
||||||
|
@ -156,9 +152,10 @@ func startup() {
|
||||||
sk := generateKey()
|
sk := generateKey()
|
||||||
pk := sk.Public().(ed25519.PublicKey)
|
pk := sk.Public().(ed25519.PublicKey)
|
||||||
|
|
||||||
logger := log.New(os.Stdout, "", 0)
|
pRouter := pineconeRouter.NewRouter(logrus.WithField("pinecone", "router"), sk, false)
|
||||||
pRouter := pineconeRouter.NewRouter(logger, sk, false)
|
pSessions := pineconeSessions.NewSessions(logrus.WithField("pinecone", "sessions"), pRouter, []string{"matrix"})
|
||||||
pSessions := pineconeSessions.NewSessions(logger, pRouter)
|
pManager := pineconeConnections.NewConnectionManager(pRouter)
|
||||||
|
pManager.AddPeer("wss://pinecone.matrix.org/public")
|
||||||
|
|
||||||
cfg := &config.Dendrite{}
|
cfg := &config.Dendrite{}
|
||||||
cfg.Defaults(true)
|
cfg.Defaults(true)
|
||||||
|
@ -193,7 +190,6 @@ func startup() {
|
||||||
userAPI := userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, nil, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
userAPI := userapi.NewInternalAPI(base, accountDB, &cfg.UserAPI, nil, keyAPI, rsAPI, base.PushGatewayHTTPClient())
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
eduInputAPI := eduserver.NewInternalAPI(base, cache.New(), userAPI)
|
|
||||||
asQuery := appservice.NewInternalAPI(
|
asQuery := appservice.NewInternalAPI(
|
||||||
base, userAPI, rsAPI,
|
base, userAPI, rsAPI,
|
||||||
)
|
)
|
||||||
|
@ -208,12 +204,11 @@ func startup() {
|
||||||
FedClient: federation,
|
FedClient: federation,
|
||||||
KeyRing: keyRing,
|
KeyRing: keyRing,
|
||||||
|
|
||||||
AppserviceAPI: asQuery,
|
AppserviceAPI: asQuery,
|
||||||
EDUInternalAPI: eduInputAPI,
|
FederationAPI: fedSenderAPI,
|
||||||
FederationAPI: fedSenderAPI,
|
RoomserverAPI: rsAPI,
|
||||||
RoomserverAPI: rsAPI,
|
UserAPI: userAPI,
|
||||||
UserAPI: userAPI,
|
KeyAPI: keyAPI,
|
||||||
KeyAPI: keyAPI,
|
|
||||||
//ServerKeyAPI: serverKeyAPI,
|
//ServerKeyAPI: serverKeyAPI,
|
||||||
ExtPublicRoomsProvider: rooms.NewPineconeRoomProvider(pRouter, pSessions, fedSenderAPI, federation),
|
ExtPublicRoomsProvider: rooms.NewPineconeRoomProvider(pRouter, pSessions, fedSenderAPI, federation),
|
||||||
}
|
}
|
||||||
|
@ -232,7 +227,7 @@ func startup() {
|
||||||
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
|
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
|
||||||
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
||||||
|
|
||||||
p2pRouter := pSessions.HTTP().Mux()
|
p2pRouter := pSessions.Protocol("matrix").HTTP().Mux()
|
||||||
p2pRouter.Handle(httputil.PublicFederationPathPrefix, base.PublicFederationAPIMux)
|
p2pRouter.Handle(httputil.PublicFederationPathPrefix, base.PublicFederationAPIMux)
|
||||||
p2pRouter.Handle(httputil.PublicMediaPathPrefix, base.PublicMediaAPIMux)
|
p2pRouter.Handle(httputil.PublicMediaPathPrefix, base.PublicMediaAPIMux)
|
||||||
|
|
||||||
|
@ -244,20 +239,4 @@ func startup() {
|
||||||
}
|
}
|
||||||
s.ListenAndServe("fetch")
|
s.ListenAndServe("fetch")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Connect to the static peer
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if pRouter.PeerCount(pineconeRouter.PeerTypeRemote) == 0 {
|
|
||||||
if err := conn.ConnectToPeer(pRouter, publicPeer); err != nil {
|
|
||||||
logrus.WithError(err).Error("Failed to connect to static peer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-base.ProcessContext.Context().Done():
|
|
||||||
return
|
|
||||||
case <-time.After(time.Second * 5):
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,6 @@ import (
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/appservice"
|
"github.com/matrix-org/dendrite/appservice"
|
||||||
"github.com/matrix-org/dendrite/eduserver"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi"
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/keyserver"
|
"github.com/matrix-org/dendrite/keyserver"
|
||||||
|
@ -203,7 +201,6 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
rsAPI := roomserver.NewInternalAPI(base)
|
rsAPI := roomserver.NewInternalAPI(base)
|
||||||
eduInputAPI := eduserver.NewInternalAPI(base, cache.New(), userAPI)
|
|
||||||
asQuery := appservice.NewInternalAPI(
|
asQuery := appservice.NewInternalAPI(
|
||||||
base, userAPI, rsAPI,
|
base, userAPI, rsAPI,
|
||||||
)
|
)
|
||||||
|
@ -222,7 +219,6 @@ func main() {
|
||||||
KeyRing: &keyRing,
|
KeyRing: &keyRing,
|
||||||
|
|
||||||
AppserviceAPI: asQuery,
|
AppserviceAPI: asQuery,
|
||||||
EDUInternalAPI: eduInputAPI,
|
|
||||||
FederationSenderAPI: fedSenderAPI,
|
FederationSenderAPI: fedSenderAPI,
|
||||||
RoomserverAPI: rsAPI,
|
RoomserverAPI: rsAPI,
|
||||||
UserAPI: userAPI,
|
UserAPI: userAPI,
|
||||||
|
|
|
@ -1,138 +0,0 @@
|
||||||
// Copyright 2017 Vector Creations Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const usage = `Usage: %s
|
|
||||||
|
|
||||||
Create a single endpoint URL which remote matrix servers can be pointed at.
|
|
||||||
|
|
||||||
The server-server API in Dendrite is split across multiple processes
|
|
||||||
which listen on multiple ports. You cannot point a Matrix server at
|
|
||||||
any of those ports, as there will be unimplemented functionality.
|
|
||||||
In addition, all server-server API processes start with the additional
|
|
||||||
path prefix '/api', which Matrix servers will be unaware of.
|
|
||||||
|
|
||||||
This tool will proxy requests for all server-server URLs and forward
|
|
||||||
them to their respective process. It will also add the '/api' path
|
|
||||||
prefix to incoming requests.
|
|
||||||
|
|
||||||
THIS TOOL IS FOR TESTING AND NOT INTENDED FOR PRODUCTION USE.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
var (
|
|
||||||
federationAPIURL = flag.String("federation-api-url", "", "The base URL of the listening 'dendrite-federation-api-server' process. E.g. 'http://localhost:4200'")
|
|
||||||
mediaAPIURL = flag.String("media-api-server-url", "", "The base URL of the listening 'dendrite-media-api-server' process. E.g. 'http://localhost:7779'")
|
|
||||||
bindAddress = flag.String("bind-address", ":8448", "The listening port for the proxy.")
|
|
||||||
certFile = flag.String("tls-cert", "server.crt", "The PEM formatted X509 certificate to use for TLS")
|
|
||||||
keyFile = flag.String("tls-key", "server.key", "The PEM private key to use for TLS")
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeProxy(targetURL string) (*httputil.ReverseProxy, error) {
|
|
||||||
if !strings.HasSuffix(targetURL, "/") {
|
|
||||||
targetURL += "/"
|
|
||||||
}
|
|
||||||
// Check that we can parse the URL.
|
|
||||||
_, err := url.Parse(targetURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &httputil.ReverseProxy{
|
|
||||||
Director: func(req *http.Request) {
|
|
||||||
// URL.Path() removes the % escaping from the path.
|
|
||||||
// The % encoding will be added back when the url is encoded
|
|
||||||
// when the request is forwarded.
|
|
||||||
// This means that we will lose any unessecary escaping from the URL.
|
|
||||||
// Pratically this means that any distinction between '%2F' and '/'
|
|
||||||
// in the URL will be lost by the time it reaches the target.
|
|
||||||
path := req.URL.Path
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"path": path,
|
|
||||||
"url": targetURL,
|
|
||||||
"method": req.Method,
|
|
||||||
}).Print("proxying request")
|
|
||||||
newURL, err := url.Parse(targetURL + path)
|
|
||||||
if err != nil {
|
|
||||||
// We already checked that we can parse the URL
|
|
||||||
// So this shouldn't ever get hit.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// Copy the query parameters from the request.
|
|
||||||
newURL.RawQuery = req.URL.RawQuery
|
|
||||||
req.URL = newURL
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Usage = func() {
|
|
||||||
fmt.Fprintf(os.Stderr, usage, os.Args[0])
|
|
||||||
flag.PrintDefaults()
|
|
||||||
}
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *federationAPIURL == "" {
|
|
||||||
flag.Usage()
|
|
||||||
fmt.Fprintln(os.Stderr, "no --federation-api-url specified.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *mediaAPIURL == "" {
|
|
||||||
flag.Usage()
|
|
||||||
fmt.Fprintln(os.Stderr, "no --media-api-server-url specified.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
federationProxy, err := makeProxy(*federationAPIURL)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaProxy, err := makeProxy(*mediaAPIURL)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
http.Handle("/_matrix/media/v1/", mediaProxy)
|
|
||||||
http.Handle("/", federationProxy)
|
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Addr: *bindAddress,
|
|
||||||
ReadTimeout: 1 * time.Minute, // how long we wait for the client to send the entire request (after connection accept)
|
|
||||||
WriteTimeout: 5 * time.Minute, // how long the proxy has to write the full response
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Proxying requests to:")
|
|
||||||
fmt.Println(" /_matrix/media/v1 => ", *mediaAPIURL+"/api/_matrix/media/v1")
|
|
||||||
fmt.Println(" /* => ", *federationAPIURL+"/api/*")
|
|
||||||
fmt.Println("Listening on ", *bindAddress)
|
|
||||||
panic(srv.ListenAndServeTLS(*certFile, *keyFile))
|
|
||||||
}
|
|
|
@ -91,6 +91,10 @@ func main() {
|
||||||
cfg.UserAPI.BCryptCost = bcrypt.MinCost
|
cfg.UserAPI.BCryptCost = bcrypt.MinCost
|
||||||
cfg.Global.JetStream.InMemory = true
|
cfg.Global.JetStream.InMemory = true
|
||||||
cfg.ClientAPI.RegistrationSharedSecret = "complement"
|
cfg.ClientAPI.RegistrationSharedSecret = "complement"
|
||||||
|
cfg.Global.Presence = config.PresenceOptions{
|
||||||
|
EnableInbound: true,
|
||||||
|
EnableOutbound: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := yaml.Marshal(cfg)
|
j, err := yaml.Marshal(cfg)
|
||||||
|
|
|
@ -68,6 +68,13 @@ global:
|
||||||
# to other servers and the federation API will not be exposed.
|
# to other servers and the federation API will not be exposed.
|
||||||
disable_federation: false
|
disable_federation: false
|
||||||
|
|
||||||
|
# Configures the handling of presence events.
|
||||||
|
presence:
|
||||||
|
# Whether inbound presence events are allowed, e.g. receiving presence events from other servers
|
||||||
|
enable_inbound: false
|
||||||
|
# Whether outbound presence events are allowed, e.g. sending presence events to other servers
|
||||||
|
enable_outbound: false
|
||||||
|
|
||||||
# Server notices allows server admins to send messages to all users.
|
# Server notices allows server admins to send messages to all users.
|
||||||
server_notices:
|
server_notices:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
@ -216,12 +223,6 @@ client_api:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
cooloff_ms: 500
|
cooloff_ms: 500
|
||||||
|
|
||||||
# Configuration for the EDU server.
|
|
||||||
edu_server:
|
|
||||||
internal_api:
|
|
||||||
listen: http://localhost:7778 # Only used in polylith deployments
|
|
||||||
connect: http://localhost:7778 # Only used in polylith deployments
|
|
||||||
|
|
||||||
# Configuration for the Federation API.
|
# Configuration for the Federation API.
|
||||||
federation_api:
|
federation_api:
|
||||||
internal_api:
|
internal_api:
|
||||||
|
@ -235,12 +236,6 @@ federation_api:
|
||||||
max_idle_conns: 2
|
max_idle_conns: 2
|
||||||
conn_max_lifetime: -1
|
conn_max_lifetime: -1
|
||||||
|
|
||||||
# List of paths to X.509 certificates to be used by the external federation listeners.
|
|
||||||
# These certificates will be used to calculate the TLS fingerprints and other servers
|
|
||||||
# will expect the certificate to match these fingerprints. Certificates must be in PEM
|
|
||||||
# format.
|
|
||||||
federation_certificates: []
|
|
||||||
|
|
||||||
# How many times we will try to resend a failed transaction to a specific server. The
|
# How many times we will try to resend a failed transaction to a specific server. The
|
||||||
# backoff is 2**x seconds, so 1 = 2 seconds, 2 = 4 seconds, 3 = 8 seconds etc.
|
# backoff is 2**x seconds, so 1 = 2 seconds, 2 = 4 seconds, 3 = 8 seconds etc.
|
||||||
send_max_retries: 16
|
send_max_retries: 16
|
||||||
|
|
|
@ -94,4 +94,4 @@ For more general questions please use
|
||||||
|
|
||||||
We ask that everyone who contributes to the project signs off their
|
We ask that everyone who contributes to the project signs off their
|
||||||
contributions, in accordance with the
|
contributions, in accordance with the
|
||||||
[DCO](https://github.com/matrix-org/matrix-doc/blob/main/CONTRIBUTING.rst#sign-off).
|
[DCO](https://github.com/matrix-org/matrix-spec/blob/main/CONTRIBUTING.rst#sign-off).
|
||||||
|
|
|
@ -12,6 +12,10 @@ No, although a good portion of the Matrix specification has been implemented. Mo
|
||||||
|
|
||||||
No, not at present. There will be in the future when Dendrite reaches version 1.0.
|
No, not at present. There will be in the future when Dendrite reaches version 1.0.
|
||||||
|
|
||||||
|
### Can I use Dendrite with an existing Synapse database?
|
||||||
|
|
||||||
|
No, Dendrite has a very different database schema to Synapse and the two are not interchangeable.
|
||||||
|
|
||||||
### Should I run a monolith or a polylith deployment?
|
### Should I run a monolith or a polylith deployment?
|
||||||
|
|
||||||
Monolith deployments are always preferred where possible, and at this time, are far better tested than polylith deployments are. The only reason to consider a polylith deployment is if you wish to run different Dendrite components on separate physical machines.
|
Monolith deployments are always preferred where possible, and at this time, are far better tested than polylith deployments are. The only reason to consider a polylith deployment is if you wish to run different Dendrite components on separate physical machines.
|
||||||
|
@ -33,7 +37,7 @@ It should do, although we are aware of some minor issues:
|
||||||
|
|
||||||
### Does Dendrite support push notifications?
|
### Does Dendrite support push notifications?
|
||||||
|
|
||||||
No, not yet. This is a planned feature.
|
Yes, we have experimental support for push notifications. Configure them in the usual way in your Matrix client.
|
||||||
|
|
||||||
### Does Dendrite support application services/bridges?
|
### Does Dendrite support application services/bridges?
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,10 @@ If you want to run a polylith deployment, you also need:
|
||||||
|
|
||||||
* A standalone [NATS Server](https://github.com/nats-io/nats-server) deployment with JetStream enabled
|
* A standalone [NATS Server](https://github.com/nats-io/nats-server) deployment with JetStream enabled
|
||||||
|
|
||||||
|
If you want to build it on Windows, you need `gcc` in the path:
|
||||||
|
|
||||||
|
* [MinGW-w64](https://www.mingw-w64.org/)
|
||||||
|
|
||||||
## Building Dendrite
|
## Building Dendrite
|
||||||
|
|
||||||
Start by cloning the code:
|
Start by cloning the code:
|
||||||
|
@ -45,9 +49,15 @@ cd dendrite
|
||||||
|
|
||||||
Then build it:
|
Then build it:
|
||||||
|
|
||||||
```bash
|
* Linux or UNIX-like systems:
|
||||||
./build.sh
|
```bash
|
||||||
```
|
./build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
* Windows:
|
||||||
|
```dos
|
||||||
|
build.cmd
|
||||||
|
```
|
||||||
|
|
||||||
## Install NATS Server
|
## Install NATS Server
|
||||||
|
|
||||||
|
@ -263,14 +273,6 @@ This manages end-to-end encryption keys for users.
|
||||||
./bin/dendrite-polylith-multi --config=dendrite.yaml keyserver
|
./bin/dendrite-polylith-multi --config=dendrite.yaml keyserver
|
||||||
```
|
```
|
||||||
|
|
||||||
#### EDU server
|
|
||||||
|
|
||||||
This manages processing EDUs such as typing, send-to-device events and presence. Clients do not talk to
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./bin/dendrite-polylith-multi --config=dendrite.yaml eduserver
|
|
||||||
```
|
|
||||||
|
|
||||||
#### User server
|
#### User server
|
||||||
|
|
||||||
This manages user accounts, device access tokens and user account data,
|
This manages user accounts, device access tokens and user account data,
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
// Copyright 2017 Vector Creations Ltd
|
|
||||||
// Copyright 2017-2018 New Vector Ltd
|
|
||||||
// Copyright 2019-2020 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 api provides the types that are used to communicate with the typing server.
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InputTypingEvent is an event for notifying the typing server about typing updates.
|
|
||||||
type InputTypingEvent struct {
|
|
||||||
// UserID of the user to update typing status.
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
// RoomID of the room the user is typing (or has stopped).
|
|
||||||
RoomID string `json:"room_id"`
|
|
||||||
// Typing is true if the user is typing, false if they have stopped.
|
|
||||||
Typing bool `json:"typing"`
|
|
||||||
// Timeout is the interval in milliseconds for which the user should be marked as typing.
|
|
||||||
TimeoutMS int64 `json:"timeout"`
|
|
||||||
// OriginServerTS when the server received the update.
|
|
||||||
OriginServerTS gomatrixserverlib.Timestamp `json:"origin_server_ts"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputSendToDeviceEvent struct {
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
DeviceID string `json:"device_id"`
|
|
||||||
gomatrixserverlib.SendToDeviceEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputTypingEventRequest is a request to EDUServerInputAPI
|
|
||||||
type InputTypingEventRequest struct {
|
|
||||||
InputTypingEvent InputTypingEvent `json:"input_typing_event"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputTypingEventResponse is a response to InputTypingEvents
|
|
||||||
type InputTypingEventResponse struct{}
|
|
||||||
|
|
||||||
// InputSendToDeviceEventRequest is a request to EDUServerInputAPI
|
|
||||||
type InputSendToDeviceEventRequest struct {
|
|
||||||
InputSendToDeviceEvent InputSendToDeviceEvent `json:"input_send_to_device_event"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputSendToDeviceEventResponse is a response to InputSendToDeviceEventRequest
|
|
||||||
type InputSendToDeviceEventResponse struct{}
|
|
||||||
|
|
||||||
type InputReceiptEvent struct {
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
RoomID string `json:"room_id"`
|
|
||||||
EventID string `json:"event_id"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Timestamp gomatrixserverlib.Timestamp `json:"timestamp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputReceiptEventRequest is a request to EDUServerInputAPI
|
|
||||||
type InputReceiptEventRequest struct {
|
|
||||||
InputReceiptEvent InputReceiptEvent `json:"input_receipt_event"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputReceiptEventResponse is a response to InputReceiptEventRequest
|
|
||||||
type InputReceiptEventResponse struct{}
|
|
||||||
|
|
||||||
type InputCrossSigningKeyUpdateRequest struct {
|
|
||||||
CrossSigningKeyUpdate `json:"signing_keys"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputCrossSigningKeyUpdateResponse struct{}
|
|
||||||
|
|
||||||
// EDUServerInputAPI is used to write events to the typing server.
|
|
||||||
type EDUServerInputAPI interface {
|
|
||||||
InputTypingEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *InputTypingEventRequest,
|
|
||||||
response *InputTypingEventResponse,
|
|
||||||
) error
|
|
||||||
|
|
||||||
InputSendToDeviceEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *InputSendToDeviceEventRequest,
|
|
||||||
response *InputSendToDeviceEventResponse,
|
|
||||||
) error
|
|
||||||
|
|
||||||
InputReceiptEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *InputReceiptEventRequest,
|
|
||||||
response *InputReceiptEventResponse,
|
|
||||||
) error
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
// Copyright 2017 Vector Creations Ltd
|
|
||||||
// Copyright 2017-2018 New Vector Ltd
|
|
||||||
// Copyright 2019-2020 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 api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OutputTypingEvent is an entry in typing server output kafka log.
|
|
||||||
// This contains the event with extra fields used to create 'm.typing' event
|
|
||||||
// in clientapi & federation.
|
|
||||||
type OutputTypingEvent struct {
|
|
||||||
// The Event for the typing edu event.
|
|
||||||
Event TypingEvent `json:"event"`
|
|
||||||
// ExpireTime is the interval after which the user should no longer be
|
|
||||||
// considered typing. Only available if Event.Typing is true.
|
|
||||||
ExpireTime *time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputSendToDeviceEvent is an entry in the send-to-device output kafka log.
|
|
||||||
// This contains the full event content, along with the user ID and device ID
|
|
||||||
// to which it is destined.
|
|
||||||
type OutputSendToDeviceEvent struct {
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
DeviceID string `json:"device_id"`
|
|
||||||
gomatrixserverlib.SendToDeviceEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputReceiptEvent is an entry in the receipt output kafka log
|
|
||||||
type OutputReceiptEvent struct {
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
RoomID string `json:"room_id"`
|
|
||||||
EventID string `json:"event_id"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Timestamp gomatrixserverlib.Timestamp `json:"timestamp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputCrossSigningKeyUpdate is an entry in the signing key update output kafka log
|
|
||||||
type OutputCrossSigningKeyUpdate struct {
|
|
||||||
CrossSigningKeyUpdate `json:"signing_keys"`
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import "github.com/matrix-org/gomatrixserverlib"
|
|
||||||
|
|
||||||
const (
|
|
||||||
MSigningKeyUpdate = "m.signing_key_update"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TypingEvent struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
RoomID string `json:"room_id"`
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
Typing bool `json:"typing"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReceiptEvent struct {
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
RoomID string `json:"room_id"`
|
|
||||||
EventID string `json:"event_id"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Timestamp gomatrixserverlib.Timestamp `json:"timestamp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FederationReceiptMRead struct {
|
|
||||||
User map[string]FederationReceiptData `json:"m.read"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FederationReceiptData struct {
|
|
||||||
Data ReceiptTS `json:"data"`
|
|
||||||
EventIDs []string `json:"event_ids"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReceiptMRead struct {
|
|
||||||
User map[string]ReceiptTS `json:"m.read"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReceiptTS struct {
|
|
||||||
TS gomatrixserverlib.Timestamp `json:"ts"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CrossSigningKeyUpdate struct {
|
|
||||||
MasterKey *gomatrixserverlib.CrossSigningKey `json:"master_key,omitempty"`
|
|
||||||
SelfSigningKey *gomatrixserverlib.CrossSigningKey `json:"self_signing_key,omitempty"`
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
// Copyright 2020 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 api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SendTyping sends a typing event to EDU server
|
|
||||||
func SendTyping(
|
|
||||||
ctx context.Context, eduAPI EDUServerInputAPI, userID, roomID string,
|
|
||||||
typing bool, timeoutMS int64,
|
|
||||||
) error {
|
|
||||||
requestData := InputTypingEvent{
|
|
||||||
UserID: userID,
|
|
||||||
RoomID: roomID,
|
|
||||||
Typing: typing,
|
|
||||||
TimeoutMS: timeoutMS,
|
|
||||||
OriginServerTS: gomatrixserverlib.AsTimestamp(time.Now()),
|
|
||||||
}
|
|
||||||
|
|
||||||
var response InputTypingEventResponse
|
|
||||||
err := eduAPI.InputTypingEvent(
|
|
||||||
ctx, &InputTypingEventRequest{InputTypingEvent: requestData}, &response,
|
|
||||||
)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendToDevice sends a typing event to EDU server
|
|
||||||
func SendToDevice(
|
|
||||||
ctx context.Context, eduAPI EDUServerInputAPI, sender, userID, deviceID, eventType string,
|
|
||||||
message interface{},
|
|
||||||
) error {
|
|
||||||
js, err := json.Marshal(message)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
requestData := InputSendToDeviceEvent{
|
|
||||||
UserID: userID,
|
|
||||||
DeviceID: deviceID,
|
|
||||||
SendToDeviceEvent: gomatrixserverlib.SendToDeviceEvent{
|
|
||||||
Sender: sender,
|
|
||||||
Type: eventType,
|
|
||||||
Content: js,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
request := InputSendToDeviceEventRequest{
|
|
||||||
InputSendToDeviceEvent: requestData,
|
|
||||||
}
|
|
||||||
response := InputSendToDeviceEventResponse{}
|
|
||||||
return eduAPI.InputSendToDeviceEvent(ctx, &request, &response)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendReceipt sends a receipt event to EDU Server
|
|
||||||
func SendReceipt(
|
|
||||||
ctx context.Context,
|
|
||||||
eduAPI EDUServerInputAPI, userID, roomID, eventID, receiptType string,
|
|
||||||
timestamp gomatrixserverlib.Timestamp,
|
|
||||||
) error {
|
|
||||||
request := InputReceiptEventRequest{
|
|
||||||
InputReceiptEvent: InputReceiptEvent{
|
|
||||||
UserID: userID,
|
|
||||||
RoomID: roomID,
|
|
||||||
EventID: eventID,
|
|
||||||
Type: receiptType,
|
|
||||||
Timestamp: timestamp,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
response := InputReceiptEventResponse{}
|
|
||||||
return eduAPI.InputReceiptEvent(ctx, &request, &response)
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
// Copyright 2017 Vector Creations Ltd
|
|
||||||
// Copyright 2017-2018 New Vector Ltd
|
|
||||||
// Copyright 2019-2020 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 eduserver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/input"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/inthttp"
|
|
||||||
"github.com/matrix-org/dendrite/setup/base"
|
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions
|
|
||||||
// on the given input API.
|
|
||||||
func AddInternalRoutes(internalMux *mux.Router, inputAPI api.EDUServerInputAPI) {
|
|
||||||
inthttp.AddRoutes(inputAPI, internalMux)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInternalAPI returns a concerete implementation of the internal API. Callers
|
|
||||||
// can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes.
|
|
||||||
func NewInternalAPI(
|
|
||||||
base *base.BaseDendrite,
|
|
||||||
eduCache *cache.EDUCache,
|
|
||||||
userAPI userapi.UserInternalAPI,
|
|
||||||
) api.EDUServerInputAPI {
|
|
||||||
cfg := &base.Cfg.EDUServer
|
|
||||||
|
|
||||||
js := jetstream.Prepare(&cfg.Matrix.JetStream)
|
|
||||||
|
|
||||||
return &input.EDUServerInputAPI{
|
|
||||||
Cache: eduCache,
|
|
||||||
UserAPI: userAPI,
|
|
||||||
JetStream: js,
|
|
||||||
OutputTypingEventTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputTypingEvent),
|
|
||||||
OutputSendToDeviceEventTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputSendToDeviceEvent),
|
|
||||||
OutputReceiptEventTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputReceiptEvent),
|
|
||||||
ServerName: cfg.Matrix.ServerName,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,198 +0,0 @@
|
||||||
// Copyright 2017 Vector Creations Ltd
|
|
||||||
// Copyright 2017-2018 New Vector Ltd
|
|
||||||
// Copyright 2019-2020 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 input
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/cache"
|
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
"github.com/nats-io/nats.go"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EDUServerInputAPI implements api.EDUServerInputAPI
|
|
||||||
type EDUServerInputAPI struct {
|
|
||||||
// Cache to store the current typing members in each room.
|
|
||||||
Cache *cache.EDUCache
|
|
||||||
// The kafka topic to output new typing events to.
|
|
||||||
OutputTypingEventTopic string
|
|
||||||
// The kafka topic to output new send to device events to.
|
|
||||||
OutputSendToDeviceEventTopic string
|
|
||||||
// The kafka topic to output new receipt events to
|
|
||||||
OutputReceiptEventTopic string
|
|
||||||
// kafka producer
|
|
||||||
JetStream nats.JetStreamContext
|
|
||||||
// Internal user query API
|
|
||||||
UserAPI userapi.UserInternalAPI
|
|
||||||
// our server name
|
|
||||||
ServerName gomatrixserverlib.ServerName
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputTypingEvent implements api.EDUServerInputAPI
|
|
||||||
func (t *EDUServerInputAPI) InputTypingEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.InputTypingEventRequest,
|
|
||||||
response *api.InputTypingEventResponse,
|
|
||||||
) error {
|
|
||||||
ite := &request.InputTypingEvent
|
|
||||||
if ite.Typing {
|
|
||||||
// user is typing, update our current state of users typing.
|
|
||||||
expireTime := ite.OriginServerTS.Time().Add(
|
|
||||||
time.Duration(ite.TimeoutMS) * time.Millisecond,
|
|
||||||
)
|
|
||||||
t.Cache.AddTypingUser(ite.UserID, ite.RoomID, &expireTime)
|
|
||||||
} else {
|
|
||||||
t.Cache.RemoveUser(ite.UserID, ite.RoomID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.sendTypingEvent(ite)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputTypingEvent implements api.EDUServerInputAPI
|
|
||||||
func (t *EDUServerInputAPI) InputSendToDeviceEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.InputSendToDeviceEventRequest,
|
|
||||||
response *api.InputSendToDeviceEventResponse,
|
|
||||||
) error {
|
|
||||||
ise := &request.InputSendToDeviceEvent
|
|
||||||
return t.sendToDeviceEvent(ise)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *EDUServerInputAPI) sendTypingEvent(ite *api.InputTypingEvent) error {
|
|
||||||
ev := &api.TypingEvent{
|
|
||||||
Type: gomatrixserverlib.MTyping,
|
|
||||||
RoomID: ite.RoomID,
|
|
||||||
UserID: ite.UserID,
|
|
||||||
Typing: ite.Typing,
|
|
||||||
}
|
|
||||||
ote := &api.OutputTypingEvent{
|
|
||||||
Event: *ev,
|
|
||||||
}
|
|
||||||
|
|
||||||
if ev.Typing {
|
|
||||||
expireTime := ite.OriginServerTS.Time().Add(
|
|
||||||
time.Duration(ite.TimeoutMS) * time.Millisecond,
|
|
||||||
)
|
|
||||||
ote.ExpireTime = &expireTime
|
|
||||||
}
|
|
||||||
|
|
||||||
eventJSON, err := json.Marshal(ote)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"room_id": ite.RoomID,
|
|
||||||
"user_id": ite.UserID,
|
|
||||||
"typing": ite.Typing,
|
|
||||||
}).Tracef("Producing to topic '%s'", t.OutputTypingEventTopic)
|
|
||||||
|
|
||||||
_, err = t.JetStream.PublishMsg(&nats.Msg{
|
|
||||||
Subject: t.OutputTypingEventTopic,
|
|
||||||
Header: nats.Header{},
|
|
||||||
Data: eventJSON,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *EDUServerInputAPI) sendToDeviceEvent(ise *api.InputSendToDeviceEvent) error {
|
|
||||||
devices := []string{}
|
|
||||||
_, domain, err := gomatrixserverlib.SplitID('@', ise.UserID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the event is targeted locally then we want to expand the wildcard
|
|
||||||
// out into individual device IDs so that we can send them to each respective
|
|
||||||
// device. If the event isn't targeted locally then we can't expand the
|
|
||||||
// wildcard as we don't know about the remote devices, so instead we leave it
|
|
||||||
// as-is, so that the federation sender can send it on with the wildcard intact.
|
|
||||||
if domain == t.ServerName && ise.DeviceID == "*" {
|
|
||||||
var res userapi.QueryDevicesResponse
|
|
||||||
err = t.UserAPI.QueryDevices(context.TODO(), &userapi.QueryDevicesRequest{
|
|
||||||
UserID: ise.UserID,
|
|
||||||
}, &res)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, dev := range res.Devices {
|
|
||||||
devices = append(devices, dev.ID)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
devices = append(devices, ise.DeviceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"user_id": ise.UserID,
|
|
||||||
"num_devices": len(devices),
|
|
||||||
"type": ise.Type,
|
|
||||||
}).Tracef("Producing to topic '%s'", t.OutputSendToDeviceEventTopic)
|
|
||||||
for _, device := range devices {
|
|
||||||
ote := &api.OutputSendToDeviceEvent{
|
|
||||||
UserID: ise.UserID,
|
|
||||||
DeviceID: device,
|
|
||||||
SendToDeviceEvent: ise.SendToDeviceEvent,
|
|
||||||
}
|
|
||||||
|
|
||||||
eventJSON, err := json.Marshal(ote)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Error("sendToDevice failed json.Marshal")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = t.JetStream.PublishMsg(&nats.Msg{
|
|
||||||
Subject: t.OutputSendToDeviceEventTopic,
|
|
||||||
Data: eventJSON,
|
|
||||||
}); err != nil {
|
|
||||||
logrus.WithError(err).Error("sendToDevice failed t.Producer.SendMessage")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputReceiptEvent implements api.EDUServerInputAPI
|
|
||||||
// TODO: Intelligently batch requests sent by the same user (e.g wait a few milliseconds before emitting output events)
|
|
||||||
func (t *EDUServerInputAPI) InputReceiptEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.InputReceiptEventRequest,
|
|
||||||
response *api.InputReceiptEventResponse,
|
|
||||||
) error {
|
|
||||||
logrus.WithFields(logrus.Fields{}).Tracef("Producing to topic '%s'", t.OutputReceiptEventTopic)
|
|
||||||
output := &api.OutputReceiptEvent{
|
|
||||||
UserID: request.InputReceiptEvent.UserID,
|
|
||||||
RoomID: request.InputReceiptEvent.RoomID,
|
|
||||||
EventID: request.InputReceiptEvent.EventID,
|
|
||||||
Type: request.InputReceiptEvent.Type,
|
|
||||||
Timestamp: request.InputReceiptEvent.Timestamp,
|
|
||||||
}
|
|
||||||
js, err := json.Marshal(output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = t.JetStream.PublishMsg(&nats.Msg{
|
|
||||||
Subject: t.OutputReceiptEventTopic,
|
|
||||||
Data: js,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
package inthttp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
|
||||||
"github.com/opentracing/opentracing-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HTTP paths for the internal HTTP APIs
|
|
||||||
const (
|
|
||||||
EDUServerInputTypingEventPath = "/eduserver/input"
|
|
||||||
EDUServerInputSendToDeviceEventPath = "/eduserver/sendToDevice"
|
|
||||||
EDUServerInputReceiptEventPath = "/eduserver/receipt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewEDUServerClient creates a EDUServerInputAPI implemented by talking to a HTTP POST API.
|
|
||||||
func NewEDUServerClient(eduServerURL string, httpClient *http.Client) (api.EDUServerInputAPI, error) {
|
|
||||||
if httpClient == nil {
|
|
||||||
return nil, errors.New("NewEDUServerClient: httpClient is <nil>")
|
|
||||||
}
|
|
||||||
return &httpEDUServerInputAPI{eduServerURL, httpClient}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpEDUServerInputAPI struct {
|
|
||||||
eduServerURL string
|
|
||||||
httpClient *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputTypingEvent implements EDUServerInputAPI
|
|
||||||
func (h *httpEDUServerInputAPI) InputTypingEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.InputTypingEventRequest,
|
|
||||||
response *api.InputTypingEventResponse,
|
|
||||||
) error {
|
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "InputTypingEvent")
|
|
||||||
defer span.Finish()
|
|
||||||
|
|
||||||
apiURL := h.eduServerURL + EDUServerInputTypingEventPath
|
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputSendToDeviceEvent implements EDUServerInputAPI
|
|
||||||
func (h *httpEDUServerInputAPI) InputSendToDeviceEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.InputSendToDeviceEventRequest,
|
|
||||||
response *api.InputSendToDeviceEventResponse,
|
|
||||||
) error {
|
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "InputSendToDeviceEvent")
|
|
||||||
defer span.Finish()
|
|
||||||
|
|
||||||
apiURL := h.eduServerURL + EDUServerInputSendToDeviceEventPath
|
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputSendToDeviceEvent implements EDUServerInputAPI
|
|
||||||
func (h *httpEDUServerInputAPI) InputReceiptEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.InputReceiptEventRequest,
|
|
||||||
response *api.InputReceiptEventResponse,
|
|
||||||
) error {
|
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "InputReceiptEventPath")
|
|
||||||
defer span.Finish()
|
|
||||||
|
|
||||||
apiURL := h.eduServerURL + EDUServerInputReceiptEventPath
|
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
package inthttp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
|
||||||
"github.com/matrix-org/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddRoutes adds the EDUServerInputAPI handlers to the http.ServeMux.
|
|
||||||
func AddRoutes(t api.EDUServerInputAPI, internalAPIMux *mux.Router) {
|
|
||||||
internalAPIMux.Handle(EDUServerInputTypingEventPath,
|
|
||||||
httputil.MakeInternalAPI("inputTypingEvents", func(req *http.Request) util.JSONResponse {
|
|
||||||
var request api.InputTypingEventRequest
|
|
||||||
var response api.InputTypingEventResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
if err := t.InputTypingEvent(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
internalAPIMux.Handle(EDUServerInputSendToDeviceEventPath,
|
|
||||||
httputil.MakeInternalAPI("inputSendToDeviceEvents", func(req *http.Request) util.JSONResponse {
|
|
||||||
var request api.InputSendToDeviceEventRequest
|
|
||||||
var response api.InputSendToDeviceEventResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
if err := t.InputSendToDeviceEvent(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
internalAPIMux.Handle(EDUServerInputReceiptEventPath,
|
|
||||||
httputil.MakeInternalAPI("inputReceiptEvent", func(req *http.Request) util.JSONResponse {
|
|
||||||
var request api.InputReceiptEventRequest
|
|
||||||
var response api.InputReceiptEventResponse
|
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
if err := t.InputReceiptEvent(req.Context(), &request, &response); err != nil {
|
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,257 +0,0 @@
|
||||||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package consumers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/queue"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
|
||||||
"github.com/matrix-org/dendrite/setup/process"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
|
||||||
"github.com/matrix-org/util"
|
|
||||||
"github.com/nats-io/nats.go"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OutputEDUConsumer consumes events that originate in EDU server.
|
|
||||||
type OutputEDUConsumer struct {
|
|
||||||
ctx context.Context
|
|
||||||
jetstream nats.JetStreamContext
|
|
||||||
durable string
|
|
||||||
db storage.Database
|
|
||||||
queues *queue.OutgoingQueues
|
|
||||||
ServerName gomatrixserverlib.ServerName
|
|
||||||
typingTopic string
|
|
||||||
sendToDeviceTopic string
|
|
||||||
receiptTopic string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOutputEDUConsumer creates a new OutputEDUConsumer. Call Start() to begin consuming from EDU servers.
|
|
||||||
func NewOutputEDUConsumer(
|
|
||||||
process *process.ProcessContext,
|
|
||||||
cfg *config.FederationAPI,
|
|
||||||
js nats.JetStreamContext,
|
|
||||||
queues *queue.OutgoingQueues,
|
|
||||||
store storage.Database,
|
|
||||||
) *OutputEDUConsumer {
|
|
||||||
return &OutputEDUConsumer{
|
|
||||||
ctx: process.Context(),
|
|
||||||
jetstream: js,
|
|
||||||
queues: queues,
|
|
||||||
db: store,
|
|
||||||
ServerName: cfg.Matrix.ServerName,
|
|
||||||
durable: cfg.Matrix.JetStream.Durable("FederationAPIEDUServerConsumer"),
|
|
||||||
typingTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputTypingEvent),
|
|
||||||
sendToDeviceTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputSendToDeviceEvent),
|
|
||||||
receiptTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputReceiptEvent),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start consuming from EDU servers
|
|
||||||
func (t *OutputEDUConsumer) Start() error {
|
|
||||||
if err := jetstream.JetStreamConsumer(
|
|
||||||
t.ctx, t.jetstream, t.typingTopic, t.durable, t.onTypingEvent,
|
|
||||||
nats.DeliverAll(), nats.ManualAck(),
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := jetstream.JetStreamConsumer(
|
|
||||||
t.ctx, t.jetstream, t.sendToDeviceTopic, t.durable, t.onSendToDeviceEvent,
|
|
||||||
nats.DeliverAll(), nats.ManualAck(),
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := jetstream.JetStreamConsumer(
|
|
||||||
t.ctx, t.jetstream, t.receiptTopic, t.durable, t.onReceiptEvent,
|
|
||||||
nats.DeliverAll(), nats.ManualAck(),
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// onSendToDeviceEvent is called in response to a message received on the
|
|
||||||
// send-to-device events topic from the EDU server.
|
|
||||||
func (t *OutputEDUConsumer) onSendToDeviceEvent(ctx context.Context, msg *nats.Msg) bool {
|
|
||||||
// Extract the send-to-device event from msg.
|
|
||||||
var ote api.OutputSendToDeviceEvent
|
|
||||||
if err := json.Unmarshal(msg.Data, &ote); err != nil {
|
|
||||||
log.WithError(err).Errorf("eduserver output log: message parse failed (expected send-to-device)")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// only send send-to-device events which originated from us
|
|
||||||
_, originServerName, err := gomatrixserverlib.SplitID('@', ote.Sender)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).WithField("user_id", ote.Sender).Error("Failed to extract domain from send-to-device sender")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if originServerName != t.ServerName {
|
|
||||||
log.WithField("other_server", originServerName).Info("Suppressing send-to-device: originated elsewhere")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
_, destServerName, err := gomatrixserverlib.SplitID('@', ote.UserID)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).WithField("user_id", ote.UserID).Error("Failed to extract domain from send-to-device destination")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pack the EDU and marshal it
|
|
||||||
edu := &gomatrixserverlib.EDU{
|
|
||||||
Type: gomatrixserverlib.MDirectToDevice,
|
|
||||||
Origin: string(t.ServerName),
|
|
||||||
}
|
|
||||||
tdm := gomatrixserverlib.ToDeviceMessage{
|
|
||||||
Sender: ote.Sender,
|
|
||||||
Type: ote.Type,
|
|
||||||
MessageID: util.RandomString(32),
|
|
||||||
Messages: map[string]map[string]json.RawMessage{
|
|
||||||
ote.UserID: {
|
|
||||||
ote.DeviceID: ote.Content,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if edu.Content, err = json.Marshal(tdm); err != nil {
|
|
||||||
log.WithError(err).Error("failed to marshal EDU JSON")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("Sending send-to-device message into %q destination queue", destServerName)
|
|
||||||
if err := t.queues.SendEDU(edu, t.ServerName, []gomatrixserverlib.ServerName{destServerName}); err != nil {
|
|
||||||
log.WithError(err).Error("failed to send EDU")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// onTypingEvent is called in response to a message received on the typing
|
|
||||||
// events topic from the EDU server.
|
|
||||||
func (t *OutputEDUConsumer) onTypingEvent(ctx context.Context, msg *nats.Msg) bool {
|
|
||||||
// Extract the typing event from msg.
|
|
||||||
var ote api.OutputTypingEvent
|
|
||||||
if err := json.Unmarshal(msg.Data, &ote); err != nil {
|
|
||||||
// Skip this msg but continue processing messages.
|
|
||||||
log.WithError(err).Errorf("eduserver output log: message parse failed (expected typing)")
|
|
||||||
_ = msg.Ack()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// only send typing events which originated from us
|
|
||||||
_, typingServerName, err := gomatrixserverlib.SplitID('@', ote.Event.UserID)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).WithField("user_id", ote.Event.UserID).Error("Failed to extract domain from typing sender")
|
|
||||||
_ = msg.Ack()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if typingServerName != t.ServerName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
joined, err := t.db.GetJoinedHosts(ctx, ote.Event.RoomID)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).WithField("room_id", ote.Event.RoomID).Error("failed to get joined hosts for room")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
names := make([]gomatrixserverlib.ServerName, len(joined))
|
|
||||||
for i := range joined {
|
|
||||||
names[i] = joined[i].ServerName
|
|
||||||
}
|
|
||||||
|
|
||||||
edu := &gomatrixserverlib.EDU{Type: ote.Event.Type}
|
|
||||||
if edu.Content, err = json.Marshal(map[string]interface{}{
|
|
||||||
"room_id": ote.Event.RoomID,
|
|
||||||
"user_id": ote.Event.UserID,
|
|
||||||
"typing": ote.Event.Typing,
|
|
||||||
}); err != nil {
|
|
||||||
log.WithError(err).Error("failed to marshal EDU JSON")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := t.queues.SendEDU(edu, t.ServerName, names); err != nil {
|
|
||||||
log.WithError(err).Error("failed to send EDU")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// onReceiptEvent is called in response to a message received on the receipt
|
|
||||||
// events topic from the EDU server.
|
|
||||||
func (t *OutputEDUConsumer) onReceiptEvent(ctx context.Context, msg *nats.Msg) bool {
|
|
||||||
// Extract the typing event from msg.
|
|
||||||
var receipt api.OutputReceiptEvent
|
|
||||||
if err := json.Unmarshal(msg.Data, &receipt); err != nil {
|
|
||||||
// Skip this msg but continue processing messages.
|
|
||||||
log.WithError(err).Errorf("eduserver output log: message parse failed (expected receipt)")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// only send receipt events which originated from us
|
|
||||||
_, receiptServerName, err := gomatrixserverlib.SplitID('@', receipt.UserID)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).WithField("user_id", receipt.UserID).Error("failed to extract domain from receipt sender")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if receiptServerName != t.ServerName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
joined, err := t.db.GetJoinedHosts(ctx, receipt.RoomID)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).WithField("room_id", receipt.RoomID).Error("failed to get joined hosts for room")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
names := make([]gomatrixserverlib.ServerName, len(joined))
|
|
||||||
for i := range joined {
|
|
||||||
names[i] = joined[i].ServerName
|
|
||||||
}
|
|
||||||
|
|
||||||
content := map[string]api.FederationReceiptMRead{}
|
|
||||||
content[receipt.RoomID] = api.FederationReceiptMRead{
|
|
||||||
User: map[string]api.FederationReceiptData{
|
|
||||||
receipt.UserID: {
|
|
||||||
Data: api.ReceiptTS{
|
|
||||||
TS: receipt.Timestamp,
|
|
||||||
},
|
|
||||||
EventIDs: []string{receipt.EventID},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
edu := &gomatrixserverlib.EDU{
|
|
||||||
Type: gomatrixserverlib.MReceipt,
|
|
||||||
Origin: string(t.ServerName),
|
|
||||||
}
|
|
||||||
if edu.Content, err = json.Marshal(content); err != nil {
|
|
||||||
log.WithError(err).Error("failed to marshal EDU JSON")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := t.queues.SendEDU(edu, t.ServerName, names); err != nil {
|
|
||||||
log.WithError(err).Error("failed to send EDU")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -18,9 +18,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
eduserverAPI "github.com/matrix-org/dendrite/eduserver/api"
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/queue"
|
"github.com/matrix-org/dendrite/federationapi/queue"
|
||||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/types"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
@ -55,8 +55,8 @@ func NewKeyChangeConsumer(
|
||||||
return &KeyChangeConsumer{
|
return &KeyChangeConsumer{
|
||||||
ctx: process.Context(),
|
ctx: process.Context(),
|
||||||
jetstream: js,
|
jetstream: js,
|
||||||
durable: cfg.Matrix.JetStream.TopicFor("FederationAPIKeyChangeConsumer"),
|
durable: cfg.Matrix.JetStream.Prefixed("FederationAPIKeyChangeConsumer"),
|
||||||
topic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputKeyChangeEvent),
|
topic: cfg.Matrix.JetStream.Prefixed(jetstream.OutputKeyChangeEvent),
|
||||||
queues: queues,
|
queues: queues,
|
||||||
db: store,
|
db: store,
|
||||||
serverName: cfg.Matrix.ServerName,
|
serverName: cfg.Matrix.ServerName,
|
||||||
|
@ -190,7 +190,7 @@ func (t *KeyChangeConsumer) onCrossSigningMessage(m api.DeviceMessage) bool {
|
||||||
|
|
||||||
// Pack the EDU and marshal it
|
// Pack the EDU and marshal it
|
||||||
edu := &gomatrixserverlib.EDU{
|
edu := &gomatrixserverlib.EDU{
|
||||||
Type: eduserverAPI.MSigningKeyUpdate,
|
Type: types.MSigningKeyUpdate,
|
||||||
Origin: string(t.serverName),
|
Origin: string(t.serverName),
|
||||||
}
|
}
|
||||||
if edu.Content, err = json.Marshal(output); err != nil {
|
if edu.Content, err = json.Marshal(output); err != nil {
|
||||||
|
|
143
federationapi/consumers/presence.go
Normal file
143
federationapi/consumers/presence.go
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package consumers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/queue"
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||||
|
fedTypes "github.com/matrix-org/dendrite/federationapi/types"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
|
"github.com/matrix-org/dendrite/syncapi/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OutputReceiptConsumer consumes events that originate in the clientapi.
|
||||||
|
type OutputPresenceConsumer struct {
|
||||||
|
ctx context.Context
|
||||||
|
jetstream nats.JetStreamContext
|
||||||
|
durable string
|
||||||
|
db storage.Database
|
||||||
|
queues *queue.OutgoingQueues
|
||||||
|
ServerName gomatrixserverlib.ServerName
|
||||||
|
topic string
|
||||||
|
outboundPresenceEnabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOutputPresenceConsumer creates a new OutputPresenceConsumer. Call Start() to begin consuming events.
|
||||||
|
func NewOutputPresenceConsumer(
|
||||||
|
process *process.ProcessContext,
|
||||||
|
cfg *config.FederationAPI,
|
||||||
|
js nats.JetStreamContext,
|
||||||
|
queues *queue.OutgoingQueues,
|
||||||
|
store storage.Database,
|
||||||
|
) *OutputPresenceConsumer {
|
||||||
|
return &OutputPresenceConsumer{
|
||||||
|
ctx: process.Context(),
|
||||||
|
jetstream: js,
|
||||||
|
queues: queues,
|
||||||
|
db: store,
|
||||||
|
ServerName: cfg.Matrix.ServerName,
|
||||||
|
durable: cfg.Matrix.JetStream.Durable("FederationAPIPresenceConsumer"),
|
||||||
|
topic: cfg.Matrix.JetStream.Prefixed(jetstream.OutputPresenceEvent),
|
||||||
|
outboundPresenceEnabled: cfg.Matrix.Presence.EnableOutbound,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start consuming from the clientapi
|
||||||
|
func (t *OutputPresenceConsumer) Start() error {
|
||||||
|
if !t.outboundPresenceEnabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return jetstream.JetStreamConsumer(
|
||||||
|
t.ctx, t.jetstream, t.topic, t.durable, t.onMessage,
|
||||||
|
nats.DeliverAll(), nats.ManualAck(), nats.HeadersOnly(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// onMessage is called in response to a message received on the presence
|
||||||
|
// events topic from the client api.
|
||||||
|
func (t *OutputPresenceConsumer) onMessage(ctx context.Context, msg *nats.Msg) bool {
|
||||||
|
// only send presence events which originated from us
|
||||||
|
userID := msg.Header.Get(jetstream.UserID)
|
||||||
|
_, serverName, err := gomatrixserverlib.SplitID('@', userID)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).WithField("user_id", userID).Error("failed to extract domain from receipt sender")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if serverName != t.ServerName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
presence := msg.Header.Get("presence")
|
||||||
|
|
||||||
|
ts, err := strconv.Atoi(msg.Header.Get("last_active_ts"))
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
joined, err := t.db.GetAllJoinedHosts(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("failed to get joined hosts")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(joined) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var statusMsg *string = nil
|
||||||
|
if data, ok := msg.Header["status_msg"]; ok && len(data) > 0 {
|
||||||
|
status := msg.Header.Get("status_msg")
|
||||||
|
statusMsg = &status
|
||||||
|
}
|
||||||
|
|
||||||
|
p := types.PresenceInternal{LastActiveTS: gomatrixserverlib.Timestamp(ts)}
|
||||||
|
|
||||||
|
content := fedTypes.Presence{
|
||||||
|
Push: []fedTypes.PresenceContent{
|
||||||
|
{
|
||||||
|
CurrentlyActive: p.CurrentlyActive(),
|
||||||
|
LastActiveAgo: p.LastActiveAgo(),
|
||||||
|
Presence: presence,
|
||||||
|
StatusMsg: statusMsg,
|
||||||
|
UserID: userID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
edu := &gomatrixserverlib.EDU{
|
||||||
|
Type: gomatrixserverlib.MPresence,
|
||||||
|
Origin: string(t.ServerName),
|
||||||
|
}
|
||||||
|
if edu.Content, err = json.Marshal(content); err != nil {
|
||||||
|
log.WithError(err).Error("failed to marshal EDU JSON")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("sending presence EDU to %d servers", len(joined))
|
||||||
|
if err = t.queues.SendEDU(edu, t.ServerName, joined); err != nil {
|
||||||
|
log.WithError(err).Error("failed to send EDU")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
141
federationapi/consumers/receipts.go
Normal file
141
federationapi/consumers/receipts.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package consumers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/queue"
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||||
|
fedTypes "github.com/matrix-org/dendrite/federationapi/types"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
|
syncTypes "github.com/matrix-org/dendrite/syncapi/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OutputReceiptConsumer consumes events that originate in the clientapi.
|
||||||
|
type OutputReceiptConsumer struct {
|
||||||
|
ctx context.Context
|
||||||
|
jetstream nats.JetStreamContext
|
||||||
|
durable string
|
||||||
|
db storage.Database
|
||||||
|
queues *queue.OutgoingQueues
|
||||||
|
ServerName gomatrixserverlib.ServerName
|
||||||
|
topic string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOutputReceiptConsumer creates a new OutputReceiptConsumer. Call Start() to begin consuming typing events.
|
||||||
|
func NewOutputReceiptConsumer(
|
||||||
|
process *process.ProcessContext,
|
||||||
|
cfg *config.FederationAPI,
|
||||||
|
js nats.JetStreamContext,
|
||||||
|
queues *queue.OutgoingQueues,
|
||||||
|
store storage.Database,
|
||||||
|
) *OutputReceiptConsumer {
|
||||||
|
return &OutputReceiptConsumer{
|
||||||
|
ctx: process.Context(),
|
||||||
|
jetstream: js,
|
||||||
|
queues: queues,
|
||||||
|
db: store,
|
||||||
|
ServerName: cfg.Matrix.ServerName,
|
||||||
|
durable: cfg.Matrix.JetStream.Durable("FederationAPIReceiptConsumer"),
|
||||||
|
topic: cfg.Matrix.JetStream.Prefixed(jetstream.OutputReceiptEvent),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start consuming from the clientapi
|
||||||
|
func (t *OutputReceiptConsumer) Start() error {
|
||||||
|
return jetstream.JetStreamConsumer(
|
||||||
|
t.ctx, t.jetstream, t.topic, t.durable, t.onMessage,
|
||||||
|
nats.DeliverAll(), nats.ManualAck(), nats.HeadersOnly(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// onMessage is called in response to a message received on the receipt
|
||||||
|
// events topic from the client api.
|
||||||
|
func (t *OutputReceiptConsumer) onMessage(ctx context.Context, msg *nats.Msg) bool {
|
||||||
|
receipt := syncTypes.OutputReceiptEvent{
|
||||||
|
UserID: msg.Header.Get(jetstream.UserID),
|
||||||
|
RoomID: msg.Header.Get(jetstream.RoomID),
|
||||||
|
EventID: msg.Header.Get(jetstream.EventID),
|
||||||
|
Type: msg.Header.Get("type"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// only send receipt events which originated from us
|
||||||
|
_, receiptServerName, err := gomatrixserverlib.SplitID('@', receipt.UserID)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).WithField("user_id", receipt.UserID).Error("failed to extract domain from receipt sender")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if receiptServerName != t.ServerName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp, err := strconv.Atoi(msg.Header.Get("timestamp"))
|
||||||
|
if err != nil {
|
||||||
|
// If the message was invalid, log it and move on to the next message in the stream
|
||||||
|
log.WithError(err).Errorf("EDU output log: message parse failure")
|
||||||
|
sentry.CaptureException(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
receipt.Timestamp = gomatrixserverlib.Timestamp(timestamp)
|
||||||
|
|
||||||
|
joined, err := t.db.GetJoinedHosts(ctx, receipt.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).WithField("room_id", receipt.RoomID).Error("failed to get joined hosts for room")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
names := make([]gomatrixserverlib.ServerName, len(joined))
|
||||||
|
for i := range joined {
|
||||||
|
names[i] = joined[i].ServerName
|
||||||
|
}
|
||||||
|
|
||||||
|
content := map[string]fedTypes.FederationReceiptMRead{}
|
||||||
|
content[receipt.RoomID] = fedTypes.FederationReceiptMRead{
|
||||||
|
User: map[string]fedTypes.FederationReceiptData{
|
||||||
|
receipt.UserID: {
|
||||||
|
Data: fedTypes.ReceiptTS{
|
||||||
|
TS: receipt.Timestamp,
|
||||||
|
},
|
||||||
|
EventIDs: []string{receipt.EventID},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
edu := &gomatrixserverlib.EDU{
|
||||||
|
Type: gomatrixserverlib.MReceipt,
|
||||||
|
Origin: string(t.ServerName),
|
||||||
|
}
|
||||||
|
if edu.Content, err = json.Marshal(content); err != nil {
|
||||||
|
log.WithError(err).Error("failed to marshal EDU JSON")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.queues.SendEDU(edu, t.ServerName, names); err != nil {
|
||||||
|
log.WithError(err).Error("failed to send EDU")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue