mirror of
https://github.com/matrix-org/dendrite.git
synced 2024-11-22 14:21:55 -06:00
Merge branch 'main' of github.com:matrix-org/dendrite into gh-pages
This commit is contained in:
commit
a128f100c4
2
.github/codecov.yaml
vendored
2
.github/codecov.yaml
vendored
|
@ -7,7 +7,7 @@ coverage:
|
||||||
project:
|
project:
|
||||||
default:
|
default:
|
||||||
target: auto
|
target: auto
|
||||||
threshold: 0%
|
threshold: 0.1%
|
||||||
base: auto
|
base: auto
|
||||||
flags:
|
flags:
|
||||||
- unittests
|
- unittests
|
||||||
|
|
87
.github/workflows/dendrite.yml
vendored
87
.github/workflows/dendrite.yml
vendored
|
@ -28,12 +28,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ false }} # disable for now
|
if: ${{ false }} # disable for now
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version-file: 'go.mod'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
|
@ -41,7 +41,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
node-version: 14
|
node-version: 14
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.npm
|
path: ~/.npm
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
@ -66,13 +66,13 @@ jobs:
|
||||||
name: Linting
|
name: Linting
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install libolm
|
- name: Install libolm
|
||||||
run: sudo apt-get install libolm-dev libolm3
|
run: sudo apt-get install libolm-dev libolm3
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version-file: 'go.mod'
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
|
||||||
|
@ -102,14 +102,14 @@ jobs:
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install libolm
|
- name: Install libolm
|
||||||
run: sudo apt-get install libolm-dev libolm3
|
run: sudo apt-get install libolm-dev libolm3
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version-file: 'go.mod'
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
# manually set up caches, as they otherwise clash with different steps using setup-go with cache=true
|
# manually set up caches, as they otherwise clash with different steps using setup-go with cache=true
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
|
@ -141,12 +141,12 @@ jobs:
|
||||||
goos: ["linux"]
|
goos: ["linux"]
|
||||||
goarch: ["amd64", "386"]
|
goarch: ["amd64", "386"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version-file: 'go.mod'
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -174,12 +174,12 @@ jobs:
|
||||||
goos: ["windows"]
|
goos: ["windows"]
|
||||||
goarch: ["amd64"]
|
goarch: ["amd64"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version-file: 'go.mod'
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -235,19 +235,19 @@ jobs:
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install libolm
|
- name: Install libolm
|
||||||
run: sudo apt-get install libolm-dev libolm3
|
run: sudo apt-get install libolm-dev libolm3
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version-file: 'go.mod'
|
||||||
- name: Set up gotestfmt
|
- name: Set up gotestfmt
|
||||||
uses: gotesttools/gotestfmt-action@v2
|
uses: gotesttools/gotestfmt-action@v2
|
||||||
with:
|
with:
|
||||||
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
|
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -262,10 +262,11 @@ jobs:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_DB: dendrite
|
POSTGRES_DB: dendrite
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
flags: unittests
|
flags: unittests
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
# run database upgrade tests
|
# run database upgrade tests
|
||||||
upgrade_test:
|
upgrade_test:
|
||||||
|
@ -274,12 +275,20 @@ jobs:
|
||||||
needs: initial-tests-done
|
needs: initial-tests-done
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version-file: 'go.mod'
|
||||||
cache: true
|
cache: true
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-upgrade-test-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-upgrade-test-
|
||||||
- name: Docker version
|
- name: Docker version
|
||||||
run: docker version
|
run: docker version
|
||||||
- name: Build upgrade-tests
|
- name: Build upgrade-tests
|
||||||
|
@ -296,12 +305,20 @@ jobs:
|
||||||
needs: initial-tests-done
|
needs: initial-tests-done
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version-file: 'go.mod'
|
||||||
cache: true
|
cache: true
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-upgrade-direct-test-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-upgrade-direct-test-
|
||||||
- name: Docker version
|
- name: Docker version
|
||||||
run: docker version
|
run: docker version
|
||||||
- name: Build upgrade-tests
|
- name: Build upgrade-tests
|
||||||
|
@ -340,8 +357,8 @@ jobs:
|
||||||
SYTEST_BRANCH: ${{ github.head_ref }}
|
SYTEST_BRANCH: ${{ github.head_ref }}
|
||||||
CGO_ENABLED: ${{ matrix.cgo && 1 }}
|
CGO_ENABLED: ${{ matrix.cgo && 1 }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -364,7 +381,7 @@ jobs:
|
||||||
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
||||||
continue-on-error: true # not fatal
|
continue-on-error: true # not fatal
|
||||||
- name: Upload Sytest logs
|
- name: Upload Sytest logs
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Sytest Logs - ${{ job.status }} - (Dendrite, ${{ join(matrix.*, ', ') }})
|
name: Sytest Logs - ${{ job.status }} - (Dendrite, ${{ join(matrix.*, ', ') }})
|
||||||
|
@ -404,8 +421,8 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
||||||
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||||
- name: Run actions/checkout@v3 for dendrite
|
- name: Run actions/checkout@v4 for dendrite
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: dendrite
|
path: dendrite
|
||||||
|
|
||||||
|
|
30
.github/workflows/docker.yml
vendored
30
.github/workflows/docker.yml
vendored
|
@ -27,22 +27,22 @@ jobs:
|
||||||
security-events: write # To upload Trivy sarif files
|
security-events: write # To upload Trivy sarif files
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Get release tag & build flags
|
- name: Get release tag & build flags
|
||||||
if: github.event_name == 'release' # Only for GitHub releases
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
run: |
|
run: |
|
||||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ env.DOCKER_HUB_USER }}
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
- name: Login to GitHub Containers
|
- name: Login to GitHub Containers
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
@ -98,22 +98,22 @@ jobs:
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Get release tag & build flags
|
- name: Get release tag & build flags
|
||||||
if: github.event_name == 'release' # Only for GitHub releases
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
run: |
|
run: |
|
||||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ env.DOCKER_HUB_USER }}
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
- name: Login to GitHub Containers
|
- name: Login to GitHub Containers
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
@ -159,22 +159,22 @@ jobs:
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Get release tag & build flags
|
- name: Get release tag & build flags
|
||||||
if: github.event_name == 'release' # Only for GitHub releases
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
run: |
|
run: |
|
||||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ env.DOCKER_HUB_USER }}
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
- name: Login to GitHub Containers
|
- name: Login to GitHub Containers
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
|
2
.github/workflows/gh-pages.yml
vendored
2
.github/workflows/gh-pages.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v2
|
uses: actions/configure-pages@v2
|
||||||
- name: Build with Jekyll
|
- name: Build with Jekyll
|
||||||
|
|
4
.github/workflows/helm.yml
vendored
4
.github/workflows/helm.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ jobs:
|
||||||
version: v3.10.0
|
version: v3.10.0
|
||||||
|
|
||||||
- name: Run chart-releaser
|
- name: Run chart-releaser
|
||||||
uses: helm/chart-releaser-action@ed43eb303604cbc0eeec8390544f7748dc6c790d # specific commit, since `mark_as_latest` is not yet in a release
|
uses: helm/chart-releaser-action@v1.6.0
|
||||||
env:
|
env:
|
||||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
with:
|
with:
|
||||||
|
|
6
.github/workflows/k8s.yml
vendored
6
.github/workflows/k8s.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
changed: ${{ steps.list-changed.outputs.changed }}
|
changed: ${{ steps.list-changed.outputs.changed }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: azure/setup-helm@v3
|
- uses: azure/setup-helm@v3
|
||||||
|
@ -48,7 +48,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ inputs.checkoutCommit }}
|
ref: ${{ inputs.checkoutCommit }}
|
||||||
|
@ -66,7 +66,7 @@ jobs:
|
||||||
- name: Create k3d cluster
|
- name: Create k3d cluster
|
||||||
uses: nolar/setup-k3d-k3s@v1
|
uses: nolar/setup-k3d-k3s@v1
|
||||||
with:
|
with:
|
||||||
version: v1.21
|
version: v1.28
|
||||||
- name: Remove node taints
|
- name: Remove node taints
|
||||||
run: |
|
run: |
|
||||||
kubectl taint --all=true nodes node.cloudprovider.kubernetes.io/uninitialized- || true
|
kubectl taint --all=true nodes node.cloudprovider.kubernetes.io/uninitialized- || true
|
||||||
|
|
66
.github/workflows/schedules.yaml
vendored
66
.github/workflows/schedules.yaml
vendored
|
@ -10,8 +10,26 @@ concurrency:
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
check_date: # https://stackoverflow.com/questions/63014786/how-to-schedule-a-github-actions-nightly-build-but-run-it-only-when-there-where
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Check latest commit
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.should_run.outputs.should_run }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: print latest_commit
|
||||||
|
run: echo ${{ github.sha }}
|
||||||
|
|
||||||
|
- id: should_run
|
||||||
|
continue-on-error: true
|
||||||
|
name: check latest commit is less than a day
|
||||||
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
|
run: test -z $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "::set-output name=should_run::false"
|
||||||
|
|
||||||
# run Sytest in different variations
|
# run Sytest in different variations
|
||||||
sytest:
|
sytest:
|
||||||
|
needs: check_date
|
||||||
|
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
name: "Sytest (${{ matrix.label }})"
|
name: "Sytest (${{ matrix.label }})"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -38,8 +56,8 @@ jobs:
|
||||||
RACE_DETECTION: 1
|
RACE_DETECTION: 1
|
||||||
COVER: 1
|
COVER: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -62,7 +80,7 @@ jobs:
|
||||||
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
||||||
continue-on-error: true # not fatal
|
continue-on-error: true # not fatal
|
||||||
- name: Upload Sytest logs
|
- name: Upload Sytest logs
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Sytest Logs - ${{ job.status }} - (Dendrite ${{ join(matrix.*, ' ') }})
|
name: Sytest Logs - ${{ job.status }} - (Dendrite ${{ join(matrix.*, ' ') }})
|
||||||
|
@ -75,31 +93,34 @@ jobs:
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
name: "Sytest Coverage"
|
name: "Sytest Coverage"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: sytest # only run once Sytest is done
|
needs: [ sytest, check_date ] # only run once Sytest is done and there was a commit
|
||||||
if: ${{ always() }}
|
if: ${{ always() && needs.check_date.outputs.should_run != 'false' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 'stable'
|
go-version: 'stable'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
- name: Collect coverage
|
- name: Collect coverage
|
||||||
run: |
|
run: |
|
||||||
go tool covdata textfmt -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o sytest.cov
|
go tool covdata textfmt -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o sytest.cov
|
||||||
grep -Ev 'relayapi|setup/mscs|api_trace' sytest.cov > final.cov
|
grep -Ev 'relayapi|setup/mscs|api_trace' sytest.cov > final.cov
|
||||||
go tool covdata func -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
|
go tool covdata func -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
files: ./final.cov
|
files: ./final.cov
|
||||||
flags: sytest
|
flags: sytest
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
# run Complement
|
# run Complement
|
||||||
complement:
|
complement:
|
||||||
|
needs: check_date
|
||||||
|
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||||
name: "Complement (${{ matrix.label }})"
|
name: "Complement (${{ matrix.label }})"
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -129,8 +150,8 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
||||||
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||||
- name: Run actions/checkout@v3 for dendrite
|
- name: Run actions/checkout@v4 for dendrite
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: dendrite
|
path: dendrite
|
||||||
|
|
||||||
|
@ -174,7 +195,7 @@ jobs:
|
||||||
# Run Complement
|
# Run Complement
|
||||||
- run: |
|
- run: |
|
||||||
set -o pipefail &&
|
set -o pipefail &&
|
||||||
go test -v -json -tags dendrite_blacklist ./tests/... 2>&1 | gotestfmt
|
go test -v -json -tags dendrite_blacklist ./tests ./tests/csapi 2>&1 | gotestfmt -hide all
|
||||||
shell: bash
|
shell: bash
|
||||||
name: Run Complement Tests
|
name: Run Complement Tests
|
||||||
env:
|
env:
|
||||||
|
@ -185,7 +206,7 @@ jobs:
|
||||||
working-directory: complement
|
working-directory: complement
|
||||||
|
|
||||||
- name: Upload Complement logs
|
- name: Upload Complement logs
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Complement Logs - (Dendrite ${{ join(matrix.*, ' ') }})
|
name: Complement Logs - (Dendrite ${{ join(matrix.*, ' ') }})
|
||||||
|
@ -196,30 +217,32 @@ jobs:
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
name: "Complement Coverage"
|
name: "Complement Coverage"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: complement # only run once Complement is done
|
needs: [ complement, check_date ] # only run once Complements is done and there was a commit
|
||||||
if: ${{ always() }}
|
if: ${{ always() && needs.check_date.outputs.should_run != 'false' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 'stable'
|
go-version: 'stable'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
- name: Collect coverage
|
- name: Collect coverage
|
||||||
run: |
|
run: |
|
||||||
go tool covdata textfmt -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o complement.cov
|
go tool covdata textfmt -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o complement.cov
|
||||||
grep -Ev 'relayapi|setup/mscs|api_trace' complement.cov > final.cov
|
grep -Ev 'relayapi|setup/mscs|api_trace' complement.cov > final.cov
|
||||||
go tool covdata func -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
|
go tool covdata func -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
files: ./final.cov
|
files: ./final.cov
|
||||||
flags: complement
|
flags: complement
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }} # required
|
||||||
|
|
||||||
element-web:
|
element-web:
|
||||||
|
if: ${{ false }} # disable for now, as Cypress has been replaced by Playwright
|
||||||
timeout-minutes: 120
|
timeout-minutes: 120
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -228,7 +251,7 @@ jobs:
|
||||||
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
||||||
# supposed to be covered by STIXGeneral.
|
# supposed to be covered by STIXGeneral.
|
||||||
tools: fonts-stix
|
tools: fonts-stix
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: matrix-org/matrix-react-sdk
|
repository: matrix-org/matrix-react-sdk
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
@ -259,6 +282,7 @@ jobs:
|
||||||
TMPDIR: ${{ runner.temp }}
|
TMPDIR: ${{ runner.temp }}
|
||||||
|
|
||||||
element-web-pinecone:
|
element-web-pinecone:
|
||||||
|
if: ${{ false }} # disable for now, as Cypress has been replaced by Playwright
|
||||||
timeout-minutes: 120
|
timeout-minutes: 120
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -267,7 +291,7 @@ jobs:
|
||||||
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
||||||
# supposed to be covered by STIXGeneral.
|
# supposed to be covered by STIXGeneral.
|
||||||
tools: fonts-stix
|
tools: fonts-stix
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: matrix-org/matrix-react-sdk
|
repository: matrix-org/matrix-react-sdk
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -77,4 +77,7 @@ media_store/
|
||||||
build
|
build
|
||||||
|
|
||||||
# golang workspaces
|
# golang workspaces
|
||||||
go.work*
|
go.work*
|
||||||
|
|
||||||
|
# helm chart
|
||||||
|
helm/dendrite/charts/
|
||||||
|
|
|
@ -6,7 +6,7 @@ run:
|
||||||
concurrency: 4
|
concurrency: 4
|
||||||
|
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
deadline: 30m
|
timeout: 5m
|
||||||
|
|
||||||
# exit code when at least one issue was found, default is 1
|
# exit code when at least one issue was found, default is 1
|
||||||
issues-exit-code: 1
|
issues-exit-code: 1
|
||||||
|
@ -18,24 +18,6 @@ run:
|
||||||
#build-tags:
|
#build-tags:
|
||||||
# - mytag
|
# - mytag
|
||||||
|
|
||||||
# which dirs to skip: they won't be analyzed;
|
|
||||||
# can use regexp here: generated.*, regexp is applied on full path;
|
|
||||||
# default value is empty list, but next dirs are always skipped independently
|
|
||||||
# from this option's value:
|
|
||||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
|
||||||
skip-dirs:
|
|
||||||
- bin
|
|
||||||
- docs
|
|
||||||
|
|
||||||
# which files to skip: they will be analyzed, but issues from them
|
|
||||||
# won't be reported. Default value is empty list, but there is
|
|
||||||
# no need to include all autogenerated files, we confidently recognize
|
|
||||||
# autogenerated files. If it's not please let us know.
|
|
||||||
skip-files:
|
|
||||||
- ".*\\.md$"
|
|
||||||
- ".*\\.sh$"
|
|
||||||
- "^cmd/syncserver-integration-tests/testdata.go$"
|
|
||||||
|
|
||||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||||
|
@ -50,7 +32,8 @@ run:
|
||||||
# output configuration options
|
# output configuration options
|
||||||
output:
|
output:
|
||||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||||
format: colored-line-number
|
formats:
|
||||||
|
- format: colored-line-number
|
||||||
|
|
||||||
# print lines of code with issue, default is true
|
# print lines of code with issue, default is true
|
||||||
print-issued-lines: true
|
print-issued-lines: true
|
||||||
|
@ -79,9 +62,8 @@ linters-settings:
|
||||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||||
#exclude: /path/to/file.txt
|
#exclude: /path/to/file.txt
|
||||||
govet:
|
govet:
|
||||||
# report about shadowed variables
|
enable:
|
||||||
check-shadowing: true
|
- shadow
|
||||||
|
|
||||||
# settings per analyzer
|
# settings per analyzer
|
||||||
settings:
|
settings:
|
||||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||||
|
@ -217,6 +199,24 @@ linters:
|
||||||
|
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
# which files to skip: they will be analyzed, but issues from them
|
||||||
|
# won't be reported. Default value is empty list, but there is
|
||||||
|
# no need to include all autogenerated files, we confidently recognize
|
||||||
|
# autogenerated files. If it's not please let us know.
|
||||||
|
exclude-files:
|
||||||
|
- ".*\\.md$"
|
||||||
|
- ".*\\.sh$"
|
||||||
|
- "^cmd/syncserver-integration-tests/testdata.go$"
|
||||||
|
|
||||||
|
# which dirs to skip: they won't be analyzed;
|
||||||
|
# can use regexp here: generated.*, regexp is applied on full path;
|
||||||
|
# default value is empty list, but next dirs are always skipped independently
|
||||||
|
# from this option's value:
|
||||||
|
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||||
|
exclude-dirs:
|
||||||
|
- bin
|
||||||
|
- docs
|
||||||
|
|
||||||
# List of regexps of issue texts to exclude, empty list by default.
|
# List of regexps of issue texts to exclude, empty list by default.
|
||||||
# But independently from this option we use default exclude patterns,
|
# But independently from this option we use default exclude patterns,
|
||||||
# it can be disabled by `exclude-use-default: false`. To list all
|
# it can be disabled by `exclude-use-default: false`. To list all
|
||||||
|
|
86
CHANGES.md
86
CHANGES.md
|
@ -1,5 +1,91 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Dendrite 0.13.8 (2024-09-13)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- The required Go version to build Dendrite is now 1.21
|
||||||
|
- Support for authenticated media ([MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916)) has been added
|
||||||
|
- NATS can now connect to servers requiring authentication (contributed by [paigeadelethompson](https://github.com/paigeadelethompson))
|
||||||
|
- Updated dependencies
|
||||||
|
- Internal NATS Server has been updated from v2.10.7 to v2.10.20 (contributed by [neilalexander](https://github.com/neilalexander))
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Fix parsing `?ts` query param (contributed by [tulir](https://github.com/tulir))
|
||||||
|
- Don't query the database if we could fetch all keys from cache
|
||||||
|
- Fix media DB potentially leaking connections
|
||||||
|
- Fixed a bug where we would return that an account exists if we encountered an unhandled error case
|
||||||
|
- Fixed an issues where edited message could appear twice in search results (contributed by [adnull](https://github.com/adnull))
|
||||||
|
- Outgoing threepid HTTP requests now correctly close the returned body (contributed by [ testwill](https://github.com/testwill))
|
||||||
|
- Presence conflicts are handled better, reducing the amount of outgoing federation requests (contributed by [jjj333-p](https://github.com/jjj333-p))
|
||||||
|
- Internal NATS now uses `SyncAlways` which should improve resilience against crashes (contributed by [neilalexander](https://github.com/neilalexander))
|
||||||
|
- Whitespaces in the `X-Matrix` header are now handled correctly
|
||||||
|
- `/.well-known/matrix/server` lookups now timeout after 30 seconds
|
||||||
|
- Purging rooms has seen a huge speed-up
|
||||||
|
|
||||||
|
## Dendrite 0.13.7 (2024-04-09)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Fixed an issue where the displayname/avatar of an invited user was replaced with the inviter's details
|
||||||
|
- Improved server startup performance by avoiding unnecessary room ACL queries
|
||||||
|
- This change reduces memory footprint as it caches ACL regex patterns once instead of for each room
|
||||||
|
- Unnecessary Relay related queries have been removed. **Note**: To use relays, you now need to explicitly enable them using the `federation_api.enable_relays` config
|
||||||
|
- Fixed space summaries over federation
|
||||||
|
- Improved usage of external NATS JetStream by reusing existing connections instead of opening new ones unnecessarily
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Modernized Appservices (contributed by [tulir](https://github.com/tulir))
|
||||||
|
- Added event reporting with Synapse Admin endpoints for querying them
|
||||||
|
- Updated dependencies
|
||||||
|
|
||||||
|
## Dendrite 0.13.6 (2024-01-26)
|
||||||
|
|
||||||
|
Upgrading to this version is **highly** recommended, as it contains several QoL improvements.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Use `AckExplicitPolicy` for JetStream consumers, so messages don't pile up in NATS
|
||||||
|
- A rare panic when assigning a state key NID has been fixed
|
||||||
|
- A rare panic when checking powerlevels has been fixed
|
||||||
|
- Notary keys requests for all keys now work correctly
|
||||||
|
- Spec compliance:
|
||||||
|
- Return `M_INVALID_PARAM` when querying room aliases
|
||||||
|
- Handle empty `from` parameter when requesting `/messages`
|
||||||
|
- Add CORP headers on media endpoints
|
||||||
|
- Remove `aliases` from `/publicRooms` responses
|
||||||
|
- Allow `+` in MXIDs (Contributed by [RosstheRoss](https://github.com/RosstheRoss))
|
||||||
|
- Fixes membership transitions from `knock` to `join` in `knock_restricted` rooms
|
||||||
|
- Incremental syncs now batch querying events (Contributed by [recht](https://github.com/recht))
|
||||||
|
- Move `/joined_members` back to the clientAPI/roomserver, which should make bridges happier again
|
||||||
|
- Backfilling from other servers now only uses at max 100 events instead of potentially thousands
|
||||||
|
|
||||||
|
## Dendrite 0.13.5 (2023-12-12)
|
||||||
|
|
||||||
|
Upgrading to this version is **highly** recommended, as it fixes several long-standing bugs in
|
||||||
|
our CanonicalJSON implementation.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Convert unicode escapes to lowercase (gomatrixserverlib)
|
||||||
|
- Fix canonical json utf-16 surrogate pair detection logic (gomatrixserverlib)
|
||||||
|
- Handle negative zero and exponential numbers in Canonical JSON verification (gomatrixserverlib)
|
||||||
|
- Avoid logging unnecessary messages when unable to fetch server keys if multiple fetchers are used (gomatrixserverlib)
|
||||||
|
- Issues around the device list updater have been fixed, which should ensure that there are always
|
||||||
|
workers available to process incoming device list updates.
|
||||||
|
- A panic in the `/hierarchy` endpoints used for spaces has been fixed (client-server and server-server API)
|
||||||
|
- Fixes around the way we handle database transactions (including a potential connection leak)
|
||||||
|
- ACLs are now updated when received as outliers
|
||||||
|
- A race condition, which could lead to bridges instantly leaving a room after joining it, between the SyncAPI and
|
||||||
|
Appservices has been fixed
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- **Appservice login is now supported!**
|
||||||
|
- Users can now kick themselves (used by some bridges)
|
||||||
|
|
||||||
## Dendrite 0.13.4 (2023-10-25)
|
## Dendrite 0.13.4 (2023-10-25)
|
||||||
|
|
||||||
Upgrading to this version is **highly** recommended, as it fixes a long-standing bug in the state resolution
|
Upgrading to this version is **highly** recommended, as it fixes a long-standing bug in the state resolution
|
||||||
|
|
59
CONTRIBUTING.md
Normal file
59
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# Contributing to Dendrite
|
||||||
|
|
||||||
|
Thank you for taking the time to contribute to Matrix!
|
||||||
|
|
||||||
|
This is the repository for Dendrite, a second-generation Matrix homeserver written in Go.
|
||||||
|
|
||||||
|
## Sign off
|
||||||
|
|
||||||
|
We ask that everybody who contributes to this project signs off their contributions, as explained below.
|
||||||
|
|
||||||
|
We follow a simple 'inbound=outbound' model for contributions: the act of submitting an 'inbound' contribution means that the contributor agrees to license their contribution under the same terms as the project's overall 'outbound' license - in our case, this is Apache Software License v2 (see [LICENSE](./LICENSE)).
|
||||||
|
|
||||||
|
In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as the project's license, we've adopted the same lightweight approach used by the [Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html), [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other projects: the [Developer Certificate of Origin](https://developercertificate.org/) (DCO). This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to Matrix:
|
||||||
|
|
||||||
|
```
|
||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
660 York Street, Suite 102,
|
||||||
|
San Francisco, CA 94110 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
|
If you agree to this for your contribution, then all that's needed is to include the line in your commit or pull request comment:
|
||||||
|
|
||||||
|
```
|
||||||
|
Signed-off-by: Your Name <your@email.example.org>
|
||||||
|
```
|
||||||
|
|
||||||
|
Git allows you to add this signoff automatically when using the `-s` flag to `git commit`, which uses the name and email set in your `user.name` and `user.email` git configs.
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# base installs required dependencies and runs go mod download to cache dependencies
|
# base installs required dependencies and runs go mod download to cache dependencies
|
||||||
#
|
#
|
||||||
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21-alpine AS base
|
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.22-alpine AS base
|
||||||
RUN apk --update --no-cache add bash build-base curl git
|
RUN apk --update --no-cache add bash build-base curl git
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
10
README.md
10
README.md
|
@ -1,5 +1,13 @@
|
||||||
# Dendrite
|
# Dendrite
|
||||||
|
|
||||||
|
## Dendrite is now maintained at [element-hq/dendrite](https://github.com/element-hq/dendrite)
|
||||||
|
|
||||||
|
Dendrite is an open-source [Matrix](https://matrix.org/) homeserver developed from 2019 through 2023 as part of the Matrix.org Foundation.
|
||||||
|
The Matrix.org Foundation is not able to resource maintenance of Dendrite and it [continues to be developed by Element](https://github.com/element-hq/dendrite)
|
||||||
|
additionally you have the choice of [other Matrix homeservers](https://matrix.org/ecosystem/servers/>)
|
||||||
|
|
||||||
|
See [The future of Synapse and Dendrite](https://matrix.org/blog/2023/11/06/future-of-synapse-dendrite/) blog post for more information.
|
||||||
|
|
||||||
[![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)
|
[![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.
|
||||||
|
@ -36,7 +44,7 @@ If you have further questions, please take a look at [our FAQ](docs/FAQ.md) or j
|
||||||
See the [Planning your Installation](https://matrix-org.github.io/dendrite/installation/planning) page for
|
See the [Planning your Installation](https://matrix-org.github.io/dendrite/installation/planning) page for
|
||||||
more information on requirements.
|
more information on requirements.
|
||||||
|
|
||||||
To build Dendrite, you will need Go 1.20 or later.
|
To build Dendrite, you will need Go 1.21 or later.
|
||||||
|
|
||||||
For a usable federating Dendrite deployment, you will also need:
|
For a usable federating Dendrite deployment, you will also need:
|
||||||
|
|
||||||
|
|
|
@ -82,9 +82,17 @@ type UserIDExistsResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ASProtocolPath = "/_matrix/app/unstable/thirdparty/protocol/"
|
ASProtocolLegacyPath = "/_matrix/app/unstable/thirdparty/protocol/"
|
||||||
ASUserPath = "/_matrix/app/unstable/thirdparty/user"
|
ASUserLegacyPath = "/_matrix/app/unstable/thirdparty/user"
|
||||||
ASLocationPath = "/_matrix/app/unstable/thirdparty/location"
|
ASLocationLegacyPath = "/_matrix/app/unstable/thirdparty/location"
|
||||||
|
ASRoomAliasExistsLegacyPath = "/rooms/"
|
||||||
|
ASUserExistsLegacyPath = "/users/"
|
||||||
|
|
||||||
|
ASProtocolPath = "/_matrix/app/v1/thirdparty/protocol/"
|
||||||
|
ASUserPath = "/_matrix/app/v1/thirdparty/user"
|
||||||
|
ASLocationPath = "/_matrix/app/v1/thirdparty/location"
|
||||||
|
ASRoomAliasExistsPath = "/_matrix/app/v1/rooms/"
|
||||||
|
ASUserExistsPath = "/_matrix/app/v1/users/"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProtocolRequest struct {
|
type ProtocolRequest struct {
|
||||||
|
|
|
@ -14,7 +14,18 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||||
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/types"
|
||||||
|
"github.com/matrix-org/dendrite/syncapi"
|
||||||
|
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/appservice"
|
"github.com/matrix-org/dendrite/appservice"
|
||||||
"github.com/matrix-org/dendrite/appservice/api"
|
"github.com/matrix-org/dendrite/appservice/api"
|
||||||
|
@ -32,6 +43,10 @@ import (
|
||||||
"github.com/matrix-org/dendrite/test/testrig"
|
"github.com/matrix-org/dendrite/test/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||||
|
return &statistics.ServerStatistics{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestAppserviceInternalAPI(t *testing.T) {
|
func TestAppserviceInternalAPI(t *testing.T) {
|
||||||
|
|
||||||
// Set expected results
|
// Set expected results
|
||||||
|
@ -144,7 +159,7 @@ func TestAppserviceInternalAPI(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil)
|
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI)
|
||||||
|
|
||||||
runCases(t, asAPI)
|
runCases(t, asAPI)
|
||||||
|
@ -239,7 +254,7 @@ func TestAppserviceInternalAPI_UnixSocket_Simple(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil)
|
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI)
|
||||||
|
|
||||||
t.Run("UserIDExists", func(t *testing.T) {
|
t.Run("UserIDExists", func(t *testing.T) {
|
||||||
|
@ -378,7 +393,7 @@ func TestRoomserverConsumerOneInvite(t *testing.T) {
|
||||||
// Create required internal APIs
|
// Create required internal APIs
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil)
|
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
// start the consumer
|
// start the consumer
|
||||||
appservice.NewInternalAPI(processCtx, cfg, natsInstance, usrAPI, rsAPI)
|
appservice.NewInternalAPI(processCtx, cfg, natsInstance, usrAPI, rsAPI)
|
||||||
|
|
||||||
|
@ -402,3 +417,190 @@ func TestRoomserverConsumerOneInvite(t *testing.T) {
|
||||||
close(evChan)
|
close(evChan)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: If this test panics, it is because we timed out waiting for the
|
||||||
|
// join event to come through to the appservice and we close the DB/shutdown Dendrite. This makes the
|
||||||
|
// syncAPI unhappy, as it is unable to write to the database.
|
||||||
|
func TestOutputAppserviceEvent(t *testing.T) {
|
||||||
|
alice := test.NewUser(t)
|
||||||
|
bob := test.NewUser(t)
|
||||||
|
|
||||||
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||||
|
cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType)
|
||||||
|
defer closeDB()
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
natsInstance := &jetstream.NATSInstance{}
|
||||||
|
|
||||||
|
evChan := make(chan struct{})
|
||||||
|
|
||||||
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
// Create required internal APIs
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||||
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
|
|
||||||
|
// Create the router, so we can hit `/joined_members`
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
|
||||||
|
accessTokens := map[*test.User]userDevice{
|
||||||
|
bob: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
clientapi.AddPublicRoutes(processCtx, routers, cfg, natsInstance, nil, rsAPI, nil, nil, nil, usrAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
createAccessTokens(t, accessTokens, usrAPI, processCtx.Context(), routers)
|
||||||
|
|
||||||
|
room := test.NewRoom(t, alice)
|
||||||
|
|
||||||
|
// Invite Bob
|
||||||
|
room.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "invite",
|
||||||
|
}, test.WithStateKey(bob.ID))
|
||||||
|
|
||||||
|
// create a dummy AS url, handling the events
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var txn consumers.ApplicationServiceTransaction
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&txn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, ev := range txn.Events {
|
||||||
|
if ev.Type != spec.MRoomMember {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ev.StateKey != nil && *ev.StateKey == bob.ID {
|
||||||
|
membership := gjson.GetBytes(ev.Content, "membership").Str
|
||||||
|
t.Logf("Processing membership: %s", membership)
|
||||||
|
switch membership {
|
||||||
|
case spec.Invite:
|
||||||
|
// Accept the invite
|
||||||
|
joinEv := room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "join",
|
||||||
|
}, test.WithStateKey(bob.ID))
|
||||||
|
|
||||||
|
if err := rsapi.SendEvents(context.Background(), rsAPI, rsapi.KindNew, []*types.HeaderedEvent{joinEv}, "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
case spec.Join: // the AS has received the join event, now hit `/joined_members` to validate that
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/_matrix/client/v3/rooms/"+room.ID+"/joined_members", nil)
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||||
|
routers.Client.ServeHTTP(rec, req)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected HTTP 200, got %d: %s", rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both Alice and Bob should be joined. If not, we have a race condition
|
||||||
|
if !gjson.GetBytes(rec.Body.Bytes(), "joined."+alice.ID).Exists() {
|
||||||
|
t.Errorf("Alice is not joined to the room") // in theory should not happen
|
||||||
|
}
|
||||||
|
if !gjson.GetBytes(rec.Body.Bytes(), "joined."+bob.ID).Exists() {
|
||||||
|
t.Errorf("Bob is not joined to the room")
|
||||||
|
}
|
||||||
|
evChan <- struct{}{}
|
||||||
|
default:
|
||||||
|
t.Fatalf("Unexpected membership: %s", membership)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
as := &config.ApplicationService{
|
||||||
|
ID: "someID",
|
||||||
|
URL: srv.URL,
|
||||||
|
ASToken: "",
|
||||||
|
HSToken: "",
|
||||||
|
SenderLocalpart: "senderLocalPart",
|
||||||
|
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
|
||||||
|
"users": {{RegexpObject: regexp.MustCompile(bob.ID)}},
|
||||||
|
"aliases": {{RegexpObject: regexp.MustCompile(room.ID)}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
as.CreateHTTPClient(cfg.AppServiceAPI.DisableTLSValidation)
|
||||||
|
|
||||||
|
// Create a dummy application service
|
||||||
|
cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as}
|
||||||
|
|
||||||
|
// Prepare AS Streams on the old topic to validate that they get deleted
|
||||||
|
jsCtx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||||
|
|
||||||
|
token := jetstream.Tokenise(as.ID)
|
||||||
|
if err := jetstream.JetStreamConsumer(
|
||||||
|
processCtx.Context(), jsCtx, cfg.Global.JetStream.Prefixed(jetstream.OutputRoomEvent),
|
||||||
|
cfg.Global.JetStream.Durable("Appservice_"+token),
|
||||||
|
50, // maximum number of events to send in a single transaction
|
||||||
|
func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the syncAPI to have `/joined_members` available
|
||||||
|
syncapi.AddPublicRoutes(processCtx, routers, cfg, cm, natsInstance, usrAPI, rsAPI, caches, caching.DisableMetrics)
|
||||||
|
|
||||||
|
// start the consumer
|
||||||
|
appservice.NewInternalAPI(processCtx, cfg, natsInstance, usrAPI, rsAPI)
|
||||||
|
|
||||||
|
// At this point, the old JetStream consumers should be deleted
|
||||||
|
for consumer := range jsCtx.Consumers(cfg.Global.JetStream.Prefixed(jetstream.OutputRoomEvent)) {
|
||||||
|
if consumer.Name == cfg.Global.JetStream.Durable("Appservice_"+token)+"Pull" {
|
||||||
|
t.Fatalf("Consumer still exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the room, this triggers the AS to receive an invite for Bob.
|
||||||
|
if err := rsapi.SendEvents(context.Background(), rsAPI, rsapi.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
// Pretty generous timeout duration...
|
||||||
|
case <-time.After(time.Millisecond * 1000): // wait for the AS to process the events
|
||||||
|
t.Errorf("Timed out waiting for join event")
|
||||||
|
case <-evChan:
|
||||||
|
}
|
||||||
|
close(evChan)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type userDevice struct {
|
||||||
|
accessToken string
|
||||||
|
deviceID string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAccessTokens(t *testing.T, accessTokens map[*test.User]userDevice, userAPI uapi.UserInternalAPI, ctx context.Context, routers httputil.Routers) {
|
||||||
|
t.Helper()
|
||||||
|
for u := range accessTokens {
|
||||||
|
localpart, serverName, _ := gomatrixserverlib.SplitID('@', u.ID)
|
||||||
|
userRes := &uapi.PerformAccountCreationResponse{}
|
||||||
|
password := util.RandomString(8)
|
||||||
|
if err := userAPI.PerformAccountCreation(ctx, &uapi.PerformAccountCreationRequest{
|
||||||
|
AccountType: u.AccountType,
|
||||||
|
Localpart: localpart,
|
||||||
|
ServerName: serverName,
|
||||||
|
Password: password,
|
||||||
|
}, userRes); err != nil {
|
||||||
|
t.Errorf("failed to create account: %s", err)
|
||||||
|
}
|
||||||
|
req := test.NewRequest(t, http.MethodPost, "/_matrix/client/v3/login", test.WithJSONBody(t, map[string]interface{}{
|
||||||
|
"type": authtypes.LoginTypePassword,
|
||||||
|
"identifier": map[string]interface{}{
|
||||||
|
"type": "m.id.user",
|
||||||
|
"user": u.ID,
|
||||||
|
},
|
||||||
|
"password": password,
|
||||||
|
}))
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
routers.Client.ServeHTTP(rec, req)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("failed to login: %s", rec.Body.String())
|
||||||
|
}
|
||||||
|
accessTokens[u] = userDevice{
|
||||||
|
accessToken: gjson.GetBytes(rec.Body.Bytes(), "access_token").String(),
|
||||||
|
deviceID: gjson.GetBytes(rec.Body.Bytes(), "device_id").String(),
|
||||||
|
password: password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -71,13 +71,14 @@ func NewOutputRoomEventConsumer(
|
||||||
ctx: process.Context(),
|
ctx: process.Context(),
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
jetstream: js,
|
jetstream: js,
|
||||||
topic: cfg.Matrix.JetStream.Prefixed(jetstream.OutputRoomEvent),
|
topic: cfg.Matrix.JetStream.Prefixed(jetstream.OutputAppserviceEvent),
|
||||||
rsAPI: rsAPI,
|
rsAPI: rsAPI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start consuming from room servers
|
// Start consuming from room servers
|
||||||
func (s *OutputRoomEventConsumer) Start() error {
|
func (s *OutputRoomEventConsumer) Start() error {
|
||||||
|
durableNames := make([]string, 0, len(s.cfg.Derived.ApplicationServices))
|
||||||
for _, as := range s.cfg.Derived.ApplicationServices {
|
for _, as := range s.cfg.Derived.ApplicationServices {
|
||||||
appsvc := as
|
appsvc := as
|
||||||
state := &appserviceState{
|
state := &appserviceState{
|
||||||
|
@ -95,6 +96,15 @@ func (s *OutputRoomEventConsumer) Start() error {
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return fmt.Errorf("failed to create %q consumer: %w", token, err)
|
return fmt.Errorf("failed to create %q consumer: %w", token, err)
|
||||||
}
|
}
|
||||||
|
durableNames = append(durableNames, s.cfg.Matrix.JetStream.Durable("Appservice_"+token))
|
||||||
|
}
|
||||||
|
// Cleanup any consumers still existing on the OutputRoomEvent stream
|
||||||
|
// to avoid messages not being deleted
|
||||||
|
for _, consumerName := range durableNames {
|
||||||
|
err := s.jetstream.DeleteConsumer(s.cfg.Matrix.JetStream.Prefixed(jetstream.OutputRoomEvent), consumerName+"Pull")
|
||||||
|
if err != nil && err != nats.ErrConsumerNotFound {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -196,13 +206,21 @@ func (s *OutputRoomEventConsumer) sendEvents(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the transaction to the appservice.
|
// Send the transaction to the appservice.
|
||||||
// https://matrix.org/docs/spec/application_service/r0.1.2#put-matrix-app-v1-transactions-txnid
|
// https://spec.matrix.org/v1.9/application-service-api/#pushing-events
|
||||||
address := fmt.Sprintf("%s/transactions/%s?access_token=%s", state.RequestUrl(), txnID, url.QueryEscape(state.HSToken))
|
path := "_matrix/app/v1/transactions"
|
||||||
|
if s.cfg.LegacyPaths {
|
||||||
|
path = "transactions"
|
||||||
|
}
|
||||||
|
address := fmt.Sprintf("%s/%s/%s", state.RequestUrl(), path, txnID)
|
||||||
|
if s.cfg.LegacyAuth {
|
||||||
|
address += "?access_token=" + url.QueryEscape(state.HSToken)
|
||||||
|
}
|
||||||
req, err := http.NewRequestWithContext(ctx, "PUT", address, bytes.NewBuffer(transaction))
|
req, err := http.NewRequestWithContext(ctx, "PUT", address, bytes.NewBuffer(transaction))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", state.HSToken))
|
||||||
resp, err := state.HTTPClient.Do(req)
|
resp, err := state.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.backoffAndPause(err)
|
return state.backoffAndPause(err)
|
||||||
|
|
|
@ -19,10 +19,10 @@ package query
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
@ -32,9 +32,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const roomAliasExistsPath = "/rooms/"
|
|
||||||
const userIDExistsPath = "/users/"
|
|
||||||
|
|
||||||
// AppServiceQueryAPI is an implementation of api.AppServiceQueryAPI
|
// AppServiceQueryAPI is an implementation of api.AppServiceQueryAPI
|
||||||
type AppServiceQueryAPI struct {
|
type AppServiceQueryAPI struct {
|
||||||
Cfg *config.AppServiceAPI
|
Cfg *config.AppServiceAPI
|
||||||
|
@ -55,14 +52,23 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
|
||||||
// Determine which application service should handle this request
|
// Determine which application service should handle this request
|
||||||
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
||||||
if appservice.URL != "" && appservice.IsInterestedInRoomAlias(request.Alias) {
|
if appservice.URL != "" && appservice.IsInterestedInRoomAlias(request.Alias) {
|
||||||
|
path := api.ASRoomAliasExistsPath
|
||||||
|
if a.Cfg.LegacyPaths {
|
||||||
|
path = api.ASRoomAliasExistsLegacyPath
|
||||||
|
}
|
||||||
// The full path to the rooms API, includes hs token
|
// The full path to the rooms API, includes hs token
|
||||||
URL, err := url.Parse(appservice.RequestUrl() + roomAliasExistsPath)
|
URL, err := url.Parse(appservice.RequestUrl() + path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
URL.Path += request.Alias
|
URL.Path += request.Alias
|
||||||
apiURL := URL.String() + "?access_token=" + appservice.HSToken
|
if a.Cfg.LegacyAuth {
|
||||||
|
q := URL.Query()
|
||||||
|
q.Set("access_token", appservice.HSToken)
|
||||||
|
URL.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
|
apiURL := URL.String()
|
||||||
|
|
||||||
// Send a request to each application service. If one responds that it has
|
// Send a request to each application service. If one responds that it has
|
||||||
// created the room, immediately return.
|
// created the room, immediately return.
|
||||||
|
@ -70,6 +76,7 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", appservice.HSToken))
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
resp, err := appservice.HTTPClient.Do(req)
|
resp, err := appservice.HTTPClient.Do(req)
|
||||||
|
@ -123,12 +130,21 @@ func (a *AppServiceQueryAPI) UserIDExists(
|
||||||
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
||||||
if appservice.URL != "" && appservice.IsInterestedInUserID(request.UserID) {
|
if appservice.URL != "" && appservice.IsInterestedInUserID(request.UserID) {
|
||||||
// The full path to the rooms API, includes hs token
|
// The full path to the rooms API, includes hs token
|
||||||
URL, err := url.Parse(appservice.RequestUrl() + userIDExistsPath)
|
path := api.ASUserExistsPath
|
||||||
|
if a.Cfg.LegacyPaths {
|
||||||
|
path = api.ASUserExistsLegacyPath
|
||||||
|
}
|
||||||
|
URL, err := url.Parse(appservice.RequestUrl() + path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
URL.Path += request.UserID
|
URL.Path += request.UserID
|
||||||
apiURL := URL.String() + "?access_token=" + appservice.HSToken
|
if a.Cfg.LegacyAuth {
|
||||||
|
q := URL.Query()
|
||||||
|
q.Set("access_token", appservice.HSToken)
|
||||||
|
URL.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
|
apiURL := URL.String()
|
||||||
|
|
||||||
// Send a request to each application service. If one responds that it has
|
// Send a request to each application service. If one responds that it has
|
||||||
// created the user, immediately return.
|
// created the user, immediately return.
|
||||||
|
@ -136,6 +152,7 @@ func (a *AppServiceQueryAPI) UserIDExists(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", appservice.HSToken))
|
||||||
resp, err := appservice.HTTPClient.Do(req.WithContext(ctx))
|
resp, err := appservice.HTTPClient.Do(req.WithContext(ctx))
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -176,25 +193,22 @@ type thirdpartyResponses interface {
|
||||||
api.ASProtocolResponse | []api.ASUserResponse | []api.ASLocationResponse
|
api.ASProtocolResponse | []api.ASUserResponse | []api.ASLocationResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestDo[T thirdpartyResponses](client *http.Client, url string, response *T) (err error) {
|
func requestDo[T thirdpartyResponses](as *config.ApplicationService, url string, response *T) error {
|
||||||
origURL := url
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
// try v1 and unstable appservice endpoints
|
if err != nil {
|
||||||
for _, version := range []string{"v1", "unstable"} {
|
return err
|
||||||
var resp *http.Response
|
|
||||||
var body []byte
|
|
||||||
asURL := strings.Replace(origURL, "unstable", version, 1)
|
|
||||||
resp, err = client.Get(asURL)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
defer resp.Body.Close() // nolint: errcheck
|
|
||||||
body, err = io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return json.Unmarshal(body, &response)
|
|
||||||
}
|
}
|
||||||
return err
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", as.HSToken))
|
||||||
|
resp, err := as.HTTPClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close() // nolint: errcheck
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(body, &response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AppServiceQueryAPI) Locations(
|
func (a *AppServiceQueryAPI) Locations(
|
||||||
|
@ -207,16 +221,22 @@ func (a *AppServiceQueryAPI) Locations(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path := api.ASLocationPath
|
||||||
|
if a.Cfg.LegacyPaths {
|
||||||
|
path = api.ASLocationLegacyPath
|
||||||
|
}
|
||||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||||
var asLocations []api.ASLocationResponse
|
var asLocations []api.ASLocationResponse
|
||||||
params.Set("access_token", as.HSToken)
|
if a.Cfg.LegacyAuth {
|
||||||
|
params.Set("access_token", as.HSToken)
|
||||||
|
}
|
||||||
|
|
||||||
url := as.RequestUrl() + api.ASLocationPath
|
url := as.RequestUrl() + path
|
||||||
if req.Protocol != "" {
|
if req.Protocol != "" {
|
||||||
url += "/" + req.Protocol
|
url += "/" + req.Protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := requestDo[[]api.ASLocationResponse](as.HTTPClient, url+"?"+params.Encode(), &asLocations); err != nil {
|
if err := requestDo[[]api.ASLocationResponse](&as, url+"?"+params.Encode(), &asLocations); err != nil {
|
||||||
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'locations' from application service")
|
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'locations' from application service")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -242,16 +262,22 @@ func (a *AppServiceQueryAPI) User(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path := api.ASUserPath
|
||||||
|
if a.Cfg.LegacyPaths {
|
||||||
|
path = api.ASUserLegacyPath
|
||||||
|
}
|
||||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||||
var asUsers []api.ASUserResponse
|
var asUsers []api.ASUserResponse
|
||||||
params.Set("access_token", as.HSToken)
|
if a.Cfg.LegacyAuth {
|
||||||
|
params.Set("access_token", as.HSToken)
|
||||||
|
}
|
||||||
|
|
||||||
url := as.RequestUrl() + api.ASUserPath
|
url := as.RequestUrl() + path
|
||||||
if req.Protocol != "" {
|
if req.Protocol != "" {
|
||||||
url += "/" + req.Protocol
|
url += "/" + req.Protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := requestDo[[]api.ASUserResponse](as.HTTPClient, url+"?"+params.Encode(), &asUsers); err != nil {
|
if err := requestDo[[]api.ASUserResponse](&as, url+"?"+params.Encode(), &asUsers); err != nil {
|
||||||
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'user' from application service")
|
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'user' from application service")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -272,6 +298,10 @@ func (a *AppServiceQueryAPI) Protocols(
|
||||||
req *api.ProtocolRequest,
|
req *api.ProtocolRequest,
|
||||||
resp *api.ProtocolResponse,
|
resp *api.ProtocolResponse,
|
||||||
) error {
|
) error {
|
||||||
|
protocolPath := api.ASProtocolPath
|
||||||
|
if a.Cfg.LegacyPaths {
|
||||||
|
protocolPath = api.ASProtocolLegacyPath
|
||||||
|
}
|
||||||
|
|
||||||
// get a single protocol response
|
// get a single protocol response
|
||||||
if req.Protocol != "" {
|
if req.Protocol != "" {
|
||||||
|
@ -289,7 +319,7 @@ func (a *AppServiceQueryAPI) Protocols(
|
||||||
response := api.ASProtocolResponse{}
|
response := api.ASProtocolResponse{}
|
||||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||||
var proto api.ASProtocolResponse
|
var proto api.ASProtocolResponse
|
||||||
if err := requestDo[api.ASProtocolResponse](as.HTTPClient, as.RequestUrl()+api.ASProtocolPath+req.Protocol, &proto); err != nil {
|
if err := requestDo[api.ASProtocolResponse](&as, as.RequestUrl()+protocolPath+req.Protocol, &proto); err != nil {
|
||||||
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'protocol' from application service")
|
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'protocol' from application service")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -319,7 +349,7 @@ func (a *AppServiceQueryAPI) Protocols(
|
||||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||||
for _, p := range as.Protocols {
|
for _, p := range as.Protocols {
|
||||||
var proto api.ASProtocolResponse
|
var proto api.ASProtocolResponse
|
||||||
if err := requestDo[api.ASProtocolResponse](as.HTTPClient, as.RequestUrl()+api.ASProtocolPath+p, &proto); err != nil {
|
if err := requestDo[api.ASProtocolResponse](&as, as.RequestUrl()+protocolPath+p, &proto); err != nil {
|
||||||
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'protocol' from application service")
|
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'protocol' from application service")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
"github.com/matrix-org/dendrite/setup/process"
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
"github.com/matrix-org/dendrite/userapi"
|
"github.com/matrix-org/dendrite/userapi"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
@ -190,13 +191,13 @@ func startup() {
|
||||||
serverKeyAPI := &signing.YggdrasilKeys{}
|
serverKeyAPI := &signing.YggdrasilKeys{}
|
||||||
keyRing := serverKeyAPI.KeyRing()
|
keyRing := serverKeyAPI.KeyRing()
|
||||||
|
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation)
|
fedSenderAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true)
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation, caching.EnableMetrics, fedSenderAPI.IsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
asQuery := appservice.NewInternalAPI(
|
asQuery := appservice.NewInternalAPI(
|
||||||
processCtx, cfg, &natsInstance, userAPI, rsAPI,
|
processCtx, cfg, &natsInstance, userAPI, rsAPI,
|
||||||
)
|
)
|
||||||
rsAPI.SetAppserviceAPI(asQuery)
|
rsAPI.SetAppserviceAPI(asQuery)
|
||||||
fedSenderAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true)
|
|
||||||
rsAPI.SetFederationAPI(fedSenderAPI, keyRing)
|
rsAPI.SetFederationAPI(fedSenderAPI, keyRing)
|
||||||
|
|
||||||
monolith := setup.Monolith{
|
monolith := setup.Monolith{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM docker.io/golang:1.21-alpine AS base
|
FROM docker.io/golang:1.22-alpine AS base
|
||||||
|
|
||||||
#
|
#
|
||||||
# Needs to be separate from the main Dockerfile for OpenShift,
|
# Needs to be separate from the main Dockerfile for OpenShift,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM docker.io/golang:1.21-alpine AS base
|
FROM docker.io/golang:1.22 AS base
|
||||||
|
|
||||||
#
|
#
|
||||||
# Needs to be separate from the main Dockerfile for OpenShift,
|
# Needs to be separate from the main Dockerfile for OpenShift,
|
||||||
|
|
|
@ -216,7 +216,7 @@ func (m *DendriteMonolith) Start() {
|
||||||
processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true,
|
processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true,
|
||||||
)
|
)
|
||||||
|
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||||
rsAPI.SetAppserviceAPI(asAPI)
|
rsAPI.SetAppserviceAPI(asAPI)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#syntax=docker/dockerfile:1.2
|
#syntax=docker/dockerfile:1.2
|
||||||
|
|
||||||
FROM golang:1.20-bullseye as build
|
FROM golang:1.22-bookworm as build
|
||||||
RUN apt-get update && apt-get install -y sqlite3
|
RUN apt-get update && apt-get install -y sqlite3
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#
|
#
|
||||||
# Use these mounts to make use of this dockerfile:
|
# Use these mounts to make use of this dockerfile:
|
||||||
# COMPLEMENT_HOST_MOUNTS='/your/local/dendrite:/dendrite:ro;/your/go/path:/go:ro'
|
# COMPLEMENT_HOST_MOUNTS='/your/local/dendrite:/dendrite:ro;/your/go/path:/go:ro'
|
||||||
FROM golang:1.18-stretch
|
FROM golang:1.22-bookworm
|
||||||
RUN apt-get update && apt-get install -y sqlite3
|
RUN apt-get update && apt-get install -y sqlite3
|
||||||
|
|
||||||
ENV SERVER_NAME=localhost
|
ENV SERVER_NAME=localhost
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
#syntax=docker/dockerfile:1.2
|
#syntax=docker/dockerfile:1.2
|
||||||
|
|
||||||
FROM golang:1.20-bullseye as build
|
FROM golang:1.22-bookworm as build
|
||||||
RUN apt-get update && apt-get install -y postgresql
|
RUN apt-get update && apt-get install -y postgresql
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# No password when connecting over localhost
|
# No password when connecting to Postgres
|
||||||
RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/postgresql/13/main/pg_hba.conf && \
|
RUN sed -i "s%peer%trust%g" /etc/postgresql/15/main/pg_hba.conf && \
|
||||||
# Bump up max conns for moar concurrency
|
# Bump up max conns for moar concurrency
|
||||||
sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/13/main/postgresql.conf
|
sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/15/main/postgresql.conf
|
||||||
|
|
||||||
# This entry script starts postgres, waits for it to be up then starts dendrite
|
# This entry script starts postgres, waits for it to be up then starts dendrite
|
||||||
RUN echo '\
|
RUN echo '\
|
||||||
#!/bin/bash -eu \n\
|
#!/bin/bash -eu \n\
|
||||||
pg_lsclusters \n\
|
pg_lsclusters \n\
|
||||||
pg_ctlcluster 13 main start \n\
|
pg_ctlcluster 15 main start \n\
|
||||||
\n\
|
\n\
|
||||||
until pg_isready \n\
|
until pg_isready \n\
|
||||||
do \n\
|
do \n\
|
||||||
|
@ -50,7 +50,7 @@ EXPOSE 8008 8448
|
||||||
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
# At runtime, generate TLS cert based on the CA now mounted at /ca
|
||||||
# At runtime, replace the SERVER_NAME with what we are told
|
# At runtime, replace the SERVER_NAME with what we are told
|
||||||
CMD /build/run_postgres.sh && ./generate-keys --keysize 1024 --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key && \
|
CMD /build/run_postgres.sh && ./generate-keys --keysize 1024 --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key && \
|
||||||
./generate-config -server $SERVER_NAME --ci --db postgresql://postgres@localhost/postgres?sslmode=disable > dendrite.yaml && \
|
./generate-config -server $SERVER_NAME --ci --db "user=postgres database=postgres host=/var/run/postgresql/" > dendrite.yaml && \
|
||||||
# Bump max_open_conns up here in the global database config
|
# Bump max_open_conns up here in the global database config
|
||||||
sed -i 's/max_open_conns:.*$/max_open_conns: 1990/g' dendrite.yaml && \
|
sed -i 's/max_open_conns:.*$/max_open_conns: 1990/g' dendrite.yaml && \
|
||||||
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
|
||||||
|
|
|
@ -2,10 +2,12 @@ package clientapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -45,7 +47,7 @@ func TestAdminCreateToken(t *testing.T) {
|
||||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
accessTokens := map[*test.User]userDevice{
|
accessTokens := map[*test.User]userDevice{
|
||||||
aliceAdmin: {},
|
aliceAdmin: {},
|
||||||
|
@ -196,7 +198,7 @@ func TestAdminListRegistrationTokens(t *testing.T) {
|
||||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
accessTokens := map[*test.User]userDevice{
|
accessTokens := map[*test.User]userDevice{
|
||||||
aliceAdmin: {},
|
aliceAdmin: {},
|
||||||
|
@ -314,7 +316,7 @@ func TestAdminGetRegistrationToken(t *testing.T) {
|
||||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
accessTokens := map[*test.User]userDevice{
|
accessTokens := map[*test.User]userDevice{
|
||||||
aliceAdmin: {},
|
aliceAdmin: {},
|
||||||
|
@ -415,7 +417,7 @@ func TestAdminDeleteRegistrationToken(t *testing.T) {
|
||||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
accessTokens := map[*test.User]userDevice{
|
accessTokens := map[*test.User]userDevice{
|
||||||
aliceAdmin: {},
|
aliceAdmin: {},
|
||||||
|
@ -509,7 +511,7 @@ func TestAdminUpdateRegistrationToken(t *testing.T) {
|
||||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
accessTokens := map[*test.User]userDevice{
|
accessTokens := map[*test.User]userDevice{
|
||||||
aliceAdmin: {},
|
aliceAdmin: {},
|
||||||
|
@ -693,7 +695,7 @@ func TestAdminResetPassword(t *testing.T) {
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
// Needed for changing the password/login
|
// Needed for changing the password/login
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
// We mostly need the userAPI for this test, so nil for other APIs/caches etc.
|
// We mostly need the userAPI for this test, so nil for other APIs/caches etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
@ -791,7 +793,7 @@ func TestPurgeRoom(t *testing.T) {
|
||||||
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, rsAPI, caches, nil, true)
|
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, rsAPI, caches, nil, true)
|
||||||
rsAPI.SetFederationAPI(fsAPI, nil)
|
rsAPI.SetFederationAPI(fsAPI, nil)
|
||||||
|
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
syncapi.AddPublicRoutes(processCtx, routers, cfg, cm, &natsInstance, userAPI, rsAPI, caches, caching.DisableMetrics)
|
syncapi.AddPublicRoutes(processCtx, routers, cfg, cm, &natsInstance, userAPI, rsAPI, caches, caching.DisableMetrics)
|
||||||
|
|
||||||
// Create the room
|
// Create the room
|
||||||
|
@ -863,7 +865,7 @@ func TestAdminEvacuateRoom(t *testing.T) {
|
||||||
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, rsAPI, caches, nil, true)
|
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, rsAPI, caches, nil, true)
|
||||||
rsAPI.SetFederationAPI(fsAPI, nil)
|
rsAPI.SetFederationAPI(fsAPI, nil)
|
||||||
|
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// Create the room
|
// Create the room
|
||||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", api.DoNotSendToOtherServers, nil, false); err != nil {
|
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", api.DoNotSendToOtherServers, nil, false); err != nil {
|
||||||
|
@ -964,7 +966,7 @@ func TestAdminEvacuateUser(t *testing.T) {
|
||||||
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, basepkg.CreateFederationClient(cfg, nil), rsAPI, caches, nil, true)
|
fsAPI := federationapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, basepkg.CreateFederationClient(cfg, nil), rsAPI, caches, nil, true)
|
||||||
rsAPI.SetFederationAPI(fsAPI, nil)
|
rsAPI.SetFederationAPI(fsAPI, nil)
|
||||||
|
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// Create the room
|
// Create the room
|
||||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", api.DoNotSendToOtherServers, nil, false); err != nil {
|
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", api.DoNotSendToOtherServers, nil, false); err != nil {
|
||||||
|
@ -1055,7 +1057,7 @@ func TestAdminMarkAsStale(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -1092,3 +1094,382 @@ func TestAdminMarkAsStale(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAdminQueryEventReports(t *testing.T) {
|
||||||
|
alice := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin))
|
||||||
|
bob := test.NewUser(t)
|
||||||
|
room := test.NewRoom(t, alice)
|
||||||
|
room2 := test.NewRoom(t, alice)
|
||||||
|
|
||||||
|
// room2 has a name and canonical alias
|
||||||
|
room2.CreateAndInsert(t, alice, spec.MRoomName, map[string]string{"name": "Testing"}, test.WithStateKey(""))
|
||||||
|
room2.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": "#testing"}, test.WithStateKey(""))
|
||||||
|
|
||||||
|
// Join the rooms with Bob
|
||||||
|
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "join",
|
||||||
|
}, test.WithStateKey(bob.ID))
|
||||||
|
room2.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "join",
|
||||||
|
}, test.WithStateKey(bob.ID))
|
||||||
|
|
||||||
|
// Create a few events to report
|
||||||
|
eventsToReportPerRoom := make(map[string][]string)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
ev1 := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||||
|
ev2 := room2.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||||
|
eventsToReportPerRoom[room.ID] = append(eventsToReportPerRoom[room.ID], ev1.EventID())
|
||||||
|
eventsToReportPerRoom[room2.ID] = append(eventsToReportPerRoom[room2.ID], ev2.EventID())
|
||||||
|
}
|
||||||
|
|
||||||
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||||
|
/*if dbType == test.DBTypeSQLite {
|
||||||
|
t.Skip()
|
||||||
|
}*/
|
||||||
|
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
defer close()
|
||||||
|
natsInstance := jetstream.NATSInstance{}
|
||||||
|
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||||
|
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||||
|
|
||||||
|
// Use an actual roomserver for this
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
|
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room2.Events(), "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
accessTokens := map[*test.User]userDevice{
|
||||||
|
alice: {},
|
||||||
|
bob: {},
|
||||||
|
}
|
||||||
|
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||||
|
|
||||||
|
reqBody := map[string]any{
|
||||||
|
"reason": "baaad",
|
||||||
|
"score": -100,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
// Report all events
|
||||||
|
for roomID, eventIDs := range eventsToReportPerRoom {
|
||||||
|
for _, eventID := range eventIDs {
|
||||||
|
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", roomID, eventID), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||||
|
|
||||||
|
routers.Client.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected report to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type response struct {
|
||||||
|
EventReports []api.QueryAdminEventReportsResponse `json:"event_reports"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
NextToken *int64 `json:"next_token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Can query all reports", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports", strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
var resp response
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wantCount := 20
|
||||||
|
// Only validating the count
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantCount) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can filter on room", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s", room.ID), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
var resp response
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wantCount := 10
|
||||||
|
// Only validating the count
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantCount) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can filter on user_id", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?user_id=%s", "@doesnotexist:test"), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
var resp response
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user does not exist, so we expect no results
|
||||||
|
wantCount := 0
|
||||||
|
// Only validating the count
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantCount) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can set direction=f", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s&dir=f", room.ID), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
var resp response
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wantCount := 10
|
||||||
|
// Only validating the count
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantCount) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
// we now should have the first reported event
|
||||||
|
wantEventID := eventsToReportPerRoom[room.ID][0]
|
||||||
|
gotEventID := resp.EventReports[0].EventID
|
||||||
|
if gotEventID != wantEventID {
|
||||||
|
t.Fatalf("expected eventID to be %v, got %v", wantEventID, gotEventID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can limit and paginate", func(t *testing.T) {
|
||||||
|
var from int64 = 0
|
||||||
|
var limit int64 = 5
|
||||||
|
var wantTotal int64 = 10 // We expect there to be 10 events in total
|
||||||
|
var resp response
|
||||||
|
for from+limit <= wantTotal {
|
||||||
|
resp = response{}
|
||||||
|
t.Logf("Getting reports starting from %d", from)
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s&limit=%d&from=%d", room2.ID, limit, from), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantCount := 5 // we are limited to 5
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantTotal) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've reached the end
|
||||||
|
if (from + int64(len(resp.EventReports))) == wantTotal {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next_token should be set
|
||||||
|
if resp.NextToken == nil {
|
||||||
|
t.Fatal("expected nextToken to be set")
|
||||||
|
}
|
||||||
|
from = *resp.NextToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEventReportsGetDelete(t *testing.T) {
|
||||||
|
alice := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin))
|
||||||
|
bob := test.NewUser(t)
|
||||||
|
room := test.NewRoom(t, alice)
|
||||||
|
|
||||||
|
// Add a name and alias
|
||||||
|
roomName := "Testing"
|
||||||
|
alias := "#testing"
|
||||||
|
room.CreateAndInsert(t, alice, spec.MRoomName, map[string]string{"name": roomName}, test.WithStateKey(""))
|
||||||
|
room.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": alias}, test.WithStateKey(""))
|
||||||
|
|
||||||
|
// Join the rooms with Bob
|
||||||
|
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "join",
|
||||||
|
}, test.WithStateKey(bob.ID))
|
||||||
|
|
||||||
|
// Create a few events to report
|
||||||
|
|
||||||
|
eventIDToReport := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||||
|
|
||||||
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||||
|
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
defer close()
|
||||||
|
natsInstance := jetstream.NATSInstance{}
|
||||||
|
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||||
|
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||||
|
|
||||||
|
// Use an actual roomserver for this
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
|
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
accessTokens := map[*test.User]userDevice{
|
||||||
|
alice: {},
|
||||||
|
bob: {},
|
||||||
|
}
|
||||||
|
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||||
|
|
||||||
|
reqBody := map[string]any{
|
||||||
|
"reason": "baaad",
|
||||||
|
"score": -100,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
// Report the event
|
||||||
|
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventIDToReport.EventID()), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||||
|
|
||||||
|
routers.Client.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected report to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Can not query with invalid ID", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/abc", strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusBadRequest {
|
||||||
|
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can query with valid ID", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
resp := api.QueryAdminEventReportResponse{}
|
||||||
|
if err = json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// test a few things
|
||||||
|
if resp.EventID != eventIDToReport.EventID() {
|
||||||
|
t.Fatalf("expected eventID to be %s, got %s instead", eventIDToReport.EventID(), resp.EventID)
|
||||||
|
}
|
||||||
|
if resp.RoomName != roomName {
|
||||||
|
t.Fatalf("expected roomName to be %s, got %s instead", roomName, resp.RoomName)
|
||||||
|
}
|
||||||
|
if resp.CanonicalAlias != alias {
|
||||||
|
t.Fatalf("expected alias to be %s, got %s instead", alias, resp.CanonicalAlias)
|
||||||
|
}
|
||||||
|
if reflect.DeepEqual(resp.EventJSON, eventIDToReport.JSON()) {
|
||||||
|
t.Fatal("mismatching eventJSON")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can delete with a valid ID", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodDelete, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can not query deleted report", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code == http.StatusOK {
|
||||||
|
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -32,8 +31,13 @@ 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, useraccountAPI uapi.UserLoginAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
func LoginFromJSONReader(
|
||||||
reqBytes, err := io.ReadAll(r)
|
req *http.Request,
|
||||||
|
useraccountAPI uapi.UserLoginAPI,
|
||||||
|
userAPI UserInternalAPIForLogin,
|
||||||
|
cfg *config.ClientAPI,
|
||||||
|
) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||||
|
reqBytes, err := io.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := &util.JSONResponse{
|
err := &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
@ -65,6 +69,20 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.U
|
||||||
UserAPI: userAPI,
|
UserAPI: userAPI,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
}
|
}
|
||||||
|
case authtypes.LoginTypeApplicationService:
|
||||||
|
token, err := ExtractAccessToken(req)
|
||||||
|
if err != nil {
|
||||||
|
err := &util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: spec.MissingToken(err.Error()),
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
typ = &LoginTypeApplicationService{
|
||||||
|
Config: cfg,
|
||||||
|
Token: token,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
err := util.JSONResponse{
|
err := util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
@ -73,7 +91,7 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.U
|
||||||
return nil, nil, &err
|
return nil, nil, &err
|
||||||
}
|
}
|
||||||
|
|
||||||
return typ.LoginFromJSON(ctx, reqBytes)
|
return typ.LoginFromJSON(req.Context(), reqBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserInternalAPIForLogin contains the aspects of UserAPI required for logging in.
|
// UserInternalAPIForLogin contains the aspects of UserAPI required for logging in.
|
||||||
|
|
55
clientapi/auth/login_application_service.go
Normal file
55
clientapi/auth/login_application_service.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoginTypeApplicationService describes how to authenticate as an
|
||||||
|
// application service
|
||||||
|
type LoginTypeApplicationService struct {
|
||||||
|
Config *config.ClientAPI
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name implements Type
|
||||||
|
func (t *LoginTypeApplicationService) Name() string {
|
||||||
|
return authtypes.LoginTypeApplicationService
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginFromJSON implements Type
|
||||||
|
func (t *LoginTypeApplicationService) LoginFromJSON(
|
||||||
|
ctx context.Context, reqBytes []byte,
|
||||||
|
) (*Login, LoginCleanupFunc, *util.JSONResponse) {
|
||||||
|
var r Login
|
||||||
|
if err := httputil.UnmarshalJSON(reqBytes, &r); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := internal.ValidateApplicationServiceRequest(t.Config, r.Identifier.User, t.Token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup := func(ctx context.Context, j *util.JSONResponse) {}
|
||||||
|
return &r, cleanup, nil
|
||||||
|
}
|
|
@ -17,7 +17,9 @@ package auth
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -33,8 +35,9 @@ func TestLoginFromJSONReader(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
tsts := []struct {
|
tsts := []struct {
|
||||||
Name string
|
Name string
|
||||||
Body string
|
Body string
|
||||||
|
Token string
|
||||||
|
|
||||||
WantUsername string
|
WantUsername string
|
||||||
WantDeviceID string
|
WantDeviceID string
|
||||||
|
@ -62,6 +65,30 @@ func TestLoginFromJSONReader(t *testing.T) {
|
||||||
WantDeviceID: "adevice",
|
WantDeviceID: "adevice",
|
||||||
WantDeletedTokens: []string{"atoken"},
|
WantDeletedTokens: []string{"atoken"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "appServiceWorksUserID",
|
||||||
|
Body: `{
|
||||||
|
"type": "m.login.application_service",
|
||||||
|
"identifier": { "type": "m.id.user", "user": "@alice:example.com" },
|
||||||
|
"device_id": "adevice"
|
||||||
|
}`,
|
||||||
|
Token: "astoken",
|
||||||
|
|
||||||
|
WantUsername: "@alice:example.com",
|
||||||
|
WantDeviceID: "adevice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "appServiceWorksLocalpart",
|
||||||
|
Body: `{
|
||||||
|
"type": "m.login.application_service",
|
||||||
|
"identifier": { "type": "m.id.user", "user": "alice" },
|
||||||
|
"device_id": "adevice"
|
||||||
|
}`,
|
||||||
|
Token: "astoken",
|
||||||
|
|
||||||
|
WantUsername: "alice",
|
||||||
|
WantDeviceID: "adevice",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tst := range tsts {
|
for _, tst := range tsts {
|
||||||
t.Run(tst.Name, func(t *testing.T) {
|
t.Run(tst.Name, func(t *testing.T) {
|
||||||
|
@ -72,11 +99,35 @@ func TestLoginFromJSONReader(t *testing.T) {
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Derived: &config.Derived{
|
||||||
|
ApplicationServices: []config.ApplicationService{
|
||||||
|
{
|
||||||
|
ID: "anapplicationservice",
|
||||||
|
ASToken: "astoken",
|
||||||
|
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
|
||||||
|
"users": {
|
||||||
|
{
|
||||||
|
Exclusive: true,
|
||||||
|
Regex: "@alice:example.com",
|
||||||
|
RegexpObject: regexp.MustCompile("@alice:example.com"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
login, cleanup, err := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, &userAPI, cfg)
|
|
||||||
if err != nil {
|
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tst.Body))
|
||||||
t.Fatalf("LoginFromJSONReader failed: %+v", err)
|
if tst.Token != "" {
|
||||||
|
req.Header.Add("Authorization", "Bearer "+tst.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
login, cleanup, jsonErr := LoginFromJSONReader(req, &userAPI, &userAPI, cfg)
|
||||||
|
if jsonErr != nil {
|
||||||
|
t.Fatalf("LoginFromJSONReader failed: %+v", jsonErr)
|
||||||
|
}
|
||||||
|
|
||||||
cleanup(ctx, &util.JSONResponse{Code: http.StatusOK})
|
cleanup(ctx, &util.JSONResponse{Code: http.StatusOK})
|
||||||
|
|
||||||
if login.Username() != tst.WantUsername {
|
if login.Username() != tst.WantUsername {
|
||||||
|
@ -104,8 +155,9 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
tsts := []struct {
|
tsts := []struct {
|
||||||
Name string
|
Name string
|
||||||
Body string
|
Body string
|
||||||
|
Token string
|
||||||
|
|
||||||
WantErrCode spec.MatrixErrorCode
|
WantErrCode spec.MatrixErrorCode
|
||||||
}{
|
}{
|
||||||
|
@ -142,6 +194,45 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
||||||
}`,
|
}`,
|
||||||
WantErrCode: spec.ErrorInvalidParam,
|
WantErrCode: spec.ErrorInvalidParam,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "noASToken",
|
||||||
|
Body: `{
|
||||||
|
"type": "m.login.application_service",
|
||||||
|
"identifier": { "type": "m.id.user", "user": "@alice:example.com" },
|
||||||
|
"device_id": "adevice"
|
||||||
|
}`,
|
||||||
|
WantErrCode: "M_MISSING_TOKEN",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "badASToken",
|
||||||
|
Token: "badastoken",
|
||||||
|
Body: `{
|
||||||
|
"type": "m.login.application_service",
|
||||||
|
"identifier": { "type": "m.id.user", "user": "@alice:example.com" },
|
||||||
|
"device_id": "adevice"
|
||||||
|
}`,
|
||||||
|
WantErrCode: "M_UNKNOWN_TOKEN",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "badASNamespace",
|
||||||
|
Token: "astoken",
|
||||||
|
Body: `{
|
||||||
|
"type": "m.login.application_service",
|
||||||
|
"identifier": { "type": "m.id.user", "user": "@bob:example.com" },
|
||||||
|
"device_id": "adevice"
|
||||||
|
}`,
|
||||||
|
WantErrCode: "M_EXCLUSIVE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "badASUserID",
|
||||||
|
Token: "astoken",
|
||||||
|
Body: `{
|
||||||
|
"type": "m.login.application_service",
|
||||||
|
"identifier": { "type": "m.id.user", "user": "@alice:wrong.example.com" },
|
||||||
|
"device_id": "adevice"
|
||||||
|
}`,
|
||||||
|
WantErrCode: "M_INVALID_USERNAME",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tst := range tsts {
|
for _, tst := range tsts {
|
||||||
t.Run(tst.Name, func(t *testing.T) {
|
t.Run(tst.Name, func(t *testing.T) {
|
||||||
|
@ -152,8 +243,30 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Derived: &config.Derived{
|
||||||
|
ApplicationServices: []config.ApplicationService{
|
||||||
|
{
|
||||||
|
ID: "anapplicationservice",
|
||||||
|
ASToken: "astoken",
|
||||||
|
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
|
||||||
|
"users": {
|
||||||
|
{
|
||||||
|
Exclusive: true,
|
||||||
|
Regex: "@alice:example.com",
|
||||||
|
RegexpObject: regexp.MustCompile("@alice:example.com"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
_, cleanup, errRes := LoginFromJSONReader(ctx, strings.NewReader(tst.Body), &userAPI, &userAPI, cfg)
|
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tst.Body))
|
||||||
|
if tst.Token != "" {
|
||||||
|
req.Header.Add("Authorization", "Bearer "+tst.Token)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, cleanup, errRes := LoginFromJSONReader(req, &userAPI, &userAPI, cfg)
|
||||||
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)
|
||||||
|
|
|
@ -55,7 +55,7 @@ type LoginCleanupFunc func(context.Context, *util.JSONResponse)
|
||||||
// https://matrix.org/docs/spec/client_server/r0.6.1#identifier-types
|
// https://matrix.org/docs/spec/client_server/r0.6.1#identifier-types
|
||||||
type LoginIdentifier struct {
|
type LoginIdentifier struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
// when type = m.id.user
|
// when type = m.id.user or m.id.application_service
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
// when type = m.id.thirdparty
|
// when type = m.id.thirdparty
|
||||||
Medium string `json:"medium"`
|
Medium string `json:"medium"`
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
"github.com/matrix-org/dendrite/clientapi/routing"
|
"github.com/matrix-org/dendrite/clientapi/routing"
|
||||||
"github.com/matrix-org/dendrite/clientapi/threepid"
|
"github.com/matrix-org/dendrite/clientapi/threepid"
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||||
"github.com/matrix-org/dendrite/internal/caching"
|
"github.com/matrix-org/dendrite/internal/caching"
|
||||||
"github.com/matrix-org/dendrite/internal/httputil"
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
"github.com/matrix-org/dendrite/internal/pushrules"
|
"github.com/matrix-org/dendrite/internal/pushrules"
|
||||||
|
@ -49,6 +50,10 @@ type userDevice struct {
|
||||||
password string
|
password string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||||
|
return &statistics.ServerStatistics{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetPutDevices(t *testing.T) {
|
func TestGetPutDevices(t *testing.T) {
|
||||||
alice := test.NewUser(t)
|
alice := test.NewUser(t)
|
||||||
bob := test.NewUser(t)
|
bob := test.NewUser(t)
|
||||||
|
@ -121,7 +126,7 @@ func TestGetPutDevices(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -170,7 +175,7 @@ func TestDeleteDevice(t *testing.T) {
|
||||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// We mostly need the rsAPI/ for this test, so nil for other APIs/caches etc.
|
// We mostly need the rsAPI/ for this test, so nil for other APIs/caches etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -275,7 +280,7 @@ func TestDeleteDevices(t *testing.T) {
|
||||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// We mostly need the rsAPI/ for this test, so nil for other APIs/caches etc.
|
// We mostly need the rsAPI/ for this test, so nil for other APIs/caches etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -442,7 +447,7 @@ func TestSetDisplayname(t *testing.T) {
|
||||||
|
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
asPI := appservice.NewInternalAPI(processCtx, cfg, natsInstance, userAPI, rsAPI)
|
asPI := appservice.NewInternalAPI(processCtx, cfg, natsInstance, userAPI, rsAPI)
|
||||||
|
|
||||||
AddPublicRoutes(processCtx, routers, cfg, natsInstance, base.CreateFederationClient(cfg, nil), rsAPI, asPI, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, natsInstance, base.CreateFederationClient(cfg, nil), rsAPI, asPI, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -554,7 +559,7 @@ func TestSetAvatarURL(t *testing.T) {
|
||||||
|
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
asPI := appservice.NewInternalAPI(processCtx, cfg, natsInstance, userAPI, rsAPI)
|
asPI := appservice.NewInternalAPI(processCtx, cfg, natsInstance, userAPI, rsAPI)
|
||||||
|
|
||||||
AddPublicRoutes(processCtx, routers, cfg, natsInstance, base.CreateFederationClient(cfg, nil), rsAPI, asPI, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, natsInstance, base.CreateFederationClient(cfg, nil), rsAPI, asPI, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -632,7 +637,7 @@ func TestTyping(t *testing.T) {
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
// Needed to create accounts
|
// Needed to create accounts
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
@ -716,7 +721,7 @@ func TestMembership(t *testing.T) {
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
// Needed to create accounts
|
// Needed to create accounts
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
rsAPI.SetUserAPI(userAPI)
|
rsAPI.SetUserAPI(userAPI)
|
||||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -953,9 +958,10 @@ func TestCapabilities(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
|
||||||
// Needed to create accounts
|
// Needed to create accounts
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
@ -1000,9 +1006,10 @@ func TestTurnserver(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
|
||||||
// Needed to create accounts
|
// Needed to create accounts
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
//rsAPI.SetUserAPI(userAPI)
|
//rsAPI.SetUserAPI(userAPI)
|
||||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -1098,9 +1105,10 @@ func Test3PID(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
|
||||||
// Needed to create accounts
|
// Needed to create accounts
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
@ -1276,7 +1284,7 @@ func TestPushRules(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -1663,7 +1671,7 @@ func TestKeys(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -2125,7 +2133,7 @@ func TestKeyBackup(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -2146,3 +2154,284 @@ func TestKeyBackup(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMembership(t *testing.T) {
|
||||||
|
alice := test.NewUser(t)
|
||||||
|
bob := test.NewUser(t)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
roomID string
|
||||||
|
user *test.User
|
||||||
|
additionalEvents func(t *testing.T, room *test.Room)
|
||||||
|
request func(t *testing.T, room *test.Room, accessToken string) *http.Request
|
||||||
|
wantOK bool
|
||||||
|
wantMemberCount int
|
||||||
|
}{
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "/joined_members - Bob never joined",
|
||||||
|
user: bob,
|
||||||
|
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
|
||||||
|
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
|
||||||
|
"access_token": accessToken,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
wantOK: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/joined_members - Alice joined",
|
||||||
|
user: alice,
|
||||||
|
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
|
||||||
|
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
|
||||||
|
"access_token": accessToken,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
wantOK: true,
|
||||||
|
wantMemberCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/joined_members - Alice leaves, shouldn't be able to see members ",
|
||||||
|
user: alice,
|
||||||
|
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
|
||||||
|
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
|
||||||
|
"access_token": accessToken,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
additionalEvents: func(t *testing.T, room *test.Room) {
|
||||||
|
room.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "leave",
|
||||||
|
}, test.WithStateKey(alice.ID))
|
||||||
|
},
|
||||||
|
wantOK: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/joined_members - Bob joins, Alice sees two members",
|
||||||
|
user: alice,
|
||||||
|
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
|
||||||
|
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
|
||||||
|
"access_token": accessToken,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
additionalEvents: func(t *testing.T, room *test.Room) {
|
||||||
|
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "join",
|
||||||
|
}, test.WithStateKey(bob.ID))
|
||||||
|
},
|
||||||
|
wantOK: true,
|
||||||
|
wantMemberCount: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||||
|
|
||||||
|
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
defer close()
|
||||||
|
natsInstance := jetstream.NATSInstance{}
|
||||||
|
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||||
|
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||||
|
|
||||||
|
// Use an actual roomserver for this
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
accessTokens := map[*test.User]userDevice{
|
||||||
|
alice: {},
|
||||||
|
bob: {},
|
||||||
|
}
|
||||||
|
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
room := test.NewRoom(t, alice)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
t.Logf("running cleanup for %s", tc.name)
|
||||||
|
})
|
||||||
|
// inject additional events
|
||||||
|
if tc.additionalEvents != nil {
|
||||||
|
tc.additionalEvents(t, room)
|
||||||
|
}
|
||||||
|
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
routers.Client.ServeHTTP(w, tc.request(t, room, accessTokens[tc.user].accessToken))
|
||||||
|
if w.Code != 200 && tc.wantOK {
|
||||||
|
t.Logf("%s", w.Body.String())
|
||||||
|
t.Fatalf("got HTTP %d want %d", w.Code, 200)
|
||||||
|
}
|
||||||
|
t.Logf("[%s] Resp: %s", tc.name, w.Body.String())
|
||||||
|
|
||||||
|
// check we got the expected events
|
||||||
|
if tc.wantOK {
|
||||||
|
memberCount := len(gjson.GetBytes(w.Body.Bytes(), "joined").Map())
|
||||||
|
if memberCount != tc.wantMemberCount {
|
||||||
|
t.Fatalf("expected %d members, got %d", tc.wantMemberCount, memberCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateRoomInvite(t *testing.T) {
|
||||||
|
alice := test.NewUser(t)
|
||||||
|
bob := test.NewUser(t)
|
||||||
|
|
||||||
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||||
|
|
||||||
|
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
defer close()
|
||||||
|
natsInstance := jetstream.NATSInstance{}
|
||||||
|
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||||
|
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||||
|
|
||||||
|
// Use an actual roomserver for this
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
accessTokens := map[*test.User]userDevice{
|
||||||
|
alice: {},
|
||||||
|
}
|
||||||
|
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||||
|
|
||||||
|
reqBody := map[string]any{
|
||||||
|
"invite": []string{bob.ID},
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/createRoom", strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.Client.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected room creation to be successful, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
roomID := gjson.GetBytes(w.Body.Bytes(), "room_id").Str
|
||||||
|
validRoomID, _ := spec.NewRoomID(roomID)
|
||||||
|
// Now ask the roomserver about the membership event of Bob
|
||||||
|
ev, err := rsAPI.CurrentStateEvent(context.Background(), *validRoomID, spec.MRoomMember, bob.ID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ev == nil {
|
||||||
|
t.Fatal("Membership event for Bob does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that there is NO displayname in content
|
||||||
|
if gjson.GetBytes(ev.Content(), "displayname").Exists() {
|
||||||
|
t.Fatal("Found displayname in invite")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReportEvent(t *testing.T) {
|
||||||
|
alice := test.NewUser(t)
|
||||||
|
bob := test.NewUser(t)
|
||||||
|
charlie := test.NewUser(t)
|
||||||
|
room := test.NewRoom(t, alice)
|
||||||
|
|
||||||
|
room.CreateAndInsert(t, charlie, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "join",
|
||||||
|
}, test.WithStateKey(charlie.ID))
|
||||||
|
eventToReport := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||||
|
|
||||||
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||||
|
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
defer close()
|
||||||
|
natsInstance := jetstream.NATSInstance{}
|
||||||
|
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||||
|
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||||
|
|
||||||
|
// Use an actual roomserver for this
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
|
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
accessTokens := map[*test.User]userDevice{
|
||||||
|
alice: {},
|
||||||
|
bob: {},
|
||||||
|
charlie: {},
|
||||||
|
}
|
||||||
|
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||||
|
|
||||||
|
reqBody := map[string]any{
|
||||||
|
"reason": "baaad",
|
||||||
|
"score": -100,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
t.Run("Bob is not joined and should not be able to report the event", func(t *testing.T) {
|
||||||
|
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventToReport.EventID()), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||||
|
|
||||||
|
routers.Client.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusNotFound {
|
||||||
|
t.Fatalf("expected report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Charlie is joined but the event does not exist", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/$doesNotExist", room.ID), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[charlie].accessToken)
|
||||||
|
|
||||||
|
routers.Client.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusNotFound {
|
||||||
|
t.Fatalf("expected report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Charlie is joined and allowed to report the event", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventToReport.EventID()), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[charlie].accessToken)
|
||||||
|
|
||||||
|
routers.Client.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected report to be successful, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -35,5 +35,5 @@ func ParseTSParam(req *http.Request) (time.Time, error) {
|
||||||
return time.Time{}, fmt.Errorf("param 'ts' is no valid int (%s)", err.Error())
|
return time.Time{}, fmt.Errorf("param 'ts' is no valid int (%s)", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Unix(ts/1000, 0), nil
|
return time.UnixMilli(ts), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -328,7 +328,7 @@ func AdminPurgeRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI api.ClientUserAPI) util.JSONResponse {
|
func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
|
||||||
if req.Body == nil {
|
if req.Body == nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
@ -423,7 +423,7 @@ func AdminReindex(req *http.Request, cfg *config.ClientAPI, device *api.Device,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI api.ClientKeyAPI) util.JSONResponse {
|
func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI userapi.ClientKeyAPI) util.JSONResponse {
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
|
@ -495,3 +495,93 @@ func AdminDownloadState(req *http.Request, device *api.Device, rsAPI roomserverA
|
||||||
JSON: struct{}{},
|
JSON: struct{}{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEventReports returns reported events for a given user/room.
|
||||||
|
func GetEventReports(
|
||||||
|
req *http.Request,
|
||||||
|
rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||||
|
from, limit uint64,
|
||||||
|
backwards bool,
|
||||||
|
userID, roomID string,
|
||||||
|
) util.JSONResponse {
|
||||||
|
|
||||||
|
eventReports, count, err := rsAPI.QueryAdminEventReports(req.Context(), from, limit, backwards, userID, roomID)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("failed to query event reports")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := map[string]any{
|
||||||
|
"event_reports": eventReports,
|
||||||
|
"total": count,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a next_token if there are still reports
|
||||||
|
if int64(from+limit) < count {
|
||||||
|
resp["next_token"] = int(from) + len(eventReports)
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: resp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEventReport(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, reportID string) util.JSONResponse {
|
||||||
|
parsedReportID, err := strconv.ParseUint(reportID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
// Given this is an admin endpoint, let them know what didn't work.
|
||||||
|
JSON: spec.InvalidParam(err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
report, err := rsAPI.QueryAdminEventReport(req.Context(), parsedReportID)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.Unknown(err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: report,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteEventReport(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, reportID string) util.JSONResponse {
|
||||||
|
parsedReportID, err := strconv.ParseUint(reportID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
// Given this is an admin endpoint, let them know what didn't work.
|
||||||
|
JSON: spec.InvalidParam(err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rsAPI.PerformAdminDeleteEventReport(req.Context(), parsedReportID)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.Unknown(err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint64OrDefault(input string, defaultValue uint64) uint64 {
|
||||||
|
v, err := strconv.ParseUint(input, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ func DirectoryRoom(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: spec.BadJSON("Room alias must be in the form '#localpart:domain'"),
|
JSON: spec.InvalidParam("Room alias must be in the form '#localpart:domain'"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ func SetLocalAlias(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: spec.BadJSON("Room alias must be in the form '#localpart:domain'"),
|
JSON: spec.InvalidParam("Room alias must be in the form '#localpart:domain'"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||||
"github.com/matrix-org/dendrite/internal/caching"
|
"github.com/matrix-org/dendrite/internal/caching"
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
|
@ -21,6 +22,10 @@ import (
|
||||||
uapi "github.com/matrix-org/dendrite/userapi/api"
|
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
|
||||||
|
return &statistics.ServerStatistics{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestJoinRoomByIDOrAlias(t *testing.T) {
|
func TestJoinRoomByIDOrAlias(t *testing.T) {
|
||||||
alice := test.NewUser(t)
|
alice := test.NewUser(t)
|
||||||
bob := test.NewUser(t)
|
bob := test.NewUser(t)
|
||||||
|
@ -36,7 +41,7 @@ func TestJoinRoomByIDOrAlias(t *testing.T) {
|
||||||
natsInstance := jetstream.NATSInstance{}
|
natsInstance := jetstream.NATSInstance{}
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil) // creates the rs.Inputer etc
|
rsAPI.SetFederationAPI(nil, nil) // creates the rs.Inputer etc
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||||
|
|
||||||
// Create the users in the userapi
|
// Create the users in the userapi
|
||||||
|
|
|
@ -93,7 +93,6 @@ func UploadKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *api.Device)
|
||||||
|
|
||||||
type queryKeysRequest struct {
|
type queryKeysRequest struct {
|
||||||
Timeout int `json:"timeout"`
|
Timeout int `json:"timeout"`
|
||||||
Token string `json:"token"`
|
|
||||||
DeviceKeys map[string][]string `json:"device_keys"`
|
DeviceKeys map[string][]string `json:"device_keys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +118,6 @@ func QueryKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *api.Device) u
|
||||||
UserID: device.UserID,
|
UserID: device.UserID,
|
||||||
UserToDevices: r.DeviceKeys,
|
UserToDevices: r.DeviceKeys,
|
||||||
Timeout: r.GetTimeout(),
|
Timeout: r.GetTimeout(),
|
||||||
// TODO: Token?
|
|
||||||
}, &queryRes)
|
}, &queryRes)
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"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/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"
|
||||||
|
@ -40,28 +41,25 @@ type flow struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func passwordLogin() flows {
|
|
||||||
f := flows{}
|
|
||||||
s := flow{
|
|
||||||
Type: "m.login.password",
|
|
||||||
}
|
|
||||||
f.Flows = append(f.Flows, s)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Login implements GET and POST /login
|
// Login implements GET and POST /login
|
||||||
func Login(
|
func Login(
|
||||||
req *http.Request, userAPI userapi.ClientUserAPI,
|
req *http.Request, userAPI userapi.ClientUserAPI,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if req.Method == http.MethodGet {
|
if req.Method == http.MethodGet {
|
||||||
// TODO: support other forms of login other than password, depending on config options
|
loginFlows := []flow{{Type: authtypes.LoginTypePassword}}
|
||||||
|
if len(cfg.Derived.ApplicationServices) > 0 {
|
||||||
|
loginFlows = append(loginFlows, flow{Type: authtypes.LoginTypeApplicationService})
|
||||||
|
}
|
||||||
|
// TODO: support other forms of login, depending on config options
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
JSON: passwordLogin(),
|
JSON: flows{
|
||||||
|
Flows: loginFlows,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else if req.Method == http.MethodPost {
|
} else if req.Method == http.MethodPost {
|
||||||
login, cleanup, authErr := auth.LoginFromJSONReader(req.Context(), req.Body, userAPI, userAPI, cfg)
|
login, cleanup, authErr := auth.LoginFromJSONReader(req, userAPI, userAPI, cfg)
|
||||||
if authErr != nil {
|
if authErr != nil {
|
||||||
return *authErr
|
return *authErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ func TestLogin(t *testing.T) {
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
// Needed for /login
|
// Needed for /login
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
// We mostly need the userAPI for this test, so nil for other APIs/caches etc.
|
// We mostly need the userAPI for this test, so nil for other APIs/caches etc.
|
||||||
Setup(routers, cfg, nil, nil, userAPI, nil, nil, nil, nil, nil, nil, nil, caching.DisableMetrics)
|
Setup(routers, cfg, nil, nil, userAPI, nil, nil, nil, nil, nil, nil, nil, caching.DisableMetrics)
|
||||||
|
@ -114,6 +114,44 @@ func TestLogin(t *testing.T) {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Inject a dummy application service, so we have a "m.login.application_service"
|
||||||
|
// in the login flows
|
||||||
|
as := &config.ApplicationService{}
|
||||||
|
cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as}
|
||||||
|
|
||||||
|
t.Run("Supported log-in flows are returned", func(t *testing.T) {
|
||||||
|
req := test.NewRequest(t, http.MethodGet, "/_matrix/client/v3/login")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
routers.Client.ServeHTTP(rec, req)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("failed to get log-in flows: %s", rec.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("response: %s", rec.Body.String())
|
||||||
|
resp := flows{}
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
appServiceFound := false
|
||||||
|
passwordFound := false
|
||||||
|
for _, flow := range resp.Flows {
|
||||||
|
if flow.Type == "m.login.password" {
|
||||||
|
passwordFound = true
|
||||||
|
} else if flow.Type == "m.login.application_service" {
|
||||||
|
appServiceFound = true
|
||||||
|
} else {
|
||||||
|
t.Fatalf("got unknown login flow: %s", flow.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !appServiceFound {
|
||||||
|
t.Fatal("m.login.application_service missing from login flows")
|
||||||
|
}
|
||||||
|
if !passwordFound {
|
||||||
|
t.Fatal("m.login.password missing from login flows")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
req := test.NewRequest(t, http.MethodPost, "/_matrix/client/v3/login", test.WithJSONBody(t, map[string]interface{}{
|
req := test.NewRequest(t, http.MethodPost, "/_matrix/client/v3/login", test.WithJSONBody(t, map[string]interface{}{
|
||||||
|
|
|
@ -181,18 +181,6 @@ func SendKick(
|
||||||
return *errRes
|
return *errRes
|
||||||
}
|
}
|
||||||
|
|
||||||
pl, errRes := getPowerlevels(req, rsAPI, roomID)
|
|
||||||
if errRes != nil {
|
|
||||||
return *errRes
|
|
||||||
}
|
|
||||||
allowedToKick := pl.UserLevel(*senderID) >= pl.Kick
|
|
||||||
if !allowedToKick {
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
JSON: spec.Forbidden("You don't have permission to kick this user, power level too low."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bodyUserID, err := spec.NewUserID(body.UserID, true)
|
bodyUserID, err := spec.NewUserID(body.UserID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -200,6 +188,19 @@ func SendKick(
|
||||||
JSON: spec.BadJSON("body userID is invalid"),
|
JSON: spec.BadJSON("body userID is invalid"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pl, errRes := getPowerlevels(req, rsAPI, roomID)
|
||||||
|
if errRes != nil {
|
||||||
|
return *errRes
|
||||||
|
}
|
||||||
|
allowedToKick := pl.UserLevel(*senderID) >= pl.Kick || bodyUserID.String() == deviceUserID.String()
|
||||||
|
if !allowedToKick {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: spec.Forbidden("You don't have permission to kick this user, power level too low."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var queryRes roomserverAPI.QueryMembershipForUserResponse
|
var queryRes roomserverAPI.QueryMembershipForUserResponse
|
||||||
err = rsAPI.QueryMembershipForUser(req.Context(), &roomserverAPI.QueryMembershipForUserRequest{
|
err = rsAPI.QueryMembershipForUser(req.Context(), &roomserverAPI.QueryMembershipForUserRequest{
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
|
@ -323,19 +324,18 @@ 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(), profileAPI, device, roomID, body.UserID, body.Reason, cfg, rsAPI, asAPI, evTime)
|
response, _ := sendInvite(req.Context(), device, roomID, body.UserID, body.Reason, cfg, rsAPI, 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,
|
||||||
profileAPI userapi.ClientUserAPI,
|
|
||||||
device *userapi.Device,
|
device *userapi.Device,
|
||||||
roomID, userID, reason string,
|
roomID, userID, reason string,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
rsAPI roomserverAPI.ClientRoomserverAPI,
|
rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||||
asAPI appserviceAPI.AppServiceInternalAPI, evTime time.Time,
|
evTime time.Time,
|
||||||
) (util.JSONResponse, error) {
|
) (util.JSONResponse, error) {
|
||||||
validRoomID, err := spec.NewRoomID(roomID)
|
validRoomID, err := spec.NewRoomID(roomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -358,13 +358,7 @@ func sendInvite(
|
||||||
JSON: spec.InvalidParam("UserID is invalid"),
|
JSON: spec.InvalidParam("UserID is invalid"),
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
profile, err := loadProfile(ctx, userID, cfg, profileAPI, asAPI)
|
|
||||||
if err != nil {
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}, err
|
|
||||||
}
|
|
||||||
identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain())
|
identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -374,16 +368,14 @@ func sendInvite(
|
||||||
}
|
}
|
||||||
err = rsAPI.PerformInvite(ctx, &api.PerformInviteRequest{
|
err = rsAPI.PerformInvite(ctx, &api.PerformInviteRequest{
|
||||||
InviteInput: roomserverAPI.InviteInput{
|
InviteInput: roomserverAPI.InviteInput{
|
||||||
RoomID: *validRoomID,
|
RoomID: *validRoomID,
|
||||||
Inviter: *inviter,
|
Inviter: *inviter,
|
||||||
Invitee: *invitee,
|
Invitee: *invitee,
|
||||||
DisplayName: profile.DisplayName,
|
Reason: reason,
|
||||||
AvatarURL: profile.AvatarURL,
|
IsDirect: false,
|
||||||
Reason: reason,
|
KeyID: identity.KeyID,
|
||||||
IsDirect: false,
|
PrivateKey: identity.PrivateKey,
|
||||||
KeyID: identity.KeyID,
|
EventTime: evTime,
|
||||||
PrivateKey: identity.PrivateKey,
|
|
||||||
EventTime: evTime,
|
|
||||||
},
|
},
|
||||||
InviteRoomState: nil, // ask the roomserver to draw up invite room state for us
|
InviteRoomState: nil, // ask the roomserver to draw up invite room state for us
|
||||||
SendAsServer: string(device.UserDomain()),
|
SendAsServer: string(device.UserDomain()),
|
||||||
|
|
139
clientapi/routing/memberships.go
Normal file
139
clientapi/routing/memberships.go
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
// Copyright 2024 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 (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-joined-members
|
||||||
|
type getJoinedMembersResponse struct {
|
||||||
|
Joined map[string]joinedMember `json:"joined"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type joinedMember struct {
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The database stores 'displayname' without an underscore.
|
||||||
|
// Deserialize into this and then change to the actual API response
|
||||||
|
type databaseJoinedMember struct {
|
||||||
|
DisplayName string `json:"displayname"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJoinedMembers implements
|
||||||
|
//
|
||||||
|
// GET /rooms/{roomId}/joined_members
|
||||||
|
func GetJoinedMembers(
|
||||||
|
req *http.Request, device *userapi.Device, roomID string,
|
||||||
|
rsAPI api.ClientRoomserverAPI,
|
||||||
|
) util.JSONResponse {
|
||||||
|
// Validate the userID
|
||||||
|
userID, err := spec.NewUserID(device.UserID, true)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: spec.InvalidParam("Device UserID is invalid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the roomID
|
||||||
|
validRoomID, err := spec.NewRoomID(roomID)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: spec.InvalidParam("RoomID is invalid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current memberships for the requesting user to determine
|
||||||
|
// if they are allowed to query this endpoint.
|
||||||
|
queryReq := api.QueryMembershipForUserRequest{
|
||||||
|
RoomID: validRoomID.String(),
|
||||||
|
UserID: *userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
var queryRes api.QueryMembershipForUserResponse
|
||||||
|
if queryErr := rsAPI.QueryMembershipForUser(req.Context(), &queryReq, &queryRes); queryErr != nil {
|
||||||
|
util.GetLogger(req.Context()).WithError(queryErr).Error("rsAPI.QueryMembershipsForRoom failed")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !queryRes.HasBeenInRoom {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: spec.Forbidden("You aren't a member of the room and weren't previously a member of the room."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !queryRes.IsInRoom {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: spec.Forbidden("You aren't a member of the room and weren't previously a member of the room."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current membership events
|
||||||
|
var membershipsForRoomResp api.QueryMembershipsForRoomResponse
|
||||||
|
if err = rsAPI.QueryMembershipsForRoom(req.Context(), &api.QueryMembershipsForRoomRequest{
|
||||||
|
JoinedOnly: true,
|
||||||
|
RoomID: validRoomID.String(),
|
||||||
|
}, &membershipsForRoomResp); err != nil {
|
||||||
|
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryEventsByID failed")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var res getJoinedMembersResponse
|
||||||
|
res.Joined = make(map[string]joinedMember)
|
||||||
|
for _, ev := range membershipsForRoomResp.JoinEvents {
|
||||||
|
var content databaseJoinedMember
|
||||||
|
if err := json.Unmarshal(ev.Content, &content); err != nil {
|
||||||
|
util.GetLogger(req.Context()).WithError(err).Error("failed to unmarshal event content")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, err := rsAPI.QueryUserIDForSender(req.Context(), *validRoomID, spec.SenderID(ev.Sender))
|
||||||
|
if err != nil || userID == nil {
|
||||||
|
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryUserIDForSender failed")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Joined[userID.String()] = joinedMember(content)
|
||||||
|
}
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: res,
|
||||||
|
}
|
||||||
|
}
|
|
@ -70,7 +70,7 @@ func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi
|
||||||
}
|
}
|
||||||
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
|
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
|
||||||
// Even if rulesPtr is not nil, there may not be any rules for this kind
|
// Even if rulesPtr is not nil, there may not be any rules for this kind
|
||||||
if rulesPtr == nil || (rulesPtr != nil && len(*rulesPtr) == 0) {
|
if rulesPtr == nil || len(*rulesPtr) == 0 {
|
||||||
return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
|
return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
|
||||||
}
|
}
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
|
|
@ -23,13 +23,12 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/userapi/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, userAPI api.ClientUserAPI, syncProducer *producers.SyncAPIProducer, device *userapi.Device, roomID, receiptType, eventID string) util.JSONResponse {
|
func SetReceipt(req *http.Request, userAPI userapi.ClientUserAPI, syncProducer *producers.SyncAPIProducer, device *userapi.Device, roomID, receiptType, eventID string) util.JSONResponse {
|
||||||
timestamp := spec.AsTimestamp(time.Now())
|
timestamp := spec.AsTimestamp(time.Now())
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"roomID": roomID,
|
"roomID": roomID,
|
||||||
|
@ -54,13 +53,13 @@ func SetReceipt(req *http.Request, userAPI api.ClientUserAPI, syncProducer *prod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataReq := api.InputAccountDataRequest{
|
dataReq := userapi.InputAccountDataRequest{
|
||||||
UserID: device.UserID,
|
UserID: device.UserID,
|
||||||
DataType: "m.fully_read",
|
DataType: "m.fully_read",
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
AccountData: data,
|
AccountData: data,
|
||||||
}
|
}
|
||||||
dataRes := api.InputAccountDataResponse{}
|
dataRes := userapi.InputAccountDataResponse{}
|
||||||
if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil {
|
if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("userAPI.InputAccountData failed")
|
util.GetLogger(req.Context()).WithError(err).Error("userAPI.InputAccountData failed")
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
|
|
|
@ -630,6 +630,7 @@ func handleGuestRegistration(
|
||||||
AccessToken: token,
|
AccessToken: token,
|
||||||
IPAddr: req.RemoteAddr,
|
IPAddr: req.RemoteAddr,
|
||||||
UserAgent: req.UserAgent(),
|
UserAgent: req.UserAgent(),
|
||||||
|
FromRegistration: true,
|
||||||
}, &devRes)
|
}, &devRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -647,6 +648,16 @@ func handleGuestRegistration(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// localpartMatchesExclusiveNamespaces will check if a given username matches any
|
||||||
|
// application service's exclusive users namespace
|
||||||
|
func localpartMatchesExclusiveNamespaces(
|
||||||
|
cfg *config.ClientAPI,
|
||||||
|
localpart string,
|
||||||
|
) bool {
|
||||||
|
userID := userutil.MakeUserID(localpart, cfg.Matrix.ServerName)
|
||||||
|
return cfg.Derived.ExclusiveApplicationServicesUsernameRegexp.MatchString(userID)
|
||||||
|
}
|
||||||
|
|
||||||
// handleRegistrationFlow will direct and complete registration flow stages
|
// handleRegistrationFlow will direct and complete registration flow stages
|
||||||
// that the client has requested.
|
// that the client has requested.
|
||||||
// nolint: gocyclo
|
// nolint: gocyclo
|
||||||
|
@ -695,7 +706,7 @@ func handleRegistrationFlow(
|
||||||
// If an access token is provided, ignore this check this is an appservice
|
// If an access token is provided, ignore this check this is an appservice
|
||||||
// request and we will validate in validateApplicationService
|
// request and we will validate in validateApplicationService
|
||||||
if len(cfg.Derived.ApplicationServices) != 0 &&
|
if len(cfg.Derived.ApplicationServices) != 0 &&
|
||||||
UsernameMatchesExclusiveNamespaces(cfg, r.Username) {
|
localpartMatchesExclusiveNamespaces(cfg, r.Username) {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: spec.ASExclusive("This username is reserved by an application service."),
|
JSON: spec.ASExclusive("This username is reserved by an application service."),
|
||||||
|
@ -772,7 +783,7 @@ func handleApplicationServiceRegistration(
|
||||||
|
|
||||||
// Check application service register user request is valid.
|
// Check application service register user request is valid.
|
||||||
// The application service's ID is returned if so.
|
// The application service's ID is returned if so.
|
||||||
appserviceID, err := validateApplicationService(
|
appserviceID, err := internal.ValidateApplicationServiceRequest(
|
||||||
cfg, r.Username, accessToken,
|
cfg, r.Username, accessToken,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -909,6 +920,7 @@ func completeRegistration(
|
||||||
DeviceID: deviceID,
|
DeviceID: deviceID,
|
||||||
IPAddr: ipAddr,
|
IPAddr: ipAddr,
|
||||||
UserAgent: userAgent,
|
UserAgent: userAgent,
|
||||||
|
FromRegistration: true,
|
||||||
}, &devRes)
|
}, &devRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
|
|
@ -298,25 +298,29 @@ func Test_register(t *testing.T) {
|
||||||
guestsDisabled bool
|
guestsDisabled bool
|
||||||
enableRecaptcha bool
|
enableRecaptcha bool
|
||||||
captchaBody string
|
captchaBody string
|
||||||
wantResponse util.JSONResponse
|
// in case of an error, the expected response
|
||||||
|
wantErrorResponse util.JSONResponse
|
||||||
|
// in case of success, the expected username assigned
|
||||||
|
wantUsername string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "disallow guests",
|
name: "disallow guests",
|
||||||
kind: "guest",
|
kind: "guest",
|
||||||
guestsDisabled: true,
|
guestsDisabled: true,
|
||||||
wantResponse: util.JSONResponse{
|
wantErrorResponse: util.JSONResponse{
|
||||||
Code: http.StatusForbidden,
|
Code: http.StatusForbidden,
|
||||||
JSON: spec.Forbidden(`Guest registration is disabled on "test"`),
|
JSON: spec.Forbidden(`Guest registration is disabled on "test"`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allow guests",
|
name: "allow guests",
|
||||||
kind: "guest",
|
kind: "guest",
|
||||||
|
wantUsername: "1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unknown login type",
|
name: "unknown login type",
|
||||||
loginType: "im.not.known",
|
loginType: "im.not.known",
|
||||||
wantResponse: util.JSONResponse{
|
wantErrorResponse: util.JSONResponse{
|
||||||
Code: http.StatusNotImplemented,
|
Code: http.StatusNotImplemented,
|
||||||
JSON: spec.Unknown("unknown/unimplemented auth type"),
|
JSON: spec.Unknown("unknown/unimplemented auth type"),
|
||||||
},
|
},
|
||||||
|
@ -324,25 +328,33 @@ func Test_register(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "disabled registration",
|
name: "disabled registration",
|
||||||
registrationDisabled: true,
|
registrationDisabled: true,
|
||||||
wantResponse: util.JSONResponse{
|
wantErrorResponse: util.JSONResponse{
|
||||||
Code: http.StatusForbidden,
|
Code: http.StatusForbidden,
|
||||||
JSON: spec.Forbidden(`Registration is disabled on "test"`),
|
JSON: spec.Forbidden(`Registration is disabled on "test"`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "successful registration, numeric ID",
|
name: "successful registration, numeric ID",
|
||||||
username: "",
|
username: "",
|
||||||
password: "someRandomPassword",
|
password: "someRandomPassword",
|
||||||
forceEmpty: true,
|
forceEmpty: true,
|
||||||
|
wantUsername: "2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "successful registration",
|
name: "successful registration",
|
||||||
username: "success",
|
username: "success",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "successful registration, sequential numeric ID",
|
||||||
|
username: "",
|
||||||
|
password: "someRandomPassword",
|
||||||
|
forceEmpty: true,
|
||||||
|
wantUsername: "3",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "failing registration - user already exists",
|
name: "failing registration - user already exists",
|
||||||
username: "success",
|
username: "success",
|
||||||
wantResponse: util.JSONResponse{
|
wantErrorResponse: util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: spec.UserInUse("Desired user ID is already taken."),
|
JSON: spec.UserInUse("Desired user ID is already taken."),
|
||||||
},
|
},
|
||||||
|
@ -352,14 +364,14 @@ func Test_register(t *testing.T) {
|
||||||
username: "LOWERCASED", // this is going to be lower-cased
|
username: "LOWERCASED", // this is going to be lower-cased
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid username",
|
name: "invalid username",
|
||||||
username: "#totalyNotValid",
|
username: "#totalyNotValid",
|
||||||
wantResponse: *internal.UsernameResponse(internal.ErrUsernameInvalid),
|
wantErrorResponse: *internal.UsernameResponse(internal.ErrUsernameInvalid),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "numeric username is forbidden",
|
name: "numeric username is forbidden",
|
||||||
username: "1337",
|
username: "1337",
|
||||||
wantResponse: util.JSONResponse{
|
wantErrorResponse: util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: spec.InvalidUsername("Numeric user IDs are reserved"),
|
JSON: spec.InvalidUsername("Numeric user IDs are reserved"),
|
||||||
},
|
},
|
||||||
|
@ -367,7 +379,7 @@ func Test_register(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "disabled recaptcha login",
|
name: "disabled recaptcha login",
|
||||||
loginType: authtypes.LoginTypeRecaptcha,
|
loginType: authtypes.LoginTypeRecaptcha,
|
||||||
wantResponse: util.JSONResponse{
|
wantErrorResponse: util.JSONResponse{
|
||||||
Code: http.StatusForbidden,
|
Code: http.StatusForbidden,
|
||||||
JSON: spec.Unknown(ErrCaptchaDisabled.Error()),
|
JSON: spec.Unknown(ErrCaptchaDisabled.Error()),
|
||||||
},
|
},
|
||||||
|
@ -376,7 +388,7 @@ func Test_register(t *testing.T) {
|
||||||
name: "enabled recaptcha, no response defined",
|
name: "enabled recaptcha, no response defined",
|
||||||
enableRecaptcha: true,
|
enableRecaptcha: true,
|
||||||
loginType: authtypes.LoginTypeRecaptcha,
|
loginType: authtypes.LoginTypeRecaptcha,
|
||||||
wantResponse: util.JSONResponse{
|
wantErrorResponse: util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: spec.BadJSON(ErrMissingResponse.Error()),
|
JSON: spec.BadJSON(ErrMissingResponse.Error()),
|
||||||
},
|
},
|
||||||
|
@ -386,7 +398,7 @@ func Test_register(t *testing.T) {
|
||||||
enableRecaptcha: true,
|
enableRecaptcha: true,
|
||||||
loginType: authtypes.LoginTypeRecaptcha,
|
loginType: authtypes.LoginTypeRecaptcha,
|
||||||
captchaBody: `notvalid`,
|
captchaBody: `notvalid`,
|
||||||
wantResponse: util.JSONResponse{
|
wantErrorResponse: util.JSONResponse{
|
||||||
Code: http.StatusUnauthorized,
|
Code: http.StatusUnauthorized,
|
||||||
JSON: spec.BadJSON(ErrInvalidCaptcha.Error()),
|
JSON: spec.BadJSON(ErrInvalidCaptcha.Error()),
|
||||||
},
|
},
|
||||||
|
@ -398,11 +410,11 @@ func Test_register(t *testing.T) {
|
||||||
captchaBody: `success`,
|
captchaBody: `success`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "captcha invalid from remote",
|
name: "captcha invalid from remote",
|
||||||
enableRecaptcha: true,
|
enableRecaptcha: true,
|
||||||
loginType: authtypes.LoginTypeRecaptcha,
|
loginType: authtypes.LoginTypeRecaptcha,
|
||||||
captchaBody: `i should fail for other reasons`,
|
captchaBody: `i should fail for other reasons`,
|
||||||
wantResponse: util.JSONResponse{Code: http.StatusInternalServerError, JSON: spec.InternalServerError{}},
|
wantErrorResponse: util.JSONResponse{Code: http.StatusInternalServerError, JSON: spec.InternalServerError{}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +428,7 @@ func Test_register(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
@ -486,8 +498,8 @@ func Test_register(t *testing.T) {
|
||||||
t.Fatalf("unexpected registration flows: %+v, want %+v", r.Flows, cfg.Derived.Registration.Flows)
|
t.Fatalf("unexpected registration flows: %+v, want %+v", r.Flows, cfg.Derived.Registration.Flows)
|
||||||
}
|
}
|
||||||
case spec.MatrixError:
|
case spec.MatrixError:
|
||||||
if !reflect.DeepEqual(tc.wantResponse, resp) {
|
if !reflect.DeepEqual(tc.wantErrorResponse, resp) {
|
||||||
t.Fatalf("(%s), unexpected response: %+v, want: %+v", tc.name, resp, tc.wantResponse)
|
t.Fatalf("(%s), unexpected response: %+v, want: %+v", tc.name, resp, tc.wantErrorResponse)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case registerResponse:
|
case registerResponse:
|
||||||
|
@ -505,6 +517,13 @@ func Test_register(t *testing.T) {
|
||||||
if r.DeviceID == "" {
|
if r.DeviceID == "" {
|
||||||
t.Fatalf("missing deviceID in response")
|
t.Fatalf("missing deviceID in response")
|
||||||
}
|
}
|
||||||
|
// if an expected username is provided, assert that it is a match
|
||||||
|
if tc.wantUsername != "" {
|
||||||
|
wantUserID := strings.ToLower(fmt.Sprintf("@%s:%s", tc.wantUsername, "test"))
|
||||||
|
if wantUserID != r.UserID {
|
||||||
|
t.Fatalf("unexpected userID: %s, want %s", r.UserID, wantUserID)
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
t.Logf("Got response: %T", resp.JSON)
|
t.Logf("Got response: %T", resp.JSON)
|
||||||
|
@ -541,44 +560,29 @@ func Test_register(t *testing.T) {
|
||||||
|
|
||||||
resp = Register(req, userAPI, &cfg.ClientAPI)
|
resp = Register(req, userAPI, &cfg.ClientAPI)
|
||||||
|
|
||||||
switch resp.JSON.(type) {
|
switch rr := resp.JSON.(type) {
|
||||||
case spec.InternalServerError:
|
case spec.InternalServerError, spec.MatrixError, util.JSONResponse:
|
||||||
if !reflect.DeepEqual(tc.wantResponse, resp) {
|
if !reflect.DeepEqual(tc.wantErrorResponse, resp) {
|
||||||
t.Fatalf("unexpected response: %+v, want: %+v", resp, tc.wantResponse)
|
t.Fatalf("unexpected response: %+v, want: %+v", resp, tc.wantErrorResponse)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case spec.MatrixError:
|
case registerResponse:
|
||||||
if !reflect.DeepEqual(tc.wantResponse, resp) {
|
// validate the response
|
||||||
t.Fatalf("unexpected response: %+v, want: %+v", resp, tc.wantResponse)
|
if tc.wantUsername != "" {
|
||||||
|
// if an expected username is provided, assert that it is a match
|
||||||
|
wantUserID := strings.ToLower(fmt.Sprintf("@%s:%s", tc.wantUsername, "test"))
|
||||||
|
if wantUserID != rr.UserID {
|
||||||
|
t.Fatalf("unexpected userID: %s, want %s", rr.UserID, wantUserID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
if rr.DeviceID != *reg.DeviceID {
|
||||||
case util.JSONResponse:
|
t.Fatalf("unexpected deviceID: %s, want %s", rr.DeviceID, *reg.DeviceID)
|
||||||
if !reflect.DeepEqual(tc.wantResponse, resp) {
|
|
||||||
t.Fatalf("unexpected response: %+v, want: %+v", resp, tc.wantResponse)
|
|
||||||
}
|
}
|
||||||
return
|
if rr.AccessToken == "" {
|
||||||
}
|
t.Fatalf("missing accessToken in response")
|
||||||
|
}
|
||||||
rr, ok := resp.JSON.(registerResponse)
|
default:
|
||||||
if !ok {
|
t.Fatalf("expected one of internalservererror, matrixerror, jsonresponse, registerresponse, got %T", resp.JSON)
|
||||||
t.Fatalf("expected a registerresponse, got %T", resp.JSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate the response
|
|
||||||
if tc.forceEmpty {
|
|
||||||
// when not supplying a username, one will be generated. Given this _SHOULD_ be
|
|
||||||
// the second user, set the username accordingly
|
|
||||||
reg.Username = "2"
|
|
||||||
}
|
|
||||||
wantUserID := strings.ToLower(fmt.Sprintf("@%s:%s", reg.Username, "test"))
|
|
||||||
if wantUserID != rr.UserID {
|
|
||||||
t.Fatalf("unexpected userID: %s, want %s", rr.UserID, wantUserID)
|
|
||||||
}
|
|
||||||
if rr.DeviceID != *reg.DeviceID {
|
|
||||||
t.Fatalf("unexpected deviceID: %s, want %s", rr.DeviceID, *reg.DeviceID)
|
|
||||||
}
|
|
||||||
if rr.AccessToken == "" {
|
|
||||||
t.Fatalf("missing accessToken in response")
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -596,7 +600,7 @@ func TestRegisterUserWithDisplayName(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
deviceName, deviceID := "deviceName", "deviceID"
|
deviceName, deviceID := "deviceName", "deviceID"
|
||||||
expectedDisplayName := "DisplayName"
|
expectedDisplayName := "DisplayName"
|
||||||
response := completeRegistration(
|
response := completeRegistration(
|
||||||
|
@ -637,7 +641,7 @@ func TestRegisterAdminUsingSharedSecret(t *testing.T) {
|
||||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
rsAPI.SetFederationAPI(nil, nil)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
expectedDisplayName := "rabbit"
|
expectedDisplayName := "rabbit"
|
||||||
jsonStr := []byte(`{"admin":true,"mac":"24dca3bba410e43fe64b9b5c28306693bf3baa9f","nonce":"759f047f312b99ff428b21d581256f8592b8976e58bc1b543972dc6147e529a79657605b52d7becd160ff5137f3de11975684319187e06901955f79e5a6c5a79","password":"wonderland","username":"alice","displayname":"rabbit"}`)
|
jsonStr := []byte(`{"admin":true,"mac":"24dca3bba410e43fe64b9b5c28306693bf3baa9f","nonce":"759f047f312b99ff428b21d581256f8592b8976e58bc1b543972dc6147e529a79657605b52d7becd160ff5137f3de11975684319187e06901955f79e5a6c5a79","password":"wonderland","username":"alice","displayname":"rabbit"}`)
|
||||||
|
|
93
clientapi/routing/report_event.go
Normal file
93
clientapi/routing/report_event.go
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
userAPI "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reportEventRequest struct {
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
Score int64 `json:"score"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReportEvent(
|
||||||
|
req *http.Request,
|
||||||
|
device *userAPI.Device,
|
||||||
|
roomID, eventID string,
|
||||||
|
rsAPI api.ClientRoomserverAPI,
|
||||||
|
) util.JSONResponse {
|
||||||
|
defer req.Body.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
deviceUserID, err := spec.NewUserID(device.UserID, true)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: spec.NotFound("You don't have permission to report this event, bad userID"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The requesting user must be a member of the room
|
||||||
|
errRes := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID)
|
||||||
|
if errRes != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound, // Spec demands this...
|
||||||
|
JSON: spec.NotFound("The event was not found or you are not joined to the room."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the request
|
||||||
|
report := reportEventRequest{}
|
||||||
|
if resErr := httputil.UnmarshalJSONRequest(req, &report); resErr != nil {
|
||||||
|
return *resErr
|
||||||
|
}
|
||||||
|
|
||||||
|
queryRes := &api.QueryEventsByIDResponse{}
|
||||||
|
if err = rsAPI.QueryEventsByID(req.Context(), &api.QueryEventsByIDRequest{
|
||||||
|
RoomID: roomID,
|
||||||
|
EventIDs: []string{eventID},
|
||||||
|
}, queryRes); err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{Err: err.Error()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No event was found or it was already redacted
|
||||||
|
if len(queryRes.Events) == 0 || queryRes.Events[0].Redacted() {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: spec.NotFound("The event was not found or you are not joined to the room."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = rsAPI.InsertReportedEvent(req.Context(), roomID, eventID, device.UserID, report.Reason, report.Score)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{Err: err.Error()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: struct{}{},
|
||||||
|
}
|
||||||
|
}
|
|
@ -138,7 +138,7 @@ func QueryRoomHierarchy(req *http.Request, device *userapi.Device, roomIDStr str
|
||||||
walker = *cachedWalker
|
walker = *cachedWalker
|
||||||
}
|
}
|
||||||
|
|
||||||
discoveredRooms, nextWalker, err := rsAPI.QueryNextRoomHierarchyPage(req.Context(), walker, limit)
|
discoveredRooms, _, nextWalker, err := rsAPI.QueryNextRoomHierarchyPage(req.Context(), walker, limit)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
|
|
|
@ -94,6 +94,7 @@ func Setup(
|
||||||
unstableFeatures := map[string]bool{
|
unstableFeatures := map[string]bool{
|
||||||
"org.matrix.e2e_cross_signing": true,
|
"org.matrix.e2e_cross_signing": true,
|
||||||
"org.matrix.msc2285.stable": true,
|
"org.matrix.msc2285.stable": true,
|
||||||
|
"org.matrix.msc3916.stable": true,
|
||||||
}
|
}
|
||||||
for _, msc := range cfg.MSCs.MSCs {
|
for _, msc := range cfg.MSCs.MSCs {
|
||||||
unstableFeatures["org.matrix."+msc] = true
|
unstableFeatures["org.matrix."+msc] = true
|
||||||
|
@ -255,7 +256,7 @@ func Setup(
|
||||||
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(), rsAPI, userAPI, cfg)
|
serverNotificationSender, err := getSenderDevice(context.Background(), rsAPI, 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 server notices")
|
||||||
}
|
}
|
||||||
|
|
||||||
synapseAdminRouter.Handle("/admin/v1/send_server_notice/{txnID}",
|
synapseAdminRouter.Handle("/admin/v1/send_server_notice/{txnID}",
|
||||||
|
@ -732,7 +733,7 @@ func Setup(
|
||||||
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
v3mux.Handle("/auth/{authType}/fallback/web",
|
v3mux.Handle("/auth/{authType}/fallback/web",
|
||||||
httputil.MakeHTMLAPI("auth_fallback", enableMetrics, func(w http.ResponseWriter, req *http.Request) {
|
httputil.MakeHTTPAPI("auth_fallback", userAPI, enableMetrics, func(w http.ResponseWriter, req *http.Request) {
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
AuthFallback(w, req, vars["authType"], cfg)
|
AuthFallback(w, req, vars["authType"], cfg)
|
||||||
}),
|
}),
|
||||||
|
@ -1513,4 +1514,58 @@ func Setup(
|
||||||
return GetPresence(req, device, natsClient, cfg.Matrix.JetStream.Prefixed(jetstream.RequestPresence), vars["userId"])
|
return GetPresence(req, device, natsClient, cfg.Matrix.JetStream.Prefixed(jetstream.RequestPresence), vars["userId"])
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
v3mux.Handle("/rooms/{roomID}/joined_members",
|
||||||
|
httputil.MakeAuthAPI("rooms_members", 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 GetJoinedMembers(req, device, vars["roomID"], rsAPI)
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
v3mux.Handle("/rooms/{roomID}/report/{eventID}",
|
||||||
|
httputil.MakeAuthAPI("report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
return ReportEvent(req, device, vars["roomID"], vars["eventID"], rsAPI)
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
synapseAdminRouter.Handle("/admin/v1/event_reports",
|
||||||
|
httputil.MakeAdminAPI("admin_report_events", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
from := parseUint64OrDefault(req.URL.Query().Get("from"), 0)
|
||||||
|
limit := parseUint64OrDefault(req.URL.Query().Get("limit"), 100)
|
||||||
|
dir := req.URL.Query().Get("dir")
|
||||||
|
userID := req.URL.Query().Get("user_id")
|
||||||
|
roomID := req.URL.Query().Get("room_id")
|
||||||
|
|
||||||
|
// Go backwards if direction is empty or "b"
|
||||||
|
backwards := dir == "" || dir == "b"
|
||||||
|
return GetEventReports(req, rsAPI, from, limit, backwards, userID, roomID)
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}",
|
||||||
|
httputil.MakeAdminAPI("admin_report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
return GetEventReport(req, rsAPI, vars["reportID"])
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}",
|
||||||
|
httputil.MakeAdminAPI("admin_report_event_delete", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
return DeleteEventReport(req, rsAPI, vars["reportID"])
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodDelete, http.MethodOptions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ func SendEvent(
|
||||||
req.Context(), rsAPI,
|
req.Context(), rsAPI,
|
||||||
api.KindNew,
|
api.KindNew,
|
||||||
[]*types.HeaderedEvent{
|
[]*types.HeaderedEvent{
|
||||||
&types.HeaderedEvent{PDU: e},
|
{PDU: e},
|
||||||
},
|
},
|
||||||
device.UserDomain(),
|
device.UserDomain(),
|
||||||
domain,
|
domain,
|
||||||
|
|
|
@ -265,7 +265,7 @@ func createEvents(eventsJSON []string, roomVer gomatrixserverlib.RoomVersion) ([
|
||||||
for i, eventJSON := range eventsJSON {
|
for i, eventJSON := range eventsJSON {
|
||||||
pdu, evErr := roomVerImpl.NewEventFromTrustedJSON([]byte(eventJSON), false)
|
pdu, evErr := roomVerImpl.NewEventFromTrustedJSON([]byte(eventJSON), false)
|
||||||
if evErr != nil {
|
if evErr != nil {
|
||||||
return nil, fmt.Errorf("failed to make event: %s", err.Error())
|
return nil, fmt.Errorf("failed to make event: %s", evErr.Error())
|
||||||
}
|
}
|
||||||
ev := types.HeaderedEvent{PDU: pdu}
|
ev := types.HeaderedEvent{PDU: pdu}
|
||||||
events[i] = &ev
|
events[i] = &ev
|
||||||
|
|
|
@ -215,7 +215,7 @@ func SendServerNotice(
|
||||||
}
|
}
|
||||||
if !membershipRes.IsInRoom {
|
if !membershipRes.IsInRoom {
|
||||||
// re-invite the user
|
// re-invite the user
|
||||||
res, err := sendInvite(ctx, userAPI, senderDevice, roomID, r.UserID, "Server notice room", cfgClient, rsAPI, asAPI, time.Now())
|
res, err := sendInvite(ctx, senderDevice, roomID, r.UserID, "Server notice room", cfgClient, rsAPI, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ func CreateSession(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close() // nolint: errcheck
|
||||||
|
|
||||||
// Error if the status isn't OK
|
// Error if the status isn't OK
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
|
|
@ -145,7 +145,7 @@ func (p *P2PMonolith) SetupDendrite(
|
||||||
)
|
)
|
||||||
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||||
|
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation, enableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,13 @@ package relay
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
relayServerAPI "github.com/matrix-org/dendrite/relayapi/api"
|
relayServerAPI "github.com/matrix-org/dendrite/relayapi/api"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -54,7 +54,7 @@ func NewRelayServerRetriever(
|
||||||
federationAPI: federationAPI,
|
federationAPI: federationAPI,
|
||||||
relayAPI: relayAPI,
|
relayAPI: relayAPI,
|
||||||
relayServersQueried: make(map[spec.ServerName]bool),
|
relayServersQueried: make(map[spec.ServerName]bool),
|
||||||
running: *atomic.NewBool(false),
|
running: atomic.Bool{},
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Yggdrasil Demo
|
# Yggdrasil Demo
|
||||||
|
|
||||||
This is the Dendrite Yggdrasil demo! It's easy to get started - all you need is Go 1.20 or later.
|
This is the Dendrite Yggdrasil demo! It's easy to get started - all you need is Go 1.21 or later.
|
||||||
|
|
||||||
To run the homeserver, start at the root of the Dendrite repository and run:
|
To run the homeserver, start at the root of the Dendrite repository and run:
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ var (
|
||||||
instanceName = flag.String("name", "dendrite-p2p-ygg", "the name of this P2P demo instance")
|
instanceName = flag.String("name", "dendrite-p2p-ygg", "the name of this P2P demo instance")
|
||||||
instancePort = flag.Int("port", 8008, "the port that the client API will listen on")
|
instancePort = flag.Int("port", 8008, "the port that the client API will listen on")
|
||||||
instancePeer = flag.String("peer", "", "the static Yggdrasil peers to connect to, comma separated-list")
|
instancePeer = flag.String("peer", "", "the static Yggdrasil peers to connect to, comma separated-list")
|
||||||
instanceListen = flag.String("listen", "tcp://:0", "the port Yggdrasil peers can connect to")
|
instanceListen = flag.String("listen", "tls://:0", "the port Yggdrasil peers can connect to")
|
||||||
instanceDir = flag.String("dir", ".", "the directory to store the databases in (if --config not specified)")
|
instanceDir = flag.String("dir", ".", "the directory to store the databases in (if --config not specified)")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -134,6 +134,7 @@ func main() {
|
||||||
cfg.RoomServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-roomserver.db", filepath.Join(*instanceDir, *instanceName)))
|
cfg.RoomServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-roomserver.db", filepath.Join(*instanceDir, *instanceName)))
|
||||||
cfg.KeyServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-keyserver.db", filepath.Join(*instanceDir, *instanceName)))
|
cfg.KeyServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-keyserver.db", filepath.Join(*instanceDir, *instanceName)))
|
||||||
cfg.FederationAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-federationapi.db", filepath.Join(*instanceDir, *instanceName)))
|
cfg.FederationAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-federationapi.db", filepath.Join(*instanceDir, *instanceName)))
|
||||||
|
cfg.RelayAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-relayapi.db", filepath.Join(*instanceDir, *instanceName)))
|
||||||
cfg.MSCs.MSCs = []string{"msc2836"}
|
cfg.MSCs.MSCs = []string{"msc2836"}
|
||||||
cfg.MSCs.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-mscs.db", filepath.Join(*instanceDir, *instanceName)))
|
cfg.MSCs.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-mscs.db", filepath.Join(*instanceDir, *instanceName)))
|
||||||
cfg.ClientAPI.RegistrationDisabled = false
|
cfg.ClientAPI.RegistrationDisabled = false
|
||||||
|
@ -213,16 +214,16 @@ func main() {
|
||||||
natsInstance := jetstream.NATSInstance{}
|
natsInstance := jetstream.NATSInstance{}
|
||||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.EnableMetrics)
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.EnableMetrics)
|
||||||
|
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation)
|
|
||||||
|
|
||||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
|
||||||
rsAPI.SetAppserviceAPI(asAPI)
|
|
||||||
fsAPI := federationapi.NewInternalAPI(
|
fsAPI := federationapi.NewInternalAPI(
|
||||||
processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true,
|
processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true,
|
||||||
)
|
)
|
||||||
|
|
||||||
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||||
|
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
|
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||||
|
rsAPI.SetAppserviceAPI(asAPI)
|
||||||
|
|
||||||
monolith := setup.Monolith{
|
monolith := setup.Monolith{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
Client: ygg.CreateClient(),
|
Client: ygg.CreateClient(),
|
||||||
|
|
|
@ -18,16 +18,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
"github.com/neilalexander/utp"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/yggdrasil-network/yggquic"
|
||||||
|
|
||||||
ironwoodtypes "github.com/Arceliar/ironwood/types"
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
yggdrasilcore "github.com/yggdrasil-network/yggdrasil-go/src/core"
|
yggdrasilcore "github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||||
|
@ -40,25 +39,23 @@ type Node struct {
|
||||||
core *yggdrasilcore.Core
|
core *yggdrasilcore.Core
|
||||||
multicast *yggdrasilmulticast.Multicast
|
multicast *yggdrasilmulticast.Multicast
|
||||||
log *gologme.Logger
|
log *gologme.Logger
|
||||||
utpSocket *utp.Socket
|
*yggquic.YggdrasilTransport
|
||||||
incoming chan net.Conn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) DialerContext(ctx context.Context, _, address string) (net.Conn, error) {
|
func (n *Node) DialerContext(ctx context.Context, _, address string) (net.Conn, error) {
|
||||||
tokens := strings.Split(address, ":")
|
tokens := strings.Split(address, ":")
|
||||||
raw, err := hex.DecodeString(tokens[0])
|
return n.DialContext(ctx, "yggdrasil", tokens[0])
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("hex.DecodeString: %w", err)
|
|
||||||
}
|
|
||||||
pk := make(ironwoodtypes.Addr, ed25519.PublicKeySize)
|
|
||||||
copy(pk, raw[:])
|
|
||||||
return n.utpSocket.DialAddrContext(ctx, pk)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Setup(sk ed25519.PrivateKey, instanceName, storageDirectory, peerURI, listenURI string) (*Node, error) {
|
func Setup(sk ed25519.PrivateKey, instanceName, storageDirectory, peerURI, listenURI string) (*Node, error) {
|
||||||
n := &Node{
|
n := &Node{
|
||||||
log: gologme.New(logrus.StandardLogger().Writer(), "", 0),
|
log: gologme.New(logrus.StandardLogger().Writer(), "", 0),
|
||||||
incoming: make(chan net.Conn),
|
}
|
||||||
|
|
||||||
|
cfg := config.GenerateConfig()
|
||||||
|
cfg.PrivateKey = config.KeyBytes(sk)
|
||||||
|
if err := cfg.GenerateSelfSignedCertificate(); err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.log.EnableLevel("error")
|
n.log.EnableLevel("error")
|
||||||
|
@ -78,12 +75,12 @@ func Setup(sk ed25519.PrivateKey, instanceName, storageDirectory, peerURI, liste
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n.core, err = core.New(sk[:], n.log, options...); err != nil {
|
if n.core, err = core.New(cfg.Certificate, n.log, options...); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
n.core.SetLogger(n.log)
|
n.core.SetLogger(n.log)
|
||||||
|
|
||||||
if n.utpSocket, err = utp.NewSocketFromPacketConnNoClose(n.core); err != nil {
|
if n.YggdrasilTransport, err = yggquic.New(n.core, *cfg.Certificate, nil); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +103,6 @@ func Setup(sk ed25519.PrivateKey, instanceName, storageDirectory, peerURI, liste
|
||||||
}
|
}
|
||||||
|
|
||||||
n.log.Printf("Public key: %x", n.core.PublicKey())
|
n.log.Printf("Public key: %x", n.core.PublicKey())
|
||||||
go n.listenFromYgg()
|
|
||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,56 +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 yggconn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (n *Node) listenFromYgg() {
|
|
||||||
for {
|
|
||||||
conn, err := n.utpSocket.Accept()
|
|
||||||
if err != nil {
|
|
||||||
n.log.Println("n.utpSocket.Accept:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n.incoming <- conn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements net.Listener
|
|
||||||
func (n *Node) Accept() (net.Conn, error) {
|
|
||||||
return <-n.incoming, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements net.Listener
|
|
||||||
func (n *Node) Close() error {
|
|
||||||
return n.utpSocket.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements net.Listener
|
|
||||||
func (n *Node) Addr() net.Addr {
|
|
||||||
return n.utpSocket.Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements http.Transport.Dial
|
|
||||||
func (n *Node) Dial(network, address string) (net.Conn, error) {
|
|
||||||
return n.DialContext(context.TODO(), network, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements http.Transport.DialContext
|
|
||||||
func (n *Node) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
return n.utpSocket.DialContext(ctx, network, address)
|
|
||||||
}
|
|
|
@ -54,7 +54,7 @@ var latest, _ = semver.NewVersion("v6.6.6") // Dummy version, used as "HEAD"
|
||||||
// due to the error:
|
// due to the error:
|
||||||
// When using COPY with more than one source file, the destination must be a directory and end with a /
|
// When using COPY with more than one source file, the destination must be a directory and end with a /
|
||||||
// We need to run a postgres anyway, so use the dockerfile associated with Complement instead.
|
// We need to run a postgres anyway, so use the dockerfile associated with Complement instead.
|
||||||
const DockerfilePostgreSQL = `FROM golang:1.20-bookworm as build
|
const DockerfilePostgreSQL = `FROM golang:1.22-bookworm as build
|
||||||
RUN apt-get update && apt-get install -y postgresql
|
RUN apt-get update && apt-get install -y postgresql
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
ARG BINARY
|
ARG BINARY
|
||||||
|
@ -67,13 +67,11 @@ RUN go build ./cmd/${BINARY}
|
||||||
RUN go build ./cmd/generate-keys
|
RUN go build ./cmd/generate-keys
|
||||||
RUN go build ./cmd/generate-config
|
RUN go build ./cmd/generate-config
|
||||||
RUN go build ./cmd/create-account
|
RUN go build ./cmd/create-account
|
||||||
RUN ./generate-config --ci > dendrite.yaml
|
RUN ./generate-config --ci --db "user=postgres database=postgres host=/var/run/postgresql/" > dendrite.yaml
|
||||||
RUN ./generate-keys --private-key matrix_key.pem --tls-cert server.crt --tls-key server.key
|
RUN ./generate-keys --private-key matrix_key.pem --tls-cert server.crt --tls-key server.key
|
||||||
|
|
||||||
# Replace the connection string with a single postgres DB, using user/db = 'postgres' and no password
|
# No password when connecting to Postgres
|
||||||
RUN sed -i "s%connection_string:.*$%connection_string: postgresql://postgres@localhost/postgres?sslmode=disable%g" dendrite.yaml
|
RUN sed -i "s%peer%trust%g" /etc/postgresql/15/main/pg_hba.conf
|
||||||
# No password when connecting over localhost
|
|
||||||
RUN sed -i "s%127.0.0.1/32 scram-sha-256%127.0.0.1/32 trust%g" /etc/postgresql/15/main/pg_hba.conf
|
|
||||||
# Bump up max conns for moar concurrency
|
# Bump up max conns for moar concurrency
|
||||||
RUN sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/15/main/postgresql.conf
|
RUN sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/15/main/postgresql.conf
|
||||||
RUN sed -i 's/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml
|
RUN sed -i 's/max_open_conns:.*$/max_open_conns: 100/g' dendrite.yaml
|
||||||
|
@ -100,7 +98,7 @@ ENV BINARY=dendrite
|
||||||
EXPOSE 8008 8448
|
EXPOSE 8008 8448
|
||||||
CMD /build/run_dendrite.sh`
|
CMD /build/run_dendrite.sh`
|
||||||
|
|
||||||
const DockerfileSQLite = `FROM golang:1.20-bookworm as build
|
const DockerfileSQLite = `FROM golang:1.22-bookworm as build
|
||||||
RUN apt-get update && apt-get install -y postgresql
|
RUN apt-get update && apt-get install -y postgresql
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
ARG BINARY
|
ARG BINARY
|
||||||
|
@ -410,7 +408,7 @@ func runImage(dockerClient *client.Client, volumeName string, branchNameToImageI
|
||||||
}
|
}
|
||||||
containerID = body.ID
|
containerID = body.ID
|
||||||
|
|
||||||
err = dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{})
|
err = dockerClient.ContainerStart(ctx, containerID, container.StartOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("failed to ContainerStart: %s", err)
|
return "", "", fmt.Errorf("failed to ContainerStart: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -442,7 +440,7 @@ func runImage(dockerClient *client.Client, volumeName string, branchNameToImageI
|
||||||
lastErr = nil
|
lastErr = nil
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
logs, err := dockerClient.ContainerLogs(context.Background(), containerID, types.ContainerLogsOptions{
|
logs, err := dockerClient.ContainerLogs(context.Background(), containerID, container.LogsOptions{
|
||||||
ShowStdout: true,
|
ShowStdout: true,
|
||||||
ShowStderr: true,
|
ShowStderr: true,
|
||||||
Follow: true,
|
Follow: true,
|
||||||
|
@ -463,7 +461,7 @@ func runImage(dockerClient *client.Client, volumeName string, branchNameToImageI
|
||||||
}
|
}
|
||||||
|
|
||||||
func destroyContainer(dockerClient *client.Client, containerID string) {
|
func destroyContainer(dockerClient *client.Client, containerID string) {
|
||||||
err := dockerClient.ContainerRemove(context.TODO(), containerID, types.ContainerRemoveOptions{
|
err := dockerClient.ContainerRemove(context.TODO(), containerID, container.RemoveOptions{
|
||||||
Force: true,
|
Force: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -550,7 +548,7 @@ func verifyTests(dockerClient *client.Client, volumeName string, versions []*sem
|
||||||
// cleanup old containers/volumes from a previous run
|
// cleanup old containers/volumes from a previous run
|
||||||
func cleanup(dockerClient *client.Client) {
|
func cleanup(dockerClient *client.Client) {
|
||||||
// ignore all errors, we are just cleaning up and don't want to fail just because we fail to cleanup
|
// ignore all errors, we are just cleaning up and don't want to fail just because we fail to cleanup
|
||||||
containers, _ := dockerClient.ContainerList(context.Background(), types.ContainerListOptions{
|
containers, _ := dockerClient.ContainerList(context.Background(), container.ListOptions{
|
||||||
Filters: label(dendriteUpgradeTestLabel),
|
Filters: label(dendriteUpgradeTestLabel),
|
||||||
All: true,
|
All: true,
|
||||||
})
|
})
|
||||||
|
@ -558,7 +556,7 @@ func cleanup(dockerClient *client.Client) {
|
||||||
log.Printf("Removing container: %v %v\n", c.ID, c.Names)
|
log.Printf("Removing container: %v %v\n", c.ID, c.Names)
|
||||||
timeout := 1
|
timeout := 1
|
||||||
_ = dockerClient.ContainerStop(context.Background(), c.ID, container.StopOptions{Timeout: &timeout})
|
_ = dockerClient.ContainerStop(context.Background(), c.ID, container.StopOptions{Timeout: &timeout})
|
||||||
_ = dockerClient.ContainerRemove(context.Background(), c.ID, types.ContainerRemoveOptions{
|
_ = dockerClient.ContainerRemove(context.Background(), c.ID, container.RemoveOptions{
|
||||||
Force: true,
|
Force: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ func main() {
|
||||||
// dependency. Other components also need updating after their dependencies are up.
|
// dependency. Other components also need updating after their dependencies are up.
|
||||||
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||||
|
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federationClient)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federationClient, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||||
|
|
||||||
rsAPI.SetAppserviceAPI(asAPI)
|
rsAPI.SetAppserviceAPI(asAPI)
|
||||||
|
|
185
contrib/dendrite-demo-i2p/main.go
Normal file
185
contrib/dendrite-demo-i2p/main.go
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
// 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"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/caching"
|
||||||
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/appservice"
|
||||||
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver"
|
||||||
|
"github.com/matrix-org/dendrite/setup"
|
||||||
|
basepkg "github.com/matrix-org/dendrite/setup/base"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/mscs"
|
||||||
|
"github.com/matrix-org/dendrite/userapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
samAddr = flag.String("samaddr", "127.0.0.1:7656", "Address to connect to the I2P SAMv3 API")
|
||||||
|
_, skip = os.LookupEnv("CI")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := setup.ParseFlags(true)
|
||||||
|
if skip {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
configErrors := &config.ConfigErrors{}
|
||||||
|
cfg.Verify(configErrors)
|
||||||
|
if len(*configErrors) > 0 {
|
||||||
|
for _, err := range *configErrors {
|
||||||
|
logrus.Errorf("Configuration error: %s", err)
|
||||||
|
}
|
||||||
|
logrus.Fatalf("Failed to start due to configuration errors")
|
||||||
|
}
|
||||||
|
processCtx := process.NewProcessContext()
|
||||||
|
|
||||||
|
internal.SetupStdLogging()
|
||||||
|
internal.SetupHookLogging(cfg.Logging)
|
||||||
|
internal.SetupPprof()
|
||||||
|
|
||||||
|
basepkg.PlatformSanityChecks()
|
||||||
|
|
||||||
|
logrus.Infof("Dendrite version %s", internal.VersionString())
|
||||||
|
if !cfg.ClientAPI.RegistrationDisabled && cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled {
|
||||||
|
logrus.Warn("Open registration is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create DNS cache
|
||||||
|
var dnsCache *fclient.DNSCache
|
||||||
|
if cfg.Global.DNSCache.Enabled {
|
||||||
|
dnsCache = fclient.NewDNSCache(
|
||||||
|
cfg.Global.DNSCache.CacheSize,
|
||||||
|
cfg.Global.DNSCache.CacheLifetime,
|
||||||
|
)
|
||||||
|
logrus.Infof(
|
||||||
|
"DNS cache enabled (size %d, lifetime %s)",
|
||||||
|
cfg.Global.DNSCache.CacheSize,
|
||||||
|
cfg.Global.DNSCache.CacheLifetime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup tracing
|
||||||
|
closer, err := cfg.SetupTracing()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Panicf("failed to start opentracing")
|
||||||
|
}
|
||||||
|
defer closer.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
// setup sentry
|
||||||
|
if cfg.Global.Sentry.Enabled {
|
||||||
|
logrus.Info("Setting up Sentry for debugging...")
|
||||||
|
err = sentry.Init(sentry.ClientOptions{
|
||||||
|
Dsn: cfg.Global.Sentry.DSN,
|
||||||
|
Environment: cfg.Global.Sentry.Environment,
|
||||||
|
Debug: true,
|
||||||
|
ServerName: string(cfg.Global.ServerName),
|
||||||
|
Release: "dendrite@" + internal.VersionString(),
|
||||||
|
AttachStacktrace: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Panic("failed to start Sentry")
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
processCtx.ComponentStarted()
|
||||||
|
<-processCtx.WaitForShutdown()
|
||||||
|
if !sentry.Flush(time.Second * 5) {
|
||||||
|
logrus.Warnf("failed to flush all Sentry events!")
|
||||||
|
}
|
||||||
|
processCtx.ComponentFinished()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
federationClient := basepkg.CreateFederationClient(cfg, dnsCache)
|
||||||
|
httpClient := basepkg.CreateClient(cfg, dnsCache)
|
||||||
|
|
||||||
|
// prepare required dependencies
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
|
||||||
|
caches := caching.NewRistrettoCache(cfg.Global.Cache.EstimatedMaxSize, cfg.Global.Cache.MaxAge, caching.EnableMetrics)
|
||||||
|
natsInstance := jetstream.NATSInstance{}
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.EnableMetrics)
|
||||||
|
fsAPI := federationapi.NewInternalAPI(
|
||||||
|
processCtx, cfg, cm, &natsInstance, federationClient, rsAPI, caches, nil, false,
|
||||||
|
)
|
||||||
|
|
||||||
|
keyRing := fsAPI.KeyRing()
|
||||||
|
|
||||||
|
// 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. Other components also need updating after their dependencies are up.
|
||||||
|
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||||
|
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federationClient, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||||
|
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||||
|
|
||||||
|
rsAPI.SetAppserviceAPI(asAPI)
|
||||||
|
rsAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
|
monolith := setup.Monolith{
|
||||||
|
Config: cfg,
|
||||||
|
Client: httpClient,
|
||||||
|
FedClient: federationClient,
|
||||||
|
KeyRing: keyRing,
|
||||||
|
|
||||||
|
AppserviceAPI: asAPI,
|
||||||
|
// always use the concrete impl here even in -http mode because adding public routes
|
||||||
|
// must be done on the concrete impl not an HTTP client else fedapi will call itself
|
||||||
|
FederationAPI: fsAPI,
|
||||||
|
RoomserverAPI: rsAPI,
|
||||||
|
UserAPI: userAPI,
|
||||||
|
}
|
||||||
|
monolith.AddAllPublicRoutes(processCtx, cfg, routers, cm, &natsInstance, caches, caching.EnableMetrics)
|
||||||
|
|
||||||
|
if len(cfg.MSCs.MSCs) > 0 {
|
||||||
|
if err := mscs.Enable(cfg, cm, routers, &monolith, caches); err != nil {
|
||||||
|
logrus.WithError(err).Fatalf("Failed to enable MSCs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
upCounter := prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Namespace: "dendrite",
|
||||||
|
Name: "up",
|
||||||
|
ConstLabels: map[string]string{
|
||||||
|
"version": internal.VersionString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
upCounter.Add(1)
|
||||||
|
prometheus.MustRegister(upCounter)
|
||||||
|
|
||||||
|
// Expose the matrix APIs directly rather than putting them under a /api path.
|
||||||
|
go func() {
|
||||||
|
SetupAndServeHTTPS(processCtx, cfg, routers) //, httpsAddr, nil, nil)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We want to block forever to let the HTTP and HTTPS handler serve the APIs
|
||||||
|
basepkg.WaitForShutdown(processCtx)
|
||||||
|
}
|
233
contrib/dendrite-demo-i2p/main_i2p.go
Normal file
233
contrib/dendrite-demo-i2p/main_i2p.go
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"embed"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/cretz/bine/tor"
|
||||||
|
"github.com/eyedeekay/goSam"
|
||||||
|
"github.com/eyedeekay/onramp"
|
||||||
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/kardianos/minwinsvc"
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
basepkg "github.com/matrix-org/dendrite/setup/base"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func client() (*goSam.Client, error) {
|
||||||
|
if skip {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return goSam.NewClient(*samAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sam, samError = client()
|
||||||
|
|
||||||
|
func start() (*tor.Tor, error) {
|
||||||
|
if skip {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return tor.Start(context.Background(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialer() (*tor.Dialer, error) {
|
||||||
|
if skip {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return t.Dialer(context.TODO(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
t, terr = start()
|
||||||
|
tdialer, tderr = dialer()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dial a network connection to an I2P server or a unix socket. Use Tor, or Fail for clearnet addresses.
|
||||||
|
func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
if samError != nil {
|
||||||
|
return nil, samError
|
||||||
|
}
|
||||||
|
if network == "unix" {
|
||||||
|
return net.Dial(network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the addr to a full URL
|
||||||
|
url, err := url.Parse(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(url.Host, ".i2p") {
|
||||||
|
return sam.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
if terr != nil {
|
||||||
|
return nil, terr
|
||||||
|
}
|
||||||
|
if (tderr != nil) || (tdialer == nil) {
|
||||||
|
return nil, tderr
|
||||||
|
}
|
||||||
|
return tdialer.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed static/*.gotmpl
|
||||||
|
var staticContent embed.FS
|
||||||
|
|
||||||
|
// SetupAndServeHTTPS sets up the HTTPS server to serve client & federation APIs
|
||||||
|
// and adds a prometheus handler under /_dendrite/metrics.
|
||||||
|
func SetupAndServeHTTPS(
|
||||||
|
processContext *process.ProcessContext,
|
||||||
|
cfg *config.Dendrite,
|
||||||
|
routers httputil.Routers,
|
||||||
|
) {
|
||||||
|
// create a transport that uses SAM to dial TCP Connections
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: DialContext,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
http.DefaultClient = httpClient
|
||||||
|
|
||||||
|
garlic, err := onramp.NewGarlic("dendrite", *samAddr, onramp.OPT_HUGE)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("failed to create garlic")
|
||||||
|
}
|
||||||
|
defer garlic.Close() // nolint: errcheck
|
||||||
|
listener, err := garlic.ListenTLS()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("failed to serve HTTPS")
|
||||||
|
}
|
||||||
|
defer listener.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
externalHTTPSAddr := config.ServerAddress{}
|
||||||
|
https, err := config.HTTPAddress("https://" + listener.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatalf("Failed to parse http address")
|
||||||
|
}
|
||||||
|
externalHTTPSAddr = https
|
||||||
|
|
||||||
|
externalRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||||
|
|
||||||
|
externalServ := &http.Server{
|
||||||
|
Addr: externalHTTPSAddr.Address,
|
||||||
|
WriteTimeout: basepkg.HTTPServerTimeout,
|
||||||
|
Handler: externalRouter,
|
||||||
|
BaseContext: func(_ net.Listener) context.Context {
|
||||||
|
return processContext.Context()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect for Landing Page
|
||||||
|
externalRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Redirect(w, r, httputil.PublicStaticPath, http.StatusFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
if cfg.Global.Metrics.Enabled {
|
||||||
|
externalRouter.Handle("/metrics", httputil.WrapHandlerInBasicAuth(promhttp.Handler(), cfg.Global.Metrics.BasicAuth))
|
||||||
|
}
|
||||||
|
|
||||||
|
basepkg.ConfigureAdminEndpoints(processContext, routers)
|
||||||
|
|
||||||
|
// Parse and execute the landing page template
|
||||||
|
tmpl := template.Must(template.ParseFS(staticContent, "static/*.gotmpl"))
|
||||||
|
landingPage := &bytes.Buffer{}
|
||||||
|
if err := tmpl.ExecuteTemplate(landingPage, "index.gotmpl", map[string]string{
|
||||||
|
"Version": internal.VersionString(),
|
||||||
|
}); err != nil {
|
||||||
|
logrus.WithError(err).Fatal("failed to execute landing page template")
|
||||||
|
}
|
||||||
|
|
||||||
|
routers.Static.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, _ = w.Write(landingPage.Bytes())
|
||||||
|
})
|
||||||
|
|
||||||
|
var clientHandler http.Handler
|
||||||
|
clientHandler = routers.Client
|
||||||
|
if cfg.Global.Sentry.Enabled {
|
||||||
|
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
||||||
|
Repanic: true,
|
||||||
|
})
|
||||||
|
clientHandler = sentryHandler.Handle(routers.Client)
|
||||||
|
}
|
||||||
|
var federationHandler http.Handler
|
||||||
|
federationHandler = routers.Federation
|
||||||
|
if cfg.Global.Sentry.Enabled {
|
||||||
|
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
||||||
|
Repanic: true,
|
||||||
|
})
|
||||||
|
federationHandler = sentryHandler.Handle(routers.Federation)
|
||||||
|
}
|
||||||
|
externalRouter.PathPrefix(httputil.DendriteAdminPathPrefix).Handler(routers.DendriteAdmin)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(clientHandler)
|
||||||
|
if !cfg.Global.DisableFederation {
|
||||||
|
externalRouter.PathPrefix(httputil.PublicKeyPathPrefix).Handler(routers.Keys)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicFederationPathPrefix).Handler(federationHandler)
|
||||||
|
}
|
||||||
|
externalRouter.PathPrefix(httputil.SynapseAdminPathPrefix).Handler(routers.SynapseAdmin)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(routers.Media)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicWellKnownPrefix).Handler(routers.WellKnown)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicStaticPath).Handler(routers.Static)
|
||||||
|
|
||||||
|
externalRouter.NotFoundHandler = httputil.NotFoundCORSHandler
|
||||||
|
externalRouter.MethodNotAllowedHandler = httputil.NotAllowedHandler
|
||||||
|
|
||||||
|
if externalHTTPSAddr.Enabled() {
|
||||||
|
go func() {
|
||||||
|
var externalShutdown atomic.Bool // RegisterOnShutdown can be called more than once
|
||||||
|
logrus.Infof("Starting external listener on https://%s", externalServ.Addr)
|
||||||
|
processContext.ComponentStarted()
|
||||||
|
externalServ.RegisterOnShutdown(func() {
|
||||||
|
if externalShutdown.CompareAndSwap(false, true) {
|
||||||
|
processContext.ComponentFinished()
|
||||||
|
logrus.Infof("Stopped external HTTPS listener")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
addr := listener.Addr()
|
||||||
|
externalServ.Addr = addr.String()
|
||||||
|
if err := externalServ.Serve(listener); err != nil {
|
||||||
|
if err != http.ErrServerClosed {
|
||||||
|
logrus.WithError(err).Fatal("failed to serve HTTPS")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("Stopped external listener on %s", externalServ.Addr)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
minwinsvc.SetOnExit(processContext.ShutdownDendrite)
|
||||||
|
<-processContext.WaitForShutdown()
|
||||||
|
|
||||||
|
logrus.Infof("Stopping HTTPS listeners")
|
||||||
|
_ = externalServ.Shutdown(context.Background())
|
||||||
|
logrus.Infof("Stopped HTTPS listeners")
|
||||||
|
}
|
50
contrib/dendrite-demo-i2p/main_test.go
Normal file
50
contrib/dendrite-demo-i2p/main_test.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is an instrumented main, used when running integration tests (sytest) with code coverage.
|
||||||
|
// Compile: go test -c -race -cover -covermode=atomic -o monolith.debug -coverpkg "github.com/matrix-org/..." ./cmd/dendrite
|
||||||
|
// Run the monolith: ./monolith.debug -test.coverprofile=/somewhere/to/dump/integrationcover.out DEVEL --config dendrite.yaml
|
||||||
|
// Generate HTML with coverage: go tool cover -html=/somewhere/where/there/is/integrationcover.out -o cover.html
|
||||||
|
// Source: https://dzone.com/articles/measuring-integration-test-coverage-rate-in-pouchc
|
||||||
|
func TestMain(t *testing.T) {
|
||||||
|
var args []string
|
||||||
|
|
||||||
|
for _, arg := range os.Args {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(arg, "DEVEL"):
|
||||||
|
case strings.HasPrefix(arg, "-test"):
|
||||||
|
default:
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only run the tests if there are args to be passed
|
||||||
|
if len(args) <= 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Log(args)
|
||||||
|
|
||||||
|
waitCh := make(chan int, 1)
|
||||||
|
os.Args = args
|
||||||
|
go func() {
|
||||||
|
main()
|
||||||
|
close(waitCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
signalCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGHUP)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-signalCh:
|
||||||
|
return
|
||||||
|
case <-waitCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
63
contrib/dendrite-demo-i2p/static/index.gotmpl
Normal file
63
contrib/dendrite-demo-i2p/static/index.gotmpl
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Dendrite is running</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
|
||||||
|
max-width: 40em;
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1, p {
|
||||||
|
margin: 1.5em;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
background-color: #ccc;
|
||||||
|
color: #ccc;
|
||||||
|
height: 1px;
|
||||||
|
width: 7em;
|
||||||
|
margin-top: 4em;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
display: block;
|
||||||
|
width: 12em;
|
||||||
|
margin: 4em auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="logo">
|
||||||
|
<svg role="img" aria-label="[Matrix logo]" viewBox="0 0 200 85" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g id="parent" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="child" transform="translate(-122.000000, -6.000000)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<g id="matrix-logo" transform="translate(122.000000, 6.000000)">
|
||||||
|
<polygon id="left-bracket" points="2.24708861 1.93811009 2.24708861 82.7268844 8.10278481 82.7268844 8.10278481 84.6652459 0 84.6652459 0 0 8.10278481 0 8.10278481 1.93811009"></polygon>
|
||||||
|
<path d="M24.8073418,27.5493174 L24.8073418,31.6376991 L24.924557,31.6376991 C26.0227848,30.0814294 27.3455696,28.8730642 28.8951899,28.0163743 C30.4437975,27.1611927 32.2189873,26.7318422 34.218481,26.7318422 C36.1394937,26.7318422 37.8946835,27.102622 39.4825316,27.8416679 C41.0708861,28.5819706 42.276962,29.8856073 43.1005063,31.7548404 C44.0017722,30.431345 45.2270886,29.2629486 46.7767089,28.2506569 C48.3253165,27.2388679 50.158481,26.7318422 52.2764557,26.7318422 C53.8843038,26.7318422 55.3736709,26.9269101 56.7473418,27.3162917 C58.1189873,27.7056734 59.295443,28.3285835 60.2759494,29.185022 C61.255443,30.0422147 62.02,31.1615927 62.5701266,32.5426532 C63.1187342,33.9262275 63.3936709,35.5898349 63.3936709,37.5372459 L63.3936709,57.7443688 L55.0410127,57.7441174 L55.0410127,40.6319376 C55.0410127,39.6201486 55.0020253,38.6661761 54.9232911,37.7700202 C54.8440506,36.8751211 54.6293671,36.0968606 54.2764557,35.4339817 C53.9232911,34.772611 53.403038,34.2464807 52.7177215,33.8568477 C52.0313924,33.4689743 51.0997468,33.2731523 49.9235443,33.2731523 C48.7473418,33.2731523 47.7962025,33.4983853 47.0706329,33.944578 C46.344557,34.393033 45.7764557,34.9774826 45.3650633,35.6969211 C44.9534177,36.4181193 44.6787342,37.2353431 44.5417722,38.150855 C44.4037975,39.0653615 44.3356962,39.9904257 44.3356962,40.9247908 L44.3356962,57.7443688 L35.9835443,57.7443688 L35.9835443,40.8079009 C35.9835443,39.9124991 35.963038,39.0263982 35.9253165,38.150855 C35.8853165,37.2743064 35.7192405,36.4666349 35.424557,35.7263321 C35.1303797,34.9872862 34.64,34.393033 33.9539241,33.944578 C33.2675949,33.4983853 32.2579747,33.2731523 30.9248101,33.2731523 C30.5321519,33.2731523 30.0126582,33.3608826 29.3663291,33.5365945 C28.7192405,33.7118037 28.0913924,34.0433688 27.4840506,34.5292789 C26.875443,35.0164459 26.3564557,35.7172826 25.9250633,36.6315376 C25.4934177,37.5470495 25.2779747,38.7436 25.2779747,40.2229486 L25.2779747,57.7441174 L16.9260759,57.7443688 L16.9260759,27.5493174 L24.8073418,27.5493174 Z" id="m"></path>
|
||||||
|
<path d="M68.7455696,31.9886202 C69.6075949,30.7033339 70.7060759,29.672189 72.0397468,28.8926716 C73.3724051,28.1141596 74.8716456,27.5596239 76.5387342,27.2283101 C78.2050633,26.8977505 79.8817722,26.7315908 81.5678481,26.7315908 C83.0974684,26.7315908 84.6458228,26.8391798 86.2144304,27.0525982 C87.7827848,27.2675248 89.2144304,27.6865688 90.5086076,28.3087248 C91.8025316,28.9313835 92.8610127,29.7983798 93.6848101,30.9074514 C94.5083544,32.0170257 94.92,33.4870734 94.92,35.3173431 L94.92,51.026844 C94.92,52.3913138 94.998481,53.6941963 95.1556962,54.9400165 C95.3113924,56.1865908 95.5863291,57.120956 95.9787342,57.7436147 L87.5091139,57.7436147 C87.3518987,57.276055 87.2240506,56.7996972 87.1265823,56.3125303 C87.0278481,55.8266202 86.9592405,55.3301523 86.9207595,54.8236294 C85.5873418,56.1865908 84.0182278,57.1405633 82.2156962,57.6857982 C80.4113924,58.2295248 78.5683544,58.503022 76.6860759,58.503022 C75.2346835,58.503022 73.8817722,58.3275615 72.6270886,57.9776459 C71.3718987,57.6269761 70.2744304,57.082244 69.3334177,56.3411872 C68.3921519,55.602644 67.656962,54.6680275 67.1275949,53.5390972 C66.5982278,52.410167 66.3331646,51.065556 66.3331646,49.5087835 C66.3331646,47.7961578 66.6367089,46.384178 67.2455696,45.2756092 C67.8529114,44.1652807 68.6367089,43.2799339 69.5987342,42.6173064 C70.5589873,41.9556844 71.6567089,41.4592165 72.8924051,41.1284055 C74.1273418,40.7978459 75.3721519,40.5356606 76.6270886,40.3398385 C77.8820253,40.1457761 79.116962,39.9896716 80.3329114,39.873033 C81.5483544,39.7558917 82.6270886,39.5804312 83.5681013,39.3469028 C84.5093671,39.1133743 85.2536709,38.7732624 85.8032911,38.3250587 C86.3513924,37.8773578 86.6063291,37.2252881 86.5678481,36.3680954 C86.5678481,35.4731963 86.4210127,34.7620532 86.1268354,34.2366771 C85.8329114,33.7113009 85.4405063,33.3018092 84.9506329,33.0099615 C84.4602532,32.7181138 83.8916456,32.5232972 83.2450633,32.4255119 C82.5977215,32.3294862 81.9010127,32.2797138 81.156962,32.2797138 C79.5098734,32.2797138 78.2159494,32.6303835 77.2746835,33.3312202 C76.3339241,34.0320569 75.7837975,35.2007046 75.6275949,36.8354037 L67.275443,36.8354037 C67.3924051,34.8892495 67.8817722,33.2726495 68.7455696,31.9886202 Z M85.2440506,43.6984752 C84.7149367,43.873433 84.1460759,44.0189798 83.5387342,44.1361211 C82.9306329,44.253011 82.2936709,44.350545 81.6270886,44.4279688 C80.96,44.5066495 80.2934177,44.6034294 79.6273418,44.7203193 C78.9994937,44.8362037 78.3820253,44.9933138 77.7749367,45.1871248 C77.1663291,45.3829468 76.636962,45.6451321 76.1865823,45.9759431 C75.7349367,46.3070055 75.3724051,46.7263009 75.0979747,47.2313156 C74.8232911,47.7375872 74.6863291,48.380356 74.6863291,49.1588679 C74.6863291,49.8979138 74.8232911,50.5218294 75.0979747,51.026844 C75.3724051,51.5338697 75.7455696,51.9328037 76.2159494,52.2246514 C76.6863291,52.5164991 77.2349367,52.7213706 77.8632911,52.8375064 C78.4898734,52.9546477 79.136962,53.012967 79.8037975,53.012967 C81.4506329,53.012967 82.724557,52.740978 83.6273418,52.1952404 C84.5288608,51.6507596 85.1949367,50.9981872 85.6270886,50.2382771 C86.0579747,49.4793725 86.323038,48.7119211 86.4212658,47.9321523 C86.518481,47.1536404 86.5681013,46.5304789 86.5681013,46.063422 L86.5681013,42.9677248 C86.2146835,43.2799339 85.7736709,43.5230147 85.2440506,43.6984752 Z" id="a"></path>
|
||||||
|
<path d="M116.917975,27.5493174 L116.917975,33.0976917 L110.801266,33.0976917 L110.801266,48.0492936 C110.801266,49.4502128 111.036203,50.3850807 111.507089,50.8518862 C111.976962,51.3191945 112.918734,51.5527229 114.33038,51.5527229 C114.801013,51.5527229 115.251392,51.5336183 115.683038,51.4944037 C116.114177,51.4561945 116.526076,51.3968697 116.917975,51.3194459 L116.917975,57.7438661 C116.212152,57.860756 115.427595,57.9381798 114.565316,57.9778972 C113.702785,58.0153523 112.859747,58.0357138 112.036203,58.0357138 C110.742278,58.0357138 109.516456,57.9477321 108.36,57.7722716 C107.202785,57.5975651 106.183544,57.2577046 105.301519,56.7509303 C104.418987,56.2454128 103.722785,55.5242147 103.213418,54.5898495 C102.703038,53.6562385 102.448608,52.4292716 102.448608,50.9099541 L102.448608,33.0976917 L97.3903797,33.0976917 L97.3903797,27.5493174 L102.448608,27.5493174 L102.448608,18.4967596 L110.801013,18.4967596 L110.801013,27.5493174 L116.917975,27.5493174 Z" id="t"></path>
|
||||||
|
<path d="M128.857975,27.5493174 L128.857975,33.1565138 L128.975696,33.1565138 C129.367089,32.2213945 129.896203,31.3559064 130.563544,30.557033 C131.23038,29.7596679 131.99443,29.0776844 132.857215,28.5130936 C133.719241,27.9495083 134.641266,27.5113596 135.622532,27.1988991 C136.601772,26.8879468 137.622025,26.7315908 138.681013,26.7315908 C139.229873,26.7315908 139.836962,26.8296275 140.504304,27.0239413 L140.504304,34.7336477 C140.111646,34.6552183 139.641013,34.586844 139.092658,34.5290275 C138.543291,34.4704569 138.014177,34.4410459 137.504304,34.4410459 C135.974937,34.4410459 134.681013,34.6949358 133.622785,35.2004532 C132.564051,35.7067248 131.711392,36.397255 131.064051,37.2735523 C130.417215,38.1501009 129.955443,39.1714422 129.681266,40.3398385 C129.407089,41.5074807 129.269873,42.7736624 129.269873,44.1361211 L129.269873,57.7438661 L120.917722,57.7438661 L120.917722,27.5493174 L128.857975,27.5493174 Z" id="r"></path>
|
||||||
|
<path d="M144.033165,22.8767376 L144.033165,16.0435798 L152.386076,16.0435798 L152.386076,22.8767376 L144.033165,22.8767376 Z M152.386076,27.5493174 L152.386076,57.7438661 L144.033165,57.7438661 L144.033165,27.5493174 L152.386076,27.5493174 Z" id="i"></path>
|
||||||
|
<polygon id="x" points="156.738228 27.5493174 166.266582 27.5493174 171.619494 35.4337303 176.913418 27.5493174 186.147848 27.5493174 176.148861 41.6831927 187.383544 57.7441174 177.85443 57.7441174 171.501772 48.2245028 165.148861 57.7441174 155.797468 57.7441174 166.737468 41.8589046"></polygon>
|
||||||
|
<polygon id="right-bracket" points="197.580759 82.7268844 197.580759 1.93811009 191.725063 1.93811009 191.725063 0 199.828354 0 199.828354 84.6652459 191.725063 84.6652459 191.725063 82.7268844"></polygon>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h1>It works! Dendrite {{ .Version }} is running</h1>
|
||||||
|
<p>Your Dendrite server is listening on this port and is ready for messages.</p>
|
||||||
|
<p>To use this server you'll need <a href="https://matrix.org/docs/projects/try-matrix-now.html#clients" target="_blank" rel="noopener noreferrer">a Matrix client</a>.
|
||||||
|
</p>
|
||||||
|
<p>Welcome to the Matrix universe :)</p>
|
||||||
|
<hr>
|
||||||
|
<p>
|
||||||
|
<small>
|
||||||
|
<a href="https://matrix.org" target="_blank" rel="noopener noreferrer">
|
||||||
|
matrix.org
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
180
contrib/dendrite-demo-tor/main.go
Normal file
180
contrib/dendrite-demo-tor/main.go
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
// 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 (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/caching"
|
||||||
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/appservice"
|
||||||
|
"github.com/matrix-org/dendrite/federationapi"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver"
|
||||||
|
"github.com/matrix-org/dendrite/setup"
|
||||||
|
basepkg "github.com/matrix-org/dendrite/setup/base"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/mscs"
|
||||||
|
"github.com/matrix-org/dendrite/userapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _, skip = os.LookupEnv("CI")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := setup.ParseFlags(true)
|
||||||
|
if skip {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
configErrors := &config.ConfigErrors{}
|
||||||
|
cfg.Verify(configErrors)
|
||||||
|
if len(*configErrors) > 0 {
|
||||||
|
for _, err := range *configErrors {
|
||||||
|
logrus.Errorf("Configuration error: %s", err)
|
||||||
|
}
|
||||||
|
logrus.Fatalf("Failed to start due to configuration errors")
|
||||||
|
}
|
||||||
|
processCtx := process.NewProcessContext()
|
||||||
|
|
||||||
|
internal.SetupStdLogging()
|
||||||
|
internal.SetupHookLogging(cfg.Logging)
|
||||||
|
internal.SetupPprof()
|
||||||
|
|
||||||
|
basepkg.PlatformSanityChecks()
|
||||||
|
|
||||||
|
logrus.Infof("Dendrite version %s", internal.VersionString())
|
||||||
|
if !cfg.ClientAPI.RegistrationDisabled && cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled {
|
||||||
|
logrus.Warn("Open registration is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create DNS cache
|
||||||
|
var dnsCache *fclient.DNSCache
|
||||||
|
if cfg.Global.DNSCache.Enabled {
|
||||||
|
dnsCache = fclient.NewDNSCache(
|
||||||
|
cfg.Global.DNSCache.CacheSize,
|
||||||
|
cfg.Global.DNSCache.CacheLifetime,
|
||||||
|
)
|
||||||
|
logrus.Infof(
|
||||||
|
"DNS cache enabled (size %d, lifetime %s)",
|
||||||
|
cfg.Global.DNSCache.CacheSize,
|
||||||
|
cfg.Global.DNSCache.CacheLifetime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup tracing
|
||||||
|
closer, err := cfg.SetupTracing()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Panicf("failed to start opentracing")
|
||||||
|
}
|
||||||
|
defer closer.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
// setup sentry
|
||||||
|
if cfg.Global.Sentry.Enabled {
|
||||||
|
logrus.Info("Setting up Sentry for debugging...")
|
||||||
|
err = sentry.Init(sentry.ClientOptions{
|
||||||
|
Dsn: cfg.Global.Sentry.DSN,
|
||||||
|
Environment: cfg.Global.Sentry.Environment,
|
||||||
|
Debug: true,
|
||||||
|
ServerName: string(cfg.Global.ServerName),
|
||||||
|
Release: "dendrite@" + internal.VersionString(),
|
||||||
|
AttachStacktrace: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Panic("failed to start Sentry")
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
processCtx.ComponentStarted()
|
||||||
|
<-processCtx.WaitForShutdown()
|
||||||
|
if !sentry.Flush(time.Second * 5) {
|
||||||
|
logrus.Warnf("failed to flush all Sentry events!")
|
||||||
|
}
|
||||||
|
processCtx.ComponentFinished()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
federationClient := basepkg.CreateFederationClient(cfg, dnsCache)
|
||||||
|
httpClient := basepkg.CreateClient(cfg, dnsCache)
|
||||||
|
|
||||||
|
// prepare required dependencies
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
|
||||||
|
caches := caching.NewRistrettoCache(cfg.Global.Cache.EstimatedMaxSize, cfg.Global.Cache.MaxAge, caching.EnableMetrics)
|
||||||
|
natsInstance := jetstream.NATSInstance{}
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.EnableMetrics)
|
||||||
|
fsAPI := federationapi.NewInternalAPI(
|
||||||
|
processCtx, cfg, cm, &natsInstance, federationClient, rsAPI, caches, nil, false,
|
||||||
|
)
|
||||||
|
|
||||||
|
keyRing := fsAPI.KeyRing()
|
||||||
|
|
||||||
|
// 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. Other components also need updating after their dependencies are up.
|
||||||
|
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||||
|
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federationClient, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||||
|
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||||
|
|
||||||
|
rsAPI.SetAppserviceAPI(asAPI)
|
||||||
|
rsAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
|
monolith := setup.Monolith{
|
||||||
|
Config: cfg,
|
||||||
|
Client: httpClient,
|
||||||
|
FedClient: federationClient,
|
||||||
|
KeyRing: keyRing,
|
||||||
|
|
||||||
|
AppserviceAPI: asAPI,
|
||||||
|
// always use the concrete impl here even in -http mode because adding public routes
|
||||||
|
// must be done on the concrete impl not an HTTP client else fedapi will call itself
|
||||||
|
FederationAPI: fsAPI,
|
||||||
|
RoomserverAPI: rsAPI,
|
||||||
|
UserAPI: userAPI,
|
||||||
|
}
|
||||||
|
monolith.AddAllPublicRoutes(processCtx, cfg, routers, cm, &natsInstance, caches, caching.EnableMetrics)
|
||||||
|
|
||||||
|
if len(cfg.MSCs.MSCs) > 0 {
|
||||||
|
if err := mscs.Enable(cfg, cm, routers, &monolith, caches); err != nil {
|
||||||
|
logrus.WithError(err).Fatalf("Failed to enable MSCs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
upCounter := prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Namespace: "dendrite",
|
||||||
|
Name: "up",
|
||||||
|
ConstLabels: map[string]string{
|
||||||
|
"version": internal.VersionString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
upCounter.Add(1)
|
||||||
|
prometheus.MustRegister(upCounter)
|
||||||
|
|
||||||
|
// Expose the matrix APIs directly rather than putting them under a /api path.
|
||||||
|
go func() {
|
||||||
|
SetupAndServeHTTPS(processCtx, cfg, routers) //, httpsAddr, nil, nil)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We want to block forever to let the HTTP and HTTPS handler serve the APIs
|
||||||
|
basepkg.WaitForShutdown(processCtx)
|
||||||
|
}
|
50
contrib/dendrite-demo-tor/main_test.go
Normal file
50
contrib/dendrite-demo-tor/main_test.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is an instrumented main, used when running integration tests (sytest) with code coverage.
|
||||||
|
// Compile: go test -c -race -cover -covermode=atomic -o monolith.debug -coverpkg "github.com/matrix-org/..." ./cmd/dendrite
|
||||||
|
// Run the monolith: ./monolith.debug -test.coverprofile=/somewhere/to/dump/integrationcover.out DEVEL --config dendrite.yaml
|
||||||
|
// Generate HTML with coverage: go tool cover -html=/somewhere/where/there/is/integrationcover.out -o cover.html
|
||||||
|
// Source: https://dzone.com/articles/measuring-integration-test-coverage-rate-in-pouchc
|
||||||
|
func TestMain(t *testing.T) {
|
||||||
|
var args []string
|
||||||
|
|
||||||
|
for _, arg := range os.Args {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(arg, "DEVEL"):
|
||||||
|
case strings.HasPrefix(arg, "-test"):
|
||||||
|
default:
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only run the tests if there are args to be passed
|
||||||
|
if len(args) <= 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Log(args)
|
||||||
|
|
||||||
|
waitCh := make(chan int, 1)
|
||||||
|
os.Args = args
|
||||||
|
go func() {
|
||||||
|
main()
|
||||||
|
close(waitCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
signalCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGHUP)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-signalCh:
|
||||||
|
return
|
||||||
|
case <-waitCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
215
contrib/dendrite-demo-tor/main_tor.go
Normal file
215
contrib/dendrite-demo-tor/main_tor.go
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"embed"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sync/atomic"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/cretz/bine/tor"
|
||||||
|
"github.com/eyedeekay/onramp"
|
||||||
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/kardianos/minwinsvc"
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
basepkg "github.com/matrix-org/dendrite/setup/base"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func start() (*tor.Tor, error) {
|
||||||
|
if skip {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return tor.Start(context.Background(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialer() (*tor.Dialer, error) {
|
||||||
|
if skip {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return t.Dialer(context.TODO(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
t, terr = start()
|
||||||
|
tdialer, tderr = dialer()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dial either a unix socket address, or connect to a remote address over Tor. Always uses Tor.
|
||||||
|
func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
if terr != nil {
|
||||||
|
return nil, terr
|
||||||
|
}
|
||||||
|
if (tderr != nil) || (tdialer == nil) {
|
||||||
|
return nil, tderr
|
||||||
|
}
|
||||||
|
if network == "unix" {
|
||||||
|
return net.Dial(network, addr)
|
||||||
|
}
|
||||||
|
// convert the addr to a full URL
|
||||||
|
url, err := url.Parse(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tdialer.DialContext(ctx, network, url.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed static/*.gotmpl
|
||||||
|
var staticContent embed.FS
|
||||||
|
|
||||||
|
// SetupAndServeHTTPS sets up the HTTPS server to serve client & federation APIs
|
||||||
|
// and adds a prometheus handler under /_dendrite/metrics.
|
||||||
|
func SetupAndServeHTTPS(
|
||||||
|
processContext *process.ProcessContext,
|
||||||
|
cfg *config.Dendrite,
|
||||||
|
routers httputil.Routers,
|
||||||
|
) {
|
||||||
|
// create a transport that uses SAM to dial TCP Connections
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: DialContext,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
http.DefaultClient = httpClient
|
||||||
|
|
||||||
|
onion, err := onramp.NewOnion("dendrite-onion")
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("failed to create onion")
|
||||||
|
}
|
||||||
|
defer onion.Close() // nolint: errcheck
|
||||||
|
listener, err := onion.ListenTLS()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("failed to serve HTTPS")
|
||||||
|
}
|
||||||
|
defer listener.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
externalHTTPSAddr := config.ServerAddress{}
|
||||||
|
https, err := config.HTTPAddress("https://" + listener.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatalf("Failed to parse http address")
|
||||||
|
}
|
||||||
|
externalHTTPSAddr = https
|
||||||
|
|
||||||
|
externalRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||||
|
|
||||||
|
externalServ := &http.Server{
|
||||||
|
Addr: externalHTTPSAddr.Address,
|
||||||
|
WriteTimeout: basepkg.HTTPServerTimeout,
|
||||||
|
Handler: externalRouter,
|
||||||
|
BaseContext: func(_ net.Listener) context.Context {
|
||||||
|
return processContext.Context()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect for Landing Page
|
||||||
|
externalRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Redirect(w, r, httputil.PublicStaticPath, http.StatusFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
if cfg.Global.Metrics.Enabled {
|
||||||
|
externalRouter.Handle("/metrics", httputil.WrapHandlerInBasicAuth(promhttp.Handler(), cfg.Global.Metrics.BasicAuth))
|
||||||
|
}
|
||||||
|
|
||||||
|
basepkg.ConfigureAdminEndpoints(processContext, routers)
|
||||||
|
|
||||||
|
// Parse and execute the landing page template
|
||||||
|
tmpl := template.Must(template.ParseFS(staticContent, "static/*.gotmpl"))
|
||||||
|
landingPage := &bytes.Buffer{}
|
||||||
|
if err := tmpl.ExecuteTemplate(landingPage, "index.gotmpl", map[string]string{
|
||||||
|
"Version": internal.VersionString(),
|
||||||
|
}); err != nil {
|
||||||
|
logrus.WithError(err).Fatal("failed to execute landing page template")
|
||||||
|
}
|
||||||
|
|
||||||
|
routers.Static.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, _ = w.Write(landingPage.Bytes())
|
||||||
|
})
|
||||||
|
|
||||||
|
var clientHandler http.Handler
|
||||||
|
clientHandler = routers.Client
|
||||||
|
if cfg.Global.Sentry.Enabled {
|
||||||
|
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
||||||
|
Repanic: true,
|
||||||
|
})
|
||||||
|
clientHandler = sentryHandler.Handle(routers.Client)
|
||||||
|
}
|
||||||
|
var federationHandler http.Handler
|
||||||
|
federationHandler = routers.Federation
|
||||||
|
if cfg.Global.Sentry.Enabled {
|
||||||
|
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
||||||
|
Repanic: true,
|
||||||
|
})
|
||||||
|
federationHandler = sentryHandler.Handle(routers.Federation)
|
||||||
|
}
|
||||||
|
externalRouter.PathPrefix(httputil.DendriteAdminPathPrefix).Handler(routers.DendriteAdmin)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(clientHandler)
|
||||||
|
if !cfg.Global.DisableFederation {
|
||||||
|
externalRouter.PathPrefix(httputil.PublicKeyPathPrefix).Handler(routers.Keys)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicFederationPathPrefix).Handler(federationHandler)
|
||||||
|
}
|
||||||
|
externalRouter.PathPrefix(httputil.SynapseAdminPathPrefix).Handler(routers.SynapseAdmin)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(routers.Media)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicWellKnownPrefix).Handler(routers.WellKnown)
|
||||||
|
externalRouter.PathPrefix(httputil.PublicStaticPath).Handler(routers.Static)
|
||||||
|
|
||||||
|
externalRouter.NotFoundHandler = httputil.NotFoundCORSHandler
|
||||||
|
externalRouter.MethodNotAllowedHandler = httputil.NotAllowedHandler
|
||||||
|
|
||||||
|
if externalHTTPSAddr.Enabled() {
|
||||||
|
go func() {
|
||||||
|
var externalShutdown atomic.Bool // RegisterOnShutdown can be called more than once
|
||||||
|
logrus.Infof("Starting external listener on https://%s", externalServ.Addr)
|
||||||
|
processContext.ComponentStarted()
|
||||||
|
externalServ.RegisterOnShutdown(func() {
|
||||||
|
if externalShutdown.CompareAndSwap(false, true) {
|
||||||
|
processContext.ComponentFinished()
|
||||||
|
logrus.Infof("Stopped external HTTPS listener")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
addr := listener.Addr()
|
||||||
|
externalServ.Addr = addr.String()
|
||||||
|
if err := externalServ.Serve(listener); err != nil {
|
||||||
|
if err != http.ErrServerClosed {
|
||||||
|
logrus.WithError(err).Fatal("failed to serve HTTPS")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("Stopped external listener on %s", externalServ.Addr)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
minwinsvc.SetOnExit(processContext.ShutdownDendrite)
|
||||||
|
<-processContext.WaitForShutdown()
|
||||||
|
|
||||||
|
logrus.Infof("Stopping HTTPS listeners")
|
||||||
|
_ = externalServ.Shutdown(context.Background())
|
||||||
|
logrus.Infof("Stopped HTTPS listeners")
|
||||||
|
}
|
63
contrib/dendrite-demo-tor/static/index.gotmpl
Normal file
63
contrib/dendrite-demo-tor/static/index.gotmpl
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Dendrite is running</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
|
||||||
|
max-width: 40em;
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1, p {
|
||||||
|
margin: 1.5em;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
background-color: #ccc;
|
||||||
|
color: #ccc;
|
||||||
|
height: 1px;
|
||||||
|
width: 7em;
|
||||||
|
margin-top: 4em;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
display: block;
|
||||||
|
width: 12em;
|
||||||
|
margin: 4em auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="logo">
|
||||||
|
<svg role="img" aria-label="[Matrix logo]" viewBox="0 0 200 85" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g id="parent" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="child" transform="translate(-122.000000, -6.000000)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<g id="matrix-logo" transform="translate(122.000000, 6.000000)">
|
||||||
|
<polygon id="left-bracket" points="2.24708861 1.93811009 2.24708861 82.7268844 8.10278481 82.7268844 8.10278481 84.6652459 0 84.6652459 0 0 8.10278481 0 8.10278481 1.93811009"></polygon>
|
||||||
|
<path d="M24.8073418,27.5493174 L24.8073418,31.6376991 L24.924557,31.6376991 C26.0227848,30.0814294 27.3455696,28.8730642 28.8951899,28.0163743 C30.4437975,27.1611927 32.2189873,26.7318422 34.218481,26.7318422 C36.1394937,26.7318422 37.8946835,27.102622 39.4825316,27.8416679 C41.0708861,28.5819706 42.276962,29.8856073 43.1005063,31.7548404 C44.0017722,30.431345 45.2270886,29.2629486 46.7767089,28.2506569 C48.3253165,27.2388679 50.158481,26.7318422 52.2764557,26.7318422 C53.8843038,26.7318422 55.3736709,26.9269101 56.7473418,27.3162917 C58.1189873,27.7056734 59.295443,28.3285835 60.2759494,29.185022 C61.255443,30.0422147 62.02,31.1615927 62.5701266,32.5426532 C63.1187342,33.9262275 63.3936709,35.5898349 63.3936709,37.5372459 L63.3936709,57.7443688 L55.0410127,57.7441174 L55.0410127,40.6319376 C55.0410127,39.6201486 55.0020253,38.6661761 54.9232911,37.7700202 C54.8440506,36.8751211 54.6293671,36.0968606 54.2764557,35.4339817 C53.9232911,34.772611 53.403038,34.2464807 52.7177215,33.8568477 C52.0313924,33.4689743 51.0997468,33.2731523 49.9235443,33.2731523 C48.7473418,33.2731523 47.7962025,33.4983853 47.0706329,33.944578 C46.344557,34.393033 45.7764557,34.9774826 45.3650633,35.6969211 C44.9534177,36.4181193 44.6787342,37.2353431 44.5417722,38.150855 C44.4037975,39.0653615 44.3356962,39.9904257 44.3356962,40.9247908 L44.3356962,57.7443688 L35.9835443,57.7443688 L35.9835443,40.8079009 C35.9835443,39.9124991 35.963038,39.0263982 35.9253165,38.150855 C35.8853165,37.2743064 35.7192405,36.4666349 35.424557,35.7263321 C35.1303797,34.9872862 34.64,34.393033 33.9539241,33.944578 C33.2675949,33.4983853 32.2579747,33.2731523 30.9248101,33.2731523 C30.5321519,33.2731523 30.0126582,33.3608826 29.3663291,33.5365945 C28.7192405,33.7118037 28.0913924,34.0433688 27.4840506,34.5292789 C26.875443,35.0164459 26.3564557,35.7172826 25.9250633,36.6315376 C25.4934177,37.5470495 25.2779747,38.7436 25.2779747,40.2229486 L25.2779747,57.7441174 L16.9260759,57.7443688 L16.9260759,27.5493174 L24.8073418,27.5493174 Z" id="m"></path>
|
||||||
|
<path d="M68.7455696,31.9886202 C69.6075949,30.7033339 70.7060759,29.672189 72.0397468,28.8926716 C73.3724051,28.1141596 74.8716456,27.5596239 76.5387342,27.2283101 C78.2050633,26.8977505 79.8817722,26.7315908 81.5678481,26.7315908 C83.0974684,26.7315908 84.6458228,26.8391798 86.2144304,27.0525982 C87.7827848,27.2675248 89.2144304,27.6865688 90.5086076,28.3087248 C91.8025316,28.9313835 92.8610127,29.7983798 93.6848101,30.9074514 C94.5083544,32.0170257 94.92,33.4870734 94.92,35.3173431 L94.92,51.026844 C94.92,52.3913138 94.998481,53.6941963 95.1556962,54.9400165 C95.3113924,56.1865908 95.5863291,57.120956 95.9787342,57.7436147 L87.5091139,57.7436147 C87.3518987,57.276055 87.2240506,56.7996972 87.1265823,56.3125303 C87.0278481,55.8266202 86.9592405,55.3301523 86.9207595,54.8236294 C85.5873418,56.1865908 84.0182278,57.1405633 82.2156962,57.6857982 C80.4113924,58.2295248 78.5683544,58.503022 76.6860759,58.503022 C75.2346835,58.503022 73.8817722,58.3275615 72.6270886,57.9776459 C71.3718987,57.6269761 70.2744304,57.082244 69.3334177,56.3411872 C68.3921519,55.602644 67.656962,54.6680275 67.1275949,53.5390972 C66.5982278,52.410167 66.3331646,51.065556 66.3331646,49.5087835 C66.3331646,47.7961578 66.6367089,46.384178 67.2455696,45.2756092 C67.8529114,44.1652807 68.6367089,43.2799339 69.5987342,42.6173064 C70.5589873,41.9556844 71.6567089,41.4592165 72.8924051,41.1284055 C74.1273418,40.7978459 75.3721519,40.5356606 76.6270886,40.3398385 C77.8820253,40.1457761 79.116962,39.9896716 80.3329114,39.873033 C81.5483544,39.7558917 82.6270886,39.5804312 83.5681013,39.3469028 C84.5093671,39.1133743 85.2536709,38.7732624 85.8032911,38.3250587 C86.3513924,37.8773578 86.6063291,37.2252881 86.5678481,36.3680954 C86.5678481,35.4731963 86.4210127,34.7620532 86.1268354,34.2366771 C85.8329114,33.7113009 85.4405063,33.3018092 84.9506329,33.0099615 C84.4602532,32.7181138 83.8916456,32.5232972 83.2450633,32.4255119 C82.5977215,32.3294862 81.9010127,32.2797138 81.156962,32.2797138 C79.5098734,32.2797138 78.2159494,32.6303835 77.2746835,33.3312202 C76.3339241,34.0320569 75.7837975,35.2007046 75.6275949,36.8354037 L67.275443,36.8354037 C67.3924051,34.8892495 67.8817722,33.2726495 68.7455696,31.9886202 Z M85.2440506,43.6984752 C84.7149367,43.873433 84.1460759,44.0189798 83.5387342,44.1361211 C82.9306329,44.253011 82.2936709,44.350545 81.6270886,44.4279688 C80.96,44.5066495 80.2934177,44.6034294 79.6273418,44.7203193 C78.9994937,44.8362037 78.3820253,44.9933138 77.7749367,45.1871248 C77.1663291,45.3829468 76.636962,45.6451321 76.1865823,45.9759431 C75.7349367,46.3070055 75.3724051,46.7263009 75.0979747,47.2313156 C74.8232911,47.7375872 74.6863291,48.380356 74.6863291,49.1588679 C74.6863291,49.8979138 74.8232911,50.5218294 75.0979747,51.026844 C75.3724051,51.5338697 75.7455696,51.9328037 76.2159494,52.2246514 C76.6863291,52.5164991 77.2349367,52.7213706 77.8632911,52.8375064 C78.4898734,52.9546477 79.136962,53.012967 79.8037975,53.012967 C81.4506329,53.012967 82.724557,52.740978 83.6273418,52.1952404 C84.5288608,51.6507596 85.1949367,50.9981872 85.6270886,50.2382771 C86.0579747,49.4793725 86.323038,48.7119211 86.4212658,47.9321523 C86.518481,47.1536404 86.5681013,46.5304789 86.5681013,46.063422 L86.5681013,42.9677248 C86.2146835,43.2799339 85.7736709,43.5230147 85.2440506,43.6984752 Z" id="a"></path>
|
||||||
|
<path d="M116.917975,27.5493174 L116.917975,33.0976917 L110.801266,33.0976917 L110.801266,48.0492936 C110.801266,49.4502128 111.036203,50.3850807 111.507089,50.8518862 C111.976962,51.3191945 112.918734,51.5527229 114.33038,51.5527229 C114.801013,51.5527229 115.251392,51.5336183 115.683038,51.4944037 C116.114177,51.4561945 116.526076,51.3968697 116.917975,51.3194459 L116.917975,57.7438661 C116.212152,57.860756 115.427595,57.9381798 114.565316,57.9778972 C113.702785,58.0153523 112.859747,58.0357138 112.036203,58.0357138 C110.742278,58.0357138 109.516456,57.9477321 108.36,57.7722716 C107.202785,57.5975651 106.183544,57.2577046 105.301519,56.7509303 C104.418987,56.2454128 103.722785,55.5242147 103.213418,54.5898495 C102.703038,53.6562385 102.448608,52.4292716 102.448608,50.9099541 L102.448608,33.0976917 L97.3903797,33.0976917 L97.3903797,27.5493174 L102.448608,27.5493174 L102.448608,18.4967596 L110.801013,18.4967596 L110.801013,27.5493174 L116.917975,27.5493174 Z" id="t"></path>
|
||||||
|
<path d="M128.857975,27.5493174 L128.857975,33.1565138 L128.975696,33.1565138 C129.367089,32.2213945 129.896203,31.3559064 130.563544,30.557033 C131.23038,29.7596679 131.99443,29.0776844 132.857215,28.5130936 C133.719241,27.9495083 134.641266,27.5113596 135.622532,27.1988991 C136.601772,26.8879468 137.622025,26.7315908 138.681013,26.7315908 C139.229873,26.7315908 139.836962,26.8296275 140.504304,27.0239413 L140.504304,34.7336477 C140.111646,34.6552183 139.641013,34.586844 139.092658,34.5290275 C138.543291,34.4704569 138.014177,34.4410459 137.504304,34.4410459 C135.974937,34.4410459 134.681013,34.6949358 133.622785,35.2004532 C132.564051,35.7067248 131.711392,36.397255 131.064051,37.2735523 C130.417215,38.1501009 129.955443,39.1714422 129.681266,40.3398385 C129.407089,41.5074807 129.269873,42.7736624 129.269873,44.1361211 L129.269873,57.7438661 L120.917722,57.7438661 L120.917722,27.5493174 L128.857975,27.5493174 Z" id="r"></path>
|
||||||
|
<path d="M144.033165,22.8767376 L144.033165,16.0435798 L152.386076,16.0435798 L152.386076,22.8767376 L144.033165,22.8767376 Z M152.386076,27.5493174 L152.386076,57.7438661 L144.033165,57.7438661 L144.033165,27.5493174 L152.386076,27.5493174 Z" id="i"></path>
|
||||||
|
<polygon id="x" points="156.738228 27.5493174 166.266582 27.5493174 171.619494 35.4337303 176.913418 27.5493174 186.147848 27.5493174 176.148861 41.6831927 187.383544 57.7441174 177.85443 57.7441174 171.501772 48.2245028 165.148861 57.7441174 155.797468 57.7441174 166.737468 41.8589046"></polygon>
|
||||||
|
<polygon id="right-bracket" points="197.580759 82.7268844 197.580759 1.93811009 191.725063 1.93811009 191.725063 0 199.828354 0 199.828354 84.6652459 191.725063 84.6652459 191.725063 82.7268844"></polygon>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h1>It works! Dendrite {{ .Version }} is running</h1>
|
||||||
|
<p>Your Dendrite server is listening on this port and is ready for messages.</p>
|
||||||
|
<p>To use this server you'll need <a href="https://matrix.org/docs/projects/try-matrix-now.html#clients" target="_blank" rel="noopener noreferrer">a Matrix client</a>.
|
||||||
|
</p>
|
||||||
|
<p>Welcome to the Matrix universe :)</p>
|
||||||
|
<hr>
|
||||||
|
<p>
|
||||||
|
<small>
|
||||||
|
<a href="https://matrix.org" target="_blank" rel="noopener noreferrer">
|
||||||
|
matrix.org
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -154,6 +154,13 @@ app_service_api:
|
||||||
# to be sent to an insecure endpoint.
|
# to be sent to an insecure endpoint.
|
||||||
disable_tls_validation: false
|
disable_tls_validation: false
|
||||||
|
|
||||||
|
# Send the access_token query parameter with appservice requests in addition
|
||||||
|
# to the Authorization header. This can cause hs_tokens to be saved to logs,
|
||||||
|
# so it should not be enabled unless absolutely necessary.
|
||||||
|
legacy_auth: false
|
||||||
|
# Use the legacy unprefixed paths for appservice requests.
|
||||||
|
legacy_paths: false
|
||||||
|
|
||||||
# Appservice configuration files to load into this homeserver.
|
# Appservice configuration files to load into this homeserver.
|
||||||
config_files:
|
config_files:
|
||||||
# - /path/to/appservice_registration.yaml
|
# - /path/to/appservice_registration.yaml
|
||||||
|
@ -325,6 +332,10 @@ user_api:
|
||||||
auto_join_rooms:
|
auto_join_rooms:
|
||||||
# - "#main:matrix.org"
|
# - "#main:matrix.org"
|
||||||
|
|
||||||
|
# The number of workers to start for the DeviceListUpdater. Defaults to 8.
|
||||||
|
# This only needs updating if the "InputDeviceListUpdate" stream keeps growing indefinitely.
|
||||||
|
# worker_count: 8
|
||||||
|
|
||||||
# Configuration for Opentracing.
|
# Configuration for Opentracing.
|
||||||
# See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on
|
# See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on
|
||||||
# how this works and how to set it up.
|
# how this works and how to set it up.
|
||||||
|
|
|
@ -4,6 +4,8 @@ nav_order: 1
|
||||||
permalink: /faq
|
permalink: /faq
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# FAQ
|
# FAQ
|
||||||
|
|
||||||
## Why does Dendrite exist?
|
## Why does Dendrite exist?
|
||||||
|
@ -24,7 +26,7 @@ No, although a good portion of the Matrix specification has been implemented. Mo
|
||||||
|
|
||||||
Dendrite development is currently supported by a small team of developers and due to those limited resources, the majority of the effort is focused on getting Dendrite to be
|
Dendrite development is currently supported by a small team of developers and due to those limited resources, the majority of the effort is focused on getting Dendrite to be
|
||||||
specification complete. If there are major features you're requesting (e.g. new administration endpoints), we'd like to strongly encourage you to join the community in supporting
|
specification complete. If there are major features you're requesting (e.g. new administration endpoints), we'd like to strongly encourage you to join the community in supporting
|
||||||
the development efforts through [contributing](../development/contributing).
|
the development efforts through [contributing](./development/CONTRIBUTING.md).
|
||||||
|
|
||||||
## Is there a migration path from Synapse to Dendrite?
|
## Is there a migration path from Synapse to Dendrite?
|
||||||
|
|
||||||
|
@ -105,7 +107,7 @@ This can be done by performing a room upgrade. Use the command `/upgraderoom <ve
|
||||||
|
|
||||||
## How do I reset somebody's password on my server?
|
## How do I reset somebody's password on my server?
|
||||||
|
|
||||||
Use the admin endpoint [resetpassword](./administration/adminapi#post-_dendriteadminresetpassworduserid)
|
Use the admin endpoint [resetpassword](./administration/4_adminapi.md#post-_dendriteadminresetpassworduserid)
|
||||||
|
|
||||||
## Should I use PostgreSQL or SQLite for my databases?
|
## Should I use PostgreSQL or SQLite for my databases?
|
||||||
|
|
||||||
|
@ -117,6 +119,7 @@ The list of files that need to be stored is:
|
||||||
- matrix-key.pem
|
- matrix-key.pem
|
||||||
- dendrite.yaml
|
- dendrite.yaml
|
||||||
- the postgres or sqlite DB
|
- the postgres or sqlite DB
|
||||||
|
- the jetstream directory
|
||||||
- the media store
|
- the media store
|
||||||
- the search index (although this can be regenerated)
|
- the search index (although this can be regenerated)
|
||||||
|
|
||||||
|
|
|
@ -231,9 +231,9 @@ GEM
|
||||||
jekyll-seo-tag (~> 2.1)
|
jekyll-seo-tag (~> 2.1)
|
||||||
minitest (5.17.0)
|
minitest (5.17.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
nokogiri (1.14.3-arm64-darwin)
|
nokogiri (1.16.2-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.14.3-x86_64-linux)
|
nokogiri (1.16.2-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
octokit (4.22.0)
|
octokit (4.22.0)
|
||||||
faraday (>= 0.9)
|
faraday (>= 0.9)
|
||||||
|
@ -241,11 +241,12 @@ GEM
|
||||||
pathutil (0.16.2)
|
pathutil (0.16.2)
|
||||||
forwardable-extended (~> 2.6)
|
forwardable-extended (~> 2.6)
|
||||||
public_suffix (4.0.7)
|
public_suffix (4.0.7)
|
||||||
racc (1.6.2)
|
racc (1.7.3)
|
||||||
rb-fsevent (0.11.1)
|
rb-fsevent (0.11.1)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
rexml (3.2.5)
|
rexml (3.3.2)
|
||||||
|
strscan
|
||||||
rouge (3.26.0)
|
rouge (3.26.0)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
|
@ -260,6 +261,7 @@ GEM
|
||||||
faraday (> 0.8, < 2.0)
|
faraday (> 0.8, < 2.0)
|
||||||
simpleidn (0.2.1)
|
simpleidn (0.2.1)
|
||||||
unf (~> 0.1.4)
|
unf (~> 0.1.4)
|
||||||
|
strscan (3.1.0)
|
||||||
terminal-table (1.8.0)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
Please note that new installation instructions can be found
|
Please note that new installation instructions can be found
|
||||||
|
|
6
docs/_includes/deprecation.html
Normal file
6
docs/_includes/deprecation.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{: .warning-title }
|
||||||
|
> This documentation is out of date!
|
||||||
|
>
|
||||||
|
> This documentation site is for the versions of Dendrite maintained by the <em>Matrix.org Foundation</em> (<a href="https://github.com/matrix-org/dendrite">github.com/matrix-org/dendrite</a>), available under the Apache 2.0 licence.
|
||||||
|
>
|
||||||
|
> If you are interested in the documentation for a later version of Dendrite, please refer to <a href="https://element-hq.github.io/dendrite/">https://element-hq.github.io/dendrite/</a>.
|
|
@ -1,3 +0,0 @@
|
||||||
footer.site-footer {
|
|
||||||
opacity: 10%;
|
|
||||||
}
|
|
|
@ -5,6 +5,8 @@ nav_order: 4
|
||||||
permalink: /administration
|
permalink: /administration
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Administration
|
# Administration
|
||||||
|
|
||||||
This section contains documentation on managing your existing Dendrite deployment.
|
This section contains documentation on managing your existing Dendrite deployment.
|
||||||
|
|
|
@ -5,6 +5,8 @@ permalink: /administration/createusers
|
||||||
nav_order: 1
|
nav_order: 1
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Creating user accounts
|
# Creating user accounts
|
||||||
|
|
||||||
User accounts can be created on a Dendrite instance in a number of ways.
|
User accounts can be created on a Dendrite instance in a number of ways.
|
||||||
|
|
|
@ -5,6 +5,8 @@ permalink: /administration/registration
|
||||||
nav_order: 2
|
nav_order: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Enabling registration
|
# Enabling registration
|
||||||
|
|
||||||
Enabling registration allows users to register their own user accounts on your
|
Enabling registration allows users to register their own user accounts on your
|
||||||
|
|
|
@ -5,6 +5,8 @@ permalink: /administration/presence
|
||||||
nav_order: 3
|
nav_order: 3
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Enabling presence
|
# Enabling presence
|
||||||
|
|
||||||
Dendrite supports presence, which allows you to send your online/offline status
|
Dendrite supports presence, which allows you to send your online/offline status
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 4
|
||||||
permalink: /administration/adminapi
|
permalink: /administration/adminapi
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Supported admin APIs
|
# Supported admin APIs
|
||||||
|
|
||||||
Dendrite supports, at present, a very small number of endpoints that allow
|
Dendrite supports, at present, a very small number of endpoints that allow
|
||||||
|
|
|
@ -6,6 +6,8 @@ nav_order: 5
|
||||||
permalink: /administration/optimisation
|
permalink: /administration/optimisation
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Optimise your installation
|
# Optimise your installation
|
||||||
|
|
||||||
Now that you have Dendrite running, the following tweaks will improve the reliability
|
Now that you have Dendrite running, the following tweaks will improve the reliability
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 6
|
||||||
permalink: /administration/troubleshooting
|
permalink: /administration/troubleshooting
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
|
|
||||||
If your Dendrite installation is acting strangely, there are a few things you should
|
If your Dendrite installation is acting strangely, there are a few things you should
|
||||||
|
|
|
@ -4,6 +4,10 @@ has_children: true
|
||||||
permalink: /development
|
permalink: /development
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
This section contains documentation that may be useful when helping to develop
|
This section contains documentation that may be useful when helping to develop
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 1
|
||||||
permalink: /development/contributing
|
permalink: /development/contributing
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Contributing to Dendrite
|
# Contributing to Dendrite
|
||||||
|
|
||||||
Everyone is welcome to contribute to Dendrite! We aim to make it as easy as
|
Everyone is welcome to contribute to Dendrite! We aim to make it as easy as
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 4
|
||||||
permalink: /development/profiling
|
permalink: /development/profiling
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Profiling Dendrite
|
# Profiling Dendrite
|
||||||
|
|
||||||
If you are running into problems with Dendrite using excessive resources (e.g. CPU or RAM) then you can use the profiler to work out what is happening.
|
If you are running into problems with Dendrite using excessive resources (e.g. CPU or RAM) then you can use the profiler to work out what is happening.
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 3
|
||||||
permalink: /development/coverage
|
permalink: /development/coverage
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
## Running unit tests with coverage enabled
|
## Running unit tests with coverage enabled
|
||||||
|
|
||||||
Running unit tests with coverage enabled can be done with the following commands, this will generate a `integrationcover.log`
|
Running unit tests with coverage enabled can be done with the following commands, this will generate a `integrationcover.log`
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 2
|
||||||
permalink: /development/sytest
|
permalink: /development/sytest
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# SyTest
|
# SyTest
|
||||||
|
|
||||||
Dendrite uses [SyTest](https://github.com/matrix-org/sytest) for its
|
Dendrite uses [SyTest](https://github.com/matrix-org/sytest) for its
|
||||||
|
|
|
@ -3,6 +3,8 @@ layout: home
|
||||||
nav_exclude: true
|
nav_exclude: true
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Dendrite
|
# Dendrite
|
||||||
|
|
||||||
Dendrite is a second-generation Matrix homeserver written in Go! Following the microservice
|
Dendrite is a second-generation Matrix homeserver written in Go! Following the microservice
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 2
|
||||||
permalink: /installation
|
permalink: /installation
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
This section contains documentation on installing a new Dendrite deployment.
|
This section contains documentation on installing a new Dendrite deployment.
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 1
|
||||||
permalink: /installation/planning
|
permalink: /installation/planning
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Planning your installation
|
# Planning your installation
|
||||||
|
|
||||||
## Database
|
## Database
|
||||||
|
@ -59,7 +61,7 @@ In order to install Dendrite, you will need to satisfy the following dependencie
|
||||||
|
|
||||||
### Go
|
### Go
|
||||||
|
|
||||||
At this time, Dendrite supports being built with Go 1.20 or later. We do not support building
|
At this time, Dendrite supports being built with Go 1.21 or later. We do not support building
|
||||||
Dendrite with older versions of Go than this. If you are installing Go using a package manager,
|
Dendrite with older versions of Go than this. If you are installing Go using a package manager,
|
||||||
you should check (by running `go version`) that you are using a suitable version before you start.
|
you should check (by running `go version`) that you are using a suitable version before you start.
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ nav_order: 2
|
||||||
permalink: /installation/domainname
|
permalink: /installation/domainname
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Setting up the domain
|
# Setting up the domain
|
||||||
|
|
||||||
Every Matrix server deployment requires a server name which uniquely identifies it. For
|
Every Matrix server deployment requires a server name which uniquely identifies it. For
|
||||||
|
|
|
@ -6,6 +6,8 @@ nav_order: 4
|
||||||
permalink: /docker
|
permalink: /docker
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Installation using Docker
|
# Installation using Docker
|
||||||
|
|
||||||
This section contains documentation how to install Dendrite using Docker
|
This section contains documentation how to install Dendrite using Docker
|
||||||
|
|
|
@ -7,6 +7,8 @@ nav_order: 1
|
||||||
permalink: /installation/docker/install
|
permalink: /installation/docker/install
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Installing Dendrite using Docker Compose
|
# Installing Dendrite using Docker Compose
|
||||||
|
|
||||||
Dendrite provides an [example](https://github.com/matrix-org/dendrite/blob/main/build/docker/docker-compose.yml)
|
Dendrite provides an [example](https://github.com/matrix-org/dendrite/blob/main/build/docker/docker-compose.yml)
|
||||||
|
@ -26,6 +28,8 @@ docker run --rm --entrypoint="/usr/bin/generate-keys" \
|
||||||
-v $(pwd)/config:/mnt \
|
-v $(pwd)/config:/mnt \
|
||||||
matrixdotorg/dendrite-monolith:latest \
|
matrixdotorg/dendrite-monolith:latest \
|
||||||
-private-key /mnt/matrix_key.pem
|
-private-key /mnt/matrix_key.pem
|
||||||
|
|
||||||
|
# Windows equivalent: docker run --rm --entrypoint="/usr/bin/generate-keys" -v %cd%/config:/mnt matrixdotorg/dendrite-monolith:latest -private-key /mnt/matrix_key.pem
|
||||||
```
|
```
|
||||||
(**NOTE**: This only needs to be executed **once**, as you otherwise overwrite the key)
|
(**NOTE**: This only needs to be executed **once**, as you otherwise overwrite the key)
|
||||||
|
|
||||||
|
@ -44,6 +48,8 @@ docker run --rm --entrypoint="/bin/sh" \
|
||||||
-dir /var/dendrite/ \
|
-dir /var/dendrite/ \
|
||||||
-db postgres://dendrite:itsasecret@postgres/dendrite?sslmode=disable \
|
-db postgres://dendrite:itsasecret@postgres/dendrite?sslmode=disable \
|
||||||
-server YourDomainHere > /mnt/dendrite.yaml"
|
-server YourDomainHere > /mnt/dendrite.yaml"
|
||||||
|
|
||||||
|
# Windows equivalent: docker run --rm --entrypoint="/bin/sh" -v %cd%/config:/mnt matrixdotorg/dendrite-monolith:latest -c "/usr/bin/generate-config -dir /var/dendrite/ -db postgres://dendrite:itsasecret@postgres/dendrite?sslmode=disable -server YourDomainHere > /mnt/dendrite.yaml"
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then change `config/dendrite.yaml` to your liking.
|
You can then change `config/dendrite.yaml` to your liking.
|
||||||
|
|
|
@ -6,6 +6,8 @@ nav_order: 3
|
||||||
permalink: /helm
|
permalink: /helm
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Helm
|
# Helm
|
||||||
|
|
||||||
This section contains documentation how to use [Helm](https://helm.sh/) to install Dendrite on a [Kubernetes](https://kubernetes.io/) cluster.
|
This section contains documentation how to use [Helm](https://helm.sh/) to install Dendrite on a [Kubernetes](https://kubernetes.io/) cluster.
|
||||||
|
|
|
@ -7,6 +7,8 @@ nav_order: 1
|
||||||
permalink: /installation/helm/install
|
permalink: /installation/helm/install
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Installing Dendrite using Helm
|
# Installing Dendrite using Helm
|
||||||
|
|
||||||
To install Dendrite using the Helm chart, you first have to add the repository using the following commands:
|
To install Dendrite using the Helm chart, you first have to add the repository using the following commands:
|
||||||
|
|
|
@ -6,6 +6,8 @@ nav_order: 5
|
||||||
permalink: /manual
|
permalink: /manual
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Manual Installation
|
# Manual Installation
|
||||||
|
|
||||||
This section contains documentation how to manually install Dendrite
|
This section contains documentation how to manually install Dendrite
|
||||||
|
|
|
@ -7,6 +7,8 @@ nav_order: 1
|
||||||
permalink: /installation/manual/build
|
permalink: /installation/manual/build
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Build all Dendrite commands
|
# Build all Dendrite commands
|
||||||
|
|
||||||
Dendrite has numerous utility commands in addition to the actual server binaries.
|
Dendrite has numerous utility commands in addition to the actual server binaries.
|
||||||
|
|
|
@ -7,6 +7,8 @@ grand_parent: Installation
|
||||||
permalink: /installation/manual/database
|
permalink: /installation/manual/database
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Preparing database storage
|
# Preparing database storage
|
||||||
|
|
||||||
Dendrite uses SQL databases to store data. Depending on the database engine being used, you
|
Dendrite uses SQL databases to store data. Depending on the database engine being used, you
|
||||||
|
|
|
@ -6,6 +6,8 @@ nav_order: 3
|
||||||
permalink: /installation/manual/signingkeys
|
permalink: /installation/manual/signingkeys
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Generating signing keys
|
# Generating signing keys
|
||||||
|
|
||||||
All Matrix homeservers require a signing private key, which will be used to authenticate
|
All Matrix homeservers require a signing private key, which will be used to authenticate
|
||||||
|
|
|
@ -6,6 +6,8 @@ nav_order: 4
|
||||||
permalink: /installation/manual/configuration
|
permalink: /installation/manual/configuration
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Configuring Dendrite
|
# Configuring Dendrite
|
||||||
|
|
||||||
A YAML configuration file is used to configure Dendrite. A sample configuration file is
|
A YAML configuration file is used to configure Dendrite. A sample configuration file is
|
||||||
|
|
|
@ -6,6 +6,8 @@ nav_order: 5
|
||||||
permalink: /installation/manual/start
|
permalink: /installation/manual/start
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# Starting Dendrite
|
# Starting Dendrite
|
||||||
|
|
||||||
Once you have completed all preparation and installation steps,
|
Once you have completed all preparation and installation steps,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#change IP to location of monolith server
|
# change IP to location of monolith server
|
||||||
upstream monolith{
|
upstream monolith {
|
||||||
server 127.0.0.1:8008;
|
server 127.0.0.1:8008;
|
||||||
}
|
}
|
||||||
server {
|
server {
|
||||||
|
@ -20,8 +20,9 @@ server {
|
||||||
}
|
}
|
||||||
|
|
||||||
location /.well-known/matrix/client {
|
location /.well-known/matrix/client {
|
||||||
# If your sever_name here doesn't match your matrix homeserver URL
|
# If your server_name here doesn't match your matrix homeserver URL
|
||||||
# (e.g. hostname.com as server_name and matrix.hostname.com as homeserver URL)
|
# (e.g. hostname.com as server_name and matrix.hostname.com as homeserver URL)
|
||||||
|
# uncomment the following line.
|
||||||
# add_header Access-Control-Allow-Origin '*';
|
# add_header Access-Control-Allow-Origin '*';
|
||||||
return 200 '{ "m.homeserver": { "base_url": "https://my.hostname.com" } }';
|
return 200 '{ "m.homeserver": { "base_url": "https://my.hostname.com" } }';
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ title: P2P Matrix
|
||||||
nav_exclude: true
|
nav_exclude: true
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{% include deprecation.html %}
|
||||||
|
|
||||||
# P2P Matrix
|
# P2P Matrix
|
||||||
|
|
||||||
These are the instructions for setting up P2P Dendrite, current as of May 2020. There's both Go stuff and JS stuff to do to set this up.
|
These are the instructions for setting up P2P Dendrite, current as of May 2020. There's both Go stuff and JS stuff to do to set this up.
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue