Compare commits

..

516 commits

Author SHA1 Message Date
signaryk 73bd01f7b6 Forgejo does not support build cache
All checks were successful
/ Monolith image (push) Successful in 2m0s
2024-07-14 17:26:53 -05:00
signaryk 6a9a344d9a Try changing namespace to user
Some checks failed
/ Monolith image (push) Failing after 2m34s
2024-07-14 17:22:57 -05:00
signaryk 64cad580d1 Build cache bit was wrong
Some checks failed
/ Monolith image (push) Failing after 2m33s
2024-07-14 17:17:01 -05:00
signaryk 48e3701b85 Remove condition for main image build step
Some checks failed
/ Monolith image (push) Failing after 2m50s
2024-07-14 17:12:31 -05:00
signaryk 0c22b9ea58 Revert to smaller image
All checks were successful
/ Monolith image (push) Successful in 20s
2024-07-14 17:10:25 -05:00
signaryk d428ae7f62 Remove QEMU step, not actually using it
Some checks failed
/ Monolith image (push) Failing after 14s
2024-07-14 16:04:32 -05:00
signaryk b86c5110e1 Try the full clone of github actions container
Some checks failed
/ Monolith image (push) Failing after 11m56s
2024-07-14 15:40:03 -05:00
signaryk 09587775df Change the image for one compatible with docker builds
Some checks failed
/ Monolith image (push) Failing after 40s
2024-07-14 15:29:34 -05:00
signaryk af0eadd4fe Fix different image format for actions yaml
Some checks failed
/ Monolith image (push) Failing after 13s
2024-07-14 15:05:33 -05:00
signaryk 5f187e42d3 More actions updates
Some checks failed
/ Monolith image (push) Failing after 1m32s
2024-07-14 14:59:49 -05:00
signaryk 77264c3c20 Fix actions 2024-07-14 14:54:42 -05:00
signaryk a595be09a2 Add forgejo actions 2024-07-14 14:45:20 -05:00
signaryk 0bfe418b18 Update gitignore 2024-07-14 14:43:46 -05:00
Boris Rybalkin 20aa36ada7 go tidy
Some checks failed
Dendrite / WASM build test (push) Has been cancelled
Dendrite / Linting (push) Has been cancelled
Dendrite / Unit tests (push) Has been cancelled
Dendrite / Build for Linux (386, linux) (push) Has been cancelled
Dendrite / Build for Linux (amd64, linux) (push) Has been cancelled
Dendrite / Build for Windows (amd64, windows) (push) Has been cancelled
Dendrite / Initial tests passed (push) Has been cancelled
Dendrite / Integration tests (push) Has been cancelled
Dendrite / Upgrade tests (push) Has been cancelled
Dendrite / Upgrade tests from HEAD-2 (push) Has been cancelled
Dendrite / Sytest (${{ matrix.label }}) (1, SQLite Cgo) (push) Has been cancelled
Dendrite / Sytest (${{ matrix.label }}) (PostgreSQL, postgres) (push) Has been cancelled
Dendrite / Sytest (${{ matrix.label }}) (SQLite native) (push) Has been cancelled
Dendrite / Complement (${{ matrix.label }}) (0, PostgreSQL, Postgres) (push) Has been cancelled
Dendrite / Complement (${{ matrix.label }}) (0, SQLite native) (push) Has been cancelled
Dendrite / Complement (${{ matrix.label }}) (1, SQLite Cgo) (push) Has been cancelled
Dendrite / Integration tests passed (push) Has been cancelled
Dendrite / Update Docker images (push) Has been cancelled
2024-07-10 20:31:35 -05:00
Boris Rybalkin f9c6fbab69 basic ldap authentication support 2024-07-10 20:31:27 -05:00
Richard van der Hoff 3e62b986d1
Blacklist sytests that require MSC3967 (#3384)
https://github.com/matrix-org/sytest/pull/1383 updates some sytests in
line with MSC3967. Dendrite does not support MSC3967, so these tests
fail.
2024-06-13 23:55:02 +00:00
0x1a8510f2 46902e5766
Take advantage of changes in recent Go versions (#3361)
Given that #2714 wasn't merged but we are now at a minimum supported Go
version of 1.20 (soon to be 1.21), I wanted to carry over some of the
changes. Namely:
- Fix the log typo
- Simplify build constraints for unix
- Use stdlib atomic package

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `0x1a8510f2 <admin@0x1a8510f2.space>`

---------

Co-authored-by: devonh <devon.dmytro@gmail.com>
2024-05-01 00:38:36 +00:00
dependabot[bot] 5547bf8ca6
Bump golang.org/x/net from 0.21.0 to 0.23.0 (#3365)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.21.0 to
0.23.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c48da13158"><code>c48da13</code></a>
http2: fix TestServerContinuationFlood flakes</li>
<li><a
href="762b58d1cf"><code>762b58d</code></a>
http2: fix tipos in comment</li>
<li><a
href="ba872109ef"><code>ba87210</code></a>
http2: close connections when receiving too many headers</li>
<li><a
href="ebc8168ac8"><code>ebc8168</code></a>
all: fix some typos</li>
<li><a
href="3678185f8a"><code>3678185</code></a>
http2: make TestCanonicalHeaderCacheGrowth faster</li>
<li><a
href="448c44f928"><code>448c44f</code></a>
http2: remove clientTester</li>
<li><a
href="c7877ac421"><code>c7877ac</code></a>
http2: convert the remaining clientTester tests to testClientConn</li>
<li><a
href="d8870b0bf2"><code>d8870b0</code></a>
http2: use synthetic time in TestIdleConnTimeout</li>
<li><a
href="d73acffdc9"><code>d73acff</code></a>
http2: only set up deadline when Server.IdleTimeout is positive</li>
<li><a
href="89f602b7bb"><code>89f602b</code></a>
http2: validate client/outgoing trailers</li>
<li>Additional commits viewable in <a
href="https://github.com/golang/net/compare/v0.21.0...v0.23.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/net&package-manager=go_modules&previous-version=0.21.0&new-version=0.23.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-30 23:12:52 +00:00
Till 14a6c10097
Version 0.13.7 (#3349) 2024-04-09 10:24:27 +02:00
Till 5c0ceec2a6
Don't attempt to send transactions if Dendrite is shutting down (#3356)
This should avoid confusions with logs like:

```
time="2024-04-08T08:38:45.104235081Z" level=error msg="Failed to set \"scs.ems.host\" as assumed offline" func="github.com/matrix-org/dendrite/federationapi/statistics.(*ServerStatistics).Failure" file="github.com/matrix-org/dendrite/federationapi/statistics/statistics.go:204" error="sqlutil.WithTransaction.Begin: sql: database is closed"
time="2024-04-08T08:38:45.104239201Z" level=error msg="Failed to set \"obermui.de\" as assumed offline" func="github.com/matrix-org/dendrite/federationapi/statistics.(*ServerStatistics).Failure" file="github.com/matrix-org/dendrite/federationapi/statistics/statistics.go:204" error="sqlutil.WithTransaction.Begin: sql: database is closed"
```

or 

```
time="2024-04-08T08:38:45.105235411Z" level=error msg="Failed to get pending EDUs for \"retro76.net\"" func="github.com/matrix-org/dendrite/federationapi/queue.(*destinationQueue).getPendingFromDatabase" file="github.com/matrix-org/dendritefederationapi/queue/destinationqueue.go:258" error="sqlutil.WithTransaction.Begin: sql: database is closed"
```

[skip ci]
2024-04-09 07:49:56 +02:00
Till 8aa088f713
Return correct Content-Type for unrecognized requests (#3355)
Fixes #3354
2024-04-08 07:51:04 +02:00
Till b732eede27
Fix spaces over federation (#3347)
Fixes #2504

 A few issues with the previous iteration:
- We never returned `inaccessible_children`, which (if I read the code
correctly), made Synapse raise an error and thus not returning the
requested rooms
- For restricted rooms, we didn't return the list of allowed rooms
2024-03-28 20:40:45 +01:00
Till ad0a7d09e8
Add getting/deleting single event report (#3344)
Based on https://github.com/matrix-org/dendrite/pull/3342

Adds `GET /_synapse/admin/v1/event_reports/{reportID}` and `DELETE
/_synapse/admin/v1/event_reports/{reportID}`
2024-03-22 21:54:29 +00:00
Till 81f73c9f8d
Reuse existing NATS connection (#3345)
If using external NATS, we opened unnecessary connections. This now
re-uses existing connections.

[skip ci]
2024-03-22 22:33:23 +01:00
Till 79072c3dcd
Add /_synapse/admin/v1/event_reports endpoint (#3342)
Based on #3340 

This adds a `/_synapse/admin/v1/event_reports` endpoint, the same
Synapse has. This way existing tools also work with Dendrite.
Given this is already getting huge (even though many test lines),
splitting this into two PRs. (The next adds "getting one report" and
"deleting reports")

[skip ci]
2024-03-22 22:32:30 +01:00
dependabot[bot] 1bdf0cc541
Bump github.com/docker/docker from 24.0.7+incompatible to 24.0.9+incompatible (#3341)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from
24.0.7+incompatible to 24.0.9+incompatible.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/docker/docker/releases">github.com/docker/docker's
releases</a>.</em></p>
<blockquote>
<h2>v24.0.9</h2>
<h2>24.0.9</h2>
<p>For a full list of pull requests and changes in this release, refer
to the relevant GitHub milestones:</p>
<ul>
<li><a
href="https://github.com/docker/cli/issues?q=is%3Aclosed+milestone%3A24.0.9">docker/cli,
24.0.9 milestone</a></li>
<li><a
href="https://github.com/moby/moby/issues?q=is%3Aclosed+milestone%3A24.0.9">moby/moby,
24.0.9 milestone</a></li>
</ul>
<h2>Security</h2>
<p>This release contains security fixes for the following CVEs affecting
Docker Engine and its components.</p>
<table>
<thead>
<tr>
<th>CVE</th>
<th>Component</th>
<th>Fix version</th>
<th>Severity</th>
</tr>
</thead>
<tbody>
<tr>
<td><a
href="https://scout.docker.com/v/CVE-2024-21626">CVE-2024-21626</a></td>
<td>runc</td>
<td>1.1.12</td>
<td>High, CVSS 8.6</td>
</tr>
<tr>
<td><a
href="https://scout.docker.com/v/CVE-2024-24557">CVE-2024-24557</a></td>
<td>Docker Engine</td>
<td>24.0.9</td>
<td>Medium, CVSS 6.9</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>Important</strong> ⚠️</p>
<p>Note that this release of Docker Engine doesn't include fixes for the
following known vulnerabilities in BuildKit:</p>
<ul>
<li><a
href="https://scout.docker.com/v/CVE-2024-23651">CVE-2024-23651</a></li>
<li><a
href="https://scout.docker.com/v/CVE-2024-23652">CVE-2024-23652</a></li>
<li><a
href="https://scout.docker.com/v/CVE-2024-23653">CVE-2024-23653</a></li>
<li><a
href="https://scout.docker.com/v/CVE-2024-23650">CVE-2024-23650</a></li>
</ul>
<p>To address these vulnerabilities, upgrade to <a
href="https://github.com/docker/docker/blob/HEAD/25.0.md#2502">Docker
Engine v25.0.2</a>.</p>
</blockquote>
<p>For more information about the security issues addressed in this
release, and the unaddressed vulnerabilities in BuildKit, refer to the
<a
href="https://www.docker.com/blog/docker-security-advisory-multiple-vulnerabilities-in-runc-buildkit-and-moby/">blog
post</a>. For details about each vulnerability, see the relevant
security advisory:</p>
<ul>
<li><a
href="https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv">CVE-2024-21626</a></li>
<li><a
href="https://github.com/moby/moby/security/advisories/GHSA-xw73-rw38-6vjc">CVE-2024-24557</a></li>
</ul>
<h3>Packaging updates</h3>
<ul>
<li>Upgrade runc to <a
href="https://github.com/opencontainers/runc/releases/tag/v1.1.12">v1.1.12</a>.
<a
href="https://redirect.github.com/moby/moby/pull/47269">moby/moby#47269</a></li>
<li>Upgrade containerd to <a
href="https://github.com/containerd/containerd/releases/tag/v1.7.13">v1.7.13</a>
(static binaries only). <a
href="https://redirect.github.com/moby/moby/pull/47280">moby/moby#47280</a></li>
</ul>
<h2>v24.0.8</h2>
<h2>24.0.8</h2>
<p>For a full list of pull requests and changes in this release, refer
to the relevant GitHub milestones:</p>
<ul>
<li><a
href="https://github.com/docker/cli/issues?q=is%3Aclosed+milestone%3A24.0.8">docker/cli,
24.0.8 milestone</a></li>
<li><a
href="https://github.com/moby/moby/issues?q=is%3Aclosed+milestone%3A24.0.8">moby/moby,
24.0.8 milestone</a></li>
</ul>
<h3>Bug fixes and enhancements</h3>
<ul>
<li>Live restore: Containers with auto remove (<code>docker run
--rm</code>) are no longer forcibly removed on engine restart. <a
href="https://redirect.github.com/moby/moby/pull/46869">moby/moby#46857</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fca702de7f"><code>fca702d</code></a>
Merge pull request from GHSA-xw73-rw38-6vjc</li>
<li><a
href="f78a7726d7"><code>f78a772</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/47281">#47281</a>
from thaJeztah/24.0_backport_bump_containerd_binary...</li>
<li><a
href="61afffeeb3"><code>61afffe</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/47270">#47270</a>
from thaJeztah/24.0_backport_bump_runc_binary_1.1.12</li>
<li><a
href="b38e74c4e0"><code>b38e74c</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/47276">#47276</a>
from thaJeztah/24.0_backport_bump_runc_1.1.12</li>
<li><a
href="dac56638ad"><code>dac5663</code></a>
update containerd binary to v1.7.13</li>
<li><a
href="20e1af3616"><code>20e1af3</code></a>
vendor: github.com/opencontainers/runc v1.1.12</li>
<li><a
href="858919d399"><code>858919d</code></a>
update runc binary to v1.1.12</li>
<li><a
href="141ad39e38"><code>141ad39</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/47266">#47266</a>
from vvoland/ci-fix-makeps1-templatefail-24</li>
<li><a
href="db968c672b"><code>db968c6</code></a>
hack/make.ps1: Fix go list pattern</li>
<li><a
href="61c51fbb5a"><code>61c51fb</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/47221">#47221</a>
from vvoland/pkg-pools-close-noop-24</li>
<li>Additional commits viewable in <a
href="https://github.com/docker/docker/compare/v24.0.7...v24.0.9">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/docker/docker&package-manager=go_modules&previous-version=24.0.7+incompatible&new-version=24.0.9+incompatible)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-22 22:30:28 +01:00
dependabot[bot] a00b976a00
Bump google.golang.org/protobuf from 1.30.0 to 1.33.0 (#3339)
Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/protobuf&package-manager=go_modules&previous-version=1.30.0&new-version=1.33.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-22 22:29:53 +01:00
Till b9abbf7b20
Add event reporting (#3340)
Part of #3216 and #3226 

There will be a follow up PR which is going to add the same admin
endpoints Synapse has, so existing tools also work for Dendrite.
2024-03-21 19:27:34 +01:00
Till de95499178
Update golangci config (#3343)
`deadline` is now deprecated in favor of `timeout` and currently breaks
CI.
The other changes remove some warnings produced.
2024-03-21 10:24:53 +01:00
Till 928c8c8c4a
Query rooms with ACLs instead of all rooms (#3338)
This now should actually speed up startup times.
This is because _many_ rooms (like DMs) don't have room ACLs, this means
that we had around 95% pointless DB queries. (as queried on d.m.org)
2024-03-05 20:41:35 +01:00
Till Faelligen 09f15a3d3f
[Helm] Update Postgres image to 16.2.0, update readme 2024-02-29 08:58:25 +01:00
Varac ad3a3e7bed
[helm] Update postgresql chart to 14.2.3 (#3292)
This change introduces a major Postgresql upgrade
(from 15.1.0 to 16.1.0).

From

https://artifacthub.io/packages/helm/bitnami/postgresql/13.2.24#to-13-0-0:

This major version changes the default PostgreSQL image from 15.x to
16.x. Follow the [official
instructions](https://www.postgresql.org/docs/15/upgrading.html) to
upgrade to 16.x.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

This PR doesn't need a Go unit tests since it doesn't touch any code,
only the helm chart is affected.

Signed-off-by: Varac Anero <varac@varac.net>

---------

Signed-off-by: Varac Anero <varac@varac.net>
Co-authored-by: Till Faelligen <2353100+S7evinK@users.noreply.github.com>

[skip ci]
2024-02-29 08:46:40 +01:00
Alexandre Oliveira 66865597e2
Use port number instead of name for k8s service port (#3256)
I've found an issue when deploying Dendrite's Helm chart on my local
cluster. The template for generating an Ingress resource tries to find
the service port using a name (`http`), but the template that generates
the Service resource, instead, identifies the resource with a port
number.

According to the [Kubernetes
ServiceSpec](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/),
`ports.targetPort` can be either a number or a string; if it's the
latter, it will be looked up as a named port in the pod's container
ports.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

[skip ci]
2024-02-29 08:13:59 +01:00
WrenIX 4452833099
chore(helm): use empty/nil storageClass for helm-docs (#3245)
i believe that `nil` would be false in the if :
```yaml
storageClass:
```
is still handled correct.

---
In past ( #3191 ), will have the problem with an empty string `""`:
```yaml
storageClass: ""
```

---
do you take another look @S7evinK ?

Signed-off-by: WrenIX <dev.github@wrenix.eu>
2024-02-29 08:04:40 +01:00
WrenIX 4892b08dd5
fix(helm): change strategy to Recreate (#3325)
Current dendrite needs an PVC and replica of 1 is forced, so best way of
update and change of configuration is to stop and start (instatt of
start multiple dendrite pod with deadlock of binding pvc)

see: #3258

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

 Signed-off-by: `Your Name <your@email.example.org>`
 - [x] version bump of helm Chart

Signed-off-by: WrenIX <dev.github@wrenix.eu>

[skip ci]
2024-02-28 21:20:41 +01:00
dependabot[bot] 58bc289a37
Bump nokogiri from 1.14.3 to 1.16.2 in /docs (#3319)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.14.3
to 1.16.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/releases">nokogiri's
releases</a>.</em></p>
<blockquote>
<h2>v1.16.2 / 2024-02-04</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to address CVE-2024-25062. See
<a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-xc9x-jj77-9p9j">GHSA-xc9x-jj77-9p9j</a>
for more information.</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.12.5">v2.12.5</a>
from v2.12.4. (<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</li>
</ul>
<hr />
<p>sha256 checksums:</p>

<pre><code>69ba15d2a2498324489ed63850997f0b8f684260114ea81116d3082f16551d2d
nokogiri-1.16.2-aarch64-linux.gem
6a05ce42e3587a40cf8936ece0beaa5d32922254215d2e8cf9ad40588bb42e57
nokogiri-1.16.2-arm-linux.gem
c957226c8e36b31be6a3afb8602e2128282bf8b40ea51016c4cd21aa2608d3f8
nokogiri-1.16.2-arm64-darwin.gem
122652bfc338cd8a54a692ac035e245e41fd3b8283299202ca26e7a7d50db310
nokogiri-1.16.2-java.gem
7344b5072ca69fc5bedb61cb01a3b765b93a27aae5a2a845c2ba7200e4345074
nokogiri-1.16.2-x64-mingw-ucrt.gem
a2a5e184a424111a0d5b77947986484920ad708009c667f061e8d02035c562dd
nokogiri-1.16.2-x64-mingw32.gem
833efddeb51a6c2c9f6356295623c2b2e0d50050d468695c59bd929162953323
nokogiri-1.16.2-x86-linux.gem
e67fc0418dffaff9dc8b1dc65f0605282c3fee9488832d0223b620b4319e0b53
nokogiri-1.16.2-x86-mingw32.gem
5def799e5f139f21a79d7cf71172313a7b6fb0e4b2a31ab9bd5d4ad305994539
nokogiri-1.16.2-x86_64-darwin.gem
5b146240ac6ec6c40fd4367623e74442bca45a542bd3282b1d4d18b07b8e5dfe
nokogiri-1.16.2-x86_64-linux.gem
68922ee5cde27497d995c46f2821957bae961947644eed2822d173daf7567f9c
nokogiri-1.16.2.gem
</code></pre>
<h2>v1.16.1 / 2024-02-03</h2>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.12.4">v2.12.4</a>
from v2.12.3. (<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>[CRuby] <code>XML::Reader</code> defaults the encoding to UTF-8 if
it's not specified in either the document or as a method parameter.
Previously non-ASCII characters were serialized as NCRs in this case. <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2891">#2891</a>
(<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</li>
<li>[CRuby] Restored support for compilation by GCC versions earlier
than 4.6, which was broken in v1.15.0 (540e9aee). <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3090">#3090</a>
(<a
href="https://github.com/adfoster-r7"><code>@​adfoster-r7</code></a>)</li>
<li>[CRuby] Patched upstream libxml2 to allow parsing HTML5 in the
context of a namespaced node (e.g., foreign content like MathML).
[#3112, <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3116">#3116</a>]
(<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</li>
<li>[CRuby] Fixed a small memory leak in libgumbo (HTML5 parser) when
the maximum tree depth limit is hit. [#3098, <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3100">#3100</a>]
(<a
href="https://github.com/stevecheckoway"><code>@​stevecheckoway</code></a>)</li>
</ul>
<hr />
<p>sha256 checksums:</p>

<pre><code>a541f35e5b9798a0c97300f9ee18f4217da2a2945a6d5499e4123b9018f9cafc
nokogiri-1.16.1-aarch64-linux.gem
6b82affd195000ab2f9c36cc08744ec2d2fcf6d8da88d59a2db67e83211f7c69
nokogiri-1.16.1-arm-linux.gem
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md">nokogiri's
changelog</a>.</em></p>
<blockquote>
<h2>v1.16.2 / 2024-02-04</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to address CVE-2024-25062. See
<a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-xc9x-jj77-9p9j">GHSA-xc9x-jj77-9p9j</a>
for more information.</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.12.5">v2.12.5</a>
from v2.12.4. (<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</li>
</ul>
<h2>v1.16.1 / 2024-02-03</h2>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.12.4">v2.12.4</a>
from v2.12.3. (<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>[CRuby] <code>XML::Reader</code> defaults the encoding to UTF-8 if
it's not specified in either the document or as a method parameter.
Previously non-ASCII characters were serialized as NCRs in this case. <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2891">#2891</a>
(<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</li>
<li>[CRuby] Restored support for compilation by GCC versions earlier
than 4.6, which was broken in v1.15.0 (540e9aee). <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3090">#3090</a>
(<a
href="https://github.com/adfoster-r7"><code>@​adfoster-r7</code></a>)</li>
<li>[CRuby] Patched upstream libxml2 to allow parsing HTML5 in the
context of a namespaced node (e.g., foreign content like MathML).
[#3112, <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3116">#3116</a>]
(<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</li>
<li>[CRuby] Fixed a small memory leak in libgumbo (HTML5 parser) when
the maximum tree depth limit is hit. [#3098, <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3100">#3100</a>]
(<a
href="https://github.com/stevecheckoway"><code>@​stevecheckoway</code></a>)</li>
</ul>
<h2>v1.16.0 / 2023-12-27</h2>
<h3>Notable Changes</h3>
<h4>Ruby</h4>
<p>This release introduces native gem support for Ruby 3.3.</p>
<p>This release ends support for Ruby 2.7, for which <a
href="https://www.ruby-lang.org/en/downloads/branches/">upstream support
ended 2023-03-31</a>.</p>
<h4>Pattern matching</h4>
<p>This version marks <em>official support</em> for the pattern matching
API in <code>XML::Attr</code>, <code>XML::Document</code>,
<code>XML::DocumentFragment</code>, <code>XML::Namespace</code>,
<code>XML::Node</code>, and <code>XML::NodeSet</code> (and their
subclasses), originally introduced as an experimental feature in
v1.14.0. (<a
href="https://github.com/flavorjones"><code>@​flavorjones</code></a>)</p>
<p>Documentation on what can be matched:</p>
<ul>
<li><a
href="https://nokogiri.org/rdoc/Nokogiri/XML/Attr.html?h=deconstruct#method-i-deconstruct_keys"><code>XML::Attr#deconstruct_keys</code></a></li>
<li><a
href="https://nokogiri.org/rdoc/Nokogiri/XML/Document.html?h=deconstruct#method-i-deconstruct_keys"><code>XML::Document#deconstruct_keys</code></a></li>
<li><a
href="https://nokogiri.org/rdoc/Nokogiri/XML/Namespace.html?h=deconstruct+namespace#method-i-deconstruct_keys"><code>XML::Namespace#deconstruct_keys</code></a></li>
<li><a
href="https://nokogiri.org/rdoc/Nokogiri/XML/Node.html?h=deconstruct#method-i-deconstruct_keys"><code>XML::Node#deconstruct_keys</code></a></li>
<li><a
href="https://nokogiri.org/rdoc/Nokogiri/XML/DocumentFragment.html?h=deconstruct#method-i-deconstruct"><code>XML::DocumentFragment#deconstruct</code></a></li>
<li><a
href="https://nokogiri.org/rdoc/Nokogiri/XML/NodeSet.html?h=deconstruct#method-i-deconstruct"><code>XML::NodeSet#deconstruct</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="673756fdd6"><code>673756f</code></a>
version bump to v1.16.2</li>
<li><a
href="74ffd67a8e"><code>74ffd67</code></a>
dep: update libxml to 2.12.5 (branch v1.16.x) (<a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3122">#3122</a>)</li>
<li><a
href="0d4018dc70"><code>0d4018d</code></a>
dep: update libxml2 to v2.12.5</li>
<li><a
href="f33a25f437"><code>f33a25f</code></a>
dep: remove patch from <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3112">#3112</a>
which has been released upstream</li>
<li><a
href="e99416896a"><code>e994168</code></a>
version bump to v1.16.1</li>
<li><a
href="77ea2f228c"><code>77ea2f2</code></a>
dev: add files to manifest ignore list</li>
<li><a
href="756f27c6b7"><code>756f27c</code></a>
build(deps): bump actions/{download,upload}-artifact from 3 to 4</li>
<li><a
href="464f8d41eb"><code>464f8d4</code></a>
.gitignore: clangd-related files</li>
<li><a
href="2beeb96069"><code>2beeb96</code></a>
doc: update CHANGELOG</li>
<li><a
href="a26536d7a4"><code>a26536d</code></a>
fix: apply upstream patch for in-context parsing (<a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3116">#3116</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/sparklemotion/nokogiri/compare/v1.14.3...v1.16.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nokogiri&package-manager=bundler&previous-version=1.14.3&new-version=1.16.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-02-28 21:03:35 +01:00
Anton Molyboha e4a579f10f
FAQ.md: jetstream needs to be backed up too. (#3327)
In the section "What data needs to be kept if transferring/backing up
Dendrite?" of the FAQ, add jetstream directory to the list.

It seems to be a common mistake when moving dendrite to a different
computer, that the jetstream directory is not copied.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [ ] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Anton Molyboha <anton.molyboha@gmail.com>`
2024-02-28 21:02:25 +01:00
Till 865fff5f03
Make usage of relays optional, avoid DB roundtrips (#3337)
This should avoid 2 additional DB roundtrips if we don't want to use
relays.

So instead of possibly doing roughly 20k trips to the DB, we are now
"only" doing ~6600.

---------

Co-authored-by: devonh <devon.dmytro@gmail.com>
2024-02-28 20:59:34 +01:00
Till 4ccf6d6f67
Cache ACLs regexes (#3336)
Since #3334 didn't change much on d.m.org, this is another attempt to
speed up startup.

Given moderation bots like Mjolnir/Draupnir are in many rooms with quite
often the same or similar ACLs, caching the compiled regexes _should_
reduce the startup time.

Using a pointer to the `*regexp.Regex` ensures we only store _one_
instance of a regex in memory, instead of potentially storing it hundred
of times. This should reduce memory consumption on servers with many
rooms with ACLs drastically. (5.1MB vs 1.7MB with this change on my
server with 8 ACL'd rooms [3 using the same ACLs])

[skip ci]
2024-02-28 20:58:56 +01:00
Till f4e77453cb
Speed up start up time by batch querying ACL events (#3334)
This should significantly speed up start up times on servers with many
rooms.
2024-02-21 14:10:22 +01:00
Till 8f944f6434
Limit filter to limit/2 for before/after events on /context (#3332)
Part of https://github.com/matrix-org/dendrite/issues/3224
2024-02-20 07:38:51 +00:00
Till ecb7b383e9
Remove unused token (#3331)
Part of https://github.com/matrix-org/dendrite/issues/3225
2024-02-19 19:19:06 +00:00
Till e9deb5244e
Fix /createRoom and /invite containing displayname/avatarURL of inviter (#3326)
Fixes #3324
2024-02-13 19:28:52 +01:00
Till be0c27e688
Update all the CI actions (#3323)
Also adds a job for the scheduled CI run to only run if there has been a
commit in the last 24h
([StackOverflow](https://stackoverflow.com/questions/63014786/how-to-schedule-a-github-actions-nightly-build-but-run-it-only-when-there-where))

[skip ci]
2024-02-08 09:58:59 +01:00
Till Faelligen 436773ab71
Disable Element Web tests, only run csapi and federation tests 2024-02-07 10:28:10 +01:00
Tulir Asokan 0f6b81f456
Modernize appservice paths and authentication (#3316)
This brings Dendrite's appservice spec support up to v1.4, from the
previous level of pre-release-spec support only (even r0.1.0 wasn't
supported for pushing transactions 🙃). There are config options to
revert to the old behavior, but the default is v1.4+ only. [Synapse also
does
that](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#use_appservice_legacy_authorization)

mautrix bridges will drop support for legacy paths and authentication
soon (and possibly also require matrix v1.4 to be advertised, but I
might add some workaround to not require that for dendrite)

Signed-off-by: Tulir Asokan <tulir@maunium.net>
2024-02-03 18:56:13 +01:00
Till a3a18fbcce
Fix x86 tests (#3317)
x86 tests broke with #3298
(Not exactly the tests modified here, but
`TestMessageHistoryVisibility`)
2024-01-29 20:44:43 +01:00
Till 87f028db27
Version 0.13.6 (#3315) 2024-01-26 14:41:34 +01:00
Till 8f68f1ff53
Move /joined_members back to the clientapi/roomserver (#3312)
Partly reverts #2827 by moving `/joined_members` back to the
clientAPI/roomserver
2024-01-25 21:35:05 +01:00
Matthew Strapp a4817f31c0
Allow + in MIDs as per MSC4009 (#3313)
This PR adds `+` to the username regex, per MSC4009.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Matt Strapp <matt@mattstrapp.net>`
2024-01-25 21:17:20 +01:00
Joakim Recht 00217a69d1
Only fetch events once for all rooms (#3311)
This refactors `PDUStreamProvider` a bit so that it doesn't trigger a
database query per room, but instead utilizes the fact that it's
possible to bulk query. This improves sync performance significantly
when you have 1000s of rooms.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Joakim Recht <joakim@beyondwork.ai>`
2024-01-25 20:10:46 +01:00
Till d58daf9665
Update sentry reporting (#3305)
This hopefully reduces the garbage we currently produce.
(Using [GlitchTip](https://glitchtip.com/) on my personal instance, this
seems to look better)
2024-01-24 19:24:04 +01:00
Till 8e4dc6b4ae
Optimize PrevEventIDs when getting thousands of backwards extremeties (#3308)
Changes how many `PrevEventIDs` we send to other servers when
backfilling, capped to 100 events.

Unsure about how representative this benchmark is..
```
goos: linux
goarch: amd64
pkg: github.com/matrix-org/dendrite/roomserver/api
cpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
                            │    old.txt     │               new.txt               │
                            │     sec/op     │   sec/op     vs base                │
PrevEventIDs/Original1-8         264.9n ± 5%   237.4n ± 7%  -10.36% (p=0.000 n=10)
PrevEventIDs/Original10-8        3.101µ ± 4%   1.590µ ± 2%  -48.72% (p=0.000 n=10)
PrevEventIDs/Original100-8       44.32µ ± 2%   12.80µ ± 4%  -71.11% (p=0.000 n=10)
PrevEventIDs/Original500-8     263.835µ ± 4%   7.907µ ± 4%  -97.00% (p=0.000 n=10)
PrevEventIDs/Original1000-8    578.798µ ± 2%   7.620µ ± 2%  -98.68% (p=0.000 n=10)
PrevEventIDs/Original2000-8   1272.039µ ± 2%   8.241µ ± 9%  -99.35% (p=0.000 n=10)
geomean                          43.81µ        3.659µ       -91.65%

                            │    old.txt     │               new.txt                │
                            │      B/op      │     B/op      vs base                │
PrevEventIDs/Original1-8          72.00 ± 0%     48.00 ± 0%  -33.33% (p=0.000 n=10)
PrevEventIDs/Original10-8        1512.0 ± 0%     500.0 ± 0%  -66.93% (p=0.000 n=10)
PrevEventIDs/Original100-8     11.977Ki ± 0%   7.023Ki ± 0%  -41.36% (p=0.000 n=10)
PrevEventIDs/Original500-8     67.227Ki ± 0%   7.023Ki ± 0%  -89.55% (p=0.000 n=10)
PrevEventIDs/Original1000-8   163.227Ki ± 0%   7.023Ki ± 0%  -95.70% (p=0.000 n=10)
PrevEventIDs/Original2000-8   347.227Ki ± 0%   7.023Ki ± 0%  -97.98% (p=0.000 n=10)
geomean                         12.96Ki        1.954Ki       -84.92%

                            │   old.txt   │              new.txt               │
                            │  allocs/op  │ allocs/op   vs base                │
PrevEventIDs/Original1-8       2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=10)
PrevEventIDs/Original10-8      6.000 ± 0%   2.000 ± 0%  -66.67% (p=0.000 n=10)
PrevEventIDs/Original100-8     9.000 ± 0%   3.000 ± 0%  -66.67% (p=0.000 n=10)
PrevEventIDs/Original500-8    12.000 ± 0%   3.000 ± 0%  -75.00% (p=0.000 n=10)
PrevEventIDs/Original1000-8   14.000 ± 0%   3.000 ± 0%  -78.57% (p=0.000 n=10)
PrevEventIDs/Original2000-8   16.000 ± 0%   3.000 ± 0%  -81.25% (p=0.000 n=10)
geomean                        8.137        2.335       -71.31%
```
2024-01-20 22:26:57 +01:00
Till d357615452
Don't send device list updates upon registration (#3307)
Fixes https://github.com/matrix-org/dendrite/issues/3273

As we otherwise send down device list updates which are merely useful
for the user and causes tests to be flakey:

```
 TestPushSync/Adding_a_push_rule_wakes_up_an_incremental_/sync (10ms)
      push_test.go:57: no pushrules found in sync response: {"next_batch":"s0_0_0_0_0_1_1_0_1","device_lists":{"changed":["@user-1:hs1"]}}
```

What this does: If a `PerformDeviceCreation` request is coming from
registering an account, it does **not** send device list updates, as
they are merely useful (no joined rooms, no one to inform) . In all
other cases, the behavior is unchanged and device list updates are sent
as usual.
2024-01-20 21:20:37 +01:00
Till bebf701dce
Add login fallback (#3302)
Part of https://github.com/matrix-org/dendrite/issues/3216

The files are basically copied from Synapse, with minor changes to the
called endpoints. We never seem to have had the
`/_matrix/static/client/login/` endpoint, this adds it.
2024-01-17 17:08:57 +01:00
Till dae1ef2e46
Update GMSL (#3303)
If I didn't miss anything, this should add fixes from:
https://github.com/matrix-org/gomatrixserverlib/pull/424
https://github.com/matrix-org/gomatrixserverlib/pull/426
https://github.com/matrix-org/gomatrixserverlib/pull/427
https://github.com/matrix-org/gomatrixserverlib/pull/428
https://github.com/matrix-org/gomatrixserverlib/pull/429
https://github.com/matrix-org/gomatrixserverlib/pull/430
2024-01-15 20:12:34 +00:00
dependabot[bot] 3a4b5f49ac
Bump github.com/quic-go/quic-go from 0.37.4 to 0.37.7 (#3300)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go)
from 0.37.4 to 0.37.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/quic-go/quic-go/releases">github.com/quic-go/quic-go's
releases</a>.</em></p>
<blockquote>
<h2>v0.37.7</h2>
<p>This release contains fixes for the Honeybadger vulnerability
(CVE-2023-49295):</p>
<ul>
<li>limit the number of queued PATH_RESPONSE frames to 256 (<a
href="https://redirect.github.com/quic-go/quic-go/issues/4199">#4199</a>)</li>
<li>don't retransmit PATH_CHALLENGE and PATH_RESPONSE frames (<a
href="https://redirect.github.com/quic-go/quic-go/issues/4200">#4200</a>)</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/quic-go/quic-go/compare/v0.37.6...v0.37.7">https://github.com/quic-go/quic-go/compare/v0.37.6...v0.37.7</a></p>
<h2>v0.37.6</h2>
<p>This patch release contains a backport of <a
href="https://redirect.github.com/quic-go/quic-go/pull/4038">quic-go/quic-go#4038</a>.</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/quic-go/quic-go/compare/v0.37.5...v0.37.6">https://github.com/quic-go/quic-go/compare/v0.37.5...v0.37.6</a></p>
<h2>v0.37.5</h2>
<p>This patch release contains the backport of 3 fixes:</p>
<ul>
<li>fix handshake failure if <code>tls.Config.SessionTicketDisabled =
false</code>, but <code>tls.Config.GetConfigForClient</code> returns a
config that disables session tickets: <a
href="https://redirect.github.com/quic-go/quic-go/issues/4030">#4030</a></li>
<li>use the correct hash function for TLS_AES_256_GCM_SHA384: <a
href="https://redirect.github.com/quic-go/quic-go/issues/4031">#4031</a></li>
<li>automatically set the <code>tls.Config.ServerName</code>: <a
href="https://redirect.github.com/quic-go/quic-go/issues/4032">#4032</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/quic-go/quic-go/compare/v0.37.4...v0.37.5">https://github.com/quic-go/quic-go/compare/v0.37.4...v0.37.5</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="21609ddfef"><code>21609dd</code></a>
don't retransmit PATH_CHALLENGE and PATH_RESPONSE frames (<a
href="https://redirect.github.com/quic-go/quic-go/issues/4200">#4200</a>)</li>
<li><a
href="d7aa627ebd"><code>d7aa627</code></a>
limit the number of queued PATH_RESPONSE frames to 256 (<a
href="https://redirect.github.com/quic-go/quic-go/issues/4199">#4199</a>)</li>
<li><a
href="e2c360ceec"><code>e2c360c</code></a>
reassemble post-handshake TLS messages before passing them to crypto/tls
(<a
href="https://redirect.github.com/quic-go/quic-go/issues/4038">#4038</a>)</li>
<li><a
href="e9f7f460bc"><code>e9f7f46</code></a>
automatically set the tls.Config.ServerName if unset (<a
href="https://redirect.github.com/quic-go/quic-go/issues/4032">#4032</a>)</li>
<li><a
href="12d84c4196"><code>12d84c4</code></a>
handshake: use the correct hash function for TLS_AES_256_GCM_SHA384 (<a
href="https://redirect.github.com/quic-go/quic-go/issues/4031">#4031</a>)</li>
<li><a
href="b1635df2f5"><code>b1635df</code></a>
ignore QUICConn.SendSessionTicket error if session tickets are disabled
(<a
href="https://redirect.github.com/quic-go/quic-go/issues/4030">#4030</a>)</li>
<li>See full diff in <a
href="https://github.com/quic-go/quic-go/compare/v0.37.4...v0.37.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/quic-go/quic-go&package-manager=go_modules&previous-version=0.37.4&new-version=0.37.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-10 18:55:35 +01:00
Till e34242008b
Add CORP header to /download and /thumbnail (#3299)
Part of #3222 

https://github.com/matrix-org/matrix-spec-proposals/pull/3828
2024-01-10 09:39:13 +01:00
devonh 57646d5b86
Handle empty from in /messages as per MSC3567 (#3298) 2024-01-09 19:06:02 +00:00
Till 9510fa00cc
Return M_INVALID_PARAM instead of M_BAD_JSON when setting aliases (#3297)
Part of https://github.com/matrix-org/dendrite/issues/3223
(https://github.com/matrix-org/matrix-spec/pull/1286)

(For `DELETE` we don't validate the alias, but just return a 404 if we
can't find it)
2024-01-09 20:05:45 +01:00
Till 13c5173273
Fix notary keys requests for all keys (#3296)
This should be more spec compliant:
> If no key IDs are given to be queried, the notary server should query
for all keys.
2024-01-08 19:14:29 +01:00
Till edd02ec468
Fix panic if unable to assign a state key NID (#3294) 2023-12-30 18:34:36 +01:00
dependabot[bot] 9a5a56718e
Bump golang.org/x/crypto from 0.14.0 to 0.17.0 (#3290)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from
0.14.0 to 0.17.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9d2ee975ef"><code>9d2ee97</code></a>
ssh: implement strict KEX protocol changes</li>
<li><a
href="4e5a26183e"><code>4e5a261</code></a>
ssh: close net.Conn on all NewServerConn errors</li>
<li><a
href="152cdb1503"><code>152cdb1</code></a>
x509roots/fallback: update bundle</li>
<li><a
href="fdfe1f8531"><code>fdfe1f8</code></a>
ssh: defer channel window adjustment</li>
<li><a
href="b8ffc16e10"><code>b8ffc16</code></a>
blake2b: drop Go 1.6, Go 1.8 compatibility</li>
<li><a
href="7e6fbd82c8"><code>7e6fbd8</code></a>
ssh: wrap errors from client handshake</li>
<li><a
href="bda2f3f5cf"><code>bda2f3f</code></a>
argon2: avoid clobbering BP</li>
<li><a
href="325b735346"><code>325b735</code></a>
ssh/test: skip TestSSHCLIAuth on Windows</li>
<li><a
href="1eadac50a5"><code>1eadac5</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="b2d7c26edb"><code>b2d7c26</code></a>
ssh: add (*Client).DialContext method</li>
<li>Additional commits viewable in <a
href="https://github.com/golang/crypto/compare/v0.14.0...v0.17.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/crypto&package-manager=go_modules&previous-version=0.14.0&new-version=0.17.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 08:39:22 +01:00
Till f93d1c4790
Use AckExplicitPolicy instead of AckAllPolicy (#3288)
Fixes https://github.com/matrix-org/dendrite/issues/3240 and potentially
a root cause for state resets.

While testing, I've had added some more debug logging:
```
time="2023-12-16T18:13:11.319458084Z" level=warning msg="already processed event" event_id="$qFYMl_F2vb1N0yxmvlFAMhqhGhLKq4kA-o_YCQKH7tQ" kind=KindNew times=2
time="2023-12-16T18:13:14.537389126Z" level=warning msg="already processed event" event_id="$EU-LTsKErT6Mt1k12-p_3xOHfiLaK6gtwVDlZ35lSuo" kind=KindNew times=5
time="2023-12-16T18:13:16.789551206Z" level=warning msg="already processed event" event_id="$dIPuAfTL5x0VyG873LKPslQeljCSxFT1WKxUtjIMUGE" kind=KindNew times=5
time="2023-12-16T18:13:17.383838767Z" level=warning msg="already processed event" event_id="$7noSZiCkzerpkz_UBO3iatpRnaOiPx-3IXc0GPDQVGE" kind=KindNew times=2
time="2023-12-16T18:13:22.091946597Z" level=warning msg="already processed event" event_id="$3Lvo3Wbi2ol9-nNbQ93N-E2MuGQCJZo5397KkFH-W6E" kind=KindNew times=1
time="2023-12-16T18:13:23.026417446Z" level=warning msg="already processed event" event_id="$lj1xS46zsLBCChhKOLJEG-bu7z-_pq9i_Y2DUIjzGy4" kind=KindNew times=4
```

So we did receive the same event over and over again. Given they are
`KindNew`, we don't short circuit if we already processed them, which
potentially caused the state to be calculated with a now wrong state
snapshot.

Also fixes the back pressure metric. We now correctly increment the
counter once we sent the message to NATS and decrement it once we
actually processed an event.
2023-12-19 08:25:47 +01:00
Till Faelligen d65449c782
Also pin Pinecone and Yggdrasil demo 2023-12-12 17:31:36 +01:00
Till b7054f4274
Version 0.13.5 (#3285) 2023-12-12 16:55:03 +01:00
Till 1555b3542d
Introduce a new stream for the appservice consumer (#3277)
This introduces a new stream the syncAPI produces to once it processed a
`OutputRoomEvent` and the appservices consumes.
This is to work around a race condition where appservices receive an
event before the syncAPI has handled it, this can result in e.g. calls
to `/joined_members` returning a wrong membership list.
2023-12-12 12:13:55 +01:00
Till 185ad6b00d
Allow some content types to be inlined (#3274)
"Shamelessly" stolen from
https://github.com/matrix-org/synapse/pull/15988
2023-12-12 11:15:50 +01:00
Joseph Alvarenga Beech fd11e65a9d
added a warning log , for well_known_server_name,well_known_server_name when they dont have prefix (#3205)
closing this https://github.com/matrix-org/dendrite/issues/3180

added a warning log when either well_known_server_name,
well_known_server_name: dont have a prefix in them

josephalvarengabeech@pm.me

---------

Co-authored-by: Till Faelligen <2353100+S7evinK@users.noreply.github.com>
2023-11-25 22:19:22 +01:00
Cat 61e5dc47d7
Added Docker commands for Windows (#3267)
### Pull Request Checklist

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

No tests were added due to it being a small documentation addition

Signed-off-by: `Cat Catry <denperidge@gmail.com>`
2023-11-25 20:24:13 +01:00
Till Faelligen 210bce9938
Update GMSL to avoid logging unnecessary messages 2023-11-25 19:12:21 +01:00
KuhnChris 4f943771fa
Appservice Login (2nd attempt) (#3078)
Rebase of #2936 as @vijfhoek wrote he got no time to work on this, and I
kind of needed it for my experiments.
I checked the tests, and it is working with my example code (i.e.
impersonating, registering, creating channel, invite people, write
messages).
I'm not a huge `go` pro, and still learning, but I tried to fix and/or
integrate the changes as best as possible with the current `main` branch
changes.
If there is anything left, let me know and I'll try to figure it out.

Signed-off-by: `Kuhn Christopher <kuhnchris+git@kuhnchris.eu>`

---------

Signed-off-by: Sijmen <me@sijman.nl>
Signed-off-by: Sijmen Schoon <me@sijman.nl>
Co-authored-by: Sijmen Schoon <me@sijman.nl>
Co-authored-by: Sijmen Schoon <me@vijf.life>
Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
2023-11-24 22:34:13 +01:00
Till b8f91485b4
Update ACLs when received as outliers (#3008)
This should fix #3004 by making sure we also update our in-memory ACLs
after joining a new room.
Also makes use of more caching in `GetStateEvent`

Bonus: Adds some tests, as I was about to use `GetBulkStateContent`, but
turns out that `GetStateEvent` is basically doing the same, just that it
only gets the `eventTypeNID`/`eventStateKeyNID` once and not for every
call.
2023-11-22 15:38:04 +01:00
BtbN c4528b2de8
Allow users to kick themselves (#3157)
As per the spec:
https://spec.matrix.org/v1.7/rooms/v10/#authorization-rules

"If membership is leave"
->
"If the sender matches state_key, allow if and only if that user’s
current membership state is invite, join, or knock."

I.e. a user can kick themselves. Bridges use this to make a user leave
while giving a reason.

Some recent change (likely
8ea1a11105
but I'm not 100% sure) changed that behaviour, resulting in heisenbridge
being unable to make users leave while giving a reason.
This works fine on Synapse.

Signed-off-by: Timo Rothenpieler <timo@rothenpieler.org>
Co-authored-by: kegsay <7190048+kegsay@users.noreply.github.com>
2023-11-22 12:15:45 +00:00
CicadaCinema f25cce237e
Refactor registration tests, remove hard-coded username validation (#3138)
### Pull Request Checklist

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] I have already signed off privately

This PR is in preparation for #3137 and removes the hard-coded username
validation (previously only dependent on `forceEmpty`).

---------

Co-authored-by: kegsay <7190048+kegsay@users.noreply.github.com>
2023-11-22 12:15:16 +00:00
Till 210123bab5
Add keydb_server_keys table tests (#3270)
Also moves some of the variable declarations out of the loop to,
hopefully, reduce allocations.
2023-11-22 13:05:24 +01:00
notassigned 06e079abac
Fix broken links in FAQ.md (#3259)
The links to CONTRUBITING.md and 4_adminapi.md were broken.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [ X] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [X ] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: <Private>

Co-authored-by: kegsay <kegan@matrix.org>
2023-11-22 11:14:49 +00:00
Nikolai Patrick fde4225469
fix typo (#3266)
Fix a tiny spelling mistake in the Grafana dashboard.
Literally a 1 character commit lol
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x ] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [ x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Nikolai Patrick nikolaipatrick@wws.sa.edu.au`
2023-11-22 11:13:41 +00:00
Till 7863a405a5
Use IsBlacklistedOrBackingOff to determine if we should try to fetch devices (#3254)
Use `IsBlacklistedOrBackingOff` from the federation API to check if we
should fetch devices.

To reduce back pressure, we now only queue retrying servers if there's
space in the channel.
2023-11-09 08:43:27 +01:00
Till 699f5ca8c1
More rows.Close() and rows.Err() (#3262)
Looks like we missed some `rows.Close()`

Even though `rows.Err()` is mostly not necessary, we should be more
consistent in the DB layer.

[skip ci]
2023-11-09 08:42:33 +01:00
Till ee73a90aea
Fix potential connection leak (#3247)
We didn't rollback/commit after getting events, now we're rolling back
since we didn't change anything.
2023-11-08 14:22:20 +01:00
Till 5f872f4a82
Fix panic in QueryNextRoomHierarchyPage (#3253)
Sentry reported the following panic:
```
time="2023-11-01T01:33:56.220583478Z" level=error msg="Request panicked!
goroutine 43763845 [running]:
runtime/debug.Stack()
	runtime/debug/stack.go:24 +0x5e
github.com/matrix-org/dendrite/internal/httputil.MakeExternalAPI.MakeJSONAPI.Protect.func3.1()
	github.com/matrix-org/util@v0.0.0-20221111132719-399730281e66/json.go:98 +0x13e
panic({0x15b5540?, 0x2453560?})
	runtime/panic.go:914 +0x21f
github.com/matrix-org/dendrite/internal/httputil.MakeAuthAPI.func1.1()
	github.com/matrix-org/dendrite/internal/httputil/httpapi.go:91 +0x4a
panic({0x15b5540?, 0x2453560?})
	runtime/panic.go:914 +0x21f
github.com/matrix-org/dendrite/roomserver/internal/query.(*Queryer).QueryNextRoomHierarchyPage(0x413185?, {0x1a576e0, 0xc0436705a0}, {{{0xc01e5fd260, 0x1f}, {0xc01e5fd261, 0x12}, {0xc01e5fd274, 0xb}}, {0xc145cb5200, ...}, ...}, ...)
	github.com/matrix-org/dendrite/roomserver/internal/query/query_room_hierarchy.go:116 +0xbfe
github.com/matrix-org/dendrite/clientapi/routing.QueryRoomHierarchy(0xc0be13b200, 0xc144e65dd0, {0xc01e5fd260?, 0x6?}, {0x7faf140639c8, 0xc00059af20}, 0xc08adca000?)
	github.com/matrix-org/dendrite/clientapi/routing/room_hierarchy.go:141 +0x68b
github.com/matrix-org/dendrite/clientapi/routing.Setup.func35(0xc03e7d5c20?, 0x17c3a57?)
	github.com/matrix-org/dendrite/clientapi/routing/routing.go:534 +0xbe
github.com/matrix-org/dendrite/internal/httputil.MakeAuthAPI.func1(0xc0bd097300)
	github.com/matrix-org/dendrite/internal/httputil/httpapi.go:108 +0x5ed
github.com/matrix-org/util.(*jsonRequestHandlerWrapper).OnIncomingRequest(0xc0bd097200?, 0xc13b7d6fc0?)
	github.com/matrix-org/util@v0.0.0-20221111132719-399730281e66/json.go:79 +0x19
github.com/matrix-org/dendrite/internal/httputil.MakeExternalAPI.MakeJSONAPI.func2({0x1a54880, 0xc138f28b60}, 0xc0bd097200?)
	github.com/matrix-org/util@v0.0.0-20221111132719-399730281e66/json.go:141 +0xaa
github.com/matrix-org/dendrite/internal/httputil.MakeExternalAPI.MakeJSONAPI.Protect.func3({0x1a54880?, 0xc138f28b60?}, 0x17c01d9?)
	github.com/matrix-org/util@v0.0.0-20221111132719-399730281e66/json.go:103 +0x63
net/http.HandlerFunc.ServeHTTP(...)
	net/http/server.go:2136
github.com/matrix-org/dendrite/internal/httputil.MakeExternalAPI.func1({0x1a54880?, 0xc138f28b60?}, 0xc0bd097100)
	github.com/matrix-org/dendrite/internal/httputil/httpapi.go:191 +0x411
net/http.HandlerFunc.ServeHTTP(0xc0bd097000?, {0x1a54880?, 0xc138f28b60?}, 0xbe1348905308878e?)
	net/http/server.go:2136 +0x29
github.com/gorilla/mux.(*Router).ServeHTTP(0xc000000000, {0x1a54880, 0xc138f28b60}, 0xc0bd096f00)
	github.com/gorilla/mux@v1.8.0/mux.go:210 +0x1c5
github.com/matrix-org/dendrite/setup/base.SetupAndServeHTTP.(*Handler).Handle.(*Handler).handle.func5({0x1a54880, 0xc138f28b60}, 0xc0bd096e00)
	github.com/getsentry/sentry-go@v0.14.0/http/sentryhttp.go:103 +0x298
net/http.HandlerFunc.ServeHTTP(0xc0bd096a00?, {0x1a54880?, 0xc138f28b60?}, 0x7fae6812f5d0?)
	net/http/server.go:2136 +0x29
github.com/gorilla/mux.(*Router).ServeHTTP(0xc000000a80, {0x1a54880, 0xc138f28b60}, 0xc0bd096900)
	github.com/gorilla/mux@v1.8.0/mux.go:210 +0x1c5
net/http.serverHandler.ServeHTTP({0xc02884c4e0?}, {0x1a54880?, 0xc138f28b60?}, 0x6?)
	net/http/server.go:2938 +0x8e
net/http.(*conn).serve(0xc1926922d0, {0x1a576e0, 0xc024a6ec90})
	net/http/server.go:2009 +0x5f4
created by net/http.(*Server).Serve in goroutine 16979
	net/http/server.go:3086 +0x5cb
" context=missing panic="runtime error: invalid memory address or nil pointer dereference"
```

[skip ci]
2023-11-08 14:22:02 +01:00
dependabot[bot] 5c67eb99b3
Bump golang.org/x/image from 0.5.0 to 0.10.0 (#3257)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.5.0
to 0.10.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="cb227cd2c9"><code>cb227cd</code></a>
tiff: limit work when decoding malicious images</li>
<li><a
href="a5392f068b"><code>a5392f0</code></a>
bmp: support to decode 8-bit format with up to 256 color palette</li>
<li><a
href="f9550b04a5"><code>f9550b0</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="81c166c49c"><code>81c166c</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="ed5dba0ea2"><code>ed5dba0</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="08ca817286"><code>08ca817</code></a>
font: have Glyph return !ok for U+FFFD substitute</li>
<li><a
href="b6ac75bc59"><code>b6ac75b</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="1b7441254c"><code>1b74412</code></a>
font/sfnt: set type for all NameID constants</li>
<li><a
href="f632f7f87c"><code>f632f7f</code></a>
tiff, tiff/lzw, vector: use single space in comments</li>
<li>See full diff in <a
href="https://github.com/golang/image/compare/v0.5.0...v0.10.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/image&package-manager=go_modules&previous-version=0.5.0&new-version=0.10.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-03 08:13:34 +01:00
dependabot[bot] 8b4043473c
Bump github.com/nats-io/nkeys from 0.4.4 to 0.4.6 (#3252)
Bumps [github.com/nats-io/nkeys](https://github.com/nats-io/nkeys) from
0.4.4 to 0.4.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/nats-io/nkeys/releases">github.com/nats-io/nkeys's
releases</a>.</em></p>
<blockquote>
<h2>v0.4.5</h2>
<h2>What's Changed</h2>
<ul>
<li>[CI] bump staticcheck GHAction by <a
href="https://github.com/philpennock"><code>@​philpennock</code></a> in
<a
href="https://redirect.github.com/nats-io/nkeys/pull/49">nats-io/nkeys#49</a></li>
<li>[FIX] added windows binary by <a
href="https://github.com/aricart"><code>@​aricart</code></a> in <a
href="https://redirect.github.com/nats-io/nkeys/pull/51">nats-io/nkeys#51</a></li>
<li>[FIX] YAML Enginering: quote go-version string by <a
href="https://github.com/philpennock"><code>@​philpennock</code></a> in
<a
href="https://redirect.github.com/nats-io/nkeys/pull/53">nats-io/nkeys#53</a></li>
<li>[FEAT] Use readKeyFile to read both seed file and public key file by
<a href="https://github.com/nanjj"><code>@​nanjj</code></a> in <a
href="https://redirect.github.com/nats-io/nkeys/pull/54">nats-io/nkeys#54</a></li>
<li>[FEAT] Made <code>decode</code> a little fast by <a
href="https://github.com/nanjj"><code>@​nanjj</code></a> in <a
href="https://redirect.github.com/nats-io/nkeys/pull/55">nats-io/nkeys#55</a></li>
<li>[REPO] Add issue forms by <a
href="https://github.com/bruth"><code>@​bruth</code></a> in <a
href="https://redirect.github.com/nats-io/nkeys/pull/56">nats-io/nkeys#56</a></li>
<li>[FIX] added binaries to match nats-server by <a
href="https://github.com/aricart"><code>@​aricart</code></a> in <a
href="https://redirect.github.com/nats-io/nkeys/pull/58">nats-io/nkeys#58</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/nats-io/nkeys/compare/v0.4.4...v0.4.5">https://github.com/nats-io/nkeys/compare/v0.4.4...v0.4.5</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="62e5d8c7c4"><code>62e5d8c</code></a>
Merge pull request <a
href="https://redirect.github.com/nats-io/nkeys/issues/60">#60</a> from
nats-io/0_4_6</li>
<li><a
href="f63761b84d"><code>f63761b</code></a>
[BUMP] release version and dependencies</li>
<li><a
href="d2e442ebad"><code>d2e442e</code></a>
Merge pull request <a
href="https://redirect.github.com/nats-io/nkeys/issues/59">#59</a> from
nats-io/empty</li>
<li><a
href="58fb9d69f4"><code>58fb9d6</code></a>
Make sure to use byte slice to receive proper copy, otherwise empty
public ke...</li>
<li><a
href="3e454c8ca1"><code>3e454c8</code></a>
Merge pull request <a
href="https://redirect.github.com/nats-io/nkeys/issues/58">#58</a> from
nats-io/arch-bins</li>
<li><a
href="53c0777667"><code>53c0777</code></a>
bump go to 1.21.x</li>
<li><a
href="d935834966"><code>d935834</code></a>
bump version number</li>
<li><a
href="6b488b3078"><code>6b488b3</code></a>
[FIX] added binaries to match nats-server</li>
<li><a
href="9fb41511a9"><code>9fb4151</code></a>
Merge pull request <a
href="https://redirect.github.com/nats-io/nkeys/issues/56">#56</a> from
nats-io/add-issue-forms</li>
<li><a
href="4647ec0912"><code>4647ec0</code></a>
Fix issue config discussions link</li>
<li>Additional commits viewable in <a
href="https://github.com/nats-io/nkeys/compare/v0.4.4...v0.4.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/nats-io/nkeys&package-manager=go_modules&previous-version=0.4.4&new-version=0.4.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-01 12:00:59 +01:00
Till da7bca0224
Some tweaks for the device list updater (#3251)
This makes the following changes:
- Adds two new metrics observing the usage of the `DeviceListUpdater`
workers
- Makes the number of workers configurable
- Adds a 30s timeout for DB requests when receiving a device list update
over federation
2023-10-31 16:39:45 +01:00
dependabot[bot] 32f7c4b166
Bump github.com/docker/docker from 24.0.5+incompatible to 24.0.7+incompatible (#3250)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from
24.0.5+incompatible to 24.0.7+incompatible.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/docker/docker/releases">github.com/docker/docker's
releases</a>.</em></p>
<blockquote>
<h2>v24.0.7</h2>
<h2>24.0.7</h2>
<p>For a full list of pull requests and changes in this release, refer
to the relevant GitHub milestones:</p>
<ul>
<li><a
href="https://github.com/docker/cli/issues?q=is%3Aclosed+milestone%3A24.0.7">docker/cli,
24.0.7 milestone</a></li>
<li><a
href="https://github.com/moby/moby/issues?q=is%3Aclosed+milestone%3A24.0.7">moby/moby,
24.0.7 milestone</a></li>
</ul>
<h3>Bug fixes and enhancements</h3>
<ul>
<li>Write overlay2 layer metadata atomically. <a
href="https://redirect.github.com/moby/moby/pull/46703">moby/moby#46703</a></li>
<li>Fix &quot;Rootful-in-Rootless&quot; Docker-in-Docker on systemd
version 250 and later. <a
href="https://redirect.github.com/moby/moby/pull/46626">moby/moby#46626</a></li>
<li>Fix <code>dockerd-rootless-setuptools.sh</code> when username
contains a backslash. <a
href="https://redirect.github.com/moby/moby/pull/46407">moby/moby#46407</a></li>
<li>Fix a bug that would prevent network sandboxes to be fully deleted
when stopping containers with no network attachments and when
<code>dockerd --bridge=none</code> is used. <a
href="https://redirect.github.com/moby/moby/pull/46702">moby/moby#46702</a></li>
<li>Fix a bug where cancelling an API request could interrupt container
restart. <a
href="https://redirect.github.com/moby/moby/pull/46697">moby/moby#46697</a></li>
<li>Fix an issue where containers would fail to start when providing
<code>--ip-range</code> with a range larger than the subnet. <a
href="https://redirect.github.com/docker/for-mac/issues/6870">docker/for-mac#6870</a></li>
<li>Fix data corruption with zstd output. <a
href="https://redirect.github.com/moby/moby/pull/46709">moby/moby#46709</a></li>
<li>Fix the conditions under which the container's MAC address is
applied. <a
href="https://redirect.github.com/moby/moby/pull/46478">moby/moby#46478</a></li>
<li>Improve the performance of the stats collector. <a
href="https://redirect.github.com/moby/moby/pull/46448">moby/moby#46448</a></li>
<li>Fix an issue with source policy rules ending up in the wrong order.
<a
href="https://redirect.github.com/moby/moby/pull/46441">moby/moby#46441</a></li>
</ul>
<h3>Packaging updates</h3>
<ul>
<li>Add support for Fedora 39 and Ubuntu 23.10. <a
href="https://redirect.github.com/docker/docker-ce-packaging/pull/940">docker/docker-ce-packaging#940</a>,
<a
href="https://redirect.github.com/docker/docker-ce-packaging/pull/955">docker/docker-ce-packaging#955</a></li>
<li>Fix <code>docker.socket</code> not getting disabled when
uninstalling the <code>docker-ce</code> RPM package. <a
href="https://redirect.github.com/docker/docker-ce-packaging/pull/852">docker/docker-ce-packaging#852</a></li>
<li>Upgrade Go to <code>go1.20.10</code>. <a
href="https://redirect.github.com/docker/docker-ce-packaging/pull/951">docker/docker-ce-packaging#951</a></li>
<li>Upgrade containerd to <code>v1.7.6</code> (static binaries only). <a
href="https://redirect.github.com/moby/moby/pull/46103">moby/moby#46103</a></li>
<li>Upgrade the <code>containerd.io</code> package to <a
href="https://github.com/containerd/containerd/releases/tag/v1.6.24"><code>v1.6.24</code></a>.</li>
</ul>
<h3>Security</h3>
<ul>
<li>Deny containers access to <code>/sys/devices/virtual/powercap</code>
by default. This change hardens against <a
href="https://scout.docker.com/v/CVE-2020-8694">CVE-2020-8694</a>, <a
href="https://scout.docker.com/v/CVE-2020-8695">CVE-2020-8695</a>, and
<a href="https://scout.docker.com/v/CVE-2020-12912">CVE-2020-12912</a>,
and an attack known as <a href="https://platypusattack.com/">the
PLATYPUS attack</a>. For more details, see <a
href="https://github.com/moby/moby/security/advisories/GHSA-jq35-85cj-fj4p">advisory</a>,
<a
href="c9ccbfad11">commit</a>.</li>
</ul>
<h2>v24.0.6</h2>
<h2>24.0.6</h2>
<p>For a full list of pull requests and changes in this release, refer
to the relevant GitHub milestones:</p>
<ul>
<li><a
href="https://github.com/docker/cli/issues?q=is%3Aclosed+milestone%3A24.0.6">docker/cli,
24.0.6 milestone</a></li>
<li><a
href="https://github.com/moby/moby/issues?q=is%3Aclosed+milestone%3A24.0.6">moby/moby,
24.0.6 milestone</a></li>
</ul>
<h3>Bug fixes and enhancements</h3>
<ul>
<li>containerd storage backend: Fix <code>docker ps</code> failing when
a container image is no longer present in the content store. <a
href="https://redirect.github.com/moby/moby/pull/46095">moby/moby#46095</a></li>
<li>containerd storage backend: Fix <code>docker ps -s -a</code> and
<code>docker container prune</code> failing when a container image
config is no longer present in the content store. <a
href="https://redirect.github.com/moby/moby/pull/46097">moby/moby#46097</a></li>
<li>containerd storage backend: Fix <code>docker inspect</code> failing
when a container image config is no longer (or was never) present in the
content store. <a
href="https://redirect.github.com/moby/moby/pull/46244">moby/moby#46244</a></li>
<li>containerd storage backend: Fix diff and export with the
<code>overlayfs</code> snapshotter by using reference-counted rootfs
mounts. <a
href="https://redirect.github.com/moby/moby/pull/46266">moby/moby#46266</a></li>
<li>containerd storage backend: Fix a misleading error message when the
image platforms available locally do not match the desired platform. <a
href="https://redirect.github.com/moby/moby/pull/46300">moby/moby#46300</a></li>
<li>containerd storage backend: Fix the <code>FROM scratch</code>
Dockerfile instruction with the classic builder. <a
href="https://redirect.github.com/moby/moby/pull/46302">moby/moby#46302</a></li>
<li>containerd storage backend: Fix <code>mismatched image rootfs and
manifest layers</code> errors with the classic builder. <a
href="https://redirect.github.com/moby/moby/pull/46310">moby/moby#46310</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="311b9ff0aa"><code>311b9ff</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/46697">#46697</a>
from thaJeztah/24.0_backport_restart_nocancel</li>
<li><a
href="af608045ee"><code>af60804</code></a>
Merge pull request from GHSA-jq35-85cj-fj4p</li>
<li><a
href="3cf363e1ee"><code>3cf363e</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/46709">#46709</a>
from thaJeztah/24.0_backport_bump_compress</li>
<li><a
href="05d7386665"><code>05d7386</code></a>
daemon: daemon.containerRestart: don't cancel restart on context
cancel</li>
<li><a
href="649c9440f2"><code>649c944</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/46703">#46703</a>
from thaJeztah/24.0_backport_atomic-layer-data-write</li>
<li><a
href="9b20b1a5fe"><code>9b20b1a</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/46702">#46702</a>
from thaJeztah/24.0_backport_releaseNetwork_Network...</li>
<li><a
href="dd37b0b960"><code>dd37b0b</code></a>
vendor: github.com/klauspost/compress v1.17.2</li>
<li><a
href="7058c0d24d"><code>7058c0d</code></a>
vendor: github.com/klauspost/compress v1.16.5</li>
<li><a
href="57bd388582"><code>57bd388</code></a>
daemon: overlay2: Write layer metadata atomically</li>
<li><a
href="05d95fd503"><code>05d95fd</code></a>
daemon: release sandbox even when NetworkDisabled</li>
<li>Additional commits viewable in <a
href="https://github.com/docker/docker/compare/v24.0.5...v24.0.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/docker/docker&package-manager=go_modules&previous-version=24.0.5+incompatible&new-version=24.0.7+incompatible)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 07:59:19 +01:00
Till 317b1018a3
Version 0.13.4 (#3244)
If I didn't mess up the workflow, this should remove some ugliness from
the version string (e.g. 0.13.2+57ddbe0.57ddbe0, dupe commit hash, as a
result of https://github.com/matrix-org/dendrite/pull/3147)
2023-10-25 13:53:40 +02:00
CicadaCinema 89482ad790
clean up dead links, fix typo (#3130)
I fixed any dead links beginning https://matrix.org/speculator and some
issues I found along the way.


https://web.archive.org/web/20190329152312/https://matrix.org/speculator/spec/HEAD/client_server/unstable.html#user-interactive-authentication-api
is now found at

https://spec.matrix.org/v1.7/client-server-api/#user-interactive-authentication-api


https://web.archive.org/web/20170620093435/https://matrix.org/speculator/spec/HEAD/client_server/unstable.html#post-matrix-client-unstable-register
is now found at

https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv3register


2a8d64fef7/specification/intro.rst?plain=1#L443
is now found at
https://spec.matrix.org/v1.7/appendices/#user-identifiers
2023-10-25 10:24:06 +02:00
devonh a0375d41fb
Add simple test for one time keys (#3239) 2023-10-25 10:13:18 +02:00
WrenIX e02a7948d8
fix(helm): empty storage class in pvcs (#3191)
fix #3103 

---

not yet tested

[skip ci]
2023-10-25 10:08:54 +02:00
Till 4fa8512d57
Check event is not rejected (#3243)
Companion PR to https://github.com/matrix-org/gomatrixserverlib/pull/421
2023-10-25 09:47:21 +02:00
Till 1b124fe9cb
Implement MSC3987, fix setting Element Android notifications (#3242)
Should fix https://github.com/matrix-org/dendrite/issues/3183, since
Element Android already implements
[MSC3987](https://github.com/vector-im/element-android/pull/8530)

This is also part of https://github.com/matrix-org/dendrite/issues/3225
2023-10-24 11:51:08 +02:00
dependabot[bot] c1d6b9aa8e
Bump github.com/nats-io/nats-server/v2 from 2.9.19 to 2.9.23 (#3238)
Bumps
[github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server)
from 2.9.19 to 2.9.23.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/nats-io/nats-server/releases">github.com/nats-io/nats-server/v2's
releases</a>.</em></p>
<blockquote>
<h2>Release v2.9.23</h2>
<h2>Changelog</h2>
<h3>Go Version</h3>
<ul>
<li>1.20.10</li>
</ul>
<h3>Fixed</h3>
<p>Accounts</p>
<ul>
<li>Prevent bypassing authorization block when enabling system account
access in accounts block (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4605">#4605</a>).
Backport from v2.10.2</li>
</ul>
<p>Leafnodes</p>
<ul>
<li>Prevent a leafnode cluster from receiving a message multiple times
in a queue subscription (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4578">#4578</a>).
Backport from v2.10.2</li>
</ul>
<p>JetStream</p>
<ul>
<li>Hold lock when calculating the first message for subject in a
message block (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4531">#4531</a>).
Backport from v2.10.0</li>
<li>Add self-healing mechanism to detect and delete orphaned Raft groups
(<a
href="https://redirect.github.com/nats-io/nats-server/issues/4647">#4647</a>).
Backport from v2.10.0</li>
<li>Prevent forward proposals in consumers after scaling down a stream
(<a
href="https://redirect.github.com/nats-io/nats-server/issues/4647">#4647</a>).
Backport from v2.10.0</li>
<li>Fix race condition during leader failover scenarios resulting in
potential duplicate messages being sourced (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4592">#4592</a>).
Backport from v2.10.2</li>
</ul>
<h3>Complete Changes</h3>
<p><a
href="https://github.com/nats-io/nats-server/compare/v2.9.22...v2.9.23">https://github.com/nats-io/nats-server/compare/v2.9.22...v2.9.23</a></p>
<h2>Release v2.9.22</h2>
<h2>Changelog</h2>
<h3>Go Version</h3>
<ul>
<li>1.20.8 (updated out-of-cycle since Go 1.19 is now EOL)</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>github.com/nats-io/jwt/v2 v2.5.0</li>
<li>golang.org/x/crypto v0.12.0</li>
<li>golang.org/x/sys v0.11.0</li>
</ul>
<h3>Improved</h3>
<p>Monitoring</p>
<ul>
<li>CORS Allow-Origin passthrough for monitoring server (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4423">#4423</a>)
Thanks to <a href="https://github.com/mdawar"><code>@​mdawar</code></a>
for the contribution!</li>
</ul>
<p>JetStream</p>
<ul>
<li>Improve consumer scaling reliability with filters and cluster
restart (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4404">#4404</a>)</li>
<li>Send event on lame duck mode (LDM) to avoid placing assets on
shutting down nodes (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4405">#4405</a>)</li>
<li>Skip filestore tombstones if downgrade from 2.10 occurs (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4452">#4452</a>)</li>
<li>Adjust delivered and waiting count when consumer message delivery
fails (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4472">#4472</a>)</li>
</ul>
<h3>Fixed</h3>
<p>Config</p>
<ul>
<li>Allow empty configs and fix JSON compatibility (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4394">#4394</a>,
<a
href="https://redirect.github.com/nats-io/nats-server/issues/4418">#4418</a>)</li>
<li>Remove TLS OCSP debug log on reload (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4453">#4453</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="45436e1e50"><code>45436e1</code></a>
Release v2.9.23 (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4652">#4652</a>)</li>
<li><a
href="72ffa38b05"><code>72ffa38</code></a>
Release v2.9.23</li>
<li><a
href="05fe77fd08"><code>05fe77f</code></a>
Backport <a
href="https://redirect.github.com/nats-io/nats-server/issues/4592">#4592</a>
to 2.9 (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4651">#4651</a>)</li>
<li><a
href="6a73e6824a"><code>6a73e68</code></a>
[2.9.x] Bump Travis Go version to 1.20.10 (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4650">#4650</a>)</li>
<li><a
href="8b981a2621"><code>8b981a2</code></a>
Backports from v2.10 for v2.9.23 release (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4647">#4647</a>)</li>
<li><a
href="28eb7c0ac2"><code>28eb7c0</code></a>
Only setup auto no-auth for $G account iff no authorization block was
defined.</li>
<li><a
href="9f16edd431"><code>9f16edd</code></a>
Make sure to not forward a message across a route for dq sub when we are
a sp...</li>
<li><a
href="0ac7895b98"><code>0ac7895</code></a>
Add in utility to detect and delete any NRG orphans.</li>
<li><a
href="50722e9ec1"><code>50722e9</code></a>
When scaling a consumer down make sure to pop the
loopAndForwardProposals go ...</li>
<li><a
href="770cf2edd6"><code>770cf2e</code></a>
Backport JetStream benchmarks improvements to 2.9.x (<a
href="https://redirect.github.com/nats-io/nats-server/issues/4644">#4644</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/nats-io/nats-server/compare/v2.9.19...v2.9.23">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/nats-io/nats-server/v2&package-manager=go_modules&previous-version=2.9.19&new-version=2.9.23)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
2023-10-24 09:11:58 +02:00
Till 8b3adaf244
Fix state resets (#3231)
Needs https://github.com/matrix-org/gomatrixserverlib/pull/419

May fix: https://github.com/matrix-org/dendrite/issues/2508,
https://github.com/matrix-org/dendrite/issues/1760
2023-10-23 15:17:21 +02:00
Till 8c23c1150c
Tweaks around the device list updater (#3227)
I hope the comments explain the changes.

`notifyWorkers` notifies a worker which then calls `processServer`,
which in turn gets all users and calls `processServerUser`. There is no
need to call `processServer` for the same domain on startup.
2023-10-23 11:09:05 +02:00
dependabot[bot] fe2955a4db
Bump golang.org/x/net from 0.14.0 to 0.17.0 (#3233)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to
0.17.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b225e7ca6d"><code>b225e7c</code></a>
http2: limit maximum handler goroutines to MaxConcurrentStreams</li>
<li><a
href="88194ad8ab"><code>88194ad</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="2b60a61f1e"><code>2b60a61</code></a>
quic: fix several bugs in flow control accounting</li>
<li><a
href="73d82efb96"><code>73d82ef</code></a>
quic: handle DATA_BLOCKED frames</li>
<li><a
href="5d5a036a50"><code>5d5a036</code></a>
quic: handle streams moving from the data queue to the meta queue</li>
<li><a
href="350aad2603"><code>350aad2</code></a>
quic: correctly extend peer's flow control window after MAX_DATA</li>
<li><a
href="21814e71db"><code>21814e7</code></a>
quic: validate connection id transport parameters</li>
<li><a
href="a600b3518e"><code>a600b35</code></a>
quic: avoid redundant MAX_DATA updates</li>
<li><a
href="ea633599b5"><code>ea63359</code></a>
http2: check stream body is present on read timeout</li>
<li><a
href="ddd8598e56"><code>ddd8598</code></a>
quic: version negotiation</li>
<li>Additional commits viewable in <a
href="https://github.com/golang/net/compare/v0.14.0...v0.17.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/net&package-manager=go_modules&previous-version=0.14.0&new-version=0.17.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 09:40:21 +02:00
devonh 933ae2db91
Update bug report to reflect current team members (#3234) 2023-10-12 18:03:06 +00:00
kegsay 5888329b13
Update Complement to match new public API shape (#3232)
Sister PR to matrix-org/complement#666

Context:
https://github.com/matrix-org/complement/issues/654#issuecomment-1746613495
2023-10-11 17:41:12 +01:00
Till 2259e71c0c
Fix resolve-state (#3229)
Previously we would "start" the roomserver API, which isn't the best
idea, given it also starts processing Jetstream events. We now use a
`dummyQuerier` to implement the needed interface for "converting"
userID/senderIDs. As per the comment, this **DOES NOT** do any magic for
pseudoID rooms.
2023-10-05 10:33:04 +02:00
Till 3d02c81031
Fix tests for x86 (#3214) 2023-09-28 14:50:31 +02:00
Till Faelligen 1853f58cb4
Add missing sliding sync config 2023-09-28 12:38:53 +02:00
Till b341a66152
Version 0.13.3 (#3213) 2023-09-28 12:06:21 +02:00
Tracker-Friendly 4d344b65b2
Fixed typo in documentation (#3212)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [ ] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests

This PR doesn't need tests because it's a documentation update

* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed off privately

Co-authored-by: Tracker-Friendly <jliwin98@pm.me>
2023-09-28 07:40:12 +02:00
jahway603 f1db57c7f8
Updated minimum required go version in README.md (#3194)
Updated minimum required go version in README.md

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `jahway603 <jahway603@protonmail.com>`

Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
2023-09-28 07:38:29 +02:00
Till f02d998253
Remove the creator field when upgrading to v11 (#3210)
Minor oversight
2023-09-28 07:36:57 +02:00
Till 10b4fbc66d
Fix m.direct only being partially upgraded (#3209)
Previously we would update `m.direct` once we found the old room ID. If
the roomID is found somewhere in the middle, we would never add the rest
of the users, resulting in only partially upgraded `m.direct` and chats
loosing their 1:1 flag.
2023-09-28 07:36:34 +02:00
Till 05a8f1ede3
Support for room version v11 (#3204)
Fixes #3203
2023-09-27 08:27:08 +02:00
devonh 16d922de70
Complement fixes for pseudoIDs (#3206) 2023-09-26 17:44:49 +00:00
Till d065219de1
Fix invitations not sending push notifications (#3207)
The tests added in https://github.com/matrix-org/sytest/pull/1356
uncovered that we don't consider invitations as events the userapi
should handle and thus just don't notify the client about any new
invitations received over federation.
2023-09-26 15:47:37 +02:00
devonh db83789654
Move pseudoID ClientEvent hotswapping to a common location (#3199)
Fixes a variety of issues where clients were receiving pseudoIDs in
places that should be userIDs.
This change makes pseudoIDs work with sliding sync & element x.

---------

Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
2023-09-15 15:25:09 +00:00
devonh 8245b24100
Update gmsl to use new validated RoomID on PDUs (#3200)
GMSL returns a `spec.RoomID` when calling `PDU.RoomID()`
2023-09-15 14:39:06 +00:00
Sam Wedgwood 058081e68e
[pseudoIDs] changing event ID fix (#3195)
power levels events in pseudo IDs sometimes changed event IDs (this was
already fixed earlier, but one of the edgecases was not covered, and is
now covered)

Signed-off-by: `Sam Wedgwood <sam@wedgwood.dev>`
2023-09-12 16:32:24 +01:00
Tulir Asokan bea73c765a
Fix user_id query param breaking auth for non-appservices (#3196)
The `user_id` query param only has defined behavior when authenticating
with an `as_token`. For any other tokens, the presence of the parameter
should simply be ignored.

Fixes #1738

Signed-off-by: Tulir Asokan <tulir@maunium.net>
Co-authored-by: devonh <devon.dmytro@gmail.com>
2023-09-12 14:44:51 +00:00
Sam Wedgwood 478827459c
bump GMSL back to main (#3197)
In a [previous PR](https://github.com/matrix-org/dendrite/pull/3181) I
accidentally left GMSL on a dev branch, this PR fixes it by bringing it
back to the main branch of GMSL

Signed-off-by: `Sam Wedgwood <sam@wedgwood.dev>`
2023-09-08 16:30:21 +01:00
devonh bb2ab62cbf
Handle event_format federation in /sync responses (#3192) 2023-08-31 15:33:38 +00:00
Till Faelligen 11fd2f019b
Fix Complement scheduled CI
[skip CI]
2023-08-30 07:37:14 +02:00
Omar Pakker b538f237df
[helm] Update Ingress hosts to account for IPv6 (server+client) and scheme (client) (#3182)
This updates the matchers for deriving the host values from the dendrite
config. The original version turned out to have 2 complications:
- It did not support IPv6 addresses as host value
- It failed for `well_known_client_host` which is a (base) URL instead
of a hostname+port.

I've verified `well_known_server_name` with
```
dendrite.example.net:443
dendrite.example.net
192.168.1.1
192.168.1.1:1324
[dead::beef]:1234
[dead::beef]
[ffff:dead::beef]
```
and `well_known_client_name` with:
```
https://dendrite.example.net:443
https://dendrite.example.net
https://dendrite.example.net/
http://dendrite.example.net:8080/
http://192.168.1.1
http://192.168.1.1:8080/
http://[dead::beef]:1234
http://[dead::beef]/
http://[ffff:dead::beef]
```

Fixes #3175

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Omar Pakker <Omar007@users.noreply.github.com>`

---------

Signed-off-by: Omar Pakker <Omar007@users.noreply.github.com>

[skip CI]
2023-08-29 08:20:37 +02:00
Till e3a7039c81
Fix CI, upgrade image used for upgrade tests (#3151) 2023-08-28 13:28:22 +02:00
dependabot[bot] 43b1ddb89b
Bump commonmarker from 0.23.9 to 0.23.10 in /docs (#3172)
Bumps [commonmarker](https://github.com/gjtorikian/commonmarker) from
0.23.9 to 0.23.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gjtorikian/commonmarker/releases">commonmarker's
releases</a>.</em></p>
<blockquote>
<h2>v0.23.10</h2>
<h2>What's Changed</h2>
<ul>
<li>Update to 0.29.0.gfm.13 by <a
href="https://github.com/anticomputer"><code>@​anticomputer</code></a>
in <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/247">gjtorikian/commonmarker#247</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.9...v0.23.10">https://github.com/gjtorikian/commonmarker/compare/v0.23.9...v0.23.10</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/gjtorikian/commonmarker/blob/v0.23.10/CHANGELOG.md">commonmarker's
changelog</a>.</em></p>
<blockquote>
<h2>[v0.23.10] (2023-07-31)</h2>
<ul>
<li>Update GFM release to <a
href="https://github.com/github/cmark-gfm/releases/tag/0.29.0.gfm.12"><code>0.29.0.gfm.12</code></a>
and <a
href="https://github.com/github/cmark-gfm/releases/tag/0.29.0.gfm.13"><code>0.29.0.gfm.13</code></a>,
thereby <a
href="https://github.com/github/cmark-gfm/security/advisories/GHSA-w4qg-3vf7-m9x5">fixing
a polynomial time complexity security vulnerability</a>.</li>
<li>Of note to users of this library, GFM releases
<code>0.29.0.gfm.12</code> and <code>0.29.0.gfm.13</code> also:
<ul>
<li>Normalized marker row vs. delimiter row nomenclature (<a
href="https://redirect.github.com/github/cmark-gfm/pull/273">#273</a>)</li>
<li>Exposed CMARK_NODE_FOOTNOTE_DEFINITION literal value (<a
href="https://redirect.github.com/github/cmark-gfm/pull/336">#336</a>)</li>
</ul>
</li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v0.23.4">v0.23.4</a>
(2022-03-03)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.2...v0.23.4">Full
Changelog</a></p>
<p><strong>Fixed bugs:</strong></p>
<ul>
<li><code>#render_html</code> way slower than
<code>#render_doc.to_html</code> <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/141">#141</a></li>
</ul>
<p><strong>Closed issues:</strong></p>
<ul>
<li>allow keeping text content of unknown tags <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/169">#169</a></li>
<li>STRIKETHROUGH_DOUBLE_TILDE not working <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/168">#168</a></li>
<li>Allow disabling 4-space code blocks <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/167">#167</a></li>
<li>tables with escaped pipes are not recognized <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/166">#166</a></li>
</ul>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>CI: Drop a duplicate 'bundle install' <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/173">#173</a>
(<a href="https://github.com/olleolleolle">olleolleolle</a>)</li>
<li>CI: Drop duplicate bundle install <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/172">#172</a>
(<a href="https://github.com/olleolleolle">olleolleolle</a>)</li>
<li>Fixup benchmark and speedup a little, fixes <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/141">#141</a>
<a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/171">#171</a>
(<a href="https://github.com/ojab">ojab</a>)</li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v0.23.2">v0.23.2</a>
(2021-09-17)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.1...v0.23.2">Full
Changelog</a></p>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>Update GFM release to <code>0.29.0.gfm.2</code> <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/148">#148</a>
(<a href="https://github.com/phillmv">phillmv</a>)</li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v0.23.1">v0.23.1</a>
(2021-09-03)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.0...v0.23.1">Full
Changelog</a></p>
<p><strong>Closed issues:</strong></p>
<ul>
<li>Incorrect processing of list and next block of code <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/146">#146</a></li>
</ul>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>Normalize parse and render options <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/145">#145</a>
(<a href="https://github.com/phillmv">phillmv</a>)</li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v0.23.0">v0.23.0</a>
(2021-08-30)</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="db8cd377b5"><code>db8cd37</code></a>
Merge pull request <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/247">#247</a>
from anticomputer/update-to-0.29.0.gfm.13</li>
<li><a
href="e1e450c381"><code>e1e450c</code></a>
💎 release 0.23.10</li>
<li><a
href="08b7c4b96c"><code>08b7c4b</code></a>
Update cmark-upstream to <a
href="https://github.com/github/cmark-gfm/commit/587a12bb5">https://github.com/github/cmark-gfm/commit/587a12bb5</a>...</li>
<li><a
href="d0e81e2392"><code>d0e81e2</code></a>
I've used this version of the update_submodules script for several
releases, ...</li>
<li>See full diff in <a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.9...v0.23.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=commonmarker&package-manager=bundler&previous-version=0.23.9&new-version=0.23.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-08-28 12:51:47 +02:00
devonh 1c4ec67bb6
Add configuration option for sliding sync when hosting /.well-known/matrix/client (#3189)
Adds the `org.matrix.msc3575.proxy` field (used for configuring sliding
sync) to /.well-known/matrix/client when Dendrite is serving that
endpoint and `well_known_sliding_sync_proxy` has been configured.

ie. Config values of:
``` yaml
global:
    well_known_client_name: https://example.com
    well_known_sliding_sync_proxy: https://syncv3.example.com
```
results in a /.well-known/matrix/client of:
``` json
{
    "m.homeserver": {
        "base_url": "https://example.com"
    },
    "org.matrix.msc3575.proxy": {
        "url": "https://syncv3.example.com"
    }
}
```

If `well_known_sliding_sync_proxy` is not provided, the json provided by
/.well-known/matrix/client does not include the proxy field.
ie.
``` json
{
    "m.homeserver": {
        "base_url": "https://example.com"
    }
}
```
2023-08-24 21:08:40 +00:00
Sam Wedgwood 9b5be6b9c5
[pseudoIDs] More pseudo ID fixes - Part 2 (#3181)
Fixes include:
- Translating state keys that contain user IDs to their respective room
keys for both querying and sending state events
- **NOTE**: there may be design discussion needed on what should happen
when sender keys cannot be found for users
- A simple fix for kicking guests from rooms properly
- Logic for boundary history visibilities was slightly off (I'm
surprised this only manifested in pseudo ID room versions)

Signed-off-by: `Sam Wedgwood <sam@wedgwood.dev>`
2023-08-24 16:43:51 +01:00
Devon Hudson a721294e2b
Bump pinecone docker go version 2023-08-23 08:56:44 -06:00
Till Faelligen 845800abfa
Bump helm chart version 2023-08-23 16:44:52 +02:00
Till 57ddbe015d
Version 0.13.2 (#3187) 2023-08-23 16:24:16 +02:00
Sam Wedgwood 9a12420428
[pseudoID] More pseudo ID fixes (#3167)
Signed-off-by: `Sam Wedgwood <sam@wedgwood.dev>`
2023-08-15 12:37:04 +01:00
devonh fa6c7ba456
Update pinecone to use new quic version (#3174) 2023-08-11 14:29:48 +00:00
Sam Wedgwood 35804f8493
Add config key for default room version (#3171)
This PR adds a config key `room_server.default_config_key` to set the
default room version for the room server.

Signed-off-by: `Sam Wedgwood <sam@wedgwood.dev>`
2023-08-08 14:20:05 +01:00
maxberger 294eff8a7f
Add ID in error messages for ApplicationServices (#3162)
This is to easier identify which service caused the error.

Feature is just improving logging, thus no tests added.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [X]  I have justified why this PR doesn't need tests
* [X] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Maximilian Berger <max@berger.name>`

Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
2023-08-03 09:26:42 +02:00
Sam Wedgwood c7193e24d0
Use *spec.SenderID for QuerySenderIDForUser (#3164)
There are cases where a dendrite instance is unaware of a pseudo ID for
a user, the user is not a member of that room. To represent this case,
we currently use the 'zero' value, which is often not checked and so
causes errors later down the line. To make this case more explict, and
to be consistent with `QueryUserIDForSender`, this PR changes this to
use a pointer (and `nil` to mean no sender ID).

Signed-off-by: `Sam Wedgwood <sam@wedgwood.dev>`
2023-08-02 11:12:14 +01:00
Sam Wedgwood af13fa1c75
[pseudoIDs] Fixes for room alias tests (#3159)
Some (deceptively) simple fixes for some bugs that caused room alias
tests to fail (sytext `tests/30rooms/05aliases.pl`). Each commit has
details about what it fixes.

Sytest results:

- Sytest before (79d4a0e):
https://gist.github.com/swedgwood/972ac4ef93edd130d3db0930703d6c82
- Sytest after (4b09bed):
https://gist.github.com/swedgwood/504b00ac4ee892acb757b7fac55fa28a

Room aliases go from `8/15` to `15/15`, but looks like these fixes also
managed to fix about `4` other tests, which is a nice bonus :)

Signed-off-by: `Sam Wedgwood <sam@wedgwood.dev>`
2023-07-31 14:39:41 +01:00
Till 3f727485d6
Send a more generic error message to clients if the file can't be found (#3161)
Fixes #3160
2023-07-28 08:40:05 +02:00
Till Faelligen 79d4a0e399
Restore old behaviour of PurgeRoom 2023-07-26 09:09:04 +02:00
George Antoniadis 7899f47e71
add deployment strategy option to helm chart (re #3021) (#3155)
@S7evinK sorry for the spam but any chance we get get this merged into
main at some point? It was previously merged in
https://github.com/matrix-org/dendrite/pull/3021 into a temp branch that
never made it into main. If there is an issue with this being merged let
me know.

---

Minor update to the helm chart to allow setting the update strategy as
the default `RollingUpdate` one is a bit annoying if using
`ReadWriteOnce` volumes for media. Hope this makes sense.

---

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] ~~I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests~~ Haven't touched any go files.
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `George Antoniadis <george@noodles.gr>` [skip ci]
2023-07-26 08:16:43 +02:00
Devon Hudson a48c7d33a5
Don't quit if unknown msc in config, log it and keep going 2023-07-21 13:08:28 -06:00
devonh c809e95335
Fix event federation with pseudoID rooms (#3156) 2023-07-21 16:08:40 +00:00
Till e216c2fbf0
Update ConnectionManager to still allow component defined connections (#3154) 2023-07-21 08:34:01 +02:00
Sam Wedgwood 9582827493
de-MSC-ifying space summaries (MSC2946) (#3134)
- This PR moves and refactors the
[code](https://github.com/matrix-org/dendrite/blob/main/setup/mscs/msc2946/msc2946.go)
for
[MSC2946](https://github.com/matrix-org/matrix-spec-proposals/pull/2946)
('Space Summaries') to integrate it into the rest of the codebase.
- Means space summaries are no longer hidden behind an MSC flag
- Solves #3096

Signed-off-by: Sam Wedgwood <sam@wedgwood.dev>
2023-07-20 15:06:05 +01:00
Till 297479ea49
Use pointer when passing the connection manager around (#3152)
As otherwise existing connections aren't reused.
2023-07-19 13:37:04 +02:00
devonh a01faee17c
Extend context timeout on send_join to allow for joining complex rooms (#3153)
Background federated joins are currently broken since they timeout after
30s. This timeout didn't exist before the refactor. It should still exist but it needs to be extended to allow for the additional time it can take a server to generate the /send_join response when joining a complex room.
2023-07-18 18:48:05 +00:00
Till Faelligen 33ff309572
Don't HTTP500 if a profile does't exist 2023-07-14 14:24:31 +02:00
Till Faelligen 6011ddc0a8
Discard "illegal base64 data at input byte 0" errors in the SyncAPI 2023-07-14 08:28:30 +02:00
Till Faelligen 3e314e028e
Avoid panic due to being unable to query the userID 2023-07-14 08:04:25 +02:00
Till 5267cc0f54
Optimise getting local members and membership counts (#3150)
The previous version was getting **ALL** membership events (as
`ClientEvents`, so going through `NewEventFromTrustedJSONWithID`) for a
given room.
Now we are querying only locally joined users as `ClientEvents`, which
should **significantly** reduce allocations.

Take for example a large room with 2k membership events, but only 1
local user - avoiding 1999 `NewEventFromTrustedJSONWithID` calls just to
calculate the `roomSize` which we can also query by other means.

This is also getting called for every `OutputRoomEvent` in the userAPI.

Benchmark with 1 local user and 100 remote users.
```
pkg: github.com/matrix-org/dendrite/userapi/consumers
cpu: 12th Gen Intel(R) Core(TM) i5-12500H
                    │   old.txt   │               new.txt               │
                    │   sec/op    │   sec/op     vs base                │
LocalRoomMembers-16   375.9µ ± 7%   327.6µ ± 6%  -12.85% (p=0.000 n=10)

                    │    old.txt    │               new.txt                │
                    │     B/op      │     B/op      vs base                │
LocalRoomMembers-16   79.426Ki ± 0%   8.507Ki ± 0%  -89.29% (p=0.000 n=10)

                    │   old.txt   │              new.txt               │
                    │  allocs/op  │ allocs/op   vs base                │
LocalRoomMembers-16   1015.0 ± 0%   277.0 ± 0%  -72.71% (p=0.000 n=10)
```
2023-07-13 14:19:08 +02:00
Till f12982472c
Tweaks around /messages (#3149)
Try to mitigate some issues with `/messages`
2023-07-13 14:18:37 +02:00
Till Faelligen 0df982a2e5
Update NATS again [skip ci] 2023-07-13 14:17:48 +02:00
Till 99f94fc735
Add revision to version string (#3147)
Since the removal of `build.sh`, we don't include any information about
the revision Dendrite was build from. Since go1.18, the revision a
binary was build from is automatically included, so we can try to get
that instead.

This also adds a `dendrite_up` metric showing the current version
(`dendrite_up{version="0.13.1+c796f20"} 1`)

Closes #2993
2023-07-11 13:56:25 +02:00
Till 69b2069dea
Avoid loops by setting end to an empty string if start == end (#3146) 2023-07-08 11:45:44 +02:00
Till Faelligen b965a08faa
Unknown issue 2023-07-07 22:52:23 +02:00
Till Faelligen ef32de928d
[NATS] Issue identified and fixed applied, workaround known. 2023-07-07 22:10:52 +02:00
Till 74a5ab6c24
Fix issues reported by Sentry (#3143)
This should fix a few issues reported by Sentry
2023-07-07 22:00:10 +02:00
Till eb9e90379d
Add event size checks similar to Synapse (#3140)
Companion to https://github.com/matrix-org/gomatrixserverlib/pull/400
This tries to mimic the logic found in Synapse, as dropping events can
break rooms (and we may end up in endless loops..)
2023-07-07 20:37:23 +02:00
Neil e93bdd56fd
Set max age for roomserver input stream to avoid excessive interior deletes (#3145)
If old messages build up in the input stream and do not get processed
successfully, this can create a significant drift between the stream
first sequence and the consumer ack floors, which results in a slow and
expensive start-up when interest-based retention is in use.

If a message is sat in the stream for 24 hours, it's probably not going
to get processed successfully, so let NATS drop them instead. Dendrite
can reconcile by fetching missing events later if it needs to.

---------

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
2023-07-07 19:59:34 +02:00
Till c08c7405db
Prepare statement on an existing transaction (#3144)
This should fix an issue with the database being locked for SQLite.
2023-07-07 13:09:39 +02:00
devonh cc9b695c1e
Populate syncapi state event prev_sender with userID (#3142) 2023-07-06 23:54:35 +00:00
devonh 3a125fd8fa
Fix prev event lookup in syncapi (#3141)
The syncapi operates using userID's so when querying for the previous
state event we need to lookup the userID from the given senderID before
the state query.
2023-07-06 19:50:28 +00:00
devonh d507c5fc95
Add pseudoID compatibility to Invites (#3126) 2023-07-06 15:15:24 +00:00
Till Faelligen fea946d914
Don't spam the logs - downgrade sentry 2023-07-06 10:55:21 +02:00
Till Faelligen 9f7e14e4d0
Back to the original version for now 2023-07-06 10:44:11 +02:00
Till Faelligen 4a666932f5
[debug] Downgrade NATS 2023-07-06 10:31:32 +02:00
Till Faelligen e1d76de6c6
Increase NATS server startup timeout 2023-07-06 10:04:46 +02:00
Till 49d75d3cf6
Version 0.13.1 (#3136) 2023-07-06 09:28:39 +02:00
Till Faelligen 5a87c703fa
Fix metrics.. 2023-07-05 12:34:53 +02:00
Till 4c3a526e1b
Fix adding state events to the database (#3133)
When we're adding state to the database, we check which eventNIDs are
already in a block, if we already have that eventNID, we remove it from
the list. In its current form we would skip over eventNIDs in the case
we already found a match (we're decrementing `i` twice)
My theory is, that when we later get the state blocks, we are receiving
"too many" eventNIDs (well, yea, we stored too many), which may or may
not can result in state resets when comparing different state snapshots.
(e.g. when adding state we stored a eventNID by accident because we
skipped it, later we add more state and are not adding it because we
don't skip it)
2023-07-04 17:15:44 +02:00
Till 2ee03fd657
Version 0.13.0 (#3127) 2023-06-30 08:49:37 +02:00
Omar Pakker de1ed9d486
Extend Dendrite Helm chart with some additional config options (#3077)
This set of changes introduces a few (compatible) changes to the Helm
chart:
- Allow PVC class to be set on each PVC, not only one-for-all.
- Allow Prometheus servicemonitor and rules labels to be empty.
- Have the option to generate the ingress (incl. TLS config) based on
dendrite_config.


* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: Omar Pakker <Omar007@users.noreply.github.com>

---------

Signed-off-by: Omar Pakker <Omar007@users.noreply.github.com>
Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
2023-06-30 08:26:06 +02:00
Till Faelligen 939ee325f8
Actually use the parameter 2023-06-29 18:02:11 +02:00
Till 23cd7877a1
Add MXIDMapping for pseudoID rooms (#3112)
Add `MXIDMapping` on membership events when
creating/joining rooms.
2023-06-28 20:29:49 +02:00
Till 4722f12fab
Fix setting displayname and avatar_url (#3125)
As per the spec, `displayname` and `avatar_url` may be empty.
2023-06-28 20:18:07 +02:00
Till a5ea928d0f
Fix syncAPI redactions (#3118)
Previously we were setting `redacted_because` to the PDU event, but as
per the spec it should really be a client event.
This fixes it.
2023-06-28 10:05:00 +02:00
santhoshivan23 45082d4dce
feat: admin APIs for token authenticated registration (#3101)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Santhoshivan Amudhan santhoshivan23@gmail.com`
2023-06-22 16:37:21 +00:00
Till a734b112c6
Fix backfilling (#3117)
This should fix two issues with backfilling:
1. right after creating and joining a room over federation, we are doing
a `/backfill` request, which would return redacted events, because the
`authEvents` are empty. Even though the spec states that, in the absence
of a history visibility event, it should be handled as `shared`.
2. `gomatrixserverlib: unsupported room version ''` - because, well, we
were never setting the `roomInfo` field..
2023-06-20 16:52:29 +02:00
CicadaCinema d13466c1ee
rearrange order of sections about signing keys and configuring dendrite, fix a dead link (#3114)
I thought I would rearrange these pages since the configuration step
requires that a signing key has been generated.

Co-authored-by: kegsay <kegan@matrix.org>
2023-06-18 22:54:16 +01:00
Josh Qou 420e7ec81f
Fix unsafe hotserving behaviour for multimedia uploads. (#3113)
Return multimedia with a disposition type of attachment instead of
inline. NVT#1548992

Signed-off-by: Josh Qou [jqou@icloud.com](mailto:jqou@icloud.com)

Co-authored-by: Jon <haddock.05.roast@icloud.com>
2023-06-15 12:28:34 +01:00
Devon Hudson 8cf6c381e2
Fix senderID/key conversion unit tests 2023-06-14 17:11:27 +01:00
Devon Hudson 3f4df25b31
Add missing dep 2023-06-14 17:04:19 +01:00
Devon Hudson 5aaa539e3e
Fix senderID/key conversions 2023-06-14 16:42:09 +01:00
devonh e4665979bf
Merge SenderID & Per Room User Key work (#3109) 2023-06-14 14:23:46 +00:00
Till 7a2e325d10
Add AssignRoomNID to pre-assign roomNIDs (#3111) 2023-06-13 16:28:41 +02:00
Till 2c87972a3a
Create user room key if needed (#3108) 2023-06-13 14:19:31 +02:00
Till 82b73a4906
Add sender_key to ClientEvent (#3110) 2023-06-13 12:50:22 +02:00
devonh 77d9e4e93d
Cleanup remaining statekey usage for senderIDs (#3106) 2023-06-12 11:19:25 +00:00
Till 832ccc32f6
Add initial support for storing user room keys (#3098) 2023-06-12 12:45:42 +02:00
Antonio Cheong 5713c5715c
Update sample link (#3107)
Leftover work by f956a8c1d9

Signed-off-by: `Antonio Cheong <acheong@student.dalat.org>`

[skip ci]
2023-06-12 10:51:26 +02:00
devonh 8ea1a11105
Use SenderID Type (#3105) 2023-06-07 17:14:35 +00:00
devonh 7a1fd7f512
PDU Sender split (#3100)
Initial cut of splitting PDU Sender into SenderID & looking up UserID where required.
2023-06-06 20:55:18 +00:00
Till 725ff5567d
Make StrictValidityChecking a function (#3092)
Companion PR to https://github.com/matrix-org/gomatrixserverlib/pull/388
2023-06-06 15:16:55 +02:00
Till d11da6ec7c
Fix newly found linter issues (#3099)
Fixes the issues found in
https://github.com/matrix-org/dendrite/actions/runs/5155539352/jobs/9285342056#step:5:22.
Only naked returns in longer functions.
2023-06-02 15:48:04 +02:00
devonh ea6b368ad4
Move Invite logic to GMSL (#3086)
This is both the federation receiving & sending side logic (which were
previously entangeld in a single function)
2023-05-31 16:33:49 +00:00
devonh cbdc601f1b
Move CreateRoom logic to Roomserver (#3093)
Move create room logic over to roomserver.
2023-05-31 15:27:08 +00:00
Till 61341aca50
Add tests for the UpDropEventReferenceSHAPrevEvents migration (#3087)
... as they could fail if there are duplicate events in
`roomserver_previous_events`.
This fixes the migration by trying to combine the `event_nids` if
possible (same room) as mentioned by @kegsay in
https://github.com/matrix-org/dendrite/pull/3083#discussion_r1195508963
2023-05-30 18:05:48 +02:00
Till 3dcca4017c
Fix potential state reset when trying to join a room (#3040)
When trying to join a room in short sequence, it is possible that a
state reset occurs. This fixes it by using `singleflight`.
2023-05-30 15:27:11 +02:00
Till f956a8c1d9
Docs restructure (#2953)
Needs to be merged into `gh-pages` later on.
2023-05-30 10:02:53 +02:00
Till 11b557097c
Drop reference_sha column (#3083)
Companion PR to https://github.com/matrix-org/gomatrixserverlib/pull/383
2023-05-24 12:14:42 +02:00
Till 5d6221d191
Move MakeLeave to GMSL (#3085)
Basically the same API shape as for `/make_join`
https://github.com/matrix-org/gomatrixserverlib/pull/385
2023-05-23 19:37:04 +02:00
devonh 2eae8dc489
Move SendJoin logic to GMSL (#3084)
Moves the core matrix logic for handling the send_join endpoint over to
gmsl.
2023-05-19 16:27:01 +00:00
Devon Hudson 027a9b8ce0
Fix bug with nil interface return & add test 2023-05-18 13:41:47 -06:00
dependabot[bot] 345f025ee3
Bump github.com/docker/distribution from 2.8.1+incompatible to 2.8.2+incompatible (#3082)
Bumps
[github.com/docker/distribution](https://github.com/docker/distribution)
from 2.8.1+incompatible to 2.8.2+incompatible.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/docker/distribution/releases">github.com/docker/distribution's
releases</a>.</em></p>
<blockquote>
<h2>v2.8.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Revert registry/client: set <code>Accept: identity</code> header
when getting layers by <a
href="https://github.com/ndeloof"><code>@​ndeloof</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3783">distribution/distribution#3783</a></li>
<li>Parse <code>http</code> forbidden as denied by <a
href="https://github.com/vvoland"><code>@​vvoland</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3914">distribution/distribution#3914</a></li>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2022-28391">CVE-2022-28391</a>
by bumping alpine from 3.14 to 3.16 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> (<a
href="https://redirect.github.com/distribution/distribution/pull/3650">#3650</a>)</li>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2023-2253">CVE-2023-2253</a>
runaway allocation on /v2/_catalog by <a
href="https://github.com/josegomezr"><code>@​josegomezr</code></a> <a
href="521ea3d973"><code>521ea3d9</code></a></li>
<li>Fix panic in inmemory driver by <a
href="https://github.com/wy65701436"><code>@​wy65701436</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3815">distribution/distribution#3815</a></li>
<li>bump up golang version (alternative) by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3903">distribution/distribution#3903</a></li>
<li>Dockerfile: update xx to v1.2.1 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3907">distribution/distribution#3907</a></li>
<li>update to go1.19.9 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3908">distribution/distribution#3908</a></li>
<li>Add code to handle pagination of parts. Fixes max layer size of 10GB
bug by <a
href="https://github.com/DavidSpek"><code>@​DavidSpek</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3893">distribution/distribution#3893</a></li>
<li>Dockerfile: fix filenames of artifacts by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3911">distribution/distribution#3911</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/distribution/distribution/compare/v2.8.1...v2.8.2">https://github.com/distribution/distribution/compare/v2.8.1...v2.8.2</a></p>
<h2>v2.8.2-beta.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2022-28391">CVE-2022-28391</a>
by bumping alpine from 3.14 to 3.16 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> (<a
href="https://redirect.github.com/distribution/distribution/pull/3650">#3650</a>)</li>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2023-2253">CVE-2023-2253</a>
runaway allocation on /v2/_catalog by <a
href="https://github.com/josegomezr"><code>@​josegomezr</code></a> <a
href="521ea3d973"><code>521ea3d9</code></a></li>
<li>Fix panic in inmemory driver by <a
href="https://github.com/wy65701436"><code>@​wy65701436</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3815">distribution/distribution#3815</a></li>
<li>bump up golang version (alternative) by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3903">distribution/distribution#3903</a></li>
<li>Dockerfile: update xx to v1.2.1 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3907">distribution/distribution#3907</a></li>
<li>update to go1.19.9 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3908">distribution/distribution#3908</a></li>
<li>Add code to handle pagination of parts. Fixes max layer size of 10GB
bug by <a
href="https://github.com/DavidSpek"><code>@​DavidSpek</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3893">distribution/distribution#3893</a></li>
<li>Dockerfile: fix filenames of artifacts by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3911">distribution/distribution#3911</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/distribution/distribution/compare/v2.8.1...v2.8.2-beta.2">https://github.com/distribution/distribution/compare/v2.8.1...v2.8.2-beta.2</a></p>
<h2>v2.8.2-beta.1</h2>
<h3><strong>NOTE: This is a pre-release that does not contain any
artifacts!</strong></h3>
<h2>What's Changed</h2>
<ul>
<li>Fix runaway allocation on /v2/_catalog by <a
href="https://github.com/josegomezr"><code>@​josegomezr</code></a> <a
href="521ea3d973"><code>521ea3d9</code></a></li>
<li>Fix CVE-2022-28391 by bumping alpine from 3.14 to 3.16 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3650">distribution/distribution#3650</a></li>
<li>Fix panic in inmemory driver by <a
href="https://github.com/wy65701436"><code>@​wy65701436</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3815">distribution/distribution#3815</a></li>
<li>bump up golang version (alternative) by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3903">distribution/distribution#3903</a></li>
<li>Dockerfile: update xx to v1.2.1 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3907">distribution/distribution#3907</a></li>
<li>update to go1.19.9 by <a
href="https://github.com/thaJeztah"><code>@​thaJeztah</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3908">distribution/distribution#3908</a></li>
<li>Add code to handle pagination of parts. Fixes max layer size of 10GB
bug by <a
href="https://github.com/DavidSpek"><code>@​DavidSpek</code></a> in <a
href="https://redirect.github.com/distribution/distribution/pull/3893">distribution/distribution#3893</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/distribution/distribution/compare/v2.8.1...v2.8.2-beta.1">https://github.com/distribution/distribution/compare/v2.8.1...v2.8.2-beta.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7c354a4b40"><code>7c354a4</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/distribution/issues/3915">#3915</a>
from distribution/2.8.2-release-notes</li>
<li><a
href="a173a9c625"><code>a173a9c</code></a>
Add v2.8.2 release notes</li>
<li><a
href="4894d35ecc"><code>4894d35</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/distribution/issues/3914">#3914</a>
from vvoland/handle-forbidden-28</li>
<li><a
href="f067f66d3d"><code>f067f66</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/distribution/issues/3783">#3783</a>
from ndeloof/accept-encoding-28</li>
<li><a
href="483ad69da3"><code>483ad69</code></a>
registry/errors: Parse http forbidden as denied</li>
<li><a
href="2b0f84df21"><code>2b0f84d</code></a>
Revert &quot;registry/client: set Accept: identity header when getting
layers&quot;</li>
<li><a
href="320d6a141f"><code>320d6a1</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/distribution/issues/3912">#3912</a>
from distribution/2.8.2-beta.2-release-notes</li>
<li><a
href="5f3ca1b2fb"><code>5f3ca1b</code></a>
Add release notes for 2.8.2-beta.2 release</li>
<li><a
href="cb840f63b3"><code>cb840f6</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/distribution/issues/3911">#3911</a>
from thaJeztah/2.8_backport_fix_releaser_filenames</li>
<li><a
href="e884644fff"><code>e884644</code></a>
Dockerfile: fix filenames of artifacts</li>
<li>Additional commits viewable in <a
href="https://github.com/docker/distribution/compare/v2.8.1...v2.8.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/docker/distribution&package-manager=go_modules&previous-version=2.8.1+incompatible&new-version=2.8.2+incompatible)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-17 17:44:59 +02:00
devonh 67d6876857
Move MakeJoin logic to GMSL (#3081) 2023-05-17 00:33:27 +00:00
devonh 0489d16f95
Move json errors over to gmsl (#3080) 2023-05-09 22:46:49 +00:00
devonh a49c9f01e2
Only require room version instead of room info for db.Events() (#3079)
This reduces the API requirements for the Events database to align with
what is actually required.
2023-05-08 19:25:44 +00:00
kegsay 2b34f88fde
Use ProtoEvent where needed instead of EventBuilder (#3075)
They are fundamentally different concepts, so should be represented as
such. Proto events are exchanged in /make_xxx calls over federation, and
made as "fledgling" events in /createRoom and general event sending.
*Building* events is a reasonably complex VERSION SPECIFIC process which
needs amongst other things, auth event providers, prev events, signing
keys, etc.

Requires https://github.com/matrix-org/gomatrixserverlib/pull/379
2023-05-04 11:17:42 +01:00
Devon Hudson d5c11a3c86
Fix flaky test in process context 2023-05-03 18:21:33 -06:00
Devon Hudson 99b143d4d0
Fix flaky test in clientapi 2023-05-03 18:21:10 -06:00
kegsay 6284790f98
Use PDU in even more places (#3074)
- No longer rely on *Event returning from NewEventFrom... functions
 
Requires https://github.com/matrix-org/gomatrixserverlib/pull/377
2023-05-03 10:21:27 +01:00
genofire 9b98e5a102
fix(helm): do not deploy screenshot to cluster (just dashboard) (#3063)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Your Name <your@email.example.org>`

Signed-off-by: genofire <geno+dev@fireorbit.de>
Co-authored-by: kegsay <kegan@matrix.org>
2023-05-02 17:29:21 +01:00
kegsay f5b3144dc3
Use PDU not *Event in HeaderedEvent (#3073)
Requires https://github.com/matrix-org/gomatrixserverlib/pull/376

This has numerous upsides:
 - Less type casting to `*Event` is required.
- Making Dendrite work with `PDU` interfaces means we can swap out Event
impls more easily.
 - Tests which represent weird event shapes are easier to write.

Part of a series of refactors on GMSL.
2023-05-02 15:03:16 +01:00
Devon Hudson 696cbb70b8
Pass federation API to roomserver in PurgeRoom tests 2023-05-01 21:28:10 -06:00
Devon Hudson b00e272e6f
Use new gmsl to use new String() API 2023-04-28 13:31:21 -06:00
Till 9e9617ff84
Add key backup tests (#3071)
Also slightly refactors the functions and methods to rely less on the
req/res pattern we had for polylith.

Returns `M_WRONG_ROOM_KEYS_VERSION` for some endpoints as per the spec
2023-04-28 17:49:38 +02:00
Till 6b47cf0f6a
Remove PerformError (#3066)
This removes `PerformError`, which was needed when we still had
polylith.

This removes quite a bunch of
```go
if err != nil {
	return err
}
if err := res.Error; err != nil {
	return err.JSONResponse()
}
```

Hopefully can be read commit by commit. [skip ci]
2023-04-28 17:46:01 +02:00
kegsay 1432743d1a
Use PDU in more places (#3072) 2023-04-28 16:00:22 +01:00
Devon Hudson d23d0369cc
Pass RoomID to gmsl.PerformJoin 2023-04-27 18:34:43 -06:00
kegsay 6171310307
Use PDU interface (#3070)
We only use it in a few places currently, enough to get things to
compile and run. We should be using it in much more places.

Similarly, in some places we cast []PDU back to []*Event, we need to not
do that. Likewise, in some places we cast PDU to *Event, we need to not
do that. For now though, hopefully this is a start.
2023-04-27 16:35:19 +01:00
Till c6457cd4e5
Add CS API /keys tests (#3069)
This is slightly cheating, as the heavy lifting, with regards to key
generation, is done using `mautrix/go`.
2023-04-27 16:43:28 +02:00
kegsay b189edf4f4
Remove gmsl.HeaderedEvent (#3068)
Replaced with types.HeaderedEvent _for now_. In reality we want to move
them all to gmsl.Event and only use HeaderedEvent when we _need_ to
bundle the version/event ID with the event (seriailsation boundaries,
and even then only when we don't have the room version).

Requires https://github.com/matrix-org/gomatrixserverlib/pull/373
2023-04-27 12:54:20 +01:00
Till 2475cf4b61
Add some roomserver UTs (#3067)
Adds tests for `QueryRestrictedJoinAllowed`, `IsServerAllowed` and
`PerformRoomUpgrade`. Refactors the `QueryRoomVersionForRoom` method to
accept a string and return a `gmsl.RoomVersion` instead of req/resp
structs.
Adds some more caching for `GetStateEvent`

This should also fix #2912 by ignoring state events belonging to other
users.
2023-04-27 08:07:13 +02:00
devonh dd5e47a9a7
Move high level room joining logic to GMSL (#3065)
GMSL PR: https://github.com/matrix-org/gomatrixserverlib/pull/372
2023-04-27 00:43:46 +00:00
devonh ed19efc5d7
Move fedclient interface over to gmsl (#3061)
Companion PR: https://github.com/matrix-org/gomatrixserverlib/pull/366
2023-04-24 16:23:25 +00:00
kegsay 4679098a64
Use IRoomVersion (#3064)
This is a step towards allowing arbitrary room version impls.
2023-04-24 11:50:37 +01:00
kegsay 1647213fac
Implement new RoomVersionImpl API (#3062)
As outlined in https://github.com/matrix-org/gomatrixserverlib/pull/368

The main change Dendrite side is that `RoomVersion` no longer has any
methods on it. Instead, you need to bounce via `gmsl.GetRoomVersion`.

It's very interesting to see where exactly Dendrite cares about this.
For some places it's creating events (fine) but others are way more
specific. Those areas will need to migrate to GMSL at some point.
2023-04-21 17:06:29 +01:00
kegsay 71eeccf34a
refactor: funnel event creation through room versions (#3060)
In preparation of interfacing up the room version value.
2023-04-20 19:07:31 +01:00
kegsay 72285b2659
refactor: update GMSL (#3058)
Sister PR to https://github.com/matrix-org/gomatrixserverlib/pull/364

Read this commit by commit to avoid going insane.
2023-04-19 15:50:33 +01:00
Till 9fa39263c0
Add sync API db tests (#3043)
Co-authored-by: kegsay <kegan@matrix.org>
2023-04-17 10:25:33 +01:00
devonh f66862958d
Remove event building duplication & push to GMSL (#3056)
Removes event building duplication and moves the funcionality into GMSL
since all the sub-steps are already there.
2023-04-14 15:03:07 +00:00
dependabot[bot] 914e6145a5
Bump nokogiri from 1.13.10 to 1.14.3 in /docs (#3055)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.10
to 1.14.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/releases">nokogiri's
releases</a>.</em></p>
<blockquote>
<h2>1.14.3 / 2023-04-11</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to address CVE-2023-29469,
CVE-2023-28484, and one other security-related issue. See <a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-pxvg-2qj5-37jq">GHSA-pxvg-2qj5-37jqGHSA-pxvg-2qj5-37jq</a>
for more information.</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.10.4">v2.10.4</a>
from v2.10.3.</li>
</ul>
<hr />
<p>sha256 checksums:</p>

<pre><code>9cc53dd8d92868a0f5bcee44396357a19f95e32d8b9754092622a25bc954c60c
nokogiri-1.14.3-aarch64-linux.gem
320fa1836b8e59e86a804baee534893bcf3b901cc255bbec6d87f3dd3e431610
nokogiri-1.14.3-arm-linux.gem
67dd4ac33a8cf0967c521fa57e5a5422db39da8a9d131aaa2cd53deaa12be4cd
nokogiri-1.14.3-arm64-darwin.gem
13969ec7f41d9cff46fc7707224c55490a519feef7cfea727c6945c5b444caa2
nokogiri-1.14.3-java.gem
9885085249303461ee08f9a9b161d0a570391b8f5be0316b3ac5a6d9a947e1e2
nokogiri-1.14.3-x64-mingw-ucrt.gem
997943d7582a23ad6e7a0abe081d0d40d2c1319a6b2749f9b30fd18037f0c38a
nokogiri-1.14.3-x64-mingw32.gem
58c30b763aebd62dc4222385509d7f83ac398ee520490fadc4b6d7877e29895a
nokogiri-1.14.3-x86-linux.gem
e1d58a5c56c34aab71b00901a969e19bf9f7322ee459b4e9380f433213887c04
nokogiri-1.14.3-x86-mingw32.gem
f0a1ed1460a91fd2daf558357f4c0ceac6d994899da1bf98431aeda301e4dc74
nokogiri-1.14.3-x86_64-darwin.gem
e323a7c654ef846e64582fb6e26f6fed869a96753f8e048ff723e74d8005cb11
nokogiri-1.14.3-x86_64-linux.gem
3b1cee0eb8879e9e25b6dd431be597ca68f20283b0d4f4ca986521fad107dc3a
nokogiri-1.14.3.gem
</code></pre>
<h2>1.14.2 / 2023-02-13</h2>
<h3>Fixed</h3>
<ul>
<li>Calling <code>NodeSet#to_html</code> on an empty node set no longer
raises an encoding-related exception. This bug was introduced in v1.14.0
while fixing <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2649">#2649</a>.
[<a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2784">#2784</a>]</li>
</ul>
<hr />
<p>sha256 checksums:</p>
<pre
lang="text"><code>966acf4f6c1fba10518f86498141cf44265564ac5a65dcc8496b65f8c354f776
nokogiri-1.14.2-aarch64-linux.gem
8a3a35cadae4a800ddc0b967394257343d62196d9d059b54e38cf067981db428
nokogiri-1.14.2-arm-linux.gem
81404cd014ecb597725c3847523c2ee365191a968d0b5f7d857e03f388c57631
nokogiri-1.14.2-arm64-darwin.gem
0a39222af14e75eb0243e8d969345e03b90c0e02b0f33c61f1ebb6ae53538bb5
nokogiri-1.14.2-java.gem
62a18f9213a0ceeaf563d1bc7ccfd93273323c4356ded58a5617c59bc4635bc5
nokogiri-1.14.2-x64-mingw-ucrt.gem
54f6ac2c15a7a88f431bb5e23f4616aa8fc97a92eb63336bcf65b7050f2d3be0
nokogiri-1.14.2-x64-mingw32.gem
c42fa0856f01f901954898e28c3c2b4dce0e843056b1b126f441d06e887e1b77
nokogiri-1.14.2-x86-linux.gem
f940d9c8e47b0f19875465376f2d1c8911bc9489ac9a48c124579819dc4a7f19
nokogiri-1.14.2-x86-mingw32.gem
2508978f5ca28944919973f6300f0a7355fbe72604ab6a6913f1630be1030265
nokogiri-1.14.2-x86_64-darwin.gem
bc6405e1f3ddac6e401f82d775f1c0c24c6e58c371b3fadaca0596d5d511e476
nokogiri-1.14.2-x86_64-linux.gem
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md">nokogiri's
changelog</a>.</em></p>
<blockquote>
<h2>1.14.3 / 2023-04-11</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to address CVE-2023-29469,
CVE-2023-28484, and one other security-related issue. See <a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-pxvg-2qj5-37jq">GHSA-pxvg-2qj5-37jqGHSA-pxvg-2qj5-37jq</a>
for more information.</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.10.4">v2.10.4</a>
from v2.10.3.</li>
</ul>
<h2>1.14.2 / 2023-02-13</h2>
<h3>Fixed</h3>
<ul>
<li>Calling <code>NodeSet#to_html</code> on an empty node set no longer
raises an encoding-related exception. This bug was introduced in v1.14.0
while fixing <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2649">#2649</a>.
[<a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2784">#2784</a>]</li>
</ul>
<h2>1.14.1 / 2023-01-30</h2>
<h3>Fixed</h3>
<ul>
<li>Serializing documents now works again with pseudo-IO objects that
don't support IO's encoding API (like rubyzip's
<code>Zip::OutputStream</code>). This was a regression in v1.14.0 due to
the fix for <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/752">#752</a>
in <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2434">#2434</a>,
and was not completely fixed by <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2753">#2753</a>.
[<a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2773">#2773</a>]</li>
<li>[CRuby] Address compiler warnings about <code>void*</code> casting
and old-style C function definitions.</li>
</ul>
<h2>1.14.0 / 2023-01-12</h2>
<h3>Notable Changes</h3>
<h4>Ruby</h4>
<p>This release introduces native gem support for Ruby 3.2. (Also see
&quot;Technical note&quot; under &quot;Changed&quot; below.)</p>
<p>This release ends support for:</p>
<ul>
<li>Ruby 2.6, for which <a
href="https://www.ruby-lang.org/en/downloads/branches/">upstream support
ended 2022-04-12</a>.</li>
<li>JRuby 9.3, which is not fully compatible with Ruby 2.7+</li>
</ul>
<h4>Faster, more reliable installation: Native Gem for
<code>aarch64-linux</code> (aka <code>linux/arm64/v8</code>)</h4>
<p>This version of Nokogiri ships <em>official</em> native gem support
for the <code>aarch64-linux</code> platform, which should support AWS
Graviton and other ARM64 Linux platforms. Please note that glibc &gt;=
2.29 is required for aarch64-linux systems, see <a
href="https://nokogiri.org/#supported-platforms">Supported Platforms</a>
for more information.</p>
<h4>Faster, more reliable installation: Native Gem for
<code>arm-linux</code> (aka <code>linux/arm/v7</code>)</h4>
<p>This version of Nokogiri ships <em>experimental</em> native gem
support for the <code>arm-linux</code> platform. Please note that glibc
&gt;= 2.29 is required for arm-linux systems, see <a
href="https://nokogiri.org/#supported-platforms">Supported Platforms</a>
for more information.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e8d2f4a829"><code>e8d2f4a</code></a>
version bump to v1.14.3</li>
<li><a
href="59fbc7b6d5"><code>59fbc7b</code></a>
doc: update CHANGELOG for v1.14.3</li>
<li><a
href="347eacbeea"><code>347eacb</code></a>
Merge pull request <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2852">#2852</a>
from sparklemotion/flavorjones-libxml2-2.10.4-backport</li>
<li><a
href="36b0b3355d"><code>36b0b33</code></a>
dep: update libxml2 to 2.10.4 from 2.10.3</li>
<li><a
href="ac83e6ee70"><code>ac83e6e</code></a>
test: update behavior of namespaces in HTML4</li>
<li><a
href="2cf4996c52"><code>2cf4996</code></a>
test: make default GC behavior &quot;normal&quot;</li>
<li><a
href="1580121eea"><code>1580121</code></a>
version bump to v1.14.2</li>
<li><a
href="530947753e"><code>5309477</code></a>
Merge pull request <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/2791">#2791</a>
from sparklemotion/2784-encoding-empty-strings-v1.14.x</li>
<li><a
href="975ae491c4"><code>975ae49</code></a>
doc: update CHANGELOG</li>
<li><a
href="f13cdb4640"><code>f13cdb4</code></a>
fix: empty node set serialization when document encoding is nil</li>
<li>Additional commits viewable in <a
href="https://github.com/sparklemotion/nokogiri/compare/v1.13.10...v1.14.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nokogiri&package-manager=bundler&previous-version=1.13.10&new-version=1.14.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-14 12:36:07 +01:00
dependabot[bot] 2d822356ff
Bump commonmarker from 0.23.7 to 0.23.9 in /docs (#3054)
Bumps [commonmarker](https://github.com/gjtorikian/commonmarker) from
0.23.7 to 0.23.9.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gjtorikian/commonmarker/releases">commonmarker's
releases</a>.</em></p>
<blockquote>
<h2>v0.23.9</h2>
<h2>What's Changed</h2>
<ul>
<li>Update to 0.29.0.gfm.11 by <a
href="https://github.com/anticomputer"><code>@​anticomputer</code></a>
in <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/236">gjtorikian/commonmarker#236</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.8...v0.23.9">https://github.com/gjtorikian/commonmarker/compare/v0.23.8...v0.23.9</a></p>
<h2>v0.23.8</h2>
<h2>What's Changed</h2>
<ul>
<li>Update cmark-upstream to <code>0.29.0.gfm.9</code> by <a
href="https://github.com/smockle"><code>@​smockle</code></a> in <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/227">gjtorikian/commonmarker#227</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/smockle"><code>@​smockle</code></a> made
their first contribution in <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/227">gjtorikian/commonmarker#227</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.7...v0.23.8">https://github.com/gjtorikian/commonmarker/compare/v0.23.7...v0.23.8</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/gjtorikian/commonmarker/blob/main/CHANGELOG.md">commonmarker's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v1.0.0.pre9">v1.0.0.pre9</a>
(2023-03-28)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v1.0.0.pre8...v1.0.0.pre9">Full
Changelog</a></p>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>Updates from upstream <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/235">#235</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
<li>Bump comrak from 0.16.0 to 0.17.1 <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/234">#234</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
<li>Bump magnus from 0.5.1 to 0.5.2 <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/233">#233</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
<li>Add ability to load <code>tmtheme</code>s from a folder <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/232">#232</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
<li>Bump magnus from 0.5.0 to 0.5.1 <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/231">#231</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
<li>Bump magnus from 0.4.4 to 0.5.0 <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/230">#230</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
<li>Test the new integrated rb-sys <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/228">#228</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v1.0.0.pre8">v1.0.0.pre8</a>
(2023-03-09)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.8...v1.0.0.pre8">Full
Changelog</a></p>
<p><strong>Closed issues:</strong></p>
<ul>
<li>Something changed in how header anchors are named in the output HTML
<a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/229">#229</a></li>
<li>Problem with CommonMarker on an Azure VM <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/226">#226</a></li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v0.23.8">v0.23.8</a>
(2023-01-31)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v1.0.0.pre7...v0.23.8">Full
Changelog</a></p>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v1.0.0.pre7">v1.0.0.pre7</a>
(2023-01-26)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.7...v1.0.0.pre7">Full
Changelog</a></p>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>Bump comrak from 0.15.0 to 0.16.0 <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/225">#225</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
<li>Change <code>unsafe_</code> to <code>unsafe</code> <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/220">#220</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
<li>Clarify syntax highlighter plugin usage in README <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/218">#218</a>
(<a href="https://github.com/DannyBen">DannyBen</a>)</li>
<li>Fix a couple of misleading README points <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/215">#215</a>
(<a href="https://github.com/DannyBen">DannyBen</a>)</li>
<li>remove gemspec <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/214">#214</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
<li>Add shortcodes/emoji <a
href="https://redirect.github.com/gjtorikian/commonmarker/pull/210">#210</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="42cfc90251"><code>42cfc90</code></a>
Merge pull request <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/236">#236</a>
from anticomputer/update-to-0.29.0.gfm.10</li>
<li><a
href="d793fbf451"><code>d793fbf</code></a>
Update cmark-upstream to <a
href="https://github.com/github/cmark-gfm/commit/1e230827a">https://github.com/github/cmark-gfm/commit/1e230827a</a>...</li>
<li><a
href="4e4588f2e0"><code>4e4588f</code></a>
Update Makefile for export header consolidation</li>
<li><a
href="2eb8ca8f2f"><code>2eb8ca8</code></a>
Update cmark-upstream to <a
href="https://github.com/github/cmark-gfm/commit/c8dcdc71c">https://github.com/github/cmark-gfm/commit/c8dcdc71c</a>...</li>
<li><a
href="bbb49db722"><code>bbb49db</code></a>
HtmlRenderer: don't nest &lt;strong&gt;</li>
<li><a
href="f303e6bae7"><code>f303e6b</code></a>
💎 release 0.23.9</li>
<li><a
href="d6fe4c8be4"><code>d6fe4c8</code></a>
Update cmark-upstream to <a
href="https://github.com/github/cmark-gfm/commit/dcf6b3862">https://github.com/github/cmark-gfm/commit/dcf6b3862</a>...</li>
<li><a
href="94c0af96f0"><code>94c0af9</code></a>
Merge pull request <a
href="https://redirect.github.com/gjtorikian/commonmarker/issues/227">#227</a>
from gjtorikian/update-to-0.29.0.gfm.9</li>
<li><a
href="5249f70a97"><code>5249f70</code></a>
💎 release 0.23.8</li>
<li><a
href="85c205798f"><code>85c2057</code></a>
Added aria-label changes to test-footnotes.rb</li>
<li>Additional commits viewable in <a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.7...v0.23.9">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=commonmarker&package-manager=bundler&previous-version=0.23.7&new-version=0.23.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-14 12:35:53 +01:00
Till c45d8cd688
Add pushrules tests (#3044)
partly takes care of https://github.com/matrix-org/dendrite/issues/2870
by making sure that rule IDs don't start with a dot.

Co-authored-by: kegsay <kegan@matrix.org>
2023-04-14 12:35:27 +01:00
kegsay ca63b414da
Update GMSL: use static Check functions (#3052)
Sister PR to https://github.com/matrix-org/gomatrixserverlib/pull/359 

A nice side effect is that we don't need to re-parse the events in some
cases.
2023-04-14 12:32:42 +01:00
Marcelina Hołub 94e81cc3f3
chore(linter): remove deprecated linters (#3046)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have justified why this PR doesn't need tests - linter setup
changes aren't something testable
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Marcelina Hołub <mholub@tutanota.com>`

Signed-off-by: Marcelina Hołub <mholub@tutanota.com>
Co-authored-by: kegsay <kegan@matrix.org>
2023-04-06 14:20:05 +01:00
dependabot[bot] ee57400afd
Bump github.com/docker/docker from 20.10.19+incompatible to 20.10.24+incompatible (#3047)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from
20.10.19+incompatible to 20.10.24+incompatible.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/docker/docker/releases">github.com/docker/docker's
releases</a>.</em></p>
<blockquote>
<h2>v20.10.24</h2>
<h2>20.10.24</h2>
<h3>Bug fixes and enhancements</h3>
<ul>
<li>Fixed a number of issues that can cause Swarm encrypted overlay
networks
to fail to uphold their guarantees, addressing <a
href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-28841">CVE-2023-28841</a>,
<a
href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-28840">CVE-2023-28840</a>,
and
<a
href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-28842">CVE-2023-28842</a>.
<ul>
<li>A lack of kernel support for encrypted overlay networks now reports
as an error.</li>
<li>Encrypted overlay networks are eagerly set up, rather than waiting
for
multiple nodes to attach.</li>
<li>Encrypted overlay networks are now usable on Red Hat Enterprise
Linux 9
through the use of the <code>xt_bpf</code> kernel module.</li>
<li>Users of Swarm overlay networks should review <a
href="https://github.com/moby/moby/security/advisories/GHSA-vwm3-crmr-xfxw">GHSA-vwm3-crmr-xfxw</a>
to ensure that unintentional exposure has not occurred.</li>
</ul>
</li>
<li>Upgrade github.com/containerd/fifo to v1.1.0 to fix a potential
panic <a
href="https://redirect.github.com/moby/moby/pull/45242">moby/moby#45216</a>.</li>
<li>Fix missing Bash completion for installed cli-plugins <a
href="https://redirect.github.com/docker/cli/pull/4091">docker/cli#4091</a>.</li>
</ul>
<h3>Packaging Updates</h3>
<ul>
<li>Update Go runtime to <a
href="https://go.dev/doc/devel/release#go1.19.minor">1.19.7</a>.</li>
<li>Update Docker Buildx to <a
href="https://github.com/docker/buildx/releases/tag/v0.10.4">v0.10.4</a>.</li>
<li>Update containerd to <a
href="https://github.com/containerd/containerd/releases/tag/v1.6.20">v1.6.20</a>.</li>
<li>Update runc to <a
href="https://github.com/opencontainers/runc/releases/tag/v1.1.5">v1.1.5</a>.</li>
</ul>
<h2>v20.10.23</h2>
<h3>Bug fixes and enhancements</h3>
<ul>
<li>
<p>Fix an issue where <code>docker build</code> would fail when using
<code>--add-host=host.docker.internal:host-gateway</code>
with BuildKit enabled <a
href="https://redirect.github.com/moby/moby/pull/44650">moby/moby#44650</a>.</p>
</li>
<li>
<p>Revert seccomp: block socket calls to <code>AF_VSOCK</code> in
default profile <a
href="https://redirect.github.com/moby/moby/pull/44712">moby/moby#44712</a>.
This change, while favorable from a security standpoint, caused a change
in behavior for some use-cases. As such, we are reverting it to ensure
stability and compatibility for the affected users.</p>
<p>However, users of <code>AF_VSOCK</code> in containers should
recognize that this
(special) address family is not currently namespaced in any version of
the Linux kernel, and may result in unexpected behavior, like containers
communicating directly with host hypervisors.</p>
<p>Future releases, will filter <code>AF_VSOCK</code>. Users who need to
allow containers
to communicate over the unnamespaced <code>AF_VSOCK</code> will need to
turn off seccomp
confinement or set a custom seccomp profile.</p>
</li>
</ul>
<h3>Packaging Updates</h3>
<ul>
<li>Update Docker Compose to <a
href="https://github.com/docker/compose/releases/tag/v2.15.1">v2.15.1</a>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5d6db84223"><code>5d6db84</code></a>
Merge pull request from GHSA-232p-vwff-86mp</li>
<li><a
href="d2bc43a75b"><code>d2bc43a</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/45242">#45242</a>
from neersighted/go1.19.7/20.10</li>
<li><a
href="9aa5d55a8b"><code>9aa5d55</code></a>
update to go1.19.7</li>
<li><a
href="83679bb638"><code>83679bb</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/45216">#45216</a>
from corhere/backport-20.10/containerd-fifo_v1.1</li>
<li><a
href="b4f0442da2"><code>b4f0442</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/45219">#45219</a>
from vvoland/test-windows-execstartfails-2010</li>
<li><a
href="ba043e8691"><code>ba043e8</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/44990">#44990</a>
from thaJeztah/20.10_backport_update_go1.19</li>
<li><a
href="b56fe59505"><code>b56fe59</code></a>
integration-cli: Enable TestExecStartFails on Windows</li>
<li><a
href="d9433ee096"><code>d9433ee</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/docker/issues/45197">#45197</a>
from vvoland/integration-restart-race-2010</li>
<li><a
href="a9c02c238f"><code>a9c02c2</code></a>
Upgrade containerd/fifo to v1.1.0</li>
<li><a
href="bbec6704dc"><code>bbec670</code></a>
[20.10] vendor: libnetwork c5aa85f9b25f0acaec8591ced679cb9fb5b9e32c</li>
<li>Additional commits viewable in <a
href="https://github.com/docker/docker/compare/v20.10.19...v20.10.24">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/docker/docker&package-manager=go_modules&previous-version=20.10.19+incompatible&new-version=20.10.24+incompatible)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-06 09:55:26 +01:00
kegsay 0db43f13a6
refactor: use latest GMSL which splits fed client from matrix room logic (#3051)
Part of a series of refactors on GMSL.
2023-04-06 09:55:01 +01:00
kegsay e093005bc2
ci: don't use go get, use go install (#3048)
Otherwise CI can fail with:
```
go: go.mod file not found in current directory or any parent directory.
	'go get' is no longer supported outside a module.
	To build and install a command, use 'go install' with a version,
	like 'go install example.com/cmd@latest'
	For more information, see https://golang.org/doc/go-get-install-deprecation
	or run 'go help get' or 'go help install'.
```
2023-04-05 14:35:55 +01:00
kegsay 3691423626
Move GMSL client types to Dendrite (#3045)
GMSL is intended for Federation only. Sister PR to
https://github.com/matrix-org/gomatrixserverlib/pull/357
2023-04-04 17:16:53 +00:00
Boris Rybalkin 985298cfc4
app service unix socket support (#3022)
This is the last part of unix socket support to talk to app servers, go
based app services already support unix sockets:

5a68173fe3
```
appservice:
  # The address that the homeserver can use to connect to this appservice.
  address: unix:///var/snap/matrix/current/whatsapp.socket

  # The hostname and port where this appservice should listen.
  hostname: /var/snap/matrix/current/whatsapp.socket
  port: 0
```

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Boris Rybalkin <ribalkin@gmail.com>`
2023-04-04 09:42:46 +02:00
Till 682a7d0a66
Add tests for /turnServer, /capabilities and /3pid/ (#3038)
Threepid seems to be pretty out of date, several missing endpoints.
Should also fix #3037, where we were still listening on the `/unstable`
prefix, while Element Web uses `/r0`
2023-04-03 21:42:46 +02:00
Till 560ba46272
Add tests for CSAPI membership changes (#3034)
Adds some more checks in regards to power levels, uses a less heavy way
to get the membership of a user, avoids asking the database for the room
version, since it will be queried later. [skip ci]
2023-04-03 21:21:06 +02:00
Till c2db38d295
Add user profile tests, refactor user API methods (#3030)
This adds tests for `/profile`.
Also, as a first change in this regard, refactors the methods defined on
the `UserInternalAPI` to not use structs as the request/response
parameters.
2023-04-03 20:19:26 +02:00
Aiden Leong 4cb9cd7842
hard code path of README.md (#3035)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

I was reading on
https://matrix-org.github.io/dendrite/faq#is-dendrite-feature-complete.
The link is dead due to relative path.

🍻

Signed-off-by: `Aiden Leong <aiden.leong@aibsd.com>`
2023-04-03 18:08:13 +01:00
Till Faelligen 10ef1fb11a
Remove sync.Once when setting up server notice sender 2023-04-03 15:08:33 +02:00
Till Faelligen 675926967d
Update Helm README
[skip ci]
2023-04-03 09:08:36 +02:00
genofire 8223e1f2e1
fix(helm): improve documentation and grafana dashboard (#2992)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

---

* **docs**: enabling of metrics in there config is needed
* **dashboard**: since the imported dashboard was created:
  * many metrics was dropped by dendrite (mainly #2967)
  * grafana has new version of diagrams ...

---------

Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>

[skip ci]
2023-04-03 09:04:59 +02:00
Rhea Danzey 01dd02dad2
chart - Add configuration for extra volumes / volume mounts (#3042)
Adds configuration for additional volumes / volumeMounts to the Dendrite
pod to inject configuration / secrets outside of the chart's templates

### Pull Request Checklist

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests - Helm chart changes
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: Rhea Danzey <rdanzey@element.io>

---------

Signed-off-by: Rhea Danzey <rdanzey@element.io>
Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>

[skip ci]
2023-04-03 09:00:32 +02:00
George Antoniadis 44ed0a3279
add deployment strategy option to helm chart (#3021)
@S7evinK minor update to the helm chart on top of you existing fixes to
allow setting the update strategy as the default `RollingUpdate` one is
a bit annoying if using `ReadWriteOnce` volumes for media. Hope this
makes sense.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] ~~I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests~~ Haven't touched any go files.
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `George Antoniadis <george@noodles.gr>`
[skip ci]
2023-04-03 08:24:47 +02:00
Till 2854ffeb7d
Add CS API device tests (#3029)
Adds tests for
- `/devices`
- `/delete_devices` (also adds UIA)
2023-03-31 10:15:01 +02:00
Rhea Danzey 28d3e296a8
Rdanzey/helm-fixes-existing-db-secrets (#3033)
Fixes some Helm templating issues when setting up a deployment with an
existing database / signing keys.

- Allows for `.Values.postgresql.enabled: false` as long as
`.Values.global.dendrite_config.database.connection_string` is defined
- Allows for '.Values.signing_key.create: false' if
`.Values.signing_key.existingSecret` is set

Also fixes an error in the template resulting in profiling port not
being set correctly:

```
Error: template: dendrite-meta/charts/dendrite/templates/deployment.yaml:60:35: executing "dendrite-meta/charts/dendrite/templates/deployment.yaml" at <$.Values.global.profiling.port>: nil pointer evaluating interface {}.port
```

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
  - Helm template fixes, no golang changes
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: Rhea Danzey <rdanzey@element.io>

---------

Signed-off-by: Rhea Danzey <rdanzey@element.io>
Co-authored-by: Till Faelligen <2353100+S7evinK@users.noreply.github.com>
2023-03-28 08:30:19 +02:00
Devon Hudson f4104b4b5d
Pinecone-demo: Wait on dendrite before shutting down 2023-03-27 17:19:53 -06:00
Devon Hudson 69e3bd82a9
Add dendrite-demo-pinecone cypress tests 2023-03-27 07:57:30 -06:00
Till fa7710315a
Add tests for the Dendrite admin APIs (#3028)
Contains a breaking change, since the endpoints `/_dendrite/admin/evacuateRoom/{roomID}` and `/_dendrite/admin/evacuateUser/{userID}` are now using `POST` instead of `GET`
2023-03-27 15:39:33 +02:00
Till e8b2162a01
Add /search tests (#3025) 2023-03-27 11:26:52 +02:00
Till aa1bda4c58
Add AS invite test, fix issue with invitations being processed twice (#3020)
The AS roomserver consumer would receive the events twice, one time as
type `OutputTypeNewInviteEvent` and the other time as
`OutputTypeNewRoomEvent`. 

[skip ci]
2023-03-27 11:26:26 +02:00
Till Faelligen e2d2482ca6
Get the logs for dendrite when installing the chart 2023-03-27 11:07:30 +02:00
Alex Kirk 05f72fc4be
Update docs and sample config for the relay_api (#3011)
This adds an empty `relay_api` section to the sample configuration. For
SQLite environments, or others where a `database.connection_string` is
needed for each section, there should be an entry in the configuration
sample as a basis.

This PR also changes the "Configuring Dendrite" documentation in that
respect.

The requirement was introduced in #2917. When upgrading dendrite, it
will complain about `relay_api.database.connection_string` not being
configured.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Alex Kirk <akirk@users.noreply.github.com>`
2023-03-27 10:55:36 +02:00
Till 234ed603e6
Move every db.Prepare to sqlutil.Statementlist, remove trace driver (#3026)
Doesn't buy us much, but makes everything a bit more consistent.

Also removes the SQL trace driver, as it is unused and the output is
hard to read anyway.
2023-03-23 13:52:53 +01:00
Till Faelligen cb18ba0230
Upload covdatafiles for each server 2023-03-22 17:36:33 +01:00
Till Faelligen 14085d30ac
Update workflow to not use commas when joining names 2023-03-22 16:01:12 +01:00
Till Faelligen a4400bdd76
Sytest coverage file 2023-03-22 14:58:36 +01:00
Till Faelligen b741d38e10
Update Workflow 2023-03-22 14:51:18 +01:00
Till Faelligen 6948d16527
Update Go, use go tool covdata for coverage files? 2023-03-22 14:50:21 +01:00
Till 5e85a00cb3
Remove BaseDendrite (#3023)
Removes `BaseDendrite` to, hopefully, make testing and composing of
components easier in the future.
2023-03-22 09:21:32 +01:00
Till Faelligen ec6879e5ae
Update GMSL to fix #3013 2023-03-21 16:04:51 +01:00
Till Faelligen 0459d2b9e5
Make "m.upload.size" optional 2023-03-20 09:24:00 +01:00
Till 5579121c6f
Preparations for removing BaseDendrite (#3016)
Preparations to actually remove/replace `BaseDendrite`.
Quite a few changes:
- SyncAPI accepts an `fulltext.Indexer` interface (fulltext is removed
from `BaseDendrite`)
- Caches are removed from `BaseDendrite`
- Introduces a `Router` struct (likely to change)
  - also fixes #2903
- Introduces a `sqlutil.ConnectionManager`, which should remove
`base.DatabaseConnection` later on
- probably more
2023-03-17 11:09:45 +00:00
Boris Rybalkin d88f71ab71
simplify unix socket permission format (#3014)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Boris Rybalkin <ribalkin@gmail.com>`
2023-03-16 08:51:21 +01:00
Till Faelligen 2c58bab6a8
Fix UTs on x86 2023-03-15 08:21:00 +01:00
Till 74dc54684b
Version 0.12.0 (#3009) 2023-03-13 20:02:57 +01:00
Till 232aef016c
Add basic runtime tracing (#2996)
This allows us in almost all places to use regions to further trace down
long running tasks.
Also removes an unused function.
2023-03-13 16:45:14 +01:00
Till 689b5ee72f
Change default stats reporting endpoint (#3007)
It's the same instance we report to, only using the subdomain.
2023-03-10 12:27:08 +01:00
Till c7303cbf76
Update dependencies (#3006)
In preparation for a new release, let's also update a few dependencies.
2023-03-10 10:32:50 +01:00
Till 70322699ab
Unset RoomServerEvent, since we can't be sure that Set actually updates the cached entry (#3002)
This should deflake UTs and be more correct in terms of getting
`Events`.
`Events` tries to fetch the event from the cache first and may get an
unredacted event from it, while it should already be redacted.
2023-03-09 09:52:13 +01:00
Till Faelligen baef523cb0
Fix invalid roomNID returned 2023-03-07 15:35:08 +01:00
Till Faelligen 11a3fcc6cb
RoomServerEvents are mutable, given they can be redacted 2023-03-06 17:58:08 +01:00
Till Faelligen a684b850b9
Actually ignore the error if we were able to backfill events 2023-03-06 17:45:21 +01:00
Till 7d83f8b633
Add tests for UpdateRelations (#2999)
This also fixes an issue regarding updates to relations for invalid
events, which could result in us retrying said event over and over
again, if we fail to unmarshal the event to
`gomatrixserverlib.RelationContent`, this was discovered by
`@sleroq:virto.community`
2023-03-06 12:43:59 +01:00
Tim McCormack 7fc839f751
Update admin-promotion instructions; clarify ID for evacuation (#2997)
Table name has changed since instructions were written.

There's probably a better way to describe how to get the internal room
ID than I've attempted here, so feel free to adjust as needed. (It may
even be good to show an example of what an internal room ID looks like,
e.g. `!nc93825:example.com`)

### Pull Request Checklist

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
  * Doc-only change
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)

Signed-off-by: `Tim McCormack <cortex@brainonfire.net>`

---------

Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
2023-03-05 18:42:38 +01:00
Till Faelligen 56b28b01db
Update the cache with the redacted event 2023-03-03 14:49:41 +01:00
Till 9bcd0a2105
Make redaction check easier to read (#2995)
We need to check the redaction PL in Dendrite, if we do it in GMSL, we
end up not sending the event to the output stream because it will be
rejected.

---------

Co-authored-by: kegsay <kegan@matrix.org>
2023-03-03 14:03:17 +01:00
Robin Westerik 7cde99a7a7
Updated instructions and references to monolith to their new names (#2994)
Currently, the documentation makes use of the old names for the binary
and configuration files. This updates the documentation so that users
can follow the guide without issues again.
These changes don't require any go unit tests because it does not modify
any golang code.

Signed-off-by: `Robin Westerik <gh@westerik.me>`
2023-03-03 10:20:53 +01:00
Boris Rybalkin 6b1c9eafa9
unix socket support (#2974)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Boris Rybalkin <ribalkin@gmail.com>`

I need this for Syncloud project (https://github.com/syncloud/platform)
where I run multiple apps behind an nginx on the same RPi like device so
unix socket is very convenient to not have port conflicts between apps.
Also someone opened this Issue:
https://github.com/matrix-org/dendrite/issues/2924

---------

Co-authored-by: kegsay <kegan@matrix.org>
Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
2023-03-01 22:57:30 +01:00
Till 6c20f8f742
Refactor StoreEvent, add MaybeRedactEvent, create an EventDatabase (#2989)
This PR changes the following:
- `StoreEvent` now only stores an event (and possibly prev event),
instead of also doing redactions
- Adds a `MaybeRedactEvent` (pulled out from `StoreEvent`), which should
be called after storing events
- a few other things
2023-03-01 17:06:47 +01:00
Till Faelligen 1aa70b0f56
Fix UTs 2023-03-01 15:09:10 +01:00
Till Faelligen f1ccfcf150
Only run CI if there are changes to go files or the workflow [skip ci] 2023-02-28 15:35:53 +01:00
Till Faelligen 086e205eba
Deploy on gh-pages push 2023-02-28 15:15:19 +01:00
genofire 7fff7cd2ac
feat(helm): add prometheus resources to monitor (#2958)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

---

I do not know, how you run helm-docs ....


otherwise i would like to add somewhere:
````markdown
* Works well with [Prometheus Operator](https://prometheus-operator.dev/) ([Helmchart](https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack)) and there setup of [Grafana](https://grafana.com/grafana/), by enabling following values:
```yaml
prometheus:
  servicemonitor:
    enabled: true
    labels:
      release: "kube-prometheus-stack"
  rules:
    enabled: true # will deploy alert rules
    additionalLabels:
      release: "kube-prometheus-stack"
grafana:
  dashboards:
    enabled: true # will deploy default dashboards 
```
PS: The labels `release=kube-prometheus-stack` is setup with the helmchart of the Prometheus Operator. For Grafana Dashboards it maybe need scan enable to scan in correct namespaces (or ALL), enabled by `sidecar.dashboards.searchNamespace` in [Helmchart of grafana](https://artifacthub.io/packages/helm/grafana/grafana) (which is part of PrometheusOperator, so `grafana.sidecar.dashboards.searchNamespace`) 
````

Maybe also put somewhere the Screenshot of that Grafana Dashboard:
https://grafana.com/grafana/dashboards/13916-dendrite/

---


@S7evinK do you take a look?

Signed-off-by: genofire <geno+dev@fireorbit.de>
2023-02-28 14:18:26 +01:00
Devon Hudson eddf31f915
Fix lint error 2023-02-24 15:49:51 -07:00
Devon Hudson b28406c7d0
Tweaks to pinecone demo to shutdown more cleanly 2023-02-24 15:41:47 -07:00
Till Faelligen 3d31b131fc
Cache all the things 2023-02-24 11:45:01 +01:00
Till ad07b169b8
Refactor StoreEvent and create a new RoomDatabase interface (#2985)
This PR changes a few things:
- It pulls out the creation of several NIDs from the `StoreEvent`
function to make the functions more reusable
- Uses more caching when using those NIDs to avoid DB round trips
2023-02-24 09:40:20 +01:00
David Schneider e6aa0955ff
Unify logging by using logrus for jetstream logs (#2976)
I guess tests for the logging is rather unusual so I omitted tests for
this change.

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `David Schneider <dsbrng25b@gmail.com>`

---------

Signed-off-by: David Schneider <dsbrng25b@gmail.com>
2023-02-24 08:56:53 +01:00
Yoann N d34277a6c0
Update README sample config link (#2987)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately
2023-02-23 09:07:20 +01:00
Till Faelligen c8ca23acdb
Fix building Element web in CI 2023-02-20 15:28:45 +01:00
Till 7f114cc538
Fix issue where device keys are removed if a device ID is reused (#2982)
Fixes https://github.com/matrix-org/dendrite/issues/2980
2023-02-20 15:26:09 +01:00
Till 4594233f89
Merge keyserver & userapi (#2972)
As discussed yesterday, a first draft of merging the keyserver and the
userapi.
2023-02-20 14:58:03 +01:00
dependabot[bot] bd6f0c14e5
Bump golang.org/x/net from 0.5.0 to 0.7.0 (#2979)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to
0.7.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8e2b117aee"><code>8e2b117</code></a>
http2/hpack: avoid quadratic complexity in hpack decoding</li>
<li><a
href="547e7edf38"><code>547e7ed</code></a>
http2: avoid referencing ResponseWrite.Write parameter after
returning</li>
<li><a
href="39940adcaa"><code>39940ad</code></a>
html: parse comments per HTML spec</li>
<li><a
href="87ce33ecb4"><code>87ce33e</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="415cb6d518"><code>415cb6d</code></a>
all: fix some comments</li>
<li><a
href="7e3c19ca52"><code>7e3c19c</code></a>
all: correct typos in comments</li>
<li><a
href="296f09aa38"><code>296f09a</code></a>
http2: case insensitive handling for 100-continue</li>
<li><a
href="f8411da775"><code>f8411da</code></a>
nettest: fix tests on dragonfly and js/wasm</li>
<li>See full diff in <a
href="https://github.com/golang/net/compare/v0.5.0...v0.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/net&package-manager=go_modules&previous-version=0.5.0&new-version=0.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the
default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as
the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as
the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the
default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 19:33:40 +01:00
dependabot[bot] 22c4736495
Bump golang.org/x/image from 0.1.0 to 0.5.0 (#2978)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.1.0
to 0.5.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e6c2a4cdd5"><code>e6c2a4c</code></a>
tiff: don't pre-allocate giant slices before reading</li>
<li><a
href="3db422c472"><code>3db422c</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="bb712eb67b"><code>bb712eb</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="9b8a3be425"><code>9b8a3be</code></a>
font/plan9font: limit the font image's dimensions</li>
<li><a
href="c5235ae222"><code>c5235ae</code></a>
font/plan9font: fix byteoffset for non-zero origin</li>
<li><a
href="0888fdd524"><code>0888fdd</code></a>
font/plan9font: fix bounds overflow</li>
<li><a
href="9fdfde75ec"><code>9fdfde7</code></a>
go.mod: update golang.org/x dependencies</li>
<li>See full diff in <a
href="https://github.com/golang/image/compare/v0.1.0...v0.5.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/image&package-manager=go_modules&previous-version=0.1.0&new-version=0.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the
default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as
the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as
the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the
default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 16:33:49 +01:00
Till f0805071d5
Fix SQLite session_id (#2977)
This fixes an issue with device_id/session_ids.
If a `device_id` is reused, we would reuse the same `session_id`, since
we delete one device and insert a new one directly, resulting in the
query to get a new `session_id` to return the previous session_id.
(`SELECT count(access_token)`)
2023-02-17 11:39:46 +01:00
Till 11d9b9db0e
Remove polylith/API mode (#2967)
This removes most of the code used for polylith/API mode.

This removes the `/api` internal endpoints entirely. 

Binary size change roughly 5%: 
```
51437560 Feb 13 10:15 dendrite-monolith-server # old
48759008 Feb 13 10:15 dendrite-monolith-server # new
```
2023-02-14 12:47:47 +01:00
Till cc59879faa
Version 0.11.1 (#2966) 2023-02-10 18:36:59 +01:00
ShalokShalom e64ed0934d
Update coverage (#2964)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Matthias Gramberg <ShalokShalom@riseup.net>`
2023-02-08 08:56:16 +01:00
Till eb29a31550
Optimize /sync and history visibility (#2961)
Should fix the following issues or make a lot less worse when using
Postgres:

The main issue behind #2911: The client gives up after a certain time,
causing a cascade of context errors, because the response couldn't be
built up fast enough. This mostly happens on accounts with many rooms,
due to the inefficient way we're getting recent events and current state

For #2777: The queries for getting the membership events for history
visibility were being executed for each room (I think 185?), resulting
in a whooping 2k queries for membership events. (Getting the
statesnapshot -> block nids -> actual wanted membership event)

Both should now be better by:
- Using a LATERAL join to get all recent events for all joined rooms in
one go (TODO: maybe do the same for room summary and current state etc)
- If we're lazy loading on initial syncs, we're now not getting the
whole current state, just to drop the majority of it because we're lazy
loading members - we add a filter to exclude membership events on the
first call to `CurrentState`.
- Using an optimized query to get the membership events needed to
calculate history visibility

---------

Co-authored-by: kegsay <kegan@matrix.org>
2023-02-07 14:31:23 +01:00
Devon Hudson cf254ba044
Add max frame size to pinecone bindings 2023-02-04 06:05:39 -07:00
Devon Hudson 4ed61740ab
Disable fulltext search in pinecone builds 2023-02-04 05:56:33 -07:00
Devon Hudson 26f86a76b6
Update dendrite-pinecone gobindings build script 2023-02-03 09:06:03 -07:00
Till baf118b08c
Add Sytest/Complement coverage to scheduled runs (#2962)
This adds Sytest and Complement coverage reporting to the nightly
scheduled CI runs.

Fixes a few API mode related issues as well, since we seemingly never
really ran them with Complement.

Also fixes a bug related to device list changes: When we pass in an
empty `newlyLeftRooms` slice, we got a list of all currently joined
rooms with the corresponding members. When we then got the
`newlyJoinedRooms`, we wouldn't update the `changed` slice, because we
already got the user from the `newlyLeftRooms` query. This is fixed by
simply ignoring empty `newlyLeftRooms`.
2023-02-03 13:42:35 +01:00
dependabot[bot] 9c826d064d
Bump activesupport from 6.0.5 to 6.0.6.1 in /docs (#2959)
Bumps [activesupport](https://github.com/rails/rails) from 6.0.5 to
6.0.6.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rails/rails/releases">activesupport's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.6.1</h2>
<h2>Active Support</h2>
<ul>
<li>No changes.</li>
</ul>
<h2>Active Model</h2>
<ul>
<li>No changes.</li>
</ul>
<h2>Active Record</h2>
<ul>
<li>
<p>Make <code>sanitize_as_sql_comment</code> more strict</p>
<p>Though this method was likely never meant to take user input, it was
attempting sanitization. That sanitization could be bypassed with
carefully crafted input.</p>
<p>This commit makes the sanitization more robust by replacing any
occurrances of &quot;/<em>&quot; or &quot;</em>/&quot; with &quot;/
<em>&quot; or &quot;</em> /&quot;. It also performs a
first pass to remove one surrounding comment to avoid compatibility
issues for users relying on the existing removal.</p>
<p>This also clarifies in the documentation of annotate that it should
not
be provided user input.</p>
<p>[CVE-2023-22794]</p>
</li>
</ul>
<h2>Action View</h2>
<ul>
<li>No changes.</li>
</ul>
<h2>Action Pack</h2>
<ul>
<li>No changes.</li>
</ul>
<h2>Active Job</h2>
<ul>
<li>No changes.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="28bb76d3ef"><code>28bb76d</code></a>
Version 6.0.6.1</li>
<li><a
href="91cf62e7b4"><code>91cf62e</code></a>
Version 6.0.6</li>
<li><a
href="c7d64e91b6"><code>c7d64e9</code></a>
Preparing for 6.0.5.1 release</li>
<li><a
href="c177e45858"><code>c177e45</code></a>
updating version and changelog</li>
<li>See full diff in <a
href="https://github.com/rails/rails/compare/v6.0.5...v6.0.6.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=activesupport&package-manager=bundler&previous-version=6.0.5&new-version=6.0.6.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the
default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as
the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as
the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the
default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-02 10:27:38 +01:00
Devon Hudson a666c06da1
Consolidate pinecone demo http server variations 2023-02-01 14:11:48 -07:00
Devon Hudson 048e35026c
Refactor common pinecone demo code to remove major duplication 2023-02-01 13:41:38 -07:00
Devon Hudson dbc2869cbd
Refactor pinecone demo to remove duplicate pinecone setup 2023-02-01 13:41:38 -07:00
Devon Hudson d4f64f91ca
Refactor pinecone demo to remove duplicate key setup 2023-02-01 13:41:37 -07:00
Devon Hudson 2f8377e94b
Remove nolint: gocyclo from relayapi routing setup 2023-02-01 13:41:37 -07:00
Devon Hudson 529feb07ee
Refactor conduit type from pinecone demo into its own package 2023-02-01 13:41:37 -07:00
Devon Hudson be43b9c0ea
Refactor common relay sync struct to remove duplication 2023-02-01 13:41:36 -07:00
devonh 4738fe656f
Roomserver published pkey migration (#2960)
Adds a missed migration to update the primary key on the
roomserver_published table in postgres.
Primary key was changed in #2836.
2023-02-01 16:32:31 +00:00
Devon Hudson 4af88ff0e6
Update gmsl dependency 2023-01-31 12:49:47 -07:00
Devon Hudson b935da6c33
Use new gmsl RelayEvents type for send_relay request body 2023-01-31 12:32:36 -07:00
Devon Hudson 7b3334778f
Use gmsl relay_txn response type 2023-01-31 12:31:57 -07:00
Devon Hudson f98003c030
Add cmd line option to pinecone demo for enabling relaying 2023-01-29 18:13:39 -07:00
Devon Hudson 0f998e3af3
Add pinecone demo toggle for dis/enabling relaying for other nodes 2023-01-29 12:26:16 -07:00
devonh 63df85db6d
Relay integration to pinecone demos (#2955)
This extends the dendrite monolith for pinecone to integrate the s&f
features into the mobile apps.
Also makes a few tweaks to federation queueing/statistics to make some
edge cases more robust.
2023-01-28 23:27:53 +00:00
Devon Hudson 2debabf0f0
Bump bleve to v2.3.6 2023-01-26 10:58:44 -07:00
Devon Hudson 24a865aeb7
Move relay arch into relayapi and add docs for new endpoints 2023-01-26 10:15:53 -07:00
Lukas 80738cc2a0
Added Landing Page (#2885)
I have added/copied a landing page like Synpase does.
Recently I have installed Dendrite and was wondering why it´s not
working. After some troubleshooting I figured out there is no landing
page like synpase has, so the Server was running just fine.

Hopefuly this PR can fix this problem and may help other users who run
into this issue.
I have not written any unit tests, because it´s just a simple landing
page with a redirect to a static site.


### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Lukas Huida<lukas@leucali.net>`

Co-authored-by: Till Faelligen <2353100+S7evinK@users.noreply.github.com>
2023-01-26 16:25:17 +01:00
dependabot[bot] ace44458b2
Bump commonmarker from 0.23.6 to 0.23.7 in /docs (#2952)
Bumps [commonmarker](https://github.com/gjtorikian/commonmarker) from
0.23.6 to 0.23.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gjtorikian/commonmarker/releases">commonmarker's
releases</a>.</em></p>
<blockquote>
<h2>v0.23.7</h2>
<h2>What's Changed</h2>
<ul>
<li>C API stable test by <a
href="https://github.com/gjtorikian"><code>@​gjtorikian</code></a> in <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/201">gjtorikian/commonmarker#201</a></li>
<li>Update to 29.0.gfm.7 by <a
href="https://github.com/anticomputer"><code>@​anticomputer</code></a>
in <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/224">gjtorikian/commonmarker#224</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.6...v0.23.7">https://github.com/gjtorikian/commonmarker/compare/v0.23.6...v0.23.7</a></p>
<h2>v0.23.7.pre1</h2>
<h2>What's Changed</h2>
<ul>
<li>C API stable test by <a
href="https://github.com/gjtorikian"><code>@​gjtorikian</code></a> in <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/201">gjtorikian/commonmarker#201</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.6...v0.23.7.pre1">https://github.com/gjtorikian/commonmarker/compare/v0.23.6...v0.23.7.pre1</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/gjtorikian/commonmarker/blob/main/CHANGELOG.md">commonmarker's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v1.0.0.pre6">v1.0.0.pre6</a>
(2023-01-09)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v1.0.0.pre5...v1.0.0.pre6">Full
Changelog</a></p>
<p><strong>Closed issues:</strong></p>
<ul>
<li>Cargo.lock prevents Ruby 3.2.0 from installing commonmarker
v1.0.0.pre4 <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/issues/211">#211</a></li>
</ul>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>always use rb_sys (don't use Ruby's emerging cargo tooling where
available) <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/213">#213</a>
(<a href="https://github.com/kivikakk">kivikakk</a>)</li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v1.0.0.pre5">v1.0.0.pre5</a>
(2023-01-08)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v1.0.0.pre4...v1.0.0.pre5">Full
Changelog</a></p>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>Provide 3.2 build support <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/212">#212</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v1.0.0.pre4">v1.0.0.pre4</a>
(2022-12-28)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v1.0.0.pre3...v1.0.0.pre4">Full
Changelog</a></p>
<p><strong>Closed issues:</strong></p>
<ul>
<li>Will the cmark-gfm branch continue to be maintained for awhile? <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/issues/207">#207</a></li>
</ul>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>Implement native syntax highlighting <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/209">#209</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
<li>Bump magnus from 0.4.3 to 0.4.4 <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/208">#208</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
<li>Bump magnus from 0.4.2 to 0.4.3 <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/206">#206</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
<li>Bump comrak from 0.14.0 to 0.15.0 <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/205">#205</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
<li>Bump magnus from 0.4.1 to 0.4.2 <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/204">#204</a>
(<a href="https://github.com/apps/dependabot">dependabot[bot]</a>)</li>
</ul>
<h2><a
href="https://github.com/gjtorikian/commonmarker/tree/v1.0.0.pre3">v1.0.0.pre3</a>
(2022-11-30)</h2>
<p><a
href="https://github.com/gjtorikian/commonmarker/compare/v1.0.0.pre.2...v1.0.0.pre3">Full
Changelog</a></p>
<p><strong>Closed issues:</strong></p>
<ul>
<li>Code block incorrectly parsed in commonmarker 1.0.0.pre <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/issues/202">#202</a></li>
</ul>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>Windows build <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/pull/197">#197</a>
(<a href="https://github.com/gjtorikian">gjtorikian</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="734fd86c97"><code>734fd86</code></a>
Merge pull request <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/issues/224">#224</a>
from gjtorikian/update-to-29.0.gfm.7</li>
<li><a
href="2e724ec52a"><code>2e724ec</code></a>
Turned off Rubocop.</li>
<li><a
href="9c923b0bfd"><code>9c923b0</code></a>
💎 release 0.23.7</li>
<li><a
href="30419c25e8"><code>30419c2</code></a>
Added call to cmark_init_standard_node_flags()</li>
<li><a
href="9007c3798f"><code>9007c37</code></a>
Update cmark-upstream to <a
href="https://github.com/github/cmark-gfm/commit/57d5e093e">https://github.com/github/cmark-gfm/commit/57d5e093e</a>...</li>
<li><a
href="1cfec13373"><code>1cfec13</code></a>
Merge pull request <a
href="https://github-redirect.dependabot.com/gjtorikian/commonmarker/issues/201">#201</a>
from gjtorikian/c-api-stable-test</li>
<li><a
href="bbf631b413"><code>bbf631b</code></a>
lint</li>
<li><a
href="5b807a115d"><code>5b807a1</code></a>
ease up</li>
<li><a
href="9a24e6d2fe"><code>9a24e6d</code></a>
Test fake version</li>
<li><a
href="d8a43bc73a"><code>d8a43bc</code></a>
Allow for manual dispatch</li>
<li>Additional commits viewable in <a
href="https://github.com/gjtorikian/commonmarker/compare/v0.23.6...v0.23.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=commonmarker&package-manager=bundler&previous-version=0.23.6&new-version=0.23.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the
default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as
the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as
the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the
default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-26 08:25:39 +01:00
devonh 5b73592f5a
Initial Store & Forward Implementation (#2917)
This adds store & forward relays into dendrite for p2p.
A few things have changed:
- new relay api serves new http endpoints for s&f federation
- updated outbound federation queueing which will attempt to forward
using s&f if appropriate
- database entries to track s&f relays for other nodes
2023-01-23 17:55:12 +00:00
Till 48fa869fa3
Use t.TempDir for SQLite databases, so tests don't rip out each others databases (#2950)
This should hopefully finally fix issues about `disk I/O error` as seen
[here](https://gitlab.alpinelinux.org/alpine/aports/-/jobs/955030/raw)

Hopefully this will also fix `SSL accept attempt failed` issues by
disabling HTTP keep alives when generating a config for CI.
2023-01-23 13:17:15 +01:00
Till 430932f0f1
Version 0.11.0 (#2949) 2023-01-20 16:20:01 +01:00
Catalan Lover 25cb65acdb
Change Default Room version to 10 (#2933)
This PR implements
[MSC3904](https://github.com/matrix-org/matrix-spec-proposals/pull/3904).
This PR is almost identical to #2781 but this PR is also filed well
technically 1 day before the MSC passes FCP but well everyone knows this
MSC is expected to have passed FCP on monday so im refiling this change
today on saturday as i was doing prep work for monday. I assume that
this PR wont be counted as clogging the queue since by the next time i
expect to be a work day for this project this PR will be implementing an
FCP passed disposition merge MSC.

Also as for the lack of tests i belive that this simple change does not
need to pass new tests due to that these tests are expected to already
have been passed by the successful use of Dendrite with Room version 10
already.

### Pull Request Checklist

* [X] I have added tests for PR _or_ I have justified why this PR
doesn't need tests.
* [X] Pull request includes a [sign
off](https://github.com/matrix-org/dendrite/blob/main/docs/CONTRIBUTING.md#sign-off)

Signed-off-by: Catalan Lover <catalanlover@protonmail.com>

Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
Co-authored-by: kegsay <kegan@matrix.org>
2023-01-20 15:41:29 +01:00
Till caf310fd79
AWSY missing federation tests (#2943)
In an attempt to fix the missing AWSY tests and to get to 100%
server-server compliance.
2023-01-20 15:18:06 +01:00
Bernhard Feichtinger a2b4860912
Fix oversight in cmd/generate-config (#2946)
The -dir argument was ignored for media_api->base_path.
Signed-off-by: `Bernhard Feichtinger
<43303168+BieHDC@users.noreply.github.com>`
2023-01-20 13:13:36 +01:00
Till ce2bfc3f2e
Make tests more reliable (#2948)
When using `testrig.CreateBase` and then using that base for other
`NewInternalAPI` calls, we never actually shutdown the components.
`testrig.CreateBase` returns a `close` function, which only removes the
database, so still running components have issues connecting to the
database, since we ripped it out underneath it - which can result in
"Disk I/O" or "pq deadlock detected" issues.
2023-01-20 12:45:56 +01:00
Neil 738686ae68
Add /_dendrite/admin/purgeRoom/{roomID} (#2662)
This adds a new admin endpoint `/_dendrite/admin/purgeRoom/{roomID}`. It
completely erases all database entries for a given room ID.

The roomserver will start by clearing all data for that room and then
will generate an output event to notify downstream components (i.e. the
sync API and federation API) to do the same.

It does not currently clear media and it is currently not implemented
for SQLite since it relies on SQL array operations right now.

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
Co-authored-by: Till Faelligen <2353100+S7evinK@users.noreply.github.com>
2023-01-19 21:02:32 +01:00
genofire 67f5c5bc1e
fix(helm): extract image tag to value (and use as default from Chart.… (#2934)
improve image tag handling on the default helm way.
with usage of appVersion from:

0995dc4822/helm/dendrite/Chart.yaml (L4)

maybe you like to review @S7evinK ?

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Geno <geno+dev@fireorbit.de>`
2023-01-18 08:45:34 +01:00
Till b55a7c238f
Version 0.10.9 (#2942) 2023-01-17 19:04:02 +01:00
Till 0d0280cf5f
/sync performance optimizations (#2927)
Since #2849 there is no limit for the current state we fetch to
calculate history visibility. In large rooms this can cause us to fetch
thousands of membership events we don't really care about.
This now only gets the state event types and senders in our timeline,
which should significantly reduce the amount of events we fetch from the
database.

Also removes `MaxTopologicalPosition`, as it is an unnecessary DB call,
given we use the result in `topological_position < $1` calls.
2023-01-17 10:08:23 +01:00
Umar Getagazov 8582c7520a
Omit state field from /messages response if empty (#2940)
The field type is `[ClientEvent]` in the
[spec](https://spec.matrix.org/v1.5/client-server-api/#get_matrixclientv3roomsroomidmessages),
but right now `null` can also be returned. Omit the field completely if
it's empty. Some clients (rightfully) assume it's either not present at
all or it's of the right type (see
https://github.com/matrix-org/matrix-react-sdk/pull/9913).

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
    * The PR is a simple struct tag fix
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Umar Getagazov <umar@handlerug.me>`

Signed-off-by: Umar Getagazov <umar@handlerug.me>
2023-01-17 09:07:42 +01:00
Andrew Morgan eeeb3017d6
Switch the default config option values for recaptcha_sitekey_class and recaptcha_form_field (#2939)
Attempting to use the [web auth fallback
mechanism](https://spec.matrix.org/v1.5/client-server-api/#fallback) for
Google ReCAPTCHA with the default setting for
`client_api.recaptcha_sitekey_class` of "g-recaptcha-response" results
in no captcha being rendered:


![image](https://user-images.githubusercontent.com/1342360/212482321-14980045-6e20-4d59-adaa-59a01ad88367.png)

I cross-checked the captcha code between [dendrite.matrix.org's fallback
page](https://dendrite.matrix.org/_matrix/client/r0/auth/m.login.recaptcha/fallback/web?session=asdhjaksd)
and [matrix-client.matrix.org's
one](https://matrix-client.matrix.org/_matrix/client/r0/auth/m.login.recaptcha/fallback/web?session=asdhjaksd)
(which both use the same captcha public key) and noticed a discrepancy
in the `class` attribute of the div that renders the captcha.
[ReCAPTCHA's docs
state](https://developers.google.com/recaptcha/docs/v3#automatically_bind_the_challenge_to_a_button)
to use "g-recaptcha" as the class for the submit button.

I noticed this when user `@parappanon:parappa.party` reported that they
were also seeing no captcha being rendered on their Dendrite instance.
Changing `client_api.recaptcha_sitekey_class` to "g-recaptcha" caused
their captcha to render properly as well.

There may have been a change in the class name from ReCAPTCHA v2 to v3?
The [docs for
v2](https://developers.google.com/recaptcha/docs/display#auto_render)
also request one uses "g-recaptcha" though.

Thus I propose changing the default setting to unbreak people's
recaptcha auth fallback pages. Should fix dendrite.matrix.org as well.
2023-01-16 12:52:30 +01:00
Devon Hudson 477a44faa6
Always initialize statistics server map 2023-01-12 09:23:03 -07:00
Till 0491a8e343
Fix room summary returning wrong heroes (#2930)
This should fix #2910.
Probably makes Sytest/Complement a bit upset, since this not using
`sort.Strings` anymore.
2023-01-12 10:06:03 +01:00
Devon Hudson 25dfbc6ec3
Extend cypress test timeout in ci 2023-01-11 10:47:37 -07:00
Devon Hudson 6ae1dd565c
Revert "Add cypress cloud recording"
This reverts commit b297ea7379.
2023-01-11 10:46:52 -07:00
Devon Hudson b297ea7379
Add cypress cloud recording 2023-01-11 10:40:38 -07:00
Devon Hudson 8fef692741
Edit cypress config before running tests 2023-01-11 10:10:24 -07:00
Devon Hudson 11a07d855d
Initial attempt at adding cypress tests to ci 2023-01-11 09:52:58 -07:00
Devon Hudson 97ebd72b5a
Add FAQs based on commonly asked questions from the community 2023-01-10 16:26:41 -07:00
devonh 7482cd2b47
Handle DisplayName field in admin user registration endpoint (#2935)
`/_synapse/admin/v1/register` has a `displayname` field that we were
previously ignoring.
This handles that field and adds the displayname to the new user if one
was provided.
2023-01-10 18:09:25 +00:00
Till b0c5af6674
Fix /login issue causing wrong device list updates (#2922)
Fixes https://github.com/matrix-org/dendrite/issues/2914 and possibly
https://github.com/matrix-org/dendrite/issues/2073?
2023-01-10 17:02:38 +01:00
Devon Hudson 0995dc4822
Add curl to dendrite-demo-pinecone docker container 2023-01-06 12:02:43 -07:00
Devon Hudson 54b47a98e5
Add curl to dendrite docker containers 2023-01-06 11:49:59 -07:00
Till Faelligen 3fd95e60cc
Try that again 2023-01-06 15:54:04 +01:00
Till Faelligen 002310390f
Output to docs folder, hopefully 2023-01-06 15:51:07 +01:00
Till d579ddb8e7
Add simplified helm chart (#2905)
As discussed yesterday, a simplified version of [my
helm](https://github.com/S7evinK/dendrite-helm) which deploys a monolith
with internal NATS and an optionally enabled PostgreSQL server. If the
PostgreSQL dependency is not enabled, a user specified connection string
is constructed.

Co-authored-by: kegsay <kegan@matrix.org>
2023-01-06 15:44:10 +01:00
Till 2e1fe58937
Fix backfilling (#2926)
This should fix https://github.com/matrix-org/dendrite/issues/2923
2023-01-05 09:24:00 +01:00
Till e449d174cc
Add possibility to run complement with coverage enabled (#2901)
This adds the possibility to run Complement with coverage enabled.
In combination with https://github.com/matrix-org/complement/pull/566 we
should then be able to extract the coverage logs, combine them with
https://github.com/wadey/gocovmerge (or similar) and upload them to
Codecov (with different flags, depending on SQLite, HTTP etc.)
2022-12-23 14:28:15 +01:00
Till f762ce1050
Add clientapi tests (#2916)
This PR
- adds several tests for the clientapi, mostly around `/register` and
auth fallback.
- removes the now deprecated `homeserver` field from responses to
`/register` and `/login`
- slightly refactors auth fallback handling
2022-12-23 14:11:11 +01:00
Till f47515e38b
Pushrule tweaks, make pattern non-optional on EventMatchCondition (#2918)
This should fix https://github.com/matrix-org/dendrite/issues/2882
(Tested with FluffyChat 1.7.1)
Also adds tests that the predefined push rules (as per the spec) is what
we have in Dendrite.
2022-12-23 12:52:47 +01:00
Till 5eed31fea3
Handle guest access [1/2?] (#2872)
Needs https://github.com/matrix-org/sytest/pull/1315, as otherwise the
membership events aren't persisted yet when hitting `/state` after
kicking guest users.

Makes the following tests pass:
```
Guest users denied access over federation if guest access prohibited
Guest users are kicked from guest_access rooms on revocation of guest_access
Guest users are kicked from guest_access rooms on revocation of guest_access over federation
```

Todo (in a follow up PR):
- Restrict access to CS API Endpoints as per
https://spec.matrix.org/v1.4/client-server-api/#client-behaviour-14

Co-authored-by: kegsay <kegan@matrix.org>
2022-12-22 13:05:59 +01:00
Till Faelligen 09dff951d6
More flakey tests 2022-12-22 13:04:32 +01:00
Till d1d2d16738
Fix reset password endpoint (#2921)
Fixes the admin password reset endpoint.
It was using a wrong variable, so could not detect the user.
Adds some more checks to validate we can actually change the password.
2022-12-22 11:54:03 +01:00
Till Faelligen beea2432e6
Fix flakey test 2022-12-22 11:31:54 +01:00
Till d3db542fbf
Add federation peeking table tests (#2920)
As the title says, adds tests for inbound/outbound peeking federation
table tests.

Also removes some unused code
2022-12-22 10:56:20 +01:00
Kento Okamoto 76db8e90de
Dendrite Documentation Fix (#2913)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->
I was reading through the Dendrite documentation on
https://matrix-org.github.io/dendrite/development/contributing and
noticed the installation link leads to a 404 error. This link works fine
if it is viewed directly from
[docs/CONTRIBUTING.md](https://github.com/matrix-org/dendrite/blob/main/docs/CONTRIBUTING.md)
but this might not be very obvious to new contributors who are reading
through the [contribution
page](https://matrix-org.github.io/dendrite/development/contributing)
directly.

This PR is mainly a small re-organization of the online documentation
mainly in the
[Development](https://matrix-org.github.io/dendrite/development) tab
along with any links throughout the doc that may be impacted by the
change. This does not contain any Go unit tests as this does not
actually touch core dendrite functionality.

* [ ] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Kento Okamoto <kentokamoto@proton.me>`
2022-12-12 16:46:37 +00:00
Till 7d2344049d
Cleanup stale device lists for users we don't share a room with anymore (#2857)
The stale device lists table might contain entries for users we don't
share a room with anymore. This now asks the roomserver about left users
and removes those entries from the table.

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
2022-12-12 08:20:59 +01:00
Till Faelligen aaf4e5c865
Use older sytest-dendrite image 2022-12-09 18:45:42 +01:00
dependabot[bot] 8846de7312
Bump nokogiri from 1.13.9 to 1.13.10 in /docs (#2909)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.9
to 1.13.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/releases">nokogiri's
releases</a>.</em></p>
<blockquote>
<h2>1.13.10 / 2022-12-07</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Address CVE-2022-23476, unchecked return value from
<code>xmlTextReaderExpand</code>. See <a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-qv4q-mr5r-qprj">GHSA-qv4q-mr5r-qprj</a>
for more information.</li>
</ul>
<h3>Improvements</h3>
<ul>
<li>[CRuby] <code>XML::Reader#attribute_hash</code> now returns
<code>nil</code> on parse errors. This restores the behavior of
<code>#attributes</code> from v1.13.7 and earlier. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2715">#2715</a>]</li>
</ul>
<hr />
<p>sha256 checksums:</p>

<pre><code>777ce2e80f64772e91459b943e531dfef387e768f2255f9bc7a1655f254bbaa1
nokogiri-1.13.10-aarch64-linux.gem
b432ff47c51386e07f7e275374fe031c1349e37eaef2216759063bc5fa5624aa
nokogiri-1.13.10-arm64-darwin.gem
73ac581ddcb680a912e92da928ffdbac7b36afd3368418f2cee861b96e8c830b
nokogiri-1.13.10-java.gem
916aa17e624611dddbf2976ecce1b4a80633c6378f8465cff0efab022ebc2900
nokogiri-1.13.10-x64-mingw-ucrt.gem
0f85a1ad8c2b02c166a6637237133505b71a05f1bb41b91447005449769bced0
nokogiri-1.13.10-x64-mingw32.gem
91fa3a8724a1ce20fccbd718dafd9acbde099258183ac486992a61b00bb17020
nokogiri-1.13.10-x86-linux.gem
d6663f5900ccd8f72d43660d7f082565b7ffcaade0b9a59a74b3ef8791034168
nokogiri-1.13.10-x86-mingw32.gem
81755fc4b8130ef9678c76a2e5af3db7a0a6664b3cba7d9fe8ef75e7d979e91b
nokogiri-1.13.10-x86_64-darwin.gem
51d5246705dedad0a09b374d09cc193e7383a5dd32136a690a3cd56e95adf0a3
nokogiri-1.13.10-x86_64-linux.gem
d3ee00f26c151763da1691c7fc6871ddd03e532f74f85101f5acedc2d099e958
nokogiri-1.13.10.gem
</code></pre>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md">nokogiri's
changelog</a>.</em></p>
<blockquote>
<h2>1.13.10 / 2022-12-07</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Address CVE-2022-23476, unchecked return value from
<code>xmlTextReaderExpand</code>. See <a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-qv4q-mr5r-qprj">GHSA-qv4q-mr5r-qprj</a>
for more information.</li>
</ul>
<h3>Improvements</h3>
<ul>
<li>[CRuby] <code>XML::Reader#attribute_hash</code> now returns
<code>nil</code> on parse errors. This restores the behavior of
<code>#attributes</code> from v1.13.7 and earlier. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2715">#2715</a>]</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="4c80121dc3"><code>4c80121</code></a>
version bump to v1.13.10</li>
<li><a
href="85410e3841"><code>85410e3</code></a>
Merge pull request <a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2715">#2715</a>
from sparklemotion/flavorjones-fix-reader-error-hand...</li>
<li><a
href="9fe0761c47"><code>9fe0761</code></a>
fix(cruby): XML::Reader#attribute_hash returns nil on error</li>
<li><a
href="3b9c736bee"><code>3b9c736</code></a>
Merge pull request <a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2717">#2717</a>
from sparklemotion/flavorjones-lock-psych-to-fix-bui...</li>
<li><a
href="2efa87b49a"><code>2efa87b</code></a>
test: skip large cdata test on system libxml2</li>
<li><a
href="3187d6739c"><code>3187d67</code></a>
dep(dev): pin psych to v4 until v5 builds in CI</li>
<li><a
href="a16b4bf14c"><code>a16b4bf</code></a>
style(rubocop): disable Minitest/EmptyLineBeforeAssertionMethods</li>
<li>See full diff in <a
href="https://github.com/sparklemotion/nokogiri/compare/v1.13.9...v1.13.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nokogiri&package-manager=bundler&previous-version=1.13.9&new-version=1.13.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the
default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as
the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as
the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the
default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-08 10:19:55 +00:00
Till c136a450d5
Fix newly joined users presence (#2854)
Fixes #2803 
Also refactors the presence stream to not hit the database for every
user, instead queries all users at once now.
2022-12-08 08:25:03 +01:00
Till 0351618ff4
Add UserAPI util tests (#2907)
This adds some `userapi/util` tests.
2022-12-08 08:24:24 +01:00
Till 27a1dea522
Fix issue with multiple/duplicate log entries during tests (#2906) 2022-12-08 08:24:06 +01:00
Neil Alexander ba2ffb7da9
Repeatable reads for /sync (#2783)
This puts repeatable reads into all sync streams.

Co-authored-by: kegsay <kegan@matrix.org>
2022-12-06 18:16:17 +00:00
Till Faelligen ded43e0f2d
Fix issue with sending presence events to invalid servers 2022-12-06 13:27:33 +01:00
kegsay 7583478305
Update contributing guidelines (#2904) 2022-12-05 16:54:01 +00:00
Till Faelligen b99349b18c
Use test.WithAllDatabases 2022-12-05 16:00:02 +01:00
Till Faelligen 3dc06bea81
Differentiate between project and patch 2022-12-05 15:49:11 +01:00
Till Faelligen 0e6d94757b
Enforce coverage 2022-12-05 15:24:36 +01:00
Till Faelligen 07e8ed13f6
Fix CI and test.WithAllDatabases 2022-12-05 15:09:59 +01:00
Till e245a26f6b
Enable/Disable internal metrics (#2899)
Basically enables us to use `test.WithAllDatabases` when testing
internal HTTP APIs, as this would otherwise result in Prometheus
complaining about already registered metric names.
2022-12-05 13:53:36 +01:00
Till b65f89e61e
Add tests for the AS internal API (#2898) 2022-12-02 16:42:23 +01:00
Till 9a46d8d95c
Test and CI related changes (#2896)
In an attempt to:
- make on-boarding a bit easier (`go test ./...` should now not need
additional postgres setup)
- get code coverage faster, not only scheduled at night
- test the `create-account` binary
2022-12-02 11:44:20 +01:00
Neil Alexander 934056f21f
Fix dendrite-demo-pinecone, /_dendrite namespace setup 2022-12-01 10:45:15 +00:00
Neil Alexander 1be0afa181
Expose /_dendrite and /_synapse on the P2P demo HTTP muxes 2022-12-01 10:24:17 +00:00
Neil Alexander 6f000e9801
Make create-account more verbose 2022-12-01 10:14:26 +00:00
Neil Alexander f009e54181
Push rule evaluation tweaks (#2897)
This tweaks push rule evaluation:

1. to be more strict around pattern matching and to not match empty
patterns
3. to bail if we come across a `dont_notify`, since cycles after that
are wasted
4. refactors `ActionsToTweaks` to make a bit more sense
2022-11-30 12:54:37 +00:00
Till ac5f3f025e
Calculate correct room member count for push rule evaluation (#2894)
Fixes a bug where we would return only the local member count, which
could result in wrongly calculated push rules.
2022-11-30 11:40:36 +00:00
Neil Alexander ed497aa8b2
Version 0.10.8 2022-11-29 16:26:33 +00:00
Till f8d1dc521d
Fix m.receipts causing notifications (#2893)
Fixes https://github.com/matrix-org/dendrite/issues/2353
2022-11-29 15:46:28 +01:00
Neil Alexander 1990c154e9
Update configuration 2022-11-29 11:11:08 +00:00
Neil Alexander 1ed5fb5e98
Update NATS Server to 2.9.8 2022-11-29 10:37:57 +00:00
Neil Alexander f6f1445cfa
Tweak event auth logging and cases (update to matrix-org/gomatrixserverlib@8835f6d) 2022-11-29 09:58:22 +00:00
Till 5e4b461e01
Return empty JSON if we don't have any protocols to return (#2892)
This should help with Element reporting `The homeserver may be too old
to support third party networks.`
2022-11-28 11:26:03 +01:00
Erik Johnston 31f56ac3f4
Never filter out a user's own membership when using LL (#2887) 2022-11-22 21:38:27 +00:00
devonh 7ad87eace3
Update pinecone version (#2884) 2022-11-18 19:37:13 +00:00
Neil Alexander 8299da5905
Fix registration for virtual hosting 2022-11-18 13:24:02 +00:00
devonh a8e7ffc7ab
Add p2p wakeup broadcast handling to pinecone demos (#2841)
Adds wakeup broadcast handling to the pinecone demos.
This will reset their blacklist status and interrupt any ongoing
federation queue backoffs currently in progress for this peer.
The end result is that any queued events will quickly be sent to the
peer if they had disconnected while attempting to send events to them.
2022-11-18 00:29:23 +00:00
devonh ffd8e21ce5
Fix nightly code coverage (#2881) 2022-11-17 15:30:23 +00:00
Neil Alexander 16325203af
Try that again 2022-11-17 09:32:19 +00:00
Neil Alexander 607819f425
Fix /key/v2/server, add HTTP Host matching 2022-11-17 09:26:56 +00:00
devonh df76a17234
Add test code coverage reporting (#2871) 2022-11-16 22:02:25 +00:00
Neil Alexander 163dabc498
Fix bug in a2f72dd9 2022-11-16 15:10:33 +00:00
Neil Alexander a916b041b1
Detect consumer being deleted in JetStreamConsumer 2022-11-16 10:28:22 +00:00
Neil Alexander 1e714bc3b6
Update to NATS Server 2.9.6 and nats.go 1.20.0 2022-11-16 10:05:59 +00:00
Neil Alexander a2f72dd966
Fix slice out of bounds in federation API 2022-11-16 09:39:19 +00:00
Neil Alexander d558da1c87
Virtual host server name workaround 2022-11-16 09:34:09 +00:00
Neil Alexander deddf686b9
Tweak /key/v2/server 2022-11-16 09:16:07 +00:00
Neil Alexander 9b8bb55430
Don't get blacklisted hosts when querying joined servers (#2880)
Otherwise we just waste time/CPU.
2022-11-15 17:21:16 +00:00
Neil Alexander 5c9aed6af9
Update to matrix-org/gomatrixserverlib@900369e 2022-11-15 15:11:08 +00:00
Neil Alexander 6650712a1c
Federation fixes for virtual hosting 2022-11-15 15:05:23 +00:00
Omar Kotb f4ee397734
Fix Caddy config well-known delegation example (#2879)
Signed-off-by: Omar Kotb <omar@omarkotb.com>

Signed-off-by: Omar Kotb <omar@omarkotb.com>
2022-11-14 17:15:39 +00:00
Till 2a77a910eb
Handle remote room upgrades (#2866)
Makes the following tests pass
```
/upgrade moves remote aliases to the new room
Local and remote users' homeservers remove a room from their public directory on upgrade
```
2022-11-14 12:07:13 +00:00
Till 858a4af224
Try to optimize CI (#2867)
Try to optimize CI by using caches
2022-11-14 12:06:41 +00:00
Till 1e79b0557e
Use a writer to assign state key NIDs (#2877) 2022-11-14 12:06:27 +00:00
Neil Alexander 529df30b56
Virtual hosting schema and logic changes (#2876)
Note that virtual users cannot federate correctly yet.
2022-11-11 16:41:37 +00:00
Till Faelligen e177e0ae73
Fix oops, add simple UT 2022-11-11 16:44:59 +01:00
Neil Alexander 72ce6acf71
Run upgrade tests for SQLite too (#2875)
This should hopefully catch problems with database migrations in SQLite
as well as PostgreSQL.
2022-11-11 11:21:16 +00:00
Till c648c671a3
Fix issue with missing user NIDs (#2874)
This should fix #2696 and possibly other related issues regarding
missing user NIDs.
(https://github.com/matrix-org/dendrite/issues/2094?)
2022-11-11 10:52:43 +01:00
Till d35a5642e8
Deny guest access on several endpoints (#2873)
Second part for guest access, this adds a `WithAllowGuests()` option to
`MakeAuthAPI`, allowing guests to access the specified endpoints.
Endpoints taken from the
[spec](https://spec.matrix.org/v1.4/client-server-api/#client-behaviour-14)
and by checking Synapse endpoints for `allow_guest=true`.
2022-11-11 10:52:08 +01:00
Till 0193549201
Send presence to newly added servers (#2869)
This should make `New federated private chats get full presence
information (SYN-115)` happy.
2022-11-11 10:35:17 +01:00
Neil Alexander efa50253f6
Fix lint error 2022-11-10 10:16:56 +00:00
Neil Alexander 503d9c7586
Improve logging in upgrade tests 2022-11-10 10:07:19 +00:00
Neil Alexander bdaae060cc
Update Ristretto 2022-11-09 14:07:29 +00:00
Neil Alexander a5cabdbac5
Remove unspecced fields from Transaction (update to matrix-org/gomatrixserverlib@715dc88) 2022-11-09 09:24:29 +00:00
Till Faelligen 205a15621a
Add custom build flag to satisfy Sytest 2022-11-07 15:07:47 +01:00
Till c125203eb6
Handle m.room.tombstone events in the UserAPI (#2864)
Fixes #2863 and makes 
```
/upgrade preserves direct room state
local user has tags copied to the new room
remote user has tags copied to the new room
```
pass.
2022-11-07 09:47:18 +01:00
Neil Alexander a7b74176e3
Revert Docker user change 2022-11-04 21:49:18 +00:00
Till Faelligen b2712cd2b1
Fix GHA release script 2022-11-04 20:58:24 +01:00
Neil Alexander 7c73b131f4
Version 0.10.7 (#2861)
Changelog and version bump.
2022-11-04 15:33:20 +00:00
Till efe28db631
Update latestPosition when getting reversed room delta (#2860)
Regression test added in
https://github.com/matrix-org/complement/pull/551
Should fix https://github.com/matrix-org/dendrite/issues/2514?
2022-11-04 15:39:09 +01:00
Till b13cb43785
Send presence to joined hosts only (#2858)
Send presence events only to rooms the user is participating, not all
servers we know about.
Should fix #2752
2022-11-04 13:23:00 +01:00
Till Faelligen eeabe892a9
Cache go mod directory 2022-11-04 11:54:53 +01:00
Neil Alexander 98d3f88bfb
Move prev_batch calculation (#2856)
This might help #2847.
2022-11-03 16:56:21 +00:00
Neil Alexander fb2e7d1b05
Put P2P Demos back into their own Dockerfiles 2022-11-03 13:57:44 +00:00
Neil Alexander 9625a79926
Update to matrix-org/pinecone@37f2e9b 2022-11-03 13:06:21 +00:00
gitlab-nickfreeman 23a25be904
Update README.md (#2855)
There only are 2, not 3 sample `docker-compose` files.
This is only a change to the README.md, no need for Go unit tests

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added Go unit tests or [Complement integration
tests](https://github.com/matrix-org/complement) for this PR _or_ I have
justified why this PR doesn't need tests
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Nick Freeman <nick@nickfreeman.de>`
2022-11-03 12:18:25 +00:00
Neil Alexander 8704e84898
Tweak removeDuplicates calls to use events instead of recentEvents (#2853)
... since `events` is *after* history visibility filtering, not before
it.
2022-11-03 10:19:37 +00:00
0x1a8510f2 1fcbb9b5e5
Fix workdir in Dockerfile (and make it a volume) (#2852) 2022-11-03 08:37:58 +00:00
Till Faelligen 85d740ea1b
Add GHA permission to upload security events 2022-11-03 08:26:46 +01:00
Till Faelligen f5b11e30a4
Hopefully fix GHA sarif upload 2022-11-03 08:20:51 +01:00
devonh 4afadebd99
Add readme for dendrite-demo-pinecone (#2851) 2022-11-02 20:29:30 +00:00
Neil Alexander ef52731e9f
Tweak FLAGS in GHA Docker builds 2022-11-02 14:41:38 +00:00
Neil Alexander 9c0725feac
Maybe fix GHA 2022-11-02 14:09:19 +00:00
Neil Alexander ca8bc87380
Multi-stage Docker builds (#2850)
This builds on @S7evinK's work to make multi-stage Docker builds. Now
that we can build SQLite without Cgo this should be much simpler and
should make Docker builds in CI significantly faster.

Co-authored-by: Till Faelligen <tfaelligen@gmail.com>
Co-authored-by: Till Faelligen <davidf@element.io>
Co-authored-by: Till Faelligen <2353100+S7evinK@users.noreply.github.com>
2022-11-02 14:04:08 +00:00
0x1a8510f2 51ab0a8ccf
Fix moderncsqlite errors and rebase onto main (#2832)
This is #2819 but rebased on latest `main`. This PR is against main too
as opposed to the `moderncsqlite` branch.

The main change here is simply:

```go
// add query parameters to the dsn
if strings.Contains(dsn, "?") {
	dsn += "&"
} else {
	dsn += "?"
}

// wait some time before erroring if the db is locked
// https://gitlab.com/cznic/sqlite/-/issues/106#note_1058094993
dsn += "_pragma=busy_timeout%3d10000"
```

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have added tests for PR _or_ I have justified why this PR
doesn't need tests.
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed off privately.

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
2022-11-02 13:20:10 +00:00
Neil Alexander 16c2a95900
Improve logging for processEventWithMissingState 2022-11-02 11:30:49 +00:00
Till 86b25a6337
Add message stats to reporting (#2748)
Since we're now listening on the `OutputRoomEvent` stream, we are able
to store messages stats.
2022-11-02 10:18:11 +00:00
Till b367cfeddf
Implement /thirdparty endpoints (#2831)
Implements the following endpoints 
```
GET /_matrix/client/v3/thirdparty/protocols
GET /_matrix/client/v3/thirdparty/protocols/{protocol}
GET /_matrix/client/v3/thirdparty/location
GET /_matrix/client/v3/thirdparty/location/{protocol}
GET /_matrix/client/v3/thirdparty/user
GET /_matrix/client/v3/thirdparty/user/{protocol}
```
2022-11-02 10:17:53 +00:00
Tak Wai Wong 75a508cc27
Fix issue where a member is forced to leave a room when the invite is marked deleted (#2839)
Proposed fix for issue:
https://github.com/matrix-org/dendrite/issues/2838

Suppose bob received invites to spaceA and spaceB.
When Bob joins spaceA, we add an OutputEvent event to retire the invite.
This sets the invite to "deleted" in the database. This makes sense.

The bug is in stream_invites.go. Triggered when bob received a new
invite for spaceB, and does a client sync.

In the block (line 76)
`for roomID := range retiredInvites

   if _, ok := req.Response.Rooms.Invite[roomID]; ok {
	continue
   }

   if _, ok := req.Response.Rooms.Join[roomID]; ok {
	continue
  }
...
` 

Bob is not in either maps even though he had just accepted the invite
for spaceA. Consequently, the spaceA invite is treated as a retired
invite, and a membership Leave event is generated. What bob sees is that
after accepting the invite to spaceB, he lose access to spaceA.


### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [ ] I have added tests for PR _or_ I have justified why this PR
doesn't need tests.
* [x ] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `Tak Wai Wong <tak@hntlabs.com>`

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
2022-11-02 10:02:23 +00:00
Neil Alexander 3db9e98456
Don't limit "state" (#2849)
This is apparently some incorrect behaviour that we built as a result of
a spec bug (matrix-org/matrix-spec#1314) where we were applying a filter
to the `"state"` section of the `/sync` response incorrectly. The client
then has no way to know that the state was limited.

This PR removes the state limiting, which probably also helps #2842.
2022-11-02 09:34:19 +00:00
Neil Alexander 8a1904ffe5
Update pull request template 2022-11-02 09:33:00 +00:00
Neil Alexander 52478dac3c
Version 0.10.6 2022-11-01 17:14:30 +00:00
Neil Alexander 501977f6fe
Fix a panic in ToClientEvents etc. 2022-11-01 16:58:51 +00:00
ash lea 5aaa60227a
return required room_id field in /members (#2846)
### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [ ] I have added tests for PR _or_ I have justified why this PR
doesn't need tests.
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)
_or_ I have already signed off privately

Signed-off-by: `ash lea <example@thisismyactual.email>`
2022-11-01 16:42:07 +00:00
Neil Alexander 42d7e3ee0d
Update dependencies 2022-11-01 16:15:55 +00:00
Neil Alexander 6663728eb1
Fix SQLite roomserver_published migration 2022-11-01 16:08:13 +00:00
Till 2acc1d65fb
Optimize history visibility checks (#2848)
This optimizes history visibility checks by (mostly) avoiding database
hits.
Possibly solves https://github.com/matrix-org/dendrite/issues/2777

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
2022-11-01 15:07:17 +00:00
Neil Alexander 0b21cb78aa
Try to fix a panic in the sync API PDU stream 2022-11-01 14:45:15 +00:00
Neil Alexander 7bd6631935
Move code for calculating auth difference into GMSL 2022-11-01 10:12:11 +00:00
Till Faelligen 869bf4d0ac
Fix flakey stats tests 2022-11-01 08:39:16 +01:00
Neil Alexander 8c7b274e4e
Version 0.10.5 (#2845)
Changelog and version bump.
2022-10-31 16:44:41 +00:00
Neil Alexander 4c38bd76ce
Fix go.mod 2022-10-31 15:15:18 +00:00
Neil Alexander 7307701a24
Tweak "state" and "timeline" filtering (#2844)
This should stop state events disappearing down a gap where we'd try to
separate out the sections *before* applying history visibility instead
of after.

This may be a better approach than #2843 but I hope @tak-hntlabs will
shout if it isn't.
2022-10-31 15:14:08 +00:00
Neil Alexander 66a82e0fa4
Update to matrix-org/gomatrixserverlib@0885c35 2022-10-31 15:12:05 +00:00
Neil Alexander f10c6f26e5
Add /_dendrite/admin/downloadState/{serverName}/{roomID} admin endpoint 2022-10-31 09:13:28 +00:00
Till 69aff372f3
Limit recent events when going backwards (#2840)
If we're going backwards, we were selecting potentially thousands of
events, which in turn were fed to history visibility checks, resulting
in bad sync performance.
2022-10-28 13:40:51 +02:00
X. Ding 0782011f54
Add hcaptcha support besides Google ReCaptcha (#2834)
### Pull Request Checklist
This PR add support for hcaptcha.com as an alternative to Google
ReCaptcha. It also makes possible for user to customize ReCaptcha URL
when needed. (Such as use recaptcha.net instead of www.google.com)

This feature needs manual test cuz it involves 3rd party _captcha_.

Signed-off-by: `Simon Ding <dxl@plotbridge.com>`

Co-authored-by: dxl <dxl@plotbridge.com>
2022-10-28 11:25:01 +01:00
Till Faelligen f6035822e7
Simplify error checking and check the correct error 2022-10-28 08:17:40 +02:00
Neil Alexander a2706e6498
Refactor claimRemoteKeys 2022-10-27 15:34:26 +01:00
Till Faelligen a785532463
Fix upgrade appservices 2022-10-27 16:01:51 +02:00
Till 444b4bbdb8
Add AS specific public room list endpoints (#2836)
Adds `PUT
/_matrix/client/v3/directory/list/appservice/{networkId}/{roomId}` and
`DELTE
/_matrix/client/v3/directory/list/appservice/{networkId}/{roomId}`
support, as well as the ability to filter `/publicRooms` on networkID
and including all networks.
2022-10-27 14:40:35 +02:00
Till a169a9121a
Fix /members (#2837)
Fixes a bug introduced in #2827, where the SyncAPI might not have all
requested eventIDs, resulting in too few members returned.
2022-10-27 14:18:22 +02:00
Till fa96811e64
Add scheduled tasks to run tests with race detection (#2814)
Needs https://github.com/matrix-org/sytest/pull/1308 to be actually
useful.
Not sure if we need to run Sytest in all combinations with enabled race
detection.

Closes https://github.com/matrix-org/dendrite/issues/491
2022-10-27 12:12:50 +02:00
Neil Alexander 238b6ef2cd
Update Yggdrasil demo 2022-10-26 18:37:01 +01:00
devonh 97491a174b
Associate events in db before queueing them to send (#2833)
Fixes a race condition between sending federation events and having them
fully associated in the database.
2022-10-26 17:35:01 +01:00
devonh a74aea0714
Add network interface callback to pinecone build (#2825)
Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
2022-10-26 17:25:57 +01:00
Neil Alexander 5298dd1133
Update federation API consumers 2022-10-26 14:52:33 +01:00
Neil Alexander f6dea712d2
Initial support for multiple server names (#2829)
This PR is the first step towards virtual hosting by laying the
groundwork for multiple server names being configured.
2022-10-26 12:59:19 +01:00
Neboer 2a4c7f45b3
Add support for config "auto_join_rooms" (#2823)
Add support for config "auto_join_rooms". Now new accounts can join the
rooms in config file automatically.

### Pull Request Checklist

<!-- Please read
https://matrix-org.github.io/dendrite/development/contributing before
submitting your pull request -->

* [x] I have justified why this PR doesn't need tests.
* [x] Pull request includes a [sign off below using a legally
identifiable
name](https://matrix-org.github.io/dendrite/development/contributing#sign-off)

Signed-off-by: `Rubin Poster <rubinposter@gmail.com>`
2022-10-26 11:04:53 +02:00
Till c62ac3d6ad
Fix Current state appears in timeline in private history with many messages after (#2830)
The problem was that we weren't getting enough recent events, as most of
them were removed by the history visibility filter. Now we're getting
all events between the given input range and re-slice the returned
values after applying history visibility.
2022-10-25 15:15:24 +02:00
Till Faelligen 8b7bf5e7d7
Return forbidden if not a member anymore (fix #2802) 2022-10-25 15:00:52 +02:00
Neil Alexander db6a214b04
Prettify unit test output 2022-10-25 12:28:34 +01:00
Till 313cb3fd19
Filter /members, return members at given point (#2827)
Makes the tests
```
Can get rooms/{roomId}/members at a given point
Can filter rooms/{roomId}/members
```
pass, by moving `/members` and `/joined_members` to the SyncAPI.
2022-10-25 12:39:10 +02:00
Till 7506e3303e
Get messages from before user left the room (#2824)
This is going to make `Can get rooms/{roomId}/messages for a departed
room (SPEC-216)` pass, since we now only grep events from before the
user left the room.
2022-10-24 17:03:04 +02:00
Neil Alexander a553fe7705
Fix slow querying of cross-signing signatures 2022-10-24 10:07:50 +01:00
Till Faelligen 0843bd776e
Fix wrong config key 2022-10-24 07:10:50 +02:00
Neil Alexander 411db6083b
Version 0.10.4 (#2822)
Changelog and version bump.
2022-10-21 15:00:51 +01:00
Till 3cf42a1d64
Add syncapi_memberships table tests (#2805) 2022-10-21 12:53:04 +02:00
Till 9e4c3171da
Optimize inserting pending PDUs/EDUs (#2821)
This optimizes the association of PDUs/EDUs to their destination by
inserting all destinations in one transaction.
2022-10-21 12:50:51 +02:00
Neil Alexander e98d75fd63
Verify room_id, type, sender and state_key field lengths using bytes rather than codepoints (update to matrix-org/gomatrixserverlib@7c772f1, reverts bbb3ade4a2) 2022-10-21 10:15:08 +01:00
Till e57b301722
Set display_name and/or avatar_url for server notices (#2820)
This should fix #2815 by making sure we actually set the `display_name`
and/or `avatar_url` and create the needed membership event.
To avoid creating a new membership event when starting Dendrite,
`SetAvatarURL` and `SetDisplayName` now return a `Changed` value, which
also makes the regular endpoints idempotent.
2022-10-21 10:48:25 +02:00
Till 40cfb9a4ea
Fix invite -> leave -> join dance when accepting invites (#2817)
As mentioned in
https://github.com/matrix-org/dendrite/issues/2361#issuecomment-1139394565
and observed by ourselves, this should fix the odd `invite -> leave ->
join` dance when accepting invites.
2022-10-21 09:26:22 +01:00
Neil Alexander 73e02463cf
Allow m.read.private to clear notifications (#2811)
Otherwise if a user switches to private read receipts, they may not be
able to clear notification counts.
2022-10-21 09:19:52 +01:00
devonh 9041491201
Mutex protect query keys response (#2812) 2022-10-20 15:54:18 +00:00
devonh b58c9bb094
Fix flakey queue test (#2818)
Ensure both events are added to the database, even if the destination is
already blacklisted.
2022-10-20 15:37:35 +00:00
Till Faelligen 539c61b3db
Remove test from blacklist 2022-10-20 12:34:53 +02:00
Till Faelligen 6a93858125
Fix race condition 2022-10-20 10:45:59 +02:00
Till e79bfd8fd5
Get state deltas without filters (#2810)
This makes the following changes:
- get state deltas without the user supplied filter, so we can actually
"calculate" state transitions
- closes `stmt` when using SQLite
- Adds presence for users who newly joined a room, even if the syncing
user already knows about the presence status (should fix
https://github.com/matrix-org/complement/pull/516)
2022-10-19 14:05:39 +02:00
Neil Alexander 8cbe14bd6d
Fix lock contention 2022-10-19 12:27:34 +01:00
Neil Alexander c1463db6c9
Fix concurrent map write in key server 2022-10-19 12:03:12 +01:00
dependabot[bot] f3dae0e749
Bump nokogiri from 1.13.6 to 1.13.9 in /docs (#2809)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.6
to 1.13.9.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/releases">nokogiri's
releases</a>.</em></p>
<blockquote>
<h2>1.13.9 / 2022-10-18</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to address <a
href="https://nvd.nist.gov/vuln/detail/CVE-2022-2309">CVE-2022-2309</a>,
<a
href="https://nvd.nist.gov/vuln/detail/CVE-2022-40304">CVE-2022-40304</a>,
and <a
href="https://nvd.nist.gov/vuln/detail/CVE-2022-40303">CVE-2022-40303</a>.
See <a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-2qc6-mcvw-92cw">GHSA-2qc6-mcvw-92cw</a>
for more information.</li>
<li>[CRuby] Vendored zlib is updated to address <a
href="https://ubuntu.com/security/CVE-2022-37434">CVE-2022-37434</a>.
Nokogiri was not affected by this vulnerability, but this version of
zlib was being flagged up by some vulnerability scanners, see <a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2626">#2626</a>
for more information.</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.10.3">v2.10.3</a>
from v2.9.14.</li>
<li>[CRuby] Vendored libxslt is updated to <a
href="https://gitlab.gnome.org/GNOME/libxslt/-/releases/v1.1.37">v1.1.37</a>
from v1.1.35.</li>
<li>[CRuby] Vendored zlib is updated from 1.2.12 to 1.2.13. (See <a
href="https://github.com/sparklemotion/nokogiri/blob/v1.13.x/LICENSE-DEPENDENCIES.md#platform-releases">LICENSE-DEPENDENCIES.md</a>
for details on which packages redistribute this library.)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>[CRuby] <code>Nokogiri::XML::Namespace</code> objects, when
compacted, update their internal struct's reference to the Ruby object
wrapper. Previously, with GC compaction enabled, a segmentation fault
was possible after compaction was triggered. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2658">#2658</a>]
(Thanks, <a
href="https://github.com/eightbitraptor"><code>@​eightbitraptor</code></a>
and <a
href="https://github.com/peterzhu2118"><code>@​peterzhu2118</code></a>!)</li>
<li>[CRuby] <code>Document#remove_namespaces!</code> now defers freeing
the underlying <code>xmlNs</code> struct until the <code>Document</code>
is GCed. Previously, maintaining a reference to a <code>Namespace</code>
object that was removed in this way could lead to a segfault. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2658">#2658</a>]</li>
</ul>
<hr />
<p>sha256 checksums:</p>

<pre><code>9b69829561d30c4461ea803baeaf3460e8b145cff7a26ce397119577a4083a02
nokogiri-1.13.9-aarch64-linux.gem
e76ebb4b7b2e02c72b2d1541289f8b0679fb5984867cf199d89b8ef485764956
nokogiri-1.13.9-arm64-darwin.gem
15bae7d08bddeaa898d8e3f558723300137c26a2dc2632a1f89c8574c4467165
nokogiri-1.13.9-java.gem
f6a1dbc7229184357f3129503530af73cc59ceba4932c700a458a561edbe04b9
nokogiri-1.13.9-x64-mingw-ucrt.gem
36d935d799baa4dc488024f71881ff0bc8b172cecdfc54781169c40ec02cbdb3
nokogiri-1.13.9-x64-mingw32.gem
ebaf82aa9a11b8fafb67873d19ee48efb565040f04c898cdce8ca0cd53ff1a12
nokogiri-1.13.9-x86-linux.gem
11789a2a11b28bc028ee111f23311461104d8c4468d5b901ab7536b282504154
nokogiri-1.13.9-x86-mingw32.gem
01830e1646803ff91c0fe94bc768ff40082c6de8cfa563dafd01b3f7d5f9d795
nokogiri-1.13.9-x86_64-darwin.gem
8e93b8adec22958013799c8690d81c2cdf8a90b6f6e8150ab22e11895844d781
nokogiri-1.13.9-x86_64-linux.gem
96f37c1baf0234d3ae54c2c89aef7220d4a8a1b03d2675ff7723565b0a095531
nokogiri-1.13.9.gem
</code></pre>
<h2>1.13.8 / 2022-07-23</h2>
<h3>Deprecated</h3>
<ul>
<li><code>XML::Reader#attribute_nodes</code> is deprecated due to
incompatibility between libxml2's <code>xmlReader</code> memory
semantics and Ruby's garbage collector. Although this method continues
to exist for backwards compatibility, it is unsafe to call and may
segfault. This method will be removed in a future version of Nokogiri,
and callers should use <code>#attribute_hash</code> instead. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2598">#2598</a>]</li>
</ul>
<h3>Improvements</h3>
<ul>
<li><code>XML::Reader#attribute_hash</code> is a new method to safely
retrieve the attributes of a node from <code>XML::Reader</code>. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2598">#2598</a>,
<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2599">#2599</a>]</li>
</ul>
<h3>Fixed</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md">nokogiri's
changelog</a>.</em></p>
<blockquote>
<h2>1.13.9 / 2022-10-18</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to address <a
href="https://nvd.nist.gov/vuln/detail/CVE-2022-2309">CVE-2022-2309</a>,
<a
href="https://nvd.nist.gov/vuln/detail/CVE-2022-40304">CVE-2022-40304</a>,
and <a
href="https://nvd.nist.gov/vuln/detail/CVE-2022-40303">CVE-2022-40303</a>.
See <a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-2qc6-mcvw-92cw">GHSA-2qc6-mcvw-92cw</a>
for more information.</li>
<li>[CRuby] Vendored zlib is updated to address <a
href="https://ubuntu.com/security/CVE-2022-37434">CVE-2022-37434</a>.
Nokogiri was not affected by this vulnerability, but this version of
zlib was being flagged up by some vulnerability scanners, see <a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2626">#2626</a>
for more information.</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.10.3">v2.10.3</a>
from v2.9.14.</li>
<li>[CRuby] Vendored libxslt is updated to <a
href="https://gitlab.gnome.org/GNOME/libxslt/-/releases/v1.1.37">v1.1.37</a>
from v1.1.35.</li>
<li>[CRuby] Vendored zlib is updated from 1.2.12 to 1.2.13. (See <a
href="https://github.com/sparklemotion/nokogiri/blob/v1.13.x/LICENSE-DEPENDENCIES.md#platform-releases">LICENSE-DEPENDENCIES.md</a>
for details on which packages redistribute this library.)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>[CRuby] <code>Nokogiri::XML::Namespace</code> objects, when
compacted, update their internal struct's reference to the Ruby object
wrapper. Previously, with GC compaction enabled, a segmentation fault
was possible after compaction was triggered. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2658">#2658</a>]
(Thanks, <a
href="https://github.com/eightbitraptor"><code>@​eightbitraptor</code></a>
and <a
href="https://github.com/peterzhu2118"><code>@​peterzhu2118</code></a>!)</li>
<li>[CRuby] <code>Document#remove_namespaces!</code> now defers freeing
the underlying <code>xmlNs</code> struct until the <code>Document</code>
is GCed. Previously, maintaining a reference to a <code>Namespace</code>
object that was removed in this way could lead to a segfault. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2658">#2658</a>]</li>
</ul>
<h2>1.13.8 / 2022-07-23</h2>
<h3>Deprecated</h3>
<ul>
<li><code>XML::Reader#attribute_nodes</code> is deprecated due to
incompatibility between libxml2's <code>xmlReader</code> memory
semantics and Ruby's garbage collector. Although this method continues
to exist for backwards compatibility, it is unsafe to call and may
segfault. This method will be removed in a future version of Nokogiri,
and callers should use <code>#attribute_hash</code> instead. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2598">#2598</a>]</li>
</ul>
<h3>Improvements</h3>
<ul>
<li><code>XML::Reader#attribute_hash</code> is a new method to safely
retrieve the attributes of a node from <code>XML::Reader</code>. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2598">#2598</a>,
<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2599">#2599</a>]</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>[CRuby] Calling <code>XML::Reader#attributes</code> is now safe to
call. In Nokogiri &lt;= 1.13.7 this method may segfault. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2598">#2598</a>,
<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2599">#2599</a>]</li>
</ul>
<h2>1.13.7 / 2022-07-12</h2>
<h3>Fixed</h3>
<p><code>XML::Node</code> objects, when compacted, update their internal
struct's reference to the Ruby object wrapper. Previously, with GC
compaction enabled, a segmentation fault was possible after compaction
was triggered. [<a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2578">#2578</a>]
(Thanks, <a
href="https://github.com/eightbitraptor"><code>@​eightbitraptor</code></a>!)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="897759cc25"><code>897759c</code></a>
version bump to v1.13.9</li>
<li><a
href="aeb1ac3283"><code>aeb1ac3</code></a>
doc: update CHANGELOG</li>
<li><a
href="c663e4905a"><code>c663e49</code></a>
Merge pull request <a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2671">#2671</a>
from sparklemotion/flavorjones-update-zlib-1.2.13_v1...</li>
<li><a
href="212e07da28"><code>212e07d</code></a>
ext: hack to cross-compile zlib v1.2.13 on darwin</li>
<li><a
href="76dbc8c5be"><code>76dbc8c</code></a>
dep: update zlib to v1.2.13</li>
<li><a
href="24e3a9c414"><code>24e3a9c</code></a>
doc: update CHANGELOG</li>
<li><a
href="4db3b4daa9"><code>4db3b4d</code></a>
Merge pull request <a
href="https://github-redirect.dependabot.com/sparklemotion/nokogiri/issues/2668">#2668</a>
from sparklemotion/flavorjones-namespace-scopes-comp...</li>
<li><a
href="73d73d6e43"><code>73d73d6</code></a>
fix: Document#remove_namespaces! use-after-free bug</li>
<li><a
href="5f58b34724"><code>5f58b34</code></a>
fix: namespace nodes behave properly when compacted</li>
<li><a
href="b08a8586c7"><code>b08a858</code></a>
test: repro namespace_scopes compaction issue</li>
<li>Additional commits viewable in <a
href="https://github.com/sparklemotion/nokogiri/compare/v1.13.6...v1.13.9">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nokogiri&package-manager=bundler&previous-version=1.13.6&new-version=1.13.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the
default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as
the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as
the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the
default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/matrix-org/dendrite/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-19 11:40:38 +01:00
devonh 241d5c47df
Refactor Federation Destination Queues (#2807)
This is a refactor of the federation destination queues.
It fixes a few things, namely:
- actually retry outgoing events with backoff behaviour
- obtain enough events from the database to fill messages as much as
possible
- minimize the amount of running goroutines
  - use pure timers for backoff
  - don't restart queue unless necessary
  - close the background task when backing off
- increase max edus in a transaction to match the spec
- cleanup timers more aggresively to reduce memory usage
- add jitter to backoff timers to reduce resource spikes
- add a bunch of tests (with real and fake databases) to ensure
everything is working
2022-10-19 11:03:16 +01:00
Neil Alexander 3aa92efaa3
Namespace user API tables (#2806)
This migrates all the various user API tables, indices and sequences to
be `userapi_`-namespaced, rather than the mess they are all now.
2022-10-18 15:59:08 +01:00
Neil Alexander 9c189b1b80
Try to make AddEvent less expensive (update to matrix-org/gomatrixserverlib@a72a83f) 2022-10-18 09:51:31 +01:00
Till 07bfb791ca
Scope transactions to endpoints (#2799)
To avoid returning results from e.g. `/redact` on `/sendToDevice`
requests.
Takes the raw URL path and uses `filepath.Dir` to remove the `txnID`
(file) from it.

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
2022-10-17 14:48:35 +02:00
Neil Alexander d72d4f8d5d
Set org.matrix.msc2285.stable in /versions 2022-10-17 10:38:22 +01:00
Till Faelligen 83c9dde219
Return error if we fail to read the response body 2022-10-17 07:27:11 +02:00
Neil Alexander 81dbad39a3
Dendrite 0.10.3
commit c6e18c18e93b54c006c6b4d0044aa53a0735a906
Author: Neil Alexander <neilalexander@users.noreply.github.com>
Date:   Fri Oct 14 15:42:58 2022 +0100

    Changelog and version bump
2022-10-14 15:53:58 +01:00
Neil Alexander cd8f7e1251
Set inactivity threshold on durable consumers in the roomserver input API (#2795)
This prevents us from holding onto durable consumers indefinitely for
rooms that have long since turned inactive, since they do have a bit of
a processing overhead in the NATS Server. If we clear up a consumer and
then a room becomes active again, the consumer gets recreated as needed.

The threshold is set to 24 hours for now, we can tweak it later if needs
be.
2022-10-14 15:14:29 +01:00
Neil Alexander eac5678449
Update dependency now that it is fixed 2022-10-14 14:53:53 +01:00
Neil Alexander f76969831e
Update direct dependencies (#2794)
This updates a number of Dendrite's dependencies.
2022-10-14 10:59:32 +01:00
Neil Alexander 82d1d434c5
Update to NATS Server v2.9.3 and nats.go v1.18.0 2022-10-14 10:10:25 +01:00
Till a8bc558a60
Always add UnreadNotifications to joined room reponses (#2793)
Fixes a minor bug, where we failed to add `UnreadNotifications` to the
join response, if it wasn't in
`GetUserUnreadNotificationCountsForRooms`.
2022-10-14 10:38:12 +02:00
Till Faelligen fb44e33909
Relax test a bit 2022-10-14 10:37:04 +02:00
Till 088ad1dd21
Fix outliers whose auth_events are in a different room are correctly rejected (#2791)
Fixes `outliers whose auth_events are in a different room are correctly
rejected`, by validating that auth events are all from the same room and
not using rejected events for event auth.
2022-10-14 09:14:54 +02:00
Neil Alexander f3be4b3185
Revert "Federation backoff fixes and tests (#2792)"
This reverts commit dcedd1b6bf.
2022-10-13 16:06:50 +01:00
devonh dcedd1b6bf
Federation backoff fixes and tests (#2792)
This fixes some edge cases where federation queue backoffs and
blacklisting weren't behaving as expected.
It also adds new tests for the federation queues to ensure their
behaviour continues to work correctly.
2022-10-13 14:38:13 +00:00
Neil Alexander 23a3e04579
Event relations (#2790)
This adds support for tracking `m.relates_to`, as well as adding support
for the various `/room/{roomID}/relations/...` endpoints to the CS API.
2022-10-13 14:50:52 +01:00
Till 3c1474f68f
Fix /get_missing_events for rooms with joined/invited history_visibility (#2787)
Sytest was using a wrong `history_visibility` for `invited`
(https://github.com/matrix-org/sytest/pull/1303), so `invited` was
passing for the wrong reason (-> defaulted to `shared`, as `invite`
wasn't understood).
This change now handles missing events like Synapse, if a server isn't
allowed to see the event, it gets a redacted version of it, making the
`get_missing_events` tests pass.
2022-10-11 16:04:02 +02:00
Neil Alexander 0a9aebdf01
Private read receipts (#2789)
Implement behaviours for `m.read.private` receipts.
2022-10-11 12:27:21 +01:00
Neil Alexander 3920b9f9b6
Tweak GetStateDeltas behaviour (#2788)
Improves the control flow of `GetStateDeltas` for clarity and possibly
also fixes a bug where duplicate state delta entries could be inserted
with different memberships instead of being correctly overridden by
`join`.
2022-10-11 10:58:34 +01:00
Neil Alexander 9ed8ff6b93
Tweak federation M_NOT_FOUND errors 2022-10-11 10:48:36 +01:00
780 changed files with 57445 additions and 24944 deletions

View file

@ -1,3 +1,2 @@
bin bin
*.wasm *.wasm
.git

View file

@ -0,0 +1,59 @@
on:
push:
tags:
- 'v*'
env:
GHCR_NAMESPACE: sigb.us
PLATFORMS: linux/amd64
FORGEJO_USER: signaryk
jobs:
monolith:
name: Monolith image
runs-on: docker
container:
image: ghcr.io/catthehacker/ubuntu:act-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get release tag & build flags
if: github.event_name == 'release' # Only for GitHub releases
run: |
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to sigb.us container registry
uses: docker/login-action@v3
with:
registry: git.sigb.us
username: ${{ env.FORGEJO_USER }}
password: ${{ secrets.FORGEJO_TOKEN }}
- name: Build main monolith image
id: docker_build_monolith
uses: docker/build-push-action@v3
with:
context: .
platforms: ${{ env.PLATFORMS }}
push: true
tags: |
git.sigb.us/${{ env.GHCR_NAMESPACE }}/dendrite:${{ github.ref_name }}
git.sigb.us/${{ env.GHCR_NAMESPACE }}/dendrite:latest
git.sigb.us/${{ env.GHCR_NAMESPACE }}/dendrite:devel
- name: Build release monolith image
if: github.event_name == 'release' # Only for GitHub releases
id: docker_build_monolith_release
uses: docker/build-push-action@v3
with:
context: .
platforms: ${{ env.PLATFORMS }}
push: true
tags: |
git.sigb.us/${{ env.GHCR_NAMESPACE }}/dendrite:latest
git.sigb.us/${{ env.GHCR_NAMESPACE }}/dendrite:stable
git.sigb.us/${{ env.GHCR_NAMESPACE }}/dendrite:${{ env.RELEASE_VERSION }}

View file

@ -17,7 +17,6 @@ see: https://www.matrix.org/security-disclosure-policy/
### Background information ### Background information
<!-- Please include versions of all software when known e.g database versions, docker versions, client versions --> <!-- Please include versions of all software when known e.g database versions, docker versions, client versions -->
- **Dendrite version or git SHA**: - **Dendrite version or git SHA**:
- **Monolith or Polylith?**:
- **SQLite3 or Postgres?**: - **SQLite3 or Postgres?**:
- **Running in Docker?**: - **Running in Docker?**:
- **`go version`**: - **`go version`**:
@ -63,6 +62,6 @@ If you can identify any relevant log snippets from server logs, please include
those (please be careful to remove any personal or private data). Please surround them with those (please be careful to remove any personal or private data). Please surround them with
``` (three backticks, on a line on their own), so that they are formatted legibly. ``` (three backticks, on a line on their own), so that they are formatted legibly.
Alternatively, please send logs to @kegan:matrix.org or @neilalexander:matrix.org Alternatively, please send logs to @kegan:matrix.org, @s7evink:matrix.org or @devonh:one.ems.host
with a link to the respective Github issue, thanks! with a link to the respective Github issue, thanks!
--> -->

View file

@ -2,7 +2,7 @@
<!-- Please read https://matrix-org.github.io/dendrite/development/contributing before submitting your pull request --> <!-- Please read https://matrix-org.github.io/dendrite/development/contributing before submitting your pull request -->
* [ ] I have added tests for PR _or_ I have justified why this PR doesn't need tests. * [ ] I have added Go unit tests or [Complement integration tests](https://github.com/matrix-org/complement) for this PR _or_ I have justified why this PR doesn't need tests
* [ ] Pull request includes a [sign off below using a legally identifiable name](https://matrix-org.github.io/dendrite/development/contributing#sign-off) _or_ I have already signed off privately * [ ] Pull request includes a [sign off below using a legally identifiable name](https://matrix-org.github.io/dendrite/development/contributing#sign-off) _or_ I have already signed off privately
Signed-off-by: `Your Name <your@email.example.org>` Signed-off-by: `Your Name <your@email.example.org>`

20
.github/codecov.yaml vendored Normal file
View file

@ -0,0 +1,20 @@
flag_management:
default_rules:
carryforward: true
coverage:
status:
project:
default:
target: auto
threshold: 0.1%
base: auto
flags:
- unittests
patch:
default:
target: 75%
threshold: 0%
base: auto
flags:
- unittests

View file

@ -4,7 +4,15 @@ on:
push: push:
branches: branches:
- main - main
paths:
- '**.go' # only execute on changes to go files
- 'go.sum' # or dependency updates
- '.github/workflows/**' # or workflow changes
pull_request: pull_request:
paths:
- '**.go'
- 'go.sum' # or dependency updates
- '.github/workflows/**'
release: release:
types: [published] types: [published]
workflow_dispatch: workflow_dispatch:
@ -20,28 +28,20 @@ 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: 1.18 go-version: "stable"
cache: true
- uses: actions/cache@v2
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-wasm-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-wasm
- name: Install Node - name: Install Node
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: 14 node-version: 14
- uses: actions/cache@v2 - 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,18 +66,20 @@ 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
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: 1.18 go-version: "stable"
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v3 uses: golangci/golangci-lint-action@v3
# run go test with different go versions # run go test with different go versions
test: test:
timeout-minutes: 5 timeout-minutes: 10
name: Unit tests (Go ${{ matrix.go }}) name: Unit tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Service containers to run with `container-job` # Service containers to run with `container-job`
services: services:
@ -99,25 +101,29 @@ jobs:
--health-interval 10s --health-interval 10s
--health-timeout 5s --health-timeout 5s
--health-retries 5 --health-retries 5
strategy:
fail-fast: false
matrix:
go: ["1.18", "1.19"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Install libolm
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: ${{ matrix.go }} go-version: "stable"
- 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
with: with:
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
key: ${{ runner.os }}-go${{ matrix.go }}-test-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-stable-unit-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go${{ matrix.go }}-test- ${{ runner.os }}-go-stable-unit-
- run: go test ./... - name: Set up gotestfmt
uses: gotesttools/gotestfmt-action@v2
with:
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
token: ${{ secrets.GITHUB_TOKEN }}
- run: go test -json -v ./... 2>&1 | gotestfmt -hide all
env: env:
POSTGRES_HOST: localhost POSTGRES_HOST: localhost
POSTGRES_USER: postgres POSTGRES_USER: postgres
@ -132,26 +138,25 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
go: ["1.18", "1.19"]
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: ${{ matrix.go }} go-version: "stable"
- name: Install dependencies x86 - uses: actions/cache@v4
if: ${{ matrix.goarch == '386' }}
run: sudo apt update && sudo apt-get install -y gcc-multilib
- uses: actions/cache@v3
with: with:
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
key: ${{ runner.os }}-go${{ matrix.go }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-stable-${{ matrix.goos }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go${{ matrix.go }}-${{ matrix.goarch }}- key: ${{ runner.os }}-go-stable-${{ matrix.goos }}-${{ matrix.goarch }}-
- name: Install dependencies x86
if: ${{ matrix.goarch == '386' }}
run: sudo apt update && sudo apt-get install -y gcc-multilib
- env: - env:
GOOS: ${{ matrix.goos }} GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }} GOARCH: ${{ matrix.goarch }}
@ -166,25 +171,24 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
go: ["1.18", "1.19"]
goos: ["windows"] goos: ["windows"]
goarch: ["amd64"] goarch: ["amd64"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup Go ${{ matrix.go }} - name: Setup Go
uses: actions/setup-go@v3 uses: actions/setup-go@v4
with: with:
go-version: ${{ matrix.go }} go-version: "stable"
- name: Install dependencies - uses: actions/cache@v4
run: sudo apt update && sudo apt install -y gcc-mingw-w64-x86-64 # install required gcc
- uses: actions/cache@v3
with: with:
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
key: ${{ runner.os }}-go${{ matrix.go }}-${{ matrix.goos }}-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-stable-${{ matrix.goos }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go${{ matrix.go }}-${{ matrix.goos }} key: ${{ runner.os }}-go-stable-${{ matrix.goos }}-${{ matrix.goarch }}-
- name: Install dependencies
run: sudo apt update && sudo apt install -y gcc-mingw-w64-x86-64 # install required gcc
- env: - env:
GOOS: ${{ matrix.goos }} GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }} GOARCH: ${{ matrix.goarch }}
@ -204,6 +208,66 @@ jobs:
with: with:
jobs: ${{ toJSON(needs) }} jobs: ${{ toJSON(needs) }}
# run go test with different go versions
integration:
timeout-minutes: 20
needs: initial-tests-done
name: Integration tests
runs-on: ubuntu-latest
# Service containers to run with `container-job`
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres:13-alpine
# Provide the password for postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dendrite
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Install libolm
run: sudo apt-get install libolm-dev libolm3
- name: Setup go
uses: actions/setup-go@v4
with:
go-version: "stable"
- name: Set up gotestfmt
uses: gotesttools/gotestfmt-action@v2
with:
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-stable-test-race-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-stable-test-race-
- run: go test -race -json -v -coverpkg=./... -coverprofile=cover.out $(go list ./... | grep -v /cmd/dendrite*) 2>&1 | gotestfmt -hide all
env:
POSTGRES_HOST: localhost
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dendrite
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
flags: unittests
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
# run database upgrade tests # run database upgrade tests
upgrade_test: upgrade_test:
name: Upgrade tests name: Upgrade tests
@ -211,23 +275,28 @@ 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: "1.18" go-version: "stable"
- uses: actions/cache@v3 cache: true
- uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
key: ${{ runner.os }}-go-upgrade-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-upgrade-test-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go-upgrade ${{ runner.os }}-go-upgrade-test-
- name: Docker version
run: docker version
- name: Build upgrade-tests - name: Build upgrade-tests
run: go build ./cmd/dendrite-upgrade-tests run: go build ./cmd/dendrite-upgrade-tests
- name: Test upgrade - name: Test upgrade (PostgreSQL)
run: ./dendrite-upgrade-tests --head . run: ./dendrite-upgrade-tests --head .
- name: Test upgrade (SQLite)
run: ./dendrite-upgrade-tests --sqlite --head .
# run database upgrade tests, skipping over one version # run database upgrade tests, skipping over one version
upgrade_test_direct: upgrade_test_direct:
@ -236,22 +305,27 @@ 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: "1.18" go-version: "stable"
- uses: actions/cache@v3 cache: true
- uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
key: ${{ runner.os }}-go-upgrade-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-upgrade-direct-test-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go-upgrade ${{ runner.os }}-go-upgrade-direct-test-
- name: Docker version
run: docker version
- name: Build upgrade-tests - name: Build upgrade-tests
run: go build ./cmd/dendrite-upgrade-tests run: go build ./cmd/dendrite-upgrade-tests
- name: Test upgrade - name: Test upgrade (PostgreSQL)
run: ./dendrite-upgrade-tests -direct -from HEAD-2 --head .
- name: Test upgrade (SQLite)
run: ./dendrite-upgrade-tests -direct -from HEAD-2 --head . run: ./dendrite-upgrade-tests -direct -from HEAD-2 --head .
# run Sytest in different variations # run Sytest in different variations
@ -264,27 +338,34 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- label: SQLite - label: SQLite native
- label: SQLite, full HTTP APIs - label: SQLite Cgo
api: full-http cgo: 1
- label: PostgreSQL - label: PostgreSQL
postgres: postgres postgres: postgres
- label: PostgreSQL, full HTTP APIs
postgres: postgres
api: full-http
container: container:
image: matrixdotorg/sytest-dendrite:latest image: matrixdotorg/sytest-dendrite
volumes: volumes:
- ${{ github.workspace }}:/src - ${{ github.workspace }}:/src
- /root/.cache/go-build:/github/home/.cache/go-build
- /root/.cache/go-mod:/gopath/pkg/mod
env: env:
POSTGRES: ${{ matrix.postgres && 1}} POSTGRES: ${{ matrix.postgres && 1}}
API: ${{ matrix.api && 1 }}
SYTEST_BRANCH: ${{ github.head_ref }} SYTEST_BRANCH: ${{ github.head_ref }}
CGO_ENABLED: ${{ matrix.cgo && 1 }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
/gopath/pkg/mod
key: ${{ runner.os }}-go-sytest-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-sytest-
- name: Run Sytest - name: Run Sytest
run: /bootstrap.sh dendrite run: /bootstrap.sh dendrite
working-directory: /src working-directory: /src
@ -300,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.*, ', ') }})
@ -318,17 +399,15 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- label: SQLite - label: SQLite native
cgo: 0
- label: SQLite, full HTTP APIs - label: SQLite Cgo
api: full-http cgo: 1
- label: PostgreSQL - label: PostgreSQL
postgres: Postgres postgres: Postgres
cgo: 0
- label: PostgreSQL, full HTTP APIs
postgres: Postgres
api: full-http
steps: steps:
# Env vars are set file a file given by $GITHUB_PATH. We need both Go 1.17 and GOPATH on env to run Complement. # Env vars are set file a file given by $GITHUB_PATH. We need both Go 1.17 and GOPATH on env to run Complement.
# See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path # See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
@ -336,16 +415,14 @@ jobs:
run: | run: |
echo "$GOROOT_1_17_X64/bin" >> $GITHUB_PATH echo "$GOROOT_1_17_X64/bin" >> $GITHUB_PATH
echo "~/go/bin" >> $GITHUB_PATH echo "~/go/bin" >> $GITHUB_PATH
- name: "Install Complement Dependencies" - name: "Install Complement Dependencies"
# We don't need to install Go because it is included on the Ubuntu 20.04 image: # We don't need to install Go because it is included on the Ubuntu 20.04 image:
# See https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md specifically GOROOT_1_17_X64 # See https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md specifically GOROOT_1_17_X64
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 get -v github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
- name: Run actions/checkout@v4 for dendrite
- name: Run actions/checkout@v2 for dendrite uses: actions/checkout@v4
uses: actions/checkout@v2
with: with:
path: dendrite path: dendrite
@ -369,12 +446,10 @@ jobs:
if [[ -z "$BRANCH_NAME" || $BRANCH_NAME =~ ^refs/pull/.* ]]; then if [[ -z "$BRANCH_NAME" || $BRANCH_NAME =~ ^refs/pull/.* ]]; then
continue continue
fi fi
(wget -O - "https://github.com/matrix-org/complement/archive/$BRANCH_NAME.tar.gz" | tar -xz --strip-components=1 -C complement) && break (wget -O - "https://github.com/matrix-org/complement/archive/$BRANCH_NAME.tar.gz" | tar -xz --strip-components=1 -C complement) && break
done done
# Build initial Dendrite image # Build initial Dendrite image
- run: docker build -t complement-dendrite -f build/scripts/Complement${{ matrix.postgres }}.Dockerfile . - run: docker build --build-arg=CGO=${{ matrix.cgo }} -t complement-dendrite:${{ matrix.postgres }}${{ matrix.cgo }} -f build/scripts/Complement${{ matrix.postgres }}.Dockerfile .
working-directory: dendrite working-directory: dendrite
env: env:
DOCKER_BUILDKIT: 1 DOCKER_BUILDKIT: 1
@ -382,12 +457,12 @@ 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:
COMPLEMENT_BASE_IMAGE: complement-dendrite:latest COMPLEMENT_BASE_IMAGE: complement-dendrite:${{ matrix.postgres }}${{ matrix.cgo }}
API: ${{ matrix.api && 1 }} COMPLEMENT_SHARE_ENV_PREFIX: COMPLEMENT_DENDRITE_
working-directory: complement working-directory: complement
integration-tests-done: integration-tests-done:
@ -399,6 +474,7 @@ jobs:
upgrade_test_direct, upgrade_test_direct,
sytest, sytest,
complement, complement,
integration
] ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ !cancelled() }} # Run this even if prior jobs were skipped if: ${{ !cancelled() }} # Run this even if prior jobs were skipped
@ -413,6 +489,7 @@ jobs:
permissions: permissions:
packages: write packages: write
contents: read contents: read
security-events: write # To upload Trivy sarif files
if: github.repository == 'matrix-org/dendrite' && github.ref_name == 'main' if: github.repository == 'matrix-org/dendrite' && github.ref_name == 'main'
needs: [integration-tests-done] needs: [integration-tests-done]
uses: matrix-org/dendrite/.github/workflows/docker.yml@main uses: matrix-org/dendrite/.github/workflows/docker.yml@main

View file

@ -24,23 +24,25 @@ jobs:
permissions: permissions:
contents: read contents: read
packages: write packages: write
security-events: write # To upload Trivy sarif files
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Get release tag - 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: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV run: |
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@v1 uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v1 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@v1 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@ -49,12 +51,11 @@ jobs:
- name: Build main monolith image - name: Build main monolith image
if: github.ref_name == 'main' if: github.ref_name == 'main'
id: docker_build_monolith id: docker_build_monolith
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
cache-from: type=gha cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache
cache-to: type=gha,mode=max cache-to: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache,mode=max
context: . context: .
file: ./build/docker/Dockerfile.monolith
platforms: ${{ env.PLATFORMS }} platforms: ${{ env.PLATFORMS }}
push: true push: true
tags: | tags: |
@ -64,12 +65,11 @@ jobs:
- name: Build release monolith image - name: Build release monolith image
if: github.event_name == 'release' # Only for GitHub releases if: github.event_name == 'release' # Only for GitHub releases
id: docker_build_monolith_release id: docker_build_monolith_release
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
context: . context: .
file: ./build/docker/Dockerfile.monolith
platforms: ${{ env.PLATFORMS }} platforms: ${{ env.PLATFORMS }}
push: true push: true
tags: | tags: |
@ -78,65 +78,17 @@ jobs:
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:latest ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:latest
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ env.RELEASE_VERSION }} ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ env.RELEASE_VERSION }}
polylith: - name: Run Trivy vulnerability scanner
name: Polylith image uses: aquasecurity/trivy-action@master
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Get release tag
if: github.event_name == 'release' # Only for GitHub releases
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with: with:
username: ${{ env.DOCKER_HUB_USER }} image-ref: ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }}
password: ${{ secrets.DOCKER_TOKEN }} format: "sarif"
- name: Login to GitHub Containers output: "trivy-results.sarif"
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build main polylith image - name: Upload Trivy scan results to GitHub Security tab
if: github.ref_name == 'main' uses: github/codeql-action/upload-sarif@v2
id: docker_build_polylith
uses: docker/build-push-action@v2
with: with:
cache-from: type=gha sarif_file: "trivy-results.sarif"
cache-to: type=gha,mode=max
context: .
file: ./build/docker/Dockerfile.polylith
platforms: ${{ env.PLATFORMS }}
push: true
tags: |
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }}
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }}
- name: Build release polylith image
if: github.event_name == 'release' # Only for GitHub releases
id: docker_build_polylith_release
uses: docker/build-push-action@v2
with:
cache-from: type=gha
cache-to: type=gha,mode=max
context: .
file: ./build/docker/Dockerfile.polylith
platforms: ${{ env.PLATFORMS }}
push: true
tags: |
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:latest
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:${{ env.RELEASE_VERSION }}
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:latest
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ env.RELEASE_VERSION }}
demo-pinecone: demo-pinecone:
name: Pinecone demo image name: Pinecone demo image
@ -146,30 +98,31 @@ jobs:
packages: write packages: write
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Get release tag - 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: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV run: |
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@v1 uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v1 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@v1 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build main pinecone demo image - name: Build main Pinecone demo image
if: github.ref_name == 'main' if: github.ref_name == 'main'
id: docker_build_demo_pinecone id: docker_build_demo_pinecone
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
@ -181,10 +134,10 @@ jobs:
${{ env.DOCKER_NAMESPACE }}/dendrite-demo-pinecone:${{ github.ref_name }} ${{ env.DOCKER_NAMESPACE }}/dendrite-demo-pinecone:${{ github.ref_name }}
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-pinecone:${{ github.ref_name }} ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-pinecone:${{ github.ref_name }}
- name: Build release pinecone demo image - name: Build release Pinecone demo image
if: github.event_name == 'release' # Only for GitHub releases if: github.event_name == 'release' # Only for GitHub releases
id: docker_build_demo_pinecone_release id: docker_build_demo_pinecone_release
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
@ -193,7 +146,68 @@ jobs:
platforms: ${{ env.PLATFORMS }} platforms: ${{ env.PLATFORMS }}
push: true push: true
tags: | tags: |
${{ env.DOCKER_NAMESPACE }}/dendrite-demo-pinecone:latest ${{ env.DOCKER_NAMESPACE }}/dendrite-demo-yggdrasil:latest
${{ env.DOCKER_NAMESPACE }}/dendrite-demo-pinecone:${{ env.RELEASE_VERSION }} ${{ env.DOCKER_NAMESPACE }}/dendrite-demo-yggdrasil:${{ env.RELEASE_VERSION }}
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-pinecone:latest ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-yggdrasil:latest
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-pinecone:${{ env.RELEASE_VERSION }} ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-yggdrasil:${{ env.RELEASE_VERSION }}
demo-yggdrasil:
name: Yggdrasil demo image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get release tag & build flags
if: github.event_name == 'release' # Only for GitHub releases
run: |
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ env.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to GitHub Containers
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build main Yggdrasil demo image
if: github.ref_name == 'main'
id: docker_build_demo_yggdrasil
uses: docker/build-push-action@v3
with:
cache-from: type=gha
cache-to: type=gha,mode=max
context: .
file: ./build/docker/Dockerfile.demo-yggdrasil
platforms: ${{ env.PLATFORMS }}
push: true
tags: |
${{ env.DOCKER_NAMESPACE }}/dendrite-demo-yggdrasil:${{ github.ref_name }}
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-yggdrasil:${{ github.ref_name }}
- name: Build release Yggdrasil demo image
if: github.event_name == 'release' # Only for GitHub releases
id: docker_build_demo_yggdrasil_release
uses: docker/build-push-action@v3
with:
cache-from: type=gha
cache-to: type=gha,mode=max
context: .
file: ./build/docker/Dockerfile.demo-yggdrasil
platforms: ${{ env.PLATFORMS }}
push: true
tags: |
${{ env.DOCKER_NAMESPACE }}/dendrite-demo-yggdrasil:latest
${{ env.DOCKER_NAMESPACE }}/dendrite-demo-yggdrasil:${{ env.RELEASE_VERSION }}
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-yggdrasil:latest
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-demo-yggdrasil:${{ env.RELEASE_VERSION }}

52
.github/workflows/gh-pages.yml vendored Normal file
View file

@ -0,0 +1,52 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy GitHub Pages dependencies preinstalled
on:
# Runs on pushes targeting the default branch
push:
branches: ["gh-pages"]
paths:
- 'docs/**' # only execute if we have docs changes
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./docs
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

41
.github/workflows/helm.yml vendored Normal file
View file

@ -0,0 +1,41 @@
name: Release Charts
on:
push:
branches:
- main
paths:
- 'helm/**' # only execute if we have helm chart changes
workflow_dispatch:
jobs:
release:
# depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions
# see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.10.0
- name: Run chart-releaser
uses: helm/chart-releaser-action@ed43eb303604cbc0eeec8390544f7748dc6c790d # specific commit, since `mark_as_latest` is not yet in a release
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
with:
config: helm/cr.yaml
charts_dir: helm/
mark_as_latest: false

91
.github/workflows/k8s.yml vendored Normal file
View file

@ -0,0 +1,91 @@
name: k8s
on:
push:
branches: ["main"]
paths:
- 'helm/**' # only execute if we have helm chart changes
pull_request:
branches: ["main"]
paths:
- 'helm/**'
jobs:
lint:
name: Lint Helm chart
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.list-changed.outputs.changed }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: azure/setup-helm@v3
with:
version: v3.10.0
- uses: actions/setup-python@v4
with:
python-version: 3.11
check-latest: true
- uses: helm/chart-testing-action@v2.3.1
- name: Get changed status
id: list-changed
run: |
changed=$(ct list-changed --config helm/ct.yaml --target-branch ${{ github.event.repository.default_branch }})
if [[ -n "$changed" ]]; then
echo "::set-output name=changed::true"
fi
- name: Run lint
run: ct lint --config helm/ct.yaml
# only bother to run if lint step reports a change to the helm chart
install:
needs:
- lint
if: ${{ needs.lint.outputs.changed == 'true' }}
name: Install Helm charts
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.checkoutCommit }}
- name: Install Kubernetes tools
uses: yokawasa/action-setup-kube-tools@v0.8.2
with:
setup-tools: |
helmv3
helm: "3.10.3"
- uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: Set up chart-testing
uses: helm/chart-testing-action@v2.3.1
- name: Create k3d cluster
uses: nolar/setup-k3d-k3s@v1
with:
version: v1.28
- name: Remove node taints
run: |
kubectl taint --all=true nodes node.cloudprovider.kubernetes.io/uninitialized- || true
- name: Run chart-testing (install)
run: ct install --config helm/ct.yaml
# Install the chart using helm directly and test with create-account
- name: Install chart
run: |
helm install --values helm/dendrite/ci/ct-postgres-sharedsecret-values.yaml dendrite helm/dendrite
- name: Wait for Postgres and Dendrite to be up
run: |
kubectl wait --for=condition=ready --timeout=90s pod -l app.kubernetes.io/name=postgresql || kubectl get pods -A
kubectl wait --for=condition=ready --timeout=90s pod -l app.kubernetes.io/name=dendrite || kubectl get pods -A
kubectl get pods -A
kubectl get services
kubectl get ingress
kubectl logs -l app.kubernetes.io/name=dendrite
- name: Run create account
run: |
podName=$(kubectl get pods -l app.kubernetes.io/name=dendrite -o name)
kubectl exec "${podName}" -- /usr/bin/create-account -username alice -password somerandompassword

322
.github/workflows/schedules.yaml vendored Normal file
View file

@ -0,0 +1,322 @@
name: Scheduled
on:
schedule:
- cron: '0 0 * * *' # every day at midnight
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
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
sytest:
needs: check_date
if: ${{ needs.check_date.outputs.should_run != 'false' }}
timeout-minutes: 60
name: "Sytest (${{ matrix.label }})"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- label: SQLite native
- label: SQLite Cgo
cgo: 1
- label: PostgreSQL
postgres: postgres
container:
image: matrixdotorg/sytest-dendrite:latest
volumes:
- ${{ github.workspace }}:/src
- /root/.cache/go-build:/github/home/.cache/go-build
- /root/.cache/go-mod:/gopath/pkg/mod
env:
POSTGRES: ${{ matrix.postgres && 1}}
SYTEST_BRANCH: ${{ github.head_ref }}
RACE_DETECTION: 1
COVER: 1
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
/gopath/pkg/mod
key: ${{ runner.os }}-go-sytest-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-sytest-
- name: Run Sytest
run: /bootstrap.sh dendrite
working-directory: /src
- name: Summarise results.tap
if: ${{ always() }}
run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
- name: Sytest List Maintenance
if: ${{ always() }}
run: /src/show-expected-fail-tests.sh /logs/results.tap /src/sytest-whitelist /src/sytest-blacklist
continue-on-error: true # not fatal
- name: Are We Synapse Yet?
if: ${{ always() }}
run: /src/are-we-synapse-yet.py /logs/results.tap -v
continue-on-error: true # not fatal
- name: Upload Sytest logs
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: Sytest Logs - ${{ job.status }} - (Dendrite ${{ join(matrix.*, ' ') }})
path: |
/logs/results.tap
/logs/**/*.log*
/logs/**/covdatafiles/**
sytest-coverage:
timeout-minutes: 5
name: "Sytest Coverage"
runs-on: ubuntu-latest
needs: [ sytest, check_date ] # only run once Sytest is done and there was a commit
if: ${{ always() && needs.check_date.outputs.should_run != 'false' }}
steps:
- uses: actions/checkout@v4
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: 'stable'
cache: true
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Collect coverage
run: |
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
go tool covdata func -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./final.cov
flags: sytest
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
# run Complement
complement:
needs: check_date
if: ${{ needs.check_date.outputs.should_run != 'false' }}
name: "Complement (${{ matrix.label }})"
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- label: SQLite native
cgo: 0
- label: SQLite Cgo
cgo: 1
- label: PostgreSQL
postgres: Postgres
cgo: 0
steps:
# Env vars are set file a file given by $GITHUB_PATH. We need both Go 1.17 and GOPATH on env to run Complement.
# See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
- name: "Set Go Version"
run: |
echo "$GOROOT_1_17_X64/bin" >> $GITHUB_PATH
echo "~/go/bin" >> $GITHUB_PATH
- name: "Install Complement Dependencies"
# We don't need to install Go because it is included on the Ubuntu 20.04 image:
# See https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md specifically GOROOT_1_17_X64
run: |
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
- name: Run actions/checkout@v4 for dendrite
uses: actions/checkout@v4
with:
path: dendrite
# Attempt to check out the same branch of Complement as the PR. If it
# doesn't exist, fallback to main.
- name: Checkout complement
shell: bash
run: |
mkdir -p complement
# Attempt to use the version of complement which best matches the current
# build. Depending on whether this is a PR or release, etc. we need to
# use different fallbacks.
#
# 1. First check if there's a similarly named branch (GITHUB_HEAD_REF
# for pull requests, otherwise GITHUB_REF).
# 2. Attempt to use the base branch, e.g. when merging into release-vX.Y
# (GITHUB_BASE_REF for pull requests).
# 3. Use the default complement branch ("master").
for BRANCH_NAME in "$GITHUB_HEAD_REF" "$GITHUB_BASE_REF" "${GITHUB_REF#refs/heads/}" "master"; do
# Skip empty branch names and merge commits.
if [[ -z "$BRANCH_NAME" || $BRANCH_NAME =~ ^refs/pull/.* ]]; then
continue
fi
(wget -O - "https://github.com/matrix-org/complement/archive/$BRANCH_NAME.tar.gz" | tar -xz --strip-components=1 -C complement) && break
done
# Build initial Dendrite image
- run: docker build --build-arg=CGO=${{ matrix.cgo }} -t complement-dendrite:${{ matrix.postgres }}${{ matrix.cgo }} -f build/scripts/Complement${{ matrix.postgres }}.Dockerfile .
working-directory: dendrite
env:
DOCKER_BUILDKIT: 1
- name: Create post test script
run: |
cat <<EOF > /tmp/posttest.sh
#!/bin/bash
mkdir -p /tmp/Complement/logs/\$2/\$1/
docker cp \$1:/tmp/covdatafiles/. /tmp/Complement/logs/\$2/\$1/
EOF
chmod +x /tmp/posttest.sh
# Run Complement
- run: |
set -o pipefail &&
go test -v -json -tags dendrite_blacklist ./tests ./tests/csapi 2>&1 | gotestfmt -hide all
shell: bash
name: Run Complement Tests
env:
COMPLEMENT_BASE_IMAGE: complement-dendrite:${{ matrix.postgres }}${{ matrix.cgo }}
COMPLEMENT_SHARE_ENV_PREFIX: COMPLEMENT_DENDRITE_
COMPLEMENT_DENDRITE_COVER: 1
COMPLEMENT_POST_TEST_SCRIPT: /tmp/posttest.sh
working-directory: complement
- name: Upload Complement logs
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: Complement Logs - (Dendrite ${{ join(matrix.*, ' ') }})
path: |
/tmp/Complement/logs/**
complement-coverage:
timeout-minutes: 5
name: "Complement Coverage"
runs-on: ubuntu-latest
needs: [ complement, check_date ] # only run once Complements is done and there was a commit
if: ${{ always() && needs.check_date.outputs.should_run != 'false' }}
steps:
- uses: actions/checkout@v4
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: 'stable'
cache: true
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Collect coverage
run: |
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
go tool covdata func -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./final.cov
flags: complement
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} # required
element-web:
if: ${{ false }} # disable for now, as Cypress has been replaced by Playwright
timeout-minutes: 120
runs-on: ubuntu-latest
steps:
- uses: tecolicom/actions-use-apt-tools@v1
with:
# Our test suite includes some screenshot tests with unusual diacritics, which are
# supposed to be covered by STIXGeneral.
tools: fonts-stix
- uses: actions/checkout@v4
with:
repository: matrix-org/matrix-react-sdk
- uses: actions/setup-node@v3
with:
cache: 'yarn'
- name: Fetch layered build
run: scripts/ci/layered.sh
- name: Copy config
run: cp element.io/develop/config.json config.json
working-directory: ./element-web
- name: Build
env:
CI_PACKAGE: true
NODE_OPTIONS: "--openssl-legacy-provider"
run: yarn build
working-directory: ./element-web
- name: Edit Test Config
run: |
sed -i '/HOMESERVER/c\ HOMESERVER: "dendrite",' cypress.config.ts
- name: "Run cypress tests"
uses: cypress-io/github-action@v4.1.1
with:
browser: chrome
start: npx serve -p 8080 ./element-web/webapp
wait-on: 'http://localhost:8080'
env:
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
TMPDIR: ${{ runner.temp }}
element-web-pinecone:
if: ${{ false }} # disable for now, as Cypress has been replaced by Playwright
timeout-minutes: 120
runs-on: ubuntu-latest
steps:
- uses: tecolicom/actions-use-apt-tools@v1
with:
# Our test suite includes some screenshot tests with unusual diacritics, which are
# supposed to be covered by STIXGeneral.
tools: fonts-stix
- uses: actions/checkout@v4
with:
repository: matrix-org/matrix-react-sdk
- uses: actions/setup-node@v3
with:
cache: 'yarn'
- name: Fetch layered build
run: scripts/ci/layered.sh
- name: Copy config
run: cp element.io/develop/config.json config.json
working-directory: ./element-web
- name: Build
env:
CI_PACKAGE: true
NODE_OPTIONS: "--openssl-legacy-provider"
run: yarn build
working-directory: ./element-web
- name: Edit Test Config
run: |
sed -i '/HOMESERVER/c\ HOMESERVER: "dendritePinecone",' cypress.config.ts
- name: "Run cypress tests"
uses: cypress-io/github-action@v4.1.1
with:
browser: chrome
start: npx serve -p 8080 ./element-web/webapp
wait-on: 'http://localhost:8080'
env:
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
TMPDIR: ${{ runner.temp }}

9
.gitignore vendored
View file

@ -5,6 +5,7 @@
# Allow GitHub config # Allow GitHub config
!.github !.github
!.forgejo
# Downloads # Downloads
/.downloads /.downloads
@ -56,6 +57,7 @@ dendrite.yaml
# Database files # Database files
*.db *.db
*.db-journal
# Log files # Log files
*.log* *.log*
@ -73,3 +75,10 @@ complement/
docs/_site docs/_site
media_store/ media_store/
build
# golang workspaces
go.work*
# helm chart
helm/dendrite/charts/

View file

@ -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
@ -179,9 +161,7 @@ linters-settings:
linters: linters:
enable: enable:
- deadcode
- errcheck - errcheck
- goconst
- gocyclo - gocyclo
- goimports # Does everything gofmt does - goimports # Does everything gofmt does
- gosimple - gosimple
@ -191,10 +171,8 @@ linters:
- misspell # Check code comments, whereas misspell in CI checks *.md files - misspell # Check code comments, whereas misspell in CI checks *.md files
- nakedret - nakedret
- staticcheck - staticcheck
- structcheck
- unparam - unparam
- unused - unused
- varcheck
enable-all: false enable-all: false
disable: disable:
- bodyclose - bodyclose
@ -214,12 +192,31 @@ linters:
- stylecheck - stylecheck
- typecheck # Should turn back on soon - typecheck # Should turn back on soon
- unconvert # Should turn back on soon - unconvert # Should turn back on soon
- goconst # Slightly annoying, as it reports "issues" in SQL statements
disable-all: false disable-all: false
presets: presets:
fast: false fast: false
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

View file

@ -1,5 +1,369 @@
# Changelog # Changelog
## 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)
Upgrading to this version is **highly** recommended, as it fixes a long-standing bug in the state resolution
algorithm.
### Fixes:
- The "device list updater" now de-duplicates the servers to fetch devices from on startup. (This also
avoids spamming the logs when shutting down.)
- A bug in the state resolution algorithm has been fixed. This bug could result in users "being reset"
out of rooms and other missing state events due to calculating the wrong state.
- A bug when setting notifications from Element Android has been fixed by implementing MSC3987
### Features
- Updated dependencies
- Internal NATS Server has been updated from v2.9.19 to v2.9.23
## Dendrite 0.13.3 (2023-09-28)
### Fixes:
- The `user_id` query parameter when authenticating is now used correctly (contributed by [tulir](https://github.com/tulir))
- Invitations are now correctly pushed to devices
- A bug which could result in the corruption of `m.direct` account data has been fixed
### Features
- [Sliding Sync proxy](https://github.com/matrix-org/sliding-sync) can be configured in the `/.well-known/matrix/client` response
- Room version 11 is now supported
- Clients can request the `federation` `event_format` when creating filters
- Many under the hood improvements for [MSC4014: Pseudonymous Identities](https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/pseudo-ids/proposals/4014-pseudonymous-identities.md)
### Other
- Dendrite now requires Go 1.20 if building from source
## Dendrite 0.13.2 (2023-08-23)
### Fixes:
- Migrations in SQLite are now prepared on the correct context (transaction or database)
- The `InputRoomEvent` stream now has a maximum age of 24h, which should help with slow start up times of NATS JetStream (contributed by [neilalexander](https://github.com/neilalexander))
- Event size checks are more in line with Synapse
- Requests to `/messages` have been optimized, possibly reducing database round trips
- Re-add the revision of Dendrite when building from source (Note: This only works if git is installed)
- Getting local members to notify has been optimized, which should significantly reduce memory allocation and cache usage
- When getting queried about user profiles, we now return HTTP404 if the user/profiles does not exist
- Background federated joins should now be fixed and not timeout after a short time
- Database connections are now correctly re-used
- Restored the old behavior of the `/purgeRoom` admin endpoint (does not evacuate the room before purging)
- Don't expose information about the system when trying to download files that don't exist
### Features
- Further improvements and fixes for [MSC4014: Pseudonymous Identities](https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/pseudo-ids/proposals/4014-pseudonymous-identities.md)
- Lookup correct prev events in the sync API
- Populate `prev_sender` correctly in the sync API
- Event federation should work better
- Added new `dendrite_up` Prometheus metric, containing the version of Dendrite
- Space summaries ([MSC2946](https://github.com/matrix-org/matrix-spec-proposals/pull/2946)) have been moved from MSC to being natively supported
- For easier issue investigation, logs for application services now contain the application service ID (contributed by [maxberger](https://github.com/maxberger))
- The default room version to use when creating rooms can now be configured using `room_server.default_room_version`
## Dendrite 0.13.1 (2023-07-06)
This releases fixes a long-standing "off-by-one" error which could result in state resets. Upgrading to this version is **highly** recommended.
When deduplicating state events, we were checking if the event in question was already in a state snapshot. If it was in a previous state snapshot, we would
then remove it from the list of events to store. If this happened, we were, unfortunately, skipping the next event to check. This resulted in
events getting stored in state snapshots where they may not be needed. When we now compared two of those state snapshots, one of them
contained the skipped event, while the other didn't. This difference possibly shouldn't exist, resulting in unexpected state resets and explains
reports of missing state events as well.
Rooms where a state reset occurred earlier should, hopefully, reconcile over time.
### Fixes:
- A long-standing "off-by-one" error has been fixed, which could result in state resets
- Roomserver Prometheus Metrics are available again
### Features
- Updated dependencies
- Internal NATS Server has been updated from v2.9.15 to v2.9.19
## Dendrite 0.13.0 (2023-06-30)
### Features
- Results in responses to `/search` now highlight words more accurately and not only the search terms as before
- Support for connecting to appservices listening on unix sockets has been added (contributed by [cyberb](https://github.com/cyberb))
- Admin APIs for token authenticated registration have been added (contributed by [santhoshivan23](https://github.com/santhoshivan23))
- Initial support for [MSC4014: Pseudonymous Identities](https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/pseudo-ids/proposals/4014-pseudonymous-identities.md)
- This is **highly experimental**, things like changing usernames/avatars, inviting users, upgrading rooms isn't working
### Fixes
- `m.upload.size` is now optional, finally allowing uploads with unlimited file size
- A bug while resolving server names has been fixed (contributed by [anton-molyboha](https://github.com/anton-molyboha))
- Application services should only receive one invitation instead of 2 (or worse), which could result in state resets previously
- Several admin endpoints are now using `POST` instead of `GET`
- `/delete_devices` now uses user-interactive authentication
- Several "membership" (e.g `/kick`, `/ban`) endpoints are using less heavy database queries to check if the user is allowed to perform this action
- `/3pid` endpoints are now available on `/v3` instead of the `/unstable` prefix
- Upgrading rooms ignores state events of other users, which could result in failed upgrades before
- Uploading key backups with a wrong version now returns `M_WRONG_ROOM_KEYS_VERSION`
- A potential state reset when joining the same room multiple times in short sequence has been fixed
- A bug where we returned the full event as `redacted_because` in redaction events has been fixed
- The `displayname` and `avatar_url` can now be set to empty strings
- Unsafe hotserving of files has been fixed (contributed by [joshqou](https://github.com/joshqou))
- Joining new rooms would potentially return "redacted" events, due to history visibility not being set correctly, this could result in events being rejected
- Backfilling resulting in `unsuported room version ''` should now be solved
### Other
- Huge refactoring of Dendrite and gomatrixserverlib
## Dendrite 0.12.0 (2023-03-13)
### Features
- The userapi and keyserver have been merged (no actions needed regarding the database)
- The internal NATS JetStream server is now using logrus for logging (contributed by [dvob](https://github.com/dvob))
- The roomserver database has been refactored to have separate interfaces when working with rooms and events. Also includes increased usage of the cache to avoid database round trips. (database is unchanged)
- The pinecone demo now shuts down more cleanly
- The Helm chart now has the ability to deploy a Grafana chart as well (contributed by [genofire](https://github.com/genofire))
- Support for listening on unix sockets has been added (contributed by [cyberb](https://github.com/cyberb))
- The internal NATS server was updated to v2.9.15
- Initial support for `runtime/trace` has been added, to further track down long-running tasks
### Fixes
- The `session_id` is now correctly set when using SQLite
- An issue where device keys could be removed if a device ID is reused has been fixed
- A possible DoS issue related to relations has been fixed (reported by [sleroq](https://github.com/sleroq))
- When backfilling events, errors are now ignored if we still could fetch events
### Other
- **⚠️ DEPRECATION: Polylith/HTTP API mode has been removed**
- The default endpoint to report usages stats to has been updated
## Dendrite 0.11.1 (2023-02-10)
**⚠️ DEPRECATION WARNING: This is the last release to have polylith and HTTP API mode. Future releases are monolith only.**
### Features
* Dendrite can now be compiled against Go 1.20
* Initial store and forward support has been added
* A landing page showing that Dendrite is running has been added (contributed by [LukasLJL](https://github.com/LukasLJL))
### Fixes
- `/sync` is now using significantly less database round trips when using Postgres, resulting in faster initial syncs, allowing larger accounts to login again
- Many under the hood pinecone improvements
- Publishing rooms is now possible again
## Dendrite 0.11.0 (2023-01-20)
The last three missing federation API Sytests have been fixed - bringing us to 100% server-server Synapse parity, with client-server parity at 93% 🎉
### Features
* Added `/_dendrite/admin/purgeRoom/{roomID}` to clean up the database
* The default room version was updated to 10 (contributed by [FSG-Cat](https://github.com/FSG-Cat))
### Fixes
* An oversight in the `create-config` binary, which now correctly sets the media path if specified (contributed by [BieHDC](https://github.com/BieHDC))
* The Helm chart now uses the `$.Chart.AppVersion` as the default image version to pull, with the possibility to override it (contributed by [genofire](https://github.com/genofire))
## Dendrite 0.10.9 (2023-01-17)
### Features
* Stale device lists are now cleaned up on startup, removing entries for users the server doesn't share a room with anymore
* Dendrite now has its own Helm chart
* Guest access is now handled correctly (disallow joins, kick guests on revocation of guest access, as well as over federation)
### Fixes
* Push rules have seen several tweaks and fixes, which should, for example, fix notifications for `m.read_receipts`
* Outgoing presence will now correctly be sent to newly joined hosts
* Fixes the `/_dendrite/admin/resetPassword/{userID}` admin endpoint to use the correct variable
* Federated backfilling for medium/large rooms has been fixed
* `/login` causing wrong device list updates has been resolved
* `/sync` should now return the correct room summary heroes
* The default config options for `recaptcha_sitekey_class` and `recaptcha_form_field` are now set correctly
* `/messages` now omits empty `state` to be more spec compliant (contributed by [handlerug](https://github.com/handlerug))
* `/sync` has been optimised to only query state events for history visibility if they are really needed
## Dendrite 0.10.8 (2022-11-29)
### Features
* The built-in NATS Server has been updated to version 2.9.8
* A number of under-the-hood changes have been merged for future virtual hosting support in Dendrite (running multiple domain names on the same Dendrite deployment)
### Fixes
* Event auth handling of invites has been refactored, which should fix some edge cases being handled incorrectly
* Fix a bug when returning an empty protocol list, which could cause Element to display "The homeserver may be too old to support third party networks" when opening the public room directory
* The sync API will no longer filter out the user's own membership when using lazy-loading
* Dendrite will now correctly detect JetStream consumers being deleted, stopping the consumer goroutine as needed
* A panic in the federation API where the server list could go out of bounds has been fixed
* Blacklisted servers will now be excluded when querying joined servers, which improves CPU usage and performs less unnecessary outbound requests
* A database writer will now be used to assign state key NIDs when requesting NIDs that may not exist yet
* Dendrite will now correctly move local aliases for an upgraded room when the room is upgraded remotely
* Dendrite will now correctly move account data for an upgraded room when the room is upgraded remotely
* Missing state key NIDs will now be allocated on request rather than returning an error
* Guest access is now correctly denied on a number of endpoints
* Presence information will now be correctly sent for new private chats
* A number of unspecced fields have been removed from outbound `/send` transactions
## Dendrite 0.10.7 (2022-11-04)
### Features
* Dendrite will now use a native SQLite port when building with `CGO_ENABLED=0`
* A number of `thirdparty` endpoints have been added, improving support for appservices
### Fixes
* The `"state"` section of the `/sync` response is no longer limited, so state events should not be dropped unexpectedly
* The deduplication of the `"timeline"` and `"state"` sections in `/sync` is now performed after applying history visibility, so state events should not be dropped unexpectedly
* The `prev_batch` token returned by `/sync` is now calculated after applying history visibility, so that the pagination boundaries are correct
* The room summary membership counts in `/sync` should now be calculated properly in more cases
* A false membership leave event should no longer be sent down `/sync` as a result of retiring an accepted invite (contributed by [tak-hntlabs](https://github.com/tak-hntlabs))
* Presence updates are now only sent to other servers for which the user shares rooms
* A bug which could cause a panic when converting events into the `ClientEvent` format has been fixed
## Dendrite 0.10.6 (2022-11-01)
### Features
* History visibility checks have been optimised, which should speed up response times on a variety of endpoints (including `/sync`, `/messages`, `/context` and others) and reduce database load
* The built-in NATS Server has been updated to version 2.9.4
* Some other minor dependencies have been updated
### Fixes
* A panic has been fixed in the sync API PDU stream which could cause requests to fail
* The `/members` response now contains the `room_id` field, which may fix some E2EE problems with clients using the JS SDK (contributed by [ashkitten](https://github.com/ashkitten))
* The auth difference calculation in state resolution v2 has been tweaked for clarity (and moved into gomatrixserverlib with the rest of the state resolution code)
## Dendrite 0.10.5 (2022-10-31)
### Features
* It is now possible to use hCaptcha instead of reCAPTCHA for protecting registration
* A new `auto_join_rooms` configuration option has been added for automatically joining new users to a set of rooms
* A new `/_dendrite/admin/downloadState/{serverName}/{roomID}` endpoint has been added, which allows a server administrator to attempt to repair a room with broken room state by downloading a state snapshot from another federated server in the room
### Fixes
* Querying cross-signing keys for users should now be considerably faster
* A bug in state resolution where some events were not correctly selected for third-party invites has been fixed
* A bug in state resolution which could result in `not in room` event rejections has been fixed
* When accepting a DM invite, it should now be possible to see messages that were sent before the invite was accepted
* Claiming remote E2EE one-time keys has been refactored and should be more reliable now
* Various fixes have been made to the `/members` endpoint, which may help with E2EE reliability and clients rendering memberships
* A race condition in the federation API destination queues has been fixed when associating queued events with remote server destinations
* A bug in the sync API where too many events were selected resulting in high CPU usage has been fixed
* Configuring the avatar URL for the Server Notices user should work correctly now
## Dendrite 0.10.4 (2022-10-21)
### Features
* Various tables belonging to the user API will be renamed so that they are namespaced with the `userapi_` prefix
* Note that, after upgrading to this version, you should not revert to an older version of Dendrite as the database changes **will not** be reverted automatically
* The backoff and retry behaviour in the federation API has been refactored and improved
### Fixes
* Private read receipt support is now advertised in the client `/versions` endpoint
* Private read receipts will now clear notification counts properly
* A bug where a false `leave` membership transition was inserted into the timeline after accepting an invite has been fixed
* Some panics caused by concurrent map writes in the key server have been fixed
* The sync API now calculates membership transitions from state deltas more accurately
* Transaction IDs are now scoped to endpoints, which should fix some bugs where transaction ID reuse could cause nonsensical cached responses from some endpoints
* The length of the `type`, `sender`, `state_key` and `room_id` fields in events are now verified by number of bytes rather than codepoints after a spec clarification, reverting a change made in Dendrite 0.9.6
## Dendrite 0.10.3 (2022-10-14)
### Features
* Event relations are now tracked and support for the `/room/{roomID}/relations/...` client API endpoints have been added
* Support has been added for private read receipts
* The built-in NATS Server has been updated to version 2.9.3
### Fixes
* The `unread_notifications` are now always populated in joined room responses
* The `/get_missing_events` federation API endpoint should now work correctly for rooms with `joined` and `invited` visibility settings, returning redacted events for events that other servers are not allowed to see
* The `/event` client API endpoint now applies history visibility correctly
* Read markers should now be updated much more reliably
* A rare bug in the sync API which could cause some `join` memberships to be incorrectly overwritten by other memberships when working out which rooms to populate has been fixed
* The federation API now correctly updates the joined hosts table during a state rewrite
## Dendrite 0.10.2 (2022-10-07) ## Dendrite 0.10.2 (2022-10-07)
### Features ### Features

49
Dockerfile Normal file
View file

@ -0,0 +1,49 @@
#syntax=docker/dockerfile:1.2
#
# base installs required dependencies and runs go mod download to cache dependencies
#
# Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21-alpine3.18 AS base
RUN apk --update --no-cache add bash build-base curl git
#
# build creates all needed binaries
#
FROM --platform=${BUILDPLATFORM} base AS build
WORKDIR /src
ARG TARGETOS
ARG TARGETARCH
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
USERARCH=`go env GOARCH` \
GOARCH="$TARGETARCH" \
GOOS="linux" \
CGO_ENABLED=$([ "$TARGETARCH" = "$USERARCH" ] && echo "1" || echo "0") \
go build -v -trimpath -o /out/ ./cmd/...
#
# Builds the Dendrite image containing all required binaries
#
FROM alpine:latest
RUN apk --update --no-cache add curl
LABEL org.opencontainers.image.title="Dendrite"
LABEL org.opencontainers.image.description="Next-generation Matrix homeserver written in Go"
LABEL org.opencontainers.image.source="https://github.com/matrix-org/dendrite"
LABEL org.opencontainers.image.licenses="Apache-2.0"
LABEL org.opencontainers.image.documentation="https://matrix-org.github.io/dendrite/"
LABEL org.opencontainers.image.vendor="The Matrix.org Foundation C.I.C."
COPY --from=build /out/create-account /usr/bin/create-account
COPY --from=build /out/generate-config /usr/bin/generate-config
COPY --from=build /out/generate-keys /usr/bin/generate-keys
COPY --from=build /out/dendrite /usr/bin/dendrite
VOLUME /etc/dendrite
WORKDIR /etc/dendrite
ENTRYPOINT ["/usr/bin/dendrite"]
EXPOSE 8008 8448

View file

@ -13,7 +13,7 @@ It intends to provide an **efficient**, **reliable** and **scalable** alternativ
Dendrite is **beta** software, which means: Dendrite is **beta** software, which means:
- Dendrite is ready for early adopters. We recommend running in Monolith mode with a PostgreSQL database. - Dendrite is ready for early adopters. We recommend running Dendrite with a PostgreSQL database.
- Dendrite has periodic releases. We intend to release new versions as we fix bugs and land significant features. - Dendrite has periodic releases. We intend to release new versions as we fix bugs and land significant features.
- Dendrite supports database schema upgrades between releases. This means you should never lose your messages when upgrading Dendrite. - Dendrite supports database schema upgrades between releases. This means you should never lose your messages when upgrading Dendrite.
@ -21,10 +21,9 @@ This does not mean:
- Dendrite is bug-free. It has not yet been battle-tested in the real world and so will be error prone initially. - Dendrite is bug-free. It has not yet been battle-tested in the real world and so will be error prone initially.
- Dendrite is feature-complete. There may be client or federation APIs that are not implemented. - Dendrite is feature-complete. There may be client or federation APIs that are not implemented.
- Dendrite is ready for massive homeserver deployments. There is no sharding of microservices (although it is possible to run them on separate machines) and there is no high-availability/clustering support. - Dendrite is ready for massive homeserver deployments. There is no high-availability/clustering support.
Currently, we expect Dendrite to function well for small (10s/100s of users) homeserver deployments as well as P2P Matrix nodes in-browser or on mobile devices. Currently, we expect Dendrite to function well for small (10s/100s of users) homeserver deployments as well as P2P Matrix nodes in-browser or on mobile devices.
In the future, we will be able to scale up to gigantic servers (equivalent to `matrix.org`) via polylith mode.
If you have further questions, please take a look at [our FAQ](docs/FAQ.md) or join us in: If you have further questions, please take a look at [our FAQ](docs/FAQ.md) or join us in:
@ -37,7 +36,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.18 or later. To build Dendrite, you will need Go 1.20 or later.
For a usable federating Dendrite deployment, you will also need: For a usable federating Dendrite deployment, you will also need:
@ -48,7 +47,7 @@ For a usable federating Dendrite deployment, you will also need:
Also recommended are: Also recommended are:
- A PostgreSQL database engine, which will perform better than SQLite with many users and/or larger rooms - A PostgreSQL database engine, which will perform better than SQLite with many users and/or larger rooms
- A reverse proxy server, such as nginx, configured [like this sample](https://github.com/matrix-org/dendrite/blob/master/docs/nginx/monolith-sample.conf) - A reverse proxy server, such as nginx, configured [like this sample](https://github.com/matrix-org/dendrite/blob/main/docs/nginx/dendrite-sample.conf)
The [Federation Tester](https://federationtester.matrix.org) can be used to verify your deployment. The [Federation Tester](https://federationtester.matrix.org) can be used to verify your deployment.
@ -61,7 +60,7 @@ The following instructions are enough to get Dendrite started as a non-federatin
```bash ```bash
$ git clone https://github.com/matrix-org/dendrite $ git clone https://github.com/matrix-org/dendrite
$ cd dendrite $ cd dendrite
$ ./build.sh $ go build -o bin/ ./cmd/...
# Generate a Matrix signing key for federation (required) # Generate a Matrix signing key for federation (required)
$ ./bin/generate-keys --private-key matrix_key.pem $ ./bin/generate-keys --private-key matrix_key.pem
@ -72,10 +71,10 @@ $ ./bin/generate-keys --tls-cert server.crt --tls-key server.key
# Copy and modify the config file - you'll need to set a server name and paths to the keys # Copy and modify the config file - you'll need to set a server name and paths to the keys
# at the very least, along with setting up the database connection strings. # at the very least, along with setting up the database connection strings.
$ cp dendrite-sample.monolith.yaml dendrite.yaml $ cp dendrite-sample.yaml dendrite.yaml
# Build and run the server: # Build and run the server:
$ ./bin/dendrite-monolith-server --tls-cert server.crt --tls-key server.key --config dendrite.yaml $ ./bin/dendrite --tls-cert server.crt --tls-key server.key --config dendrite.yaml
# Create an user account (add -admin for an admin user). # Create an user account (add -admin for an admin user).
# Specify the localpart only, e.g. 'alice' for '@alice:domain.com' # Specify the localpart only, e.g. 'alice' for '@alice:domain.com'
@ -86,9 +85,9 @@ Then point your favourite Matrix client at `http://localhost:8008` or `https://l
## Progress ## Progress
We use a script called Are We Synapse Yet which checks Sytest compliance rates. Sytest is a black-box homeserver We use a script called "Are We Synapse Yet" which checks Sytest compliance rates. Sytest is a black-box homeserver
test rig with around 900 tests. The script works out how many of these tests are passing on Dendrite and it test rig with around 900 tests. The script works out how many of these tests are passing on Dendrite and it
updates with CI. As of August 2022 we're at around 90% CS API coverage and 95% Federation coverage, though check updates with CI. As of January 2023, we have 100% server-server parity with Synapse, and the client-server parity is at 93% , though check
CI for the latest numbers. In practice, this means you can communicate locally and via federation with Synapse CI for the latest numbers. In practice, this means you can communicate locally and via federation with Synapse
servers such as matrix.org reasonably well, although there are still some missing features (like SSO and Third-party ID APIs). servers such as matrix.org reasonably well, although there are still some missing features (like SSO and Third-party ID APIs).

View file

@ -19,11 +19,11 @@ package api
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
) )
// AppServiceInternalAPI is used to query user and room alias data from application // AppServiceInternalAPI is used to query user and room alias data from application
@ -41,6 +41,10 @@ type AppServiceInternalAPI interface {
req *UserIDExistsRequest, req *UserIDExistsRequest,
resp *UserIDExistsResponse, resp *UserIDExistsResponse,
) error ) error
Locations(ctx context.Context, req *LocationRequest, resp *LocationResponse) error
User(ctx context.Context, request *UserRequest, response *UserResponse) error
Protocols(ctx context.Context, req *ProtocolRequest, resp *ProtocolResponse) error
} }
// RoomAliasExistsRequest is a request to an application service // RoomAliasExistsRequest is a request to an application service
@ -77,6 +81,85 @@ type UserIDExistsResponse struct {
UserIDExists bool `json:"exists"` UserIDExists bool `json:"exists"`
} }
const (
ASProtocolLegacyPath = "/_matrix/app/unstable/thirdparty/protocol/"
ASUserLegacyPath = "/_matrix/app/unstable/thirdparty/user"
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 {
Protocol string `json:"protocol,omitempty"`
}
type ProtocolResponse struct {
Protocols map[string]ASProtocolResponse `json:"protocols"`
Exists bool `json:"exists"`
}
type ASProtocolResponse struct {
FieldTypes map[string]FieldType `json:"field_types,omitempty"` // NOTSPEC: field_types is required by the spec
Icon string `json:"icon"`
Instances []ProtocolInstance `json:"instances"`
LocationFields []string `json:"location_fields"`
UserFields []string `json:"user_fields"`
}
type FieldType struct {
Placeholder string `json:"placeholder"`
Regexp string `json:"regexp"`
}
type ProtocolInstance struct {
Description string `json:"desc"`
Icon string `json:"icon,omitempty"`
NetworkID string `json:"network_id,omitempty"` // NOTSPEC: network_id is required by the spec
Fields json.RawMessage `json:"fields,omitempty"` // NOTSPEC: fields is required by the spec
}
type UserRequest struct {
Protocol string `json:"protocol"`
Params string `json:"params"`
}
type UserResponse struct {
Users []ASUserResponse `json:"users,omitempty"`
Exists bool `json:"exists,omitempty"`
}
type ASUserResponse struct {
Protocol string `json:"protocol"`
UserID string `json:"userid"`
Fields json.RawMessage `json:"fields"`
}
type LocationRequest struct {
Protocol string `json:"protocol"`
Params string `json:"params"`
}
type LocationResponse struct {
Locations []ASLocationResponse `json:"locations,omitempty"`
Exists bool `json:"exists,omitempty"`
}
type ASLocationResponse struct {
Alias string `json:"alias"`
Protocol string `json:"protocol"`
Fields json.RawMessage `json:"fields"`
}
// ErrProfileNotExists is returned when trying to lookup a user's profile that
// doesn't exist locally.
var ErrProfileNotExists = errors.New("no known profile for given user ID")
// RetrieveUserProfile is a wrapper that queries both the local database and // RetrieveUserProfile is a wrapper that queries both the local database and
// application services for a given user's profile // application services for a given user's profile
// TODO: Remove this, it's called from federationapi and clientapi but is a pure function // TODO: Remove this, it's called from federationapi and clientapi but is a pure function
@ -84,25 +167,11 @@ func RetrieveUserProfile(
ctx context.Context, ctx context.Context,
userID string, userID string,
asAPI AppServiceInternalAPI, asAPI AppServiceInternalAPI,
profileAPI userapi.ClientUserAPI, profileAPI userapi.ProfileAPI,
) (*authtypes.Profile, error) { ) (*authtypes.Profile, error) {
localpart, _, err := gomatrixserverlib.SplitID('@', userID)
if err != nil {
return nil, err
}
// Try to query the user from the local database // Try to query the user from the local database
res := &userapi.QueryProfileResponse{} profile, err := profileAPI.QueryProfile(ctx, userID)
err = profileAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: userID}, res) if err == nil {
if err != nil {
return nil, err
}
profile := &authtypes.Profile{
Localpart: localpart,
DisplayName: res.DisplayName,
AvatarURL: res.AvatarURL,
}
if res.UserExists {
return profile, nil return profile, nil
} }
@ -115,19 +184,15 @@ func RetrieveUserProfile(
// If no user exists, return // If no user exists, return
if !userResp.UserIDExists { if !userResp.UserIDExists {
return nil, errors.New("no known profile for given user ID") return nil, ErrProfileNotExists
} }
// Try to query the user from the local database again // Try to query the user from the local database again
err = profileAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: userID}, res) profile, err = profileAPI.QueryProfile(ctx, userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// profile should not be nil at this point // profile should not be nil at this point
return &authtypes.Profile{ return profile, nil
Localpart: localpart,
DisplayName: res.DisplayName,
AvatarURL: res.AvatarURL,
}, nil
} }

View file

@ -16,62 +16,49 @@ package appservice
import ( import (
"context" "context"
"crypto/tls" "sync"
"net/http"
"time"
"github.com/gorilla/mux" "github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/dendrite/setup/process"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
appserviceAPI "github.com/matrix-org/dendrite/appservice/api" appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/appservice/consumers" "github.com/matrix-org/dendrite/appservice/consumers"
"github.com/matrix-org/dendrite/appservice/inthttp"
"github.com/matrix-org/dendrite/appservice/query" "github.com/matrix-org/dendrite/appservice/query"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/base"
"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"
) )
// AddInternalRoutes registers HTTP handlers for internal API calls
func AddInternalRoutes(router *mux.Router, queryAPI appserviceAPI.AppServiceInternalAPI) {
inthttp.AddRoutes(queryAPI, router)
}
// NewInternalAPI returns a concerete implementation of the internal API. Callers // NewInternalAPI returns a concerete implementation of the internal API. Callers
// can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes. // can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes.
func NewInternalAPI( func NewInternalAPI(
base *base.BaseDendrite, processContext *process.ProcessContext,
userAPI userapi.UserInternalAPI, cfg *config.Dendrite,
natsInstance *jetstream.NATSInstance,
userAPI userapi.AppserviceUserAPI,
rsAPI roomserverAPI.RoomserverInternalAPI, rsAPI roomserverAPI.RoomserverInternalAPI,
) appserviceAPI.AppServiceInternalAPI { ) appserviceAPI.AppServiceInternalAPI {
client := &http.Client{
Timeout: time.Second * 30,
Transport: &http.Transport{
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: base.Cfg.AppServiceAPI.DisableTLSValidation,
},
Proxy: http.ProxyFromEnvironment,
},
}
// Create appserivce query API with an HTTP client that will be used for all // Create appserivce query API with an HTTP client that will be used for all
// outbound and inbound requests (inbound only for the internal API) // outbound and inbound requests (inbound only for the internal API)
appserviceQueryAPI := &query.AppServiceQueryAPI{ appserviceQueryAPI := &query.AppServiceQueryAPI{
HTTPClient: client, Cfg: &cfg.AppServiceAPI,
Cfg: &base.Cfg.AppServiceAPI, ProtocolCache: map[string]appserviceAPI.ASProtocolResponse{},
CacheMu: sync.Mutex{},
} }
if len(base.Cfg.Derived.ApplicationServices) == 0 { if len(cfg.Derived.ApplicationServices) == 0 {
return appserviceQueryAPI return appserviceQueryAPI
} }
// Wrap application services in a type that relates the application service and // Wrap application services in a type that relates the application service and
// a sync.Cond object that can be used to notify workers when there are new // a sync.Cond object that can be used to notify workers when there are new
// events to be sent out. // events to be sent out.
for _, appservice := range base.Cfg.Derived.ApplicationServices { for _, appservice := range cfg.Derived.ApplicationServices {
// Create bot account for this AS if it doesn't already exist // Create bot account for this AS if it doesn't already exist
if err := generateAppServiceAccount(userAPI, appservice); err != nil { if err := generateAppServiceAccount(userAPI, appservice, cfg.Global.ServerName); err != nil {
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"appservice": appservice.ID, "appservice": appservice.ID,
}).WithError(err).Panicf("failed to generate bot account for appservice") }).WithError(err).Panicf("failed to generate bot account for appservice")
@ -80,10 +67,10 @@ func NewInternalAPI(
// Only consume if we actually have ASes to track, else we'll just chew cycles needlessly. // Only consume if we actually have ASes to track, else we'll just chew cycles needlessly.
// We can't add ASes at runtime so this is safe to do. // We can't add ASes at runtime so this is safe to do.
js, _ := base.NATS.Prepare(base.ProcessContext, &base.Cfg.Global.JetStream) js, _ := natsInstance.Prepare(processContext, &cfg.Global.JetStream)
consumer := consumers.NewOutputRoomEventConsumer( consumer := consumers.NewOutputRoomEventConsumer(
base.ProcessContext, &base.Cfg.AppServiceAPI, processContext, &cfg.AppServiceAPI,
client, js, rsAPI, js, rsAPI,
) )
if err := consumer.Start(); err != nil { if err := consumer.Start(); err != nil {
logrus.WithError(err).Panicf("failed to start appservice roomserver consumer") logrus.WithError(err).Panicf("failed to start appservice roomserver consumer")
@ -98,11 +85,13 @@ func NewInternalAPI(
func generateAppServiceAccount( func generateAppServiceAccount(
userAPI userapi.AppserviceUserAPI, userAPI userapi.AppserviceUserAPI,
as config.ApplicationService, as config.ApplicationService,
serverName spec.ServerName,
) error { ) error {
var accRes userapi.PerformAccountCreationResponse var accRes userapi.PerformAccountCreationResponse
err := userAPI.PerformAccountCreation(context.Background(), &userapi.PerformAccountCreationRequest{ err := userAPI.PerformAccountCreation(context.Background(), &userapi.PerformAccountCreationRequest{
AccountType: userapi.AccountTypeAppService, AccountType: userapi.AccountTypeAppService,
Localpart: as.SenderLocalpart, Localpart: as.SenderLocalpart,
ServerName: serverName,
AppServiceID: as.ID, AppServiceID: as.ID,
OnConflict: userapi.ConflictUpdate, OnConflict: userapi.ConflictUpdate,
}, &accRes) }, &accRes)
@ -112,6 +101,7 @@ func generateAppServiceAccount(
var devRes userapi.PerformDeviceCreationResponse var devRes userapi.PerformDeviceCreationResponse
err = userAPI.PerformDeviceCreation(context.Background(), &userapi.PerformDeviceCreationRequest{ err = userAPI.PerformDeviceCreation(context.Background(), &userapi.PerformDeviceCreationRequest{
Localpart: as.SenderLocalpart, Localpart: as.SenderLocalpart,
ServerName: serverName,
AccessToken: as.ASToken, AccessToken: as.ASToken,
DeviceID: &as.SenderLocalpart, DeviceID: &as.SenderLocalpart,
DeviceDisplayName: &as.SenderLocalpart, DeviceDisplayName: &as.SenderLocalpart,

View file

@ -0,0 +1,606 @@
package appservice_test
import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"net/http/httptest"
"path"
"reflect"
"regexp"
"strings"
"testing"
"time"
"github.com/matrix-org/dendrite/clientapi"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/federationapi/statistics"
"github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/roomserver/types"
"github.com/matrix-org/dendrite/syncapi"
uapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
"github.com/nats-io/nats.go"
"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
"github.com/matrix-org/dendrite/appservice"
"github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/appservice/consumers"
"github.com/matrix-org/dendrite/internal/caching"
"github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/roomserver"
rsapi "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/userapi"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/dendrite/test/testrig"
)
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
return &statistics.ServerStatistics{}, nil
}
func TestAppserviceInternalAPI(t *testing.T) {
// Set expected results
existingProtocol := "irc"
wantLocationResponse := []api.ASLocationResponse{{Protocol: existingProtocol, Fields: []byte("{}")}}
wantUserResponse := []api.ASUserResponse{{Protocol: existingProtocol, Fields: []byte("{}")}}
wantProtocolResponse := api.ASProtocolResponse{Instances: []api.ProtocolInstance{{Fields: []byte("{}")}}}
wantProtocolResult := map[string]api.ASProtocolResponse{
existingProtocol: wantProtocolResponse,
}
// create a dummy AS url, handling some cases
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.Contains(r.URL.Path, "location"):
// Check if we've got an existing protocol, if so, return a proper response.
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantLocationResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode([]api.ASLocationResponse{}); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
case strings.Contains(r.URL.Path, "user"):
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantUserResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode([]api.UserResponse{}); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
case strings.Contains(r.URL.Path, "protocol"):
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantProtocolResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode(nil); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
default:
t.Logf("hit location: %s", r.URL.Path)
}
}))
// The test cases to run
runCases := func(t *testing.T, testAPI api.AppServiceInternalAPI) {
t.Run("UserIDExists", func(t *testing.T) {
testUserIDExists(t, testAPI, "@as-testing:test", true)
testUserIDExists(t, testAPI, "@as1-testing:test", false)
})
t.Run("AliasExists", func(t *testing.T) {
testAliasExists(t, testAPI, "@asroom-testing:test", true)
testAliasExists(t, testAPI, "@asroom1-testing:test", false)
})
t.Run("Locations", func(t *testing.T) {
testLocations(t, testAPI, existingProtocol, wantLocationResponse)
testLocations(t, testAPI, "abc", nil)
})
t.Run("User", func(t *testing.T) {
testUser(t, testAPI, existingProtocol, wantUserResponse)
testUser(t, testAPI, "abc", nil)
})
t.Run("Protocols", func(t *testing.T) {
testProtocol(t, testAPI, existingProtocol, wantProtocolResult)
testProtocol(t, testAPI, existingProtocol, wantProtocolResult) // tests the cache
testProtocol(t, testAPI, "", wantProtocolResult) // tests getting all protocols
testProtocol(t, testAPI, "abc", nil)
})
}
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
cfg, ctx, close := testrig.CreateConfig(t, dbType)
defer close()
// Create a dummy application service
as := &config.ApplicationService{
ID: "someID",
URL: srv.URL,
ASToken: "",
HSToken: "",
SenderLocalpart: "senderLocalPart",
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
"users": {{RegexpObject: regexp.MustCompile("as-.*")}},
"aliases": {{RegexpObject: regexp.MustCompile("asroom-.*")}},
},
Protocols: []string{existingProtocol},
}
as.CreateHTTPClient(cfg.AppServiceAPI.DisableTLSValidation)
cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as}
t.Cleanup(func() {
ctx.ShutdownDendrite()
ctx.WaitForShutdown()
})
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
// Create required internal APIs
natsInstance := jetstream.NATSInstance{}
cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions)
rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
rsAPI.SetFederationAPI(nil, nil)
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI)
runCases(t, asAPI)
})
}
func TestAppserviceInternalAPI_UnixSocket_Simple(t *testing.T) {
// Set expected results
existingProtocol := "irc"
wantLocationResponse := []api.ASLocationResponse{{Protocol: existingProtocol, Fields: []byte("{}")}}
wantUserResponse := []api.ASUserResponse{{Protocol: existingProtocol, Fields: []byte("{}")}}
wantProtocolResponse := api.ASProtocolResponse{Instances: []api.ProtocolInstance{{Fields: []byte("{}")}}}
// create a dummy AS url, handling some cases
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.Contains(r.URL.Path, "location"):
// Check if we've got an existing protocol, if so, return a proper response.
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantLocationResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode([]api.ASLocationResponse{}); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
case strings.Contains(r.URL.Path, "user"):
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantUserResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode([]api.UserResponse{}); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
case strings.Contains(r.URL.Path, "protocol"):
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantProtocolResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode(nil); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
default:
t.Logf("hit location: %s", r.URL.Path)
}
}))
tmpDir := t.TempDir()
socket := path.Join(tmpDir, "socket")
l, err := net.Listen("unix", socket)
assert.NoError(t, err)
_ = srv.Listener.Close()
srv.Listener = l
srv.Start()
defer srv.Close()
cfg, ctx, tearDown := testrig.CreateConfig(t, test.DBTypeSQLite)
defer tearDown()
// Create a dummy application service
as := &config.ApplicationService{
ID: "someID",
URL: fmt.Sprintf("unix://%s", socket),
ASToken: "",
HSToken: "",
SenderLocalpart: "senderLocalPart",
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
"users": {{RegexpObject: regexp.MustCompile("as-.*")}},
"aliases": {{RegexpObject: regexp.MustCompile("asroom-.*")}},
},
Protocols: []string{existingProtocol},
}
as.CreateHTTPClient(cfg.AppServiceAPI.DisableTLSValidation)
cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as}
t.Cleanup(func() {
ctx.ShutdownDendrite()
ctx.WaitForShutdown()
})
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
// Create required internal APIs
natsInstance := jetstream.NATSInstance{}
cm := sqlutil.NewConnectionManager(ctx, cfg.Global.DatabaseOptions)
rsAPI := roomserver.NewInternalAPI(ctx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
rsAPI.SetFederationAPI(nil, nil)
usrAPI := userapi.NewInternalAPI(ctx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
asAPI := appservice.NewInternalAPI(ctx, cfg, &natsInstance, usrAPI, rsAPI)
t.Run("UserIDExists", func(t *testing.T) {
testUserIDExists(t, asAPI, "@as-testing:test", true)
testUserIDExists(t, asAPI, "@as1-testing:test", false)
})
}
func testUserIDExists(t *testing.T, asAPI api.AppServiceInternalAPI, userID string, wantExists bool) {
ctx := context.Background()
userResp := &api.UserIDExistsResponse{}
if err := asAPI.UserIDExists(ctx, &api.UserIDExistsRequest{
UserID: userID,
}, userResp); err != nil {
t.Errorf("failed to get userID: %s", err)
}
if userResp.UserIDExists != wantExists {
t.Errorf("unexpected result for UserIDExists(%s): %v, expected %v", userID, userResp.UserIDExists, wantExists)
}
}
func testAliasExists(t *testing.T, asAPI api.AppServiceInternalAPI, alias string, wantExists bool) {
ctx := context.Background()
aliasResp := &api.RoomAliasExistsResponse{}
if err := asAPI.RoomAliasExists(ctx, &api.RoomAliasExistsRequest{
Alias: alias,
}, aliasResp); err != nil {
t.Errorf("failed to get alias: %s", err)
}
if aliasResp.AliasExists != wantExists {
t.Errorf("unexpected result for RoomAliasExists(%s): %v, expected %v", alias, aliasResp.AliasExists, wantExists)
}
}
func testLocations(t *testing.T, asAPI api.AppServiceInternalAPI, proto string, wantResult []api.ASLocationResponse) {
ctx := context.Background()
locationResp := &api.LocationResponse{}
if err := asAPI.Locations(ctx, &api.LocationRequest{
Protocol: proto,
}, locationResp); err != nil {
t.Errorf("failed to get locations: %s", err)
}
if !reflect.DeepEqual(locationResp.Locations, wantResult) {
t.Errorf("unexpected result for Locations(%s): %+v, expected %+v", proto, locationResp.Locations, wantResult)
}
}
func testUser(t *testing.T, asAPI api.AppServiceInternalAPI, proto string, wantResult []api.ASUserResponse) {
ctx := context.Background()
userResp := &api.UserResponse{}
if err := asAPI.User(ctx, &api.UserRequest{
Protocol: proto,
}, userResp); err != nil {
t.Errorf("failed to get user: %s", err)
}
if !reflect.DeepEqual(userResp.Users, wantResult) {
t.Errorf("unexpected result for User(%s): %+v, expected %+v", proto, userResp.Users, wantResult)
}
}
func testProtocol(t *testing.T, asAPI api.AppServiceInternalAPI, proto string, wantResult map[string]api.ASProtocolResponse) {
ctx := context.Background()
protoResp := &api.ProtocolResponse{}
if err := asAPI.Protocols(ctx, &api.ProtocolRequest{
Protocol: proto,
}, protoResp); err != nil {
t.Errorf("failed to get Protocols: %s", err)
}
if !reflect.DeepEqual(protoResp.Protocols, wantResult) {
t.Errorf("unexpected result for Protocols(%s): %+v, expected %+v", proto, protoResp.Protocols[proto], wantResult)
}
}
// Tests that the roomserver consumer only receives one invite
func TestRoomserverConsumerOneInvite(t *testing.T) {
alice := test.NewUser(t)
bob := test.NewUser(t)
room := test.NewRoom(t, alice)
// Invite Bob
room.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
"membership": "invite",
}, test.WithStateKey(bob.ID))
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{})
// 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
}
// Usually we would check the event content for the membership, but since
// we only invited bob, this should be fine for this test.
if ev.StateKey != nil && *ev.StateKey == bob.ID {
evChan <- struct{}{}
}
}
}))
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}
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)
usrAPI := userapi.NewInternalAPI(processCtx, cfg, cm, natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
// start the consumer
appservice.NewInternalAPI(processCtx, cfg, natsInstance, usrAPI, rsAPI)
// Create the room
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)
}
var seenInvitesForBob int
waitLoop:
for {
select {
case <-time.After(time.Millisecond * 50): // wait for the AS to process the events
break waitLoop
case <-evChan:
seenInvitesForBob++
if seenInvitesForBob != 1 {
t.Fatalf("received unexpected invites: %d", seenInvitesForBob)
}
}
}
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,
}
}
}

View file

@ -26,21 +26,29 @@ import (
"time" "time"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/roomserver/types"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/dendrite/setup/process" "github.com/matrix-org/dendrite/setup/process"
"github.com/matrix-org/dendrite/syncapi/synctypes"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// ApplicationServiceTransaction is the transaction that is sent off to an
// application service.
type ApplicationServiceTransaction struct {
Events []synctypes.ClientEvent `json:"events"`
}
// OutputRoomEventConsumer consumes events that originated in the room server. // OutputRoomEventConsumer consumes events that originated in the room server.
type OutputRoomEventConsumer struct { type OutputRoomEventConsumer struct {
ctx context.Context ctx context.Context
cfg *config.AppServiceAPI cfg *config.AppServiceAPI
client *http.Client
jetstream nats.JetStreamContext jetstream nats.JetStreamContext
topic string topic string
rsAPI api.AppserviceRoomserverAPI rsAPI api.AppserviceRoomserverAPI
@ -56,22 +64,21 @@ type appserviceState struct {
func NewOutputRoomEventConsumer( func NewOutputRoomEventConsumer(
process *process.ProcessContext, process *process.ProcessContext,
cfg *config.AppServiceAPI, cfg *config.AppServiceAPI,
client *http.Client,
js nats.JetStreamContext, js nats.JetStreamContext,
rsAPI api.AppserviceRoomserverAPI, rsAPI api.AppserviceRoomserverAPI,
) *OutputRoomEventConsumer { ) *OutputRoomEventConsumer {
return &OutputRoomEventConsumer{ return &OutputRoomEventConsumer{
ctx: process.Context(), ctx: process.Context(),
cfg: cfg, cfg: cfg,
client: client,
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{
@ -89,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
} }
@ -99,7 +115,7 @@ func (s *OutputRoomEventConsumer) onMessage(
ctx context.Context, state *appserviceState, msgs []*nats.Msg, ctx context.Context, state *appserviceState, msgs []*nats.Msg,
) bool { ) bool {
log.WithField("appservice", state.ID).Tracef("Appservice worker received %d message(s) from roomserver", len(msgs)) log.WithField("appservice", state.ID).Tracef("Appservice worker received %d message(s) from roomserver", len(msgs))
events := make([]*gomatrixserverlib.HeaderedEvent, 0, len(msgs)) events := make([]*types.HeaderedEvent, 0, len(msgs))
for _, msg := range msgs { for _, msg := range msgs {
// Only handle events we care about // Only handle events we care about
receivedType := api.OutputType(msg.Header.Get(jetstream.RoomEventType)) receivedType := api.OutputType(msg.Header.Get(jetstream.RoomEventType))
@ -122,6 +138,7 @@ func (s *OutputRoomEventConsumer) onMessage(
if len(output.NewRoomEvent.AddsStateEventIDs) > 0 { if len(output.NewRoomEvent.AddsStateEventIDs) > 0 {
newEventID := output.NewRoomEvent.Event.EventID() newEventID := output.NewRoomEvent.Event.EventID()
eventsReq := &api.QueryEventsByIDRequest{ eventsReq := &api.QueryEventsByIDRequest{
RoomID: output.NewRoomEvent.Event.RoomID().String(),
EventIDs: make([]string, 0, len(output.NewRoomEvent.AddsStateEventIDs)), EventIDs: make([]string, 0, len(output.NewRoomEvent.AddsStateEventIDs)),
} }
eventsRes := &api.QueryEventsByIDResponse{} eventsRes := &api.QueryEventsByIDResponse{}
@ -139,12 +156,6 @@ func (s *OutputRoomEventConsumer) onMessage(
} }
} }
case api.OutputTypeNewInviteEvent:
if output.NewInviteEvent == nil || !s.appserviceIsInterestedInEvent(ctx, output.NewInviteEvent.Event, state.ApplicationService) {
continue
}
events = append(events, output.NewInviteEvent.Event)
default: default:
continue continue
} }
@ -174,13 +185,15 @@ func (s *OutputRoomEventConsumer) onMessage(
// endpoint. It will block for the backoff period if necessary. // endpoint. It will block for the backoff period if necessary.
func (s *OutputRoomEventConsumer) sendEvents( func (s *OutputRoomEventConsumer) sendEvents(
ctx context.Context, state *appserviceState, ctx context.Context, state *appserviceState,
events []*gomatrixserverlib.HeaderedEvent, events []*types.HeaderedEvent,
txnID string, txnID string,
) error { ) error {
// Create the transaction body. // Create the transaction body.
transaction, err := json.Marshal( transaction, err := json.Marshal(
gomatrixserverlib.ApplicationServiceTransaction{ ApplicationServiceTransaction{
Events: gomatrixserverlib.HeaderedToClientEvents(events, gomatrixserverlib.FormatAll), Events: synctypes.ToClientEvents(gomatrixserverlib.ToPDUs(events), synctypes.FormatAll, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
return s.rsAPI.QueryUserIDForSender(ctx, roomID, senderID)
}),
}, },
) )
if err != nil { if err != nil {
@ -189,18 +202,26 @@ func (s *OutputRoomEventConsumer) sendEvents(
// If txnID is not defined, generate one from the events. // If txnID is not defined, generate one from the events.
if txnID == "" { if txnID == "" {
txnID = fmt.Sprintf("%d_%d", events[0].Event.OriginServerTS(), len(transaction)) txnID = fmt.Sprintf("%d_%d", events[0].PDU.OriginServerTS(), len(transaction))
} }
// 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.URL, 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")
resp, err := s.client.Do(req) req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", state.HSToken))
resp, err := state.HTTPClient.Do(req)
if err != nil { if err != nil {
return state.backoffAndPause(err) return state.backoffAndPause(err)
} }
@ -211,7 +232,7 @@ func (s *OutputRoomEventConsumer) sendEvents(
case http.StatusOK: case http.StatusOK:
state.backoff = 0 state.backoff = 0
default: default:
return state.backoffAndPause(fmt.Errorf("received HTTP status code %d from appservice", resp.StatusCode)) return state.backoffAndPause(fmt.Errorf("received HTTP status code %d from appservice url %s", resp.StatusCode, address))
} }
return nil return nil
} }
@ -231,24 +252,30 @@ func (s *appserviceState) backoffAndPause(err error) error {
// event falls within one of a given application service's namespaces. // event falls within one of a given application service's namespaces.
// //
// TODO: This should be cached, see https://github.com/matrix-org/dendrite/issues/1682 // TODO: This should be cached, see https://github.com/matrix-org/dendrite/issues/1682
func (s *OutputRoomEventConsumer) appserviceIsInterestedInEvent(ctx context.Context, event *gomatrixserverlib.HeaderedEvent, appservice *config.ApplicationService) bool { func (s *OutputRoomEventConsumer) appserviceIsInterestedInEvent(ctx context.Context, event *types.HeaderedEvent, appservice *config.ApplicationService) bool {
user := ""
userID, err := s.rsAPI.QueryUserIDForSender(ctx, event.RoomID(), event.SenderID())
if err == nil {
user = userID.String()
}
switch { switch {
case appservice.URL == "": case appservice.URL == "":
return false return false
case appservice.IsInterestedInUserID(event.Sender()): case appservice.IsInterestedInUserID(user):
return true return true
case appservice.IsInterestedInRoomID(event.RoomID()): case appservice.IsInterestedInRoomID(event.RoomID().String()):
return true return true
} }
if event.Type() == gomatrixserverlib.MRoomMember && event.StateKey() != nil { if event.Type() == spec.MRoomMember && event.StateKey() != nil {
if appservice.IsInterestedInUserID(*event.StateKey()) { if appservice.IsInterestedInUserID(*event.StateKey()) {
return true return true
} }
} }
// Check all known room aliases of the room the event came from // Check all known room aliases of the room the event came from
queryReq := api.GetAliasesForRoomIDRequest{RoomID: event.RoomID()} queryReq := api.GetAliasesForRoomIDRequest{RoomID: event.RoomID().String()}
var queryRes api.GetAliasesForRoomIDResponse var queryRes api.GetAliasesForRoomIDResponse
if err := s.rsAPI.GetAliasesForRoomID(ctx, &queryReq, &queryRes); err == nil { if err := s.rsAPI.GetAliasesForRoomID(ctx, &queryReq, &queryRes); err == nil {
for _, alias := range queryRes.Aliases { for _, alias := range queryRes.Aliases {
@ -259,7 +286,7 @@ func (s *OutputRoomEventConsumer) appserviceIsInterestedInEvent(ctx context.Cont
} else { } else {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"appservice": appservice.ID, "appservice": appservice.ID,
"room_id": event.RoomID(), "room_id": event.RoomID().String(),
}).WithError(err).Errorf("Unable to get aliases for room") }).WithError(err).Errorf("Unable to get aliases for room")
} }
@ -269,13 +296,13 @@ func (s *OutputRoomEventConsumer) appserviceIsInterestedInEvent(ctx context.Cont
// appserviceJoinedAtEvent returns a boolean depending on whether a given // appserviceJoinedAtEvent returns a boolean depending on whether a given
// appservice has membership at the time a given event was created. // appservice has membership at the time a given event was created.
func (s *OutputRoomEventConsumer) appserviceJoinedAtEvent(ctx context.Context, event *gomatrixserverlib.HeaderedEvent, appservice *config.ApplicationService) bool { func (s *OutputRoomEventConsumer) appserviceJoinedAtEvent(ctx context.Context, event *types.HeaderedEvent, appservice *config.ApplicationService) bool {
// TODO: This is only checking the current room state, not the state at // TODO: This is only checking the current room state, not the state at
// the event in question. Pretty sure this is what Synapse does too, but // the event in question. Pretty sure this is what Synapse does too, but
// until we have a lighter way of checking the state before the event that // until we have a lighter way of checking the state before the event that
// doesn't involve state res, then this is probably OK. // doesn't involve state res, then this is probably OK.
membershipReq := &api.QueryMembershipsForRoomRequest{ membershipReq := &api.QueryMembershipsForRoomRequest{
RoomID: event.RoomID(), RoomID: event.RoomID().String(),
JoinedOnly: true, JoinedOnly: true,
} }
membershipRes := &api.QueryMembershipsForRoomResponse{} membershipRes := &api.QueryMembershipsForRoomResponse{}
@ -287,7 +314,7 @@ func (s *OutputRoomEventConsumer) appserviceJoinedAtEvent(ctx context.Context, e
switch { switch {
case ev.StateKey == nil: case ev.StateKey == nil:
continue continue
case ev.Type != gomatrixserverlib.MRoomMember: case ev.Type != spec.MRoomMember:
continue continue
} }
var membership gomatrixserverlib.MemberContent var membership gomatrixserverlib.MemberContent
@ -295,7 +322,7 @@ func (s *OutputRoomEventConsumer) appserviceJoinedAtEvent(ctx context.Context, e
switch { switch {
case err != nil: case err != nil:
continue continue
case membership.Membership == gomatrixserverlib.Join: case membership.Membership == spec.Join:
if appservice.IsInterestedInUserID(*ev.StateKey) { if appservice.IsInterestedInUserID(*ev.StateKey) {
return true return true
} }
@ -304,7 +331,7 @@ func (s *OutputRoomEventConsumer) appserviceJoinedAtEvent(ctx context.Context, e
} else { } else {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"appservice": appservice.ID, "appservice": appservice.ID,
"room_id": event.RoomID(), "room_id": event.RoomID().String(),
}).WithError(err).Errorf("Unable to get membership for room") }).WithError(err).Errorf("Unable to get membership for room")
} }
return false return false

View file

@ -1,60 +0,0 @@
package inthttp
import (
"context"
"errors"
"net/http"
"github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/internal/httputil"
)
// HTTP paths for the internal HTTP APIs
const (
AppServiceRoomAliasExistsPath = "/appservice/RoomAliasExists"
AppServiceUserIDExistsPath = "/appservice/UserIDExists"
)
// httpAppServiceQueryAPI contains the URL to an appservice query API and a
// reference to a httpClient used to reach it
type httpAppServiceQueryAPI struct {
appserviceURL string
httpClient *http.Client
}
// NewAppserviceClient creates a AppServiceQueryAPI implemented by talking
// to a HTTP POST API.
// If httpClient is nil an error is returned
func NewAppserviceClient(
appserviceURL string,
httpClient *http.Client,
) (api.AppServiceInternalAPI, error) {
if httpClient == nil {
return nil, errors.New("NewRoomserverAliasAPIHTTP: httpClient is <nil>")
}
return &httpAppServiceQueryAPI{appserviceURL, httpClient}, nil
}
// RoomAliasExists implements AppServiceQueryAPI
func (h *httpAppServiceQueryAPI) RoomAliasExists(
ctx context.Context,
request *api.RoomAliasExistsRequest,
response *api.RoomAliasExistsResponse,
) error {
return httputil.CallInternalRPCAPI(
"RoomAliasExists", h.appserviceURL+AppServiceRoomAliasExistsPath,
h.httpClient, ctx, request, response,
)
}
// UserIDExists implements AppServiceQueryAPI
func (h *httpAppServiceQueryAPI) UserIDExists(
ctx context.Context,
request *api.UserIDExistsRequest,
response *api.UserIDExistsResponse,
) error {
return httputil.CallInternalRPCAPI(
"UserIDExists", h.appserviceURL+AppServiceUserIDExistsPath,
h.httpClient, ctx, request, response,
)
}

View file

@ -1,20 +0,0 @@
package inthttp
import (
"github.com/gorilla/mux"
"github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/internal/httputil"
)
// AddRoutes adds the AppServiceQueryAPI handlers to the http.ServeMux.
func AddRoutes(a api.AppServiceInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle(
AppServiceRoomAliasExistsPath,
httputil.MakeInternalRPCAPI("AppserviceRoomAliasExists", a.RoomAliasExists),
)
internalAPIMux.Handle(
AppServiceUserIDExistsPath,
httputil.MakeInternalRPCAPI("AppserviceUserIDExists", a.UserIDExists),
)
}

View file

@ -18,22 +18,25 @@ package query
import ( import (
"context" "context"
"encoding/json"
"fmt"
"io"
"net/http" "net/http"
"net/url" "net/url"
"sync"
log "github.com/sirupsen/logrus"
"github.com/matrix-org/dendrite/appservice/api" "github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
opentracing "github.com/opentracing/opentracing-go"
log "github.com/sirupsen/logrus"
) )
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 {
HTTPClient *http.Client
Cfg *config.AppServiceAPI Cfg *config.AppServiceAPI
ProtocolCache map[string]api.ASProtocolResponse
CacheMu sync.Mutex
} }
// RoomAliasExists performs a request to '/room/{roomAlias}' on all known // RoomAliasExists performs a request to '/room/{roomAlias}' on all known
@ -43,20 +46,29 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
request *api.RoomAliasExistsRequest, request *api.RoomAliasExistsRequest,
response *api.RoomAliasExistsResponse, response *api.RoomAliasExistsResponse,
) error { ) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "ApplicationServiceRoomAlias") trace, ctx := internal.StartRegion(ctx, "ApplicationServiceRoomAlias")
defer span.Finish() defer trace.EndRegion()
// 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.URL + 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.
@ -64,9 +76,10 @@ 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 := a.HTTPClient.Do(req) resp, err := appservice.HTTPClient.Do(req)
if resp != nil { if resp != nil {
defer func() { defer func() {
err = resp.Body.Close() err = resp.Body.Close()
@ -110,19 +123,28 @@ func (a *AppServiceQueryAPI) UserIDExists(
request *api.UserIDExistsRequest, request *api.UserIDExistsRequest,
response *api.UserIDExistsResponse, response *api.UserIDExistsResponse,
) error { ) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "ApplicationServiceUserID") trace, ctx := internal.StartRegion(ctx, "ApplicationServiceUserID")
defer span.Finish() defer trace.EndRegion()
// 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.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.URL + 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.
@ -130,7 +152,8 @@ func (a *AppServiceQueryAPI) UserIDExists(
if err != nil { if err != nil {
return err return err
} }
resp, err := a.HTTPClient.Do(req.WithContext(ctx)) req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", appservice.HSToken))
resp, err := appservice.HTTPClient.Do(req.WithContext(ctx))
if resp != nil { if resp != nil {
defer func() { defer func() {
err = resp.Body.Close() err = resp.Body.Close()
@ -165,3 +188,191 @@ func (a *AppServiceQueryAPI) UserIDExists(
response.UserIDExists = false response.UserIDExists = false
return nil return nil
} }
type thirdpartyResponses interface {
api.ASProtocolResponse | []api.ASUserResponse | []api.ASLocationResponse
}
func requestDo[T thirdpartyResponses](as *config.ApplicationService, url string, response *T) error {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
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(
ctx context.Context,
req *api.LocationRequest,
resp *api.LocationResponse,
) error {
params, err := url.ParseQuery(req.Params)
if err != nil {
return err
}
path := api.ASLocationPath
if a.Cfg.LegacyPaths {
path = api.ASLocationLegacyPath
}
for _, as := range a.Cfg.Derived.ApplicationServices {
var asLocations []api.ASLocationResponse
if a.Cfg.LegacyAuth {
params.Set("access_token", as.HSToken)
}
url := as.RequestUrl() + path
if req.Protocol != "" {
url += "/" + req.Protocol
}
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")
continue
}
resp.Locations = append(resp.Locations, asLocations...)
}
if len(resp.Locations) == 0 {
resp.Exists = false
return nil
}
resp.Exists = true
return nil
}
func (a *AppServiceQueryAPI) User(
ctx context.Context,
req *api.UserRequest,
resp *api.UserResponse,
) error {
params, err := url.ParseQuery(req.Params)
if err != nil {
return err
}
path := api.ASUserPath
if a.Cfg.LegacyPaths {
path = api.ASUserLegacyPath
}
for _, as := range a.Cfg.Derived.ApplicationServices {
var asUsers []api.ASUserResponse
if a.Cfg.LegacyAuth {
params.Set("access_token", as.HSToken)
}
url := as.RequestUrl() + path
if req.Protocol != "" {
url += "/" + req.Protocol
}
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")
continue
}
resp.Users = append(resp.Users, asUsers...)
}
if len(resp.Users) == 0 {
resp.Exists = false
return nil
}
resp.Exists = true
return nil
}
func (a *AppServiceQueryAPI) Protocols(
ctx context.Context,
req *api.ProtocolRequest,
resp *api.ProtocolResponse,
) error {
protocolPath := api.ASProtocolPath
if a.Cfg.LegacyPaths {
protocolPath = api.ASProtocolLegacyPath
}
// get a single protocol response
if req.Protocol != "" {
a.CacheMu.Lock()
defer a.CacheMu.Unlock()
if proto, ok := a.ProtocolCache[req.Protocol]; ok {
resp.Exists = true
resp.Protocols = map[string]api.ASProtocolResponse{
req.Protocol: proto,
}
return nil
}
response := api.ASProtocolResponse{}
for _, as := range a.Cfg.Derived.ApplicationServices {
var proto api.ASProtocolResponse
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")
continue
}
if len(response.Instances) != 0 {
response.Instances = append(response.Instances, proto.Instances...)
} else {
response = proto
}
}
if len(response.Instances) == 0 {
resp.Exists = false
return nil
}
resp.Exists = true
resp.Protocols = map[string]api.ASProtocolResponse{
req.Protocol: response,
}
a.ProtocolCache[req.Protocol] = response
return nil
}
response := make(map[string]api.ASProtocolResponse, len(a.Cfg.Derived.ApplicationServices))
for _, as := range a.Cfg.Derived.ApplicationServices {
for _, p := range as.Protocols {
var proto api.ASProtocolResponse
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")
continue
}
existing, ok := response[p]
if !ok {
response[p] = proto
continue
}
existing.Instances = append(existing.Instances, proto.Instances...)
response[p] = existing
}
}
if len(response) == 0 {
resp.Exists = false
return nil
}
a.CacheMu.Lock()
defer a.CacheMu.Unlock()
a.ProtocolCache = response
resp.Exists = true
resp.Protocols = response
return nil
}

View file

@ -643,7 +643,7 @@ fed Inbound federation redacts events from erased users
fme Outbound federation can request missing events fme Outbound federation can request missing events
fme Inbound federation can return missing events for world_readable visibility fme Inbound federation can return missing events for world_readable visibility
fme Inbound federation can return missing events for shared visibility fme Inbound federation can return missing events for shared visibility
fme Inbound federation can return missing events for invite visibility fme Inbound federation can return missing events for invited visibility
fme Inbound federation can return missing events for joined visibility fme Inbound federation can return missing events for joined visibility
fme outliers whose auth_events are in a different room are correctly rejected fme outliers whose auth_events are in a different room are correctly rejected
fbk Outbound federation can backfill events fbk Outbound federation can backfill events
@ -937,3 +937,19 @@ fst Room state after a rejected state event is the same as before
fpb Federation publicRoom Name/topic keys are correct fpb Federation publicRoom Name/topic keys are correct
fed New federated private chats get full presence information (SYN-115) (10 subtests) fed New federated private chats get full presence information (SYN-115) (10 subtests)
dvk Rejects invalid device keys dvk Rejects invalid device keys
rmv User can create and send/receive messages in a room with version 10
rmv local user can join room with version 10
rmv User can invite local user to room with version 10
rmv remote user can join room with version 10
rmv User can invite remote user to room with version 10
rmv Remote user can backfill in a room with version 10
rmv Can reject invites over federation for rooms with version 10
rmv Can receive redactions from regular users over federation in room version 10
rmv User can create and send/receive messages in a room with version 11
rmv local user can join room with version 11
rmv User can invite local user to room with version 11
rmv remote user can join room with version 11
rmv User can invite remote user to room with version 11
rmv Remote user can backfill in a room with version 11
rmv Can reject invites over federation for rooms with version 11
rmv Can receive redactions from regular users over federation in room version 11

View file

@ -1,51 +0,0 @@
@echo off
:ENTRY_POINT
setlocal EnableDelayedExpansion
REM script base dir
set SCRIPTDIR=%~dp0
set PROJDIR=%SCRIPTDIR:~0,-1%
REM Put installed packages into ./bin
set GOBIN=%PROJDIR%\bin
set FLAGS=
REM Check if sources are under Git control
if not exist ".git" goto :CHECK_BIN
REM set BUILD=`git rev-parse --short HEAD \\ ""`
FOR /F "tokens=*" %%X IN ('git rev-parse --short HEAD') DO (
set BUILD=%%X
)
REM set BRANCH=`(git symbolic-ref --short HEAD \ tr -d \/ ) \\ ""`
FOR /F "tokens=*" %%X IN ('git symbolic-ref --short HEAD') DO (
set BRANCHRAW=%%X
set BRANCH=!BRANCHRAW:/=!
)
if "%BRANCH%" == "main" set BRANCH=
set FLAGS=-X github.com/matrix-org/dendrite/internal.branch=%BRANCH% -X github.com/matrix-org/dendrite/internal.build=%BUILD%
:CHECK_BIN
if exist "bin" goto :ALL_SET
mkdir "bin"
:ALL_SET
set CGO_ENABLED=1
for /D %%P in (cmd\*) do (
go build -trimpath -ldflags "%FLAGS%" -v -o ".\bin" ".\%%P"
)
set CGO_ENABLED=0
set GOOS=js
set GOARCH=wasm
go build -trimpath -ldflags "%FLAGS%" -o bin\main.wasm .\cmd\dendritejs-pinecone
goto :DONE
:DONE
echo Done
endlocal

View file

@ -1,24 +0,0 @@
#!/bin/sh -eu
# Put installed packages into ./bin
export GOBIN=$PWD/`dirname $0`/bin
if [ -d ".git" ]
then
export BUILD=`git rev-parse --short HEAD || ""`
export BRANCH=`(git symbolic-ref --short HEAD | tr -d \/ ) || ""`
if [ "$BRANCH" = main ]
then
export BRANCH=""
fi
export FLAGS="-X github.com/matrix-org/dendrite/internal.branch=$BRANCH -X github.com/matrix-org/dendrite/internal.build=$BUILD"
else
export FLAGS=""
fi
mkdir -p bin
CGO_ENABLED=1 go build -trimpath -ldflags "$FLAGS" -v -o "bin/" ./cmd/...
# CGO_ENABLED=0 GOOS=js GOARCH=wasm go build -trimpath -ldflags "$FLAGS" -o bin/main.wasm ./cmd/dendritejs-pinecone

View file

@ -29,13 +29,16 @@ import (
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms" "github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing" "github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
"github.com/matrix-org/dendrite/federationapi" "github.com/matrix-org/dendrite/federationapi"
"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/keyserver" "github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/roomserver" "github.com/matrix-org/dendrite/roomserver"
"github.com/matrix-org/dendrite/setup" "github.com/matrix-org/dendrite/setup"
"github.com/matrix-org/dendrite/setup/base"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/dendrite/setup/process"
"github.com/matrix-org/dendrite/userapi" "github.com/matrix-org/dendrite/userapi"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
@ -158,9 +161,8 @@ func startup() {
pManager.AddPeer("wss://pinecone.matrix.org/public") pManager.AddPeer("wss://pinecone.matrix.org/public")
cfg := &config.Dendrite{} cfg := &config.Dendrite{}
cfg.Defaults(true) cfg.Defaults(config.DefaultOpts{Generate: true, SingleDatabase: false})
cfg.UserAPI.AccountDatabase.ConnectionString = "file:/idb/dendritejs_account.db" cfg.UserAPI.AccountDatabase.ConnectionString = "file:/idb/dendritejs_account.db"
cfg.AppServiceAPI.Database.ConnectionString = "file:/idb/dendritejs_appservice.db"
cfg.FederationAPI.Database.ConnectionString = "file:/idb/dendritejs_fedsender.db" cfg.FederationAPI.Database.ConnectionString = "file:/idb/dendritejs_fedsender.db"
cfg.MediaAPI.Database.ConnectionString = "file:/idb/dendritejs_mediaapi.db" cfg.MediaAPI.Database.ConnectionString = "file:/idb/dendritejs_mediaapi.db"
cfg.RoomServer.Database.ConnectionString = "file:/idb/dendritejs_roomserver.db" cfg.RoomServer.Database.ConnectionString = "file:/idb/dendritejs_roomserver.db"
@ -170,37 +172,37 @@ func startup() {
cfg.Global.TrustedIDServers = []string{} cfg.Global.TrustedIDServers = []string{}
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID) cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
cfg.Global.PrivateKey = sk cfg.Global.PrivateKey = sk
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk)) cfg.Global.ServerName = spec.ServerName(hex.EncodeToString(pk))
cfg.ClientAPI.RegistrationDisabled = false cfg.ClientAPI.RegistrationDisabled = false
cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled = true cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled = true
if err := cfg.Derive(); err != nil { if err := cfg.Derive(); err != nil {
logrus.Fatalf("Failed to derive values from config: %s", err) logrus.Fatalf("Failed to derive values from config: %s", err)
} }
base := base.NewBaseDendrite(cfg, "Monolith") natsInstance := jetstream.NATSInstance{}
defer base.Close() // nolint: errcheck processCtx := process.NewProcessContext()
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
routers := httputil.NewRouters()
caches := caching.NewRistrettoCache(cfg.Global.Cache.EstimatedMaxSize, cfg.Global.Cache.MaxAge, caching.EnableMetrics)
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.EnableMetrics)
federation := conn.CreateFederationClient(base, pSessions) federation := conn.CreateFederationClient(cfg, pSessions)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
serverKeyAPI := &signing.YggdrasilKeys{} serverKeyAPI := &signing.YggdrasilKeys{}
keyRing := serverKeyAPI.KeyRing() keyRing := serverKeyAPI.KeyRing()
rsAPI := roomserver.NewInternalAPI(base) 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)
userAPI := userapi.NewInternalAPI(base, &cfg.UserAPI, nil, keyAPI, rsAPI, base.PushGatewayHTTPClient())
keyAPI.SetUserAPI(userAPI)
asQuery := appservice.NewInternalAPI( asQuery := appservice.NewInternalAPI(
base, userAPI, rsAPI, processCtx, cfg, &natsInstance, userAPI, rsAPI,
) )
rsAPI.SetAppserviceAPI(asQuery) rsAPI.SetAppserviceAPI(asQuery)
fedSenderAPI := federationapi.NewInternalAPI(base, federation, rsAPI, base.Caches, keyRing, true)
rsAPI.SetFederationAPI(fedSenderAPI, keyRing) rsAPI.SetFederationAPI(fedSenderAPI, keyRing)
monolith := setup.Monolith{ monolith := setup.Monolith{
Config: base.Cfg, Config: cfg,
Client: conn.CreateClient(base, pSessions), Client: conn.CreateClient(pSessions),
FedClient: federation, FedClient: federation,
KeyRing: keyRing, KeyRing: keyRing,
@ -208,20 +210,18 @@ func startup() {
FederationAPI: fedSenderAPI, FederationAPI: fedSenderAPI,
RoomserverAPI: rsAPI, RoomserverAPI: rsAPI,
UserAPI: userAPI, UserAPI: userAPI,
KeyAPI: keyAPI,
//ServerKeyAPI: serverKeyAPI, //ServerKeyAPI: serverKeyAPI,
ExtPublicRoomsProvider: rooms.NewPineconeRoomProvider(pRouter, pSessions, fedSenderAPI, federation), ExtPublicRoomsProvider: rooms.NewPineconeRoomProvider(pRouter, pSessions, fedSenderAPI, federation),
} }
monolith.AddAllPublicRoutes(base) monolith.AddAllPublicRoutes(processCtx, cfg, routers, cm, &natsInstance, caches, caching.EnableMetrics)
httpRouter := mux.NewRouter().SkipClean(true).UseEncodedPath() httpRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux) httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(routers.Client)
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux) httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(routers.Media)
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
p2pRouter := pSessions.Protocol("matrix").HTTP().Mux() p2pRouter := pSessions.Protocol("matrix").HTTP().Mux()
p2pRouter.Handle(httputil.PublicFederationPathPrefix, base.PublicFederationAPIMux) p2pRouter.Handle(httputil.PublicFederationPathPrefix, routers.Federation)
p2pRouter.Handle(httputil.PublicMediaPathPrefix, base.PublicMediaAPIMux) p2pRouter.Handle(httputil.PublicMediaPathPrefix, routers.Media)
// Expose the matrix APIs via fetch - for local traffic // Expose the matrix APIs via fetch - for local traffic
go func() { go func() {

View file

@ -1,4 +1,10 @@
FROM docker.io/golang:1.19-alpine AS base # Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved
FROM docker.io/golang:1.21-alpine3.18 AS base
#
# Needs to be separate from the main Dockerfile for OpenShift,
# as --target is not supported there.
#
RUN apk --update --no-cache add bash build-base RUN apk --update --no-cache add bash build-base
@ -12,6 +18,7 @@ RUN go build -trimpath -o bin/ ./cmd/create-account
RUN go build -trimpath -o bin/ ./cmd/generate-keys RUN go build -trimpath -o bin/ ./cmd/generate-keys
FROM alpine:latest FROM alpine:latest
RUN apk --update --no-cache add curl
LABEL org.opencontainers.image.title="Dendrite (Pinecone demo)" LABEL org.opencontainers.image.title="Dendrite (Pinecone demo)"
LABEL org.opencontainers.image.description="Next-generation Matrix homeserver written in Go" LABEL org.opencontainers.image.description="Next-generation Matrix homeserver written in Go"
LABEL org.opencontainers.image.source="https://github.com/matrix-org/dendrite" LABEL org.opencontainers.image.source="https://github.com/matrix-org/dendrite"

View file

@ -1,4 +1,10 @@
FROM docker.io/golang:1.19-alpine AS base # Pinned to alpine3.18 until https://github.com/mattn/go-sqlite3/issues/1164 is solved
FROM docker.io/golang:1.21-alpine3.18 AS base
#
# Needs to be separate from the main Dockerfile for OpenShift,
# as --target is not supported there.
#
RUN apk --update --no-cache add bash build-base RUN apk --update --no-cache add bash build-base
@ -7,12 +13,12 @@ WORKDIR /build
COPY . /build COPY . /build
RUN mkdir -p bin RUN mkdir -p bin
RUN go build -trimpath -o bin/ ./cmd/dendrite-polylith-multi RUN go build -trimpath -o bin/ ./cmd/dendrite-demo-yggdrasil
RUN go build -trimpath -o bin/ ./cmd/create-account RUN go build -trimpath -o bin/ ./cmd/create-account
RUN go build -trimpath -o bin/ ./cmd/generate-keys RUN go build -trimpath -o bin/ ./cmd/generate-keys
FROM alpine:latest FROM alpine:latest
LABEL org.opencontainers.image.title="Dendrite (Polylith)" LABEL org.opencontainers.image.title="Dendrite (Yggdrasil demo)"
LABEL org.opencontainers.image.description="Next-generation Matrix homeserver written in Go" LABEL org.opencontainers.image.description="Next-generation Matrix homeserver written in Go"
LABEL org.opencontainers.image.source="https://github.com/matrix-org/dendrite" LABEL org.opencontainers.image.source="https://github.com/matrix-org/dendrite"
LABEL org.opencontainers.image.licenses="Apache-2.0" LABEL org.opencontainers.image.licenses="Apache-2.0"
@ -22,4 +28,4 @@ COPY --from=base /build/bin/* /usr/bin/
VOLUME /etc/dendrite VOLUME /etc/dendrite
WORKDIR /etc/dendrite WORKDIR /etc/dendrite
ENTRYPOINT ["/usr/bin/dendrite-polylith-multi"] ENTRYPOINT ["/usr/bin/dendrite-demo-yggdrasil"]

View file

@ -1,25 +0,0 @@
FROM docker.io/golang:1.19-alpine AS base
RUN apk --update --no-cache add bash build-base
WORKDIR /build
COPY . /build
RUN mkdir -p bin
RUN go build -trimpath -o bin/ ./cmd/dendrite-monolith-server
RUN go build -trimpath -o bin/ ./cmd/create-account
RUN go build -trimpath -o bin/ ./cmd/generate-keys
FROM alpine:latest
LABEL org.opencontainers.image.title="Dendrite (Monolith)"
LABEL org.opencontainers.image.description="Next-generation Matrix homeserver written in Go"
LABEL org.opencontainers.image.source="https://github.com/matrix-org/dendrite"
LABEL org.opencontainers.image.licenses="Apache-2.0"
COPY --from=base /build/bin/* /usr/bin/
VOLUME /etc/dendrite
WORKDIR /etc/dendrite
ENTRYPOINT ["/usr/bin/dendrite-monolith-server"]

View file

@ -5,22 +5,21 @@ These are Docker images for Dendrite!
They can be found on Docker Hub: They can be found on Docker Hub:
- [matrixdotorg/dendrite-monolith](https://hub.docker.com/r/matrixdotorg/dendrite-monolith) for monolith deployments - [matrixdotorg/dendrite-monolith](https://hub.docker.com/r/matrixdotorg/dendrite-monolith) for monolith deployments
- [matrixdotorg/dendrite-polylith](https://hub.docker.com/r/matrixdotorg/dendrite-polylith) for polylith deployments
## Dockerfiles ## Dockerfile
The `Dockerfile` builds the base image which contains all of the Dendrite The `Dockerfile` is a multistage file which can build Dendrite. From the root of the Dendrite
components. The `Dockerfile.component` file takes the given component, as repository, run:
specified with `--buildarg component=` from the base image and produce
smaller component-specific images, which are substantially smaller and do
not contain the Go toolchain etc.
## Compose files ```
docker build . -t matrixdotorg/dendrite-monolith
```
There are three sample `docker-compose` files: ## Compose file
- `docker-compose.monolith.yml` which runs a monolith Dendrite deployment There is one sample `docker-compose` files:
- `docker-compose.polylith.yml` which runs a polylith Dendrite deployment
- `docker-compose.yml` which runs a Dendrite deployment with Postgres
## Configuration ## Configuration
@ -46,24 +45,14 @@ docker run --rm --entrypoint="" \
The key files will now exist in your current working directory, and can be mounted into place. The key files will now exist in your current working directory, and can be mounted into place.
## Starting Dendrite as a monolith deployment ## Starting Dendrite
Create your config based on the [`dendrite-sample.monolith.yaml`](https://github.com/matrix-org/dendrite/blob/main/dendrite-sample.monolith.yaml) sample configuration file. Create your config based on the [`dendrite-sample.yaml`](https://github.com/matrix-org/dendrite/blob/main/dendrite-sample.yaml) sample configuration file.
Then start the deployment: Then start the deployment:
``` ```
docker-compose -f docker-compose.monolith.yml up docker-compose -f docker-compose.yml up
```
## Starting Dendrite as a polylith deployment
Create your config based on the [`dendrite-sample.polylith.yaml`](https://github.com/matrix-org/dendrite/blob/main/dendrite-sample.polylith.yaml) sample configuration file.
Then start the deployment:
```
docker-compose -f docker-compose.polylith.yml up
``` ```
## Building the images ## Building the images

View file

@ -1,44 +0,0 @@
version: "3.4"
services:
postgres:
hostname: postgres
image: postgres:14
restart: always
volumes:
- ./postgres/create_db.sh:/docker-entrypoint-initdb.d/20-create_db.sh
# To persist your PostgreSQL databases outside of the Docker image,
# to prevent data loss, modify the following ./path_to path:
- ./path_to/postgresql:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: itsasecret
POSTGRES_USER: dendrite
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dendrite"]
interval: 5s
timeout: 5s
retries: 5
networks:
- internal
monolith:
hostname: monolith
image: matrixdotorg/dendrite-monolith:latest
command: [
"--tls-cert=server.crt",
"--tls-key=server.key"
]
ports:
- 8008:8008
- 8448:8448
volumes:
- ./config:/etc/dendrite
- ./media:/var/dendrite/media
depends_on:
- postgres
networks:
- internal
restart: unless-stopped
networks:
internal:
attachable: true

View file

@ -1,143 +0,0 @@
version: "3.4"
services:
postgres:
hostname: postgres
image: postgres:14
restart: always
volumes:
- ./postgres/create_db.sh:/docker-entrypoint-initdb.d/20-create_db.sh
# To persist your PostgreSQL databases outside of the Docker image,
# to prevent data loss, modify the following ./path_to path:
- ./path_to/postgresql:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: itsasecret
POSTGRES_USER: dendrite
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dendrite"]
interval: 5s
timeout: 5s
retries: 5
networks:
- internal
jetstream:
hostname: jetstream
image: nats:latest
command: |
--jetstream
--store_dir /var/lib/nats
--cluster_name Dendrite
volumes:
# To persist your NATS JetStream streams outside of the Docker image,
# prevent data loss, modify the following ./path_to path:
- ./path_to/nats:/var/lib/nats
networks:
- internal
client_api:
hostname: client_api
image: matrixdotorg/dendrite-polylith:latest
command: clientapi
volumes:
- ./config:/etc/dendrite
depends_on:
- jetstream
- postgres
networks:
- internal
restart: unless-stopped
media_api:
hostname: media_api
image: matrixdotorg/dendrite-polylith:latest
command: mediaapi
volumes:
- ./config:/etc/dendrite
- ./media:/var/dendrite/media
networks:
- internal
restart: unless-stopped
sync_api:
hostname: sync_api
image: matrixdotorg/dendrite-polylith:latest
command: syncapi
volumes:
- ./config:/etc/dendrite
depends_on:
- jetstream
- postgres
networks:
- internal
restart: unless-stopped
room_server:
hostname: room_server
image: matrixdotorg/dendrite-polylith:latest
command: roomserver
volumes:
- ./config:/etc/dendrite
depends_on:
- jetstream
- postgres
networks:
- internal
restart: unless-stopped
federation_api:
hostname: federation_api
image: matrixdotorg/dendrite-polylith:latest
command: federationapi
volumes:
- ./config:/etc/dendrite
depends_on:
- jetstream
- postgres
networks:
- internal
restart: unless-stopped
key_server:
hostname: key_server
image: matrixdotorg/dendrite-polylith:latest
command: keyserver
volumes:
- ./config:/etc/dendrite
depends_on:
- jetstream
- postgres
networks:
- internal
restart: unless-stopped
user_api:
hostname: user_api
image: matrixdotorg/dendrite-polylith:latest
command: userapi
volumes:
- ./config:/etc/dendrite
depends_on:
- jetstream
- postgres
networks:
- internal
restart: unless-stopped
appservice_api:
hostname: appservice_api
image: matrixdotorg/dendrite-polylith:latest
command: appservice
volumes:
- ./config:/etc/dendrite
networks:
- internal
depends_on:
- jetstream
- postgres
- room_server
- user_api
restart: unless-stopped
networks:
internal:
attachable: true

View file

@ -0,0 +1,52 @@
version: "3.4"
services:
postgres:
hostname: postgres
image: postgres:15-alpine
restart: always
volumes:
# This will create a docker volume to persist the database files in.
# If you prefer those files to be outside of docker, you'll need to change this.
- dendrite_postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: itsasecret
POSTGRES_USER: dendrite
POSTGRES_DATABASE: dendrite
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dendrite"]
interval: 5s
timeout: 5s
retries: 5
networks:
- internal
monolith:
hostname: monolith
image: matrixdotorg/dendrite-monolith:latest
ports:
- 8008:8008
- 8448:8448
volumes:
- ./config:/etc/dendrite
# The following volumes use docker volumes, change this
# if you prefer to have those files outside of docker.
- dendrite_media:/var/dendrite/media
- dendrite_jetstream:/var/dendrite/jetstream
- dendrite_search_index:/var/dendrite/searchindex
depends_on:
postgres:
condition: service_healthy
networks:
- internal
restart: unless-stopped
networks:
internal:
attachable: true
volumes:
dendrite_postgres_data:
dendrite_media:
dendrite_jetstream:
dendrite_search_index:

View file

@ -6,5 +6,6 @@ TAG=${1:-latest}
echo "Building tag '${TAG}'" echo "Building tag '${TAG}'"
docker build -t matrixdotorg/dendrite-monolith:${TAG} -f build/docker/Dockerfile.monolith . docker build . --target monolith -t matrixdotorg/dendrite-monolith:${TAG}
docker build -t matrixdotorg/dendrite-polylith:${TAG} -f build/docker/Dockerfile.polylith . docker build . --target demo-pinecone -t matrixdotorg/dendrite-demo-pinecone:${TAG}
docker build . --target demo-yggdrasil -t matrixdotorg/dendrite-demo-yggdrasil:${TAG}

View file

@ -5,4 +5,3 @@ TAG=${1:-latest}
echo "Pulling tag '${TAG}'" echo "Pulling tag '${TAG}'"
docker pull matrixdotorg/dendrite-monolith:${TAG} docker pull matrixdotorg/dendrite-monolith:${TAG}
docker pull matrixdotorg/dendrite-polylith:${TAG}

View file

@ -5,4 +5,3 @@ TAG=${1:-latest}
echo "Pushing tag '${TAG}'" echo "Pushing tag '${TAG}'"
docker push matrixdotorg/dendrite-monolith:${TAG} docker push matrixdotorg/dendrite-monolith:${TAG}
docker push matrixdotorg/dendrite-polylith:${TAG}

View file

@ -1,5 +0,0 @@
#!/bin/sh
for db in userapi_accounts mediaapi syncapi roomserver keyserver federationapi appservice mscs; do
createdb -U dendrite -O dendrite dendrite_$db
done

2
build/gobind-pinecone/build.sh Normal file → Executable file
View file

@ -7,7 +7,7 @@ do
case "$option" case "$option"
in in
a) gomobile bind -v -target android -trimpath -ldflags="-s -w" github.com/matrix-org/dendrite/build/gobind-pinecone ;; a) gomobile bind -v -target android -trimpath -ldflags="-s -w" github.com/matrix-org/dendrite/build/gobind-pinecone ;;
i) gomobile bind -v -target ios -trimpath -ldflags="" github.com/matrix-org/dendrite/build/gobind-pinecone ;; i) gomobile bind -v -target ios -trimpath -ldflags="" -o ~/DendriteBindings/Gobind.xcframework . ;;
*) echo "No target specified, specify -a or -i"; exit 1 ;; *) echo "No target specified, specify -a or -i"; exit 1 ;;
esac esac
done done

View file

@ -18,48 +18,29 @@ import (
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"crypto/rand" "crypto/rand"
"crypto/tls"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io"
"net" "net"
"net/http"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"time"
"go.uber.org/atomic"
"github.com/gorilla/mux"
"github.com/matrix-org/dendrite/appservice"
"github.com/matrix-org/dendrite/clientapi/userutil" "github.com/matrix-org/dendrite/clientapi/userutil"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn" "github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conduit"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms" "github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/monolith"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/users" "github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/relay"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing" "github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
"github.com/matrix-org/dendrite/federationapi" "github.com/matrix-org/dendrite/federationapi/api"
"github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/keyserver" "github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/roomserver"
"github.com/matrix-org/dendrite/setup"
"github.com/matrix-org/dendrite/setup/base"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/process" "github.com/matrix-org/dendrite/setup/process"
"github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/userapi"
userapiAPI "github.com/matrix-org/dendrite/userapi/api" userapiAPI "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/pinecone/types"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
pineconeConnections "github.com/matrix-org/pinecone/connections"
pineconeMulticast "github.com/matrix-org/pinecone/multicast" pineconeMulticast "github.com/matrix-org/pinecone/multicast"
pineconeRouter "github.com/matrix-org/pinecone/router" pineconeRouter "github.com/matrix-org/pinecone/router"
pineconeSessions "github.com/matrix-org/pinecone/sessions"
"github.com/matrix-org/pinecone/types"
_ "golang.org/x/mobile/bind" _ "golang.org/x/mobile/bind"
) )
@ -69,98 +50,228 @@ const (
PeerTypeMulticast = pineconeRouter.PeerTypeMulticast PeerTypeMulticast = pineconeRouter.PeerTypeMulticast
PeerTypeBluetooth = pineconeRouter.PeerTypeBluetooth PeerTypeBluetooth = pineconeRouter.PeerTypeBluetooth
PeerTypeBonjour = pineconeRouter.PeerTypeBonjour PeerTypeBonjour = pineconeRouter.PeerTypeBonjour
MaxFrameSize = types.MaxFrameSize
) )
// Re-export Conduit in this package for bindings.
type Conduit struct {
conduit.Conduit
}
type DendriteMonolith struct { type DendriteMonolith struct {
logger logrus.Logger logger logrus.Logger
PineconeRouter *pineconeRouter.Router p2pMonolith monolith.P2PMonolith
PineconeMulticast *pineconeMulticast.Multicast
PineconeQUIC *pineconeSessions.Sessions
PineconeManager *pineconeConnections.ConnectionManager
StorageDirectory string StorageDirectory string
CacheDirectory string CacheDirectory string
listener net.Listener listener net.Listener
httpServer *http.Server
processContext *process.ProcessContext
userAPI userapiAPI.UserInternalAPI
} }
func (m *DendriteMonolith) PublicKey() string { func (m *DendriteMonolith) PublicKey() string {
return m.PineconeRouter.PublicKey().String() return m.p2pMonolith.Router.PublicKey().String()
} }
func (m *DendriteMonolith) BaseURL() string { func (m *DendriteMonolith) BaseURL() string {
return fmt.Sprintf("http://%s", m.listener.Addr().String()) return fmt.Sprintf("http://%s", m.p2pMonolith.Addr())
} }
func (m *DendriteMonolith) PeerCount(peertype int) int { func (m *DendriteMonolith) PeerCount(peertype int) int {
return m.PineconeRouter.PeerCount(peertype) return m.p2pMonolith.Router.PeerCount(peertype)
} }
func (m *DendriteMonolith) SessionCount() int { func (m *DendriteMonolith) SessionCount() int {
return len(m.PineconeQUIC.Protocol("matrix").Sessions()) return len(m.p2pMonolith.Sessions.Protocol(monolith.SessionProtocol).Sessions())
} }
func (m *DendriteMonolith) RegisterNetworkInterface(name string, index int, mtu int, up bool, broadcast bool, loopback bool, pointToPoint bool, multicast bool, addrs string) { type InterfaceInfo struct {
m.PineconeMulticast.RegisterInterface(pineconeMulticast.InterfaceInfo{ Name string
Name: name, Index int
Index: index, Mtu int
Mtu: mtu, Up bool
Up: up, Broadcast bool
Broadcast: broadcast, Loopback bool
Loopback: loopback, PointToPoint bool
PointToPoint: pointToPoint, Multicast bool
Multicast: multicast, Addrs string
Addrs: addrs, }
type InterfaceRetriever interface {
CacheCurrentInterfaces() int
GetCachedInterface(index int) *InterfaceInfo
}
func (m *DendriteMonolith) RegisterNetworkCallback(intfCallback InterfaceRetriever) {
callback := func() []pineconeMulticast.InterfaceInfo {
count := intfCallback.CacheCurrentInterfaces()
intfs := []pineconeMulticast.InterfaceInfo{}
for i := 0; i < count; i++ {
iface := intfCallback.GetCachedInterface(i)
if iface != nil {
intfs = append(intfs, pineconeMulticast.InterfaceInfo{
Name: iface.Name,
Index: iface.Index,
Mtu: iface.Mtu,
Up: iface.Up,
Broadcast: iface.Broadcast,
Loopback: iface.Loopback,
PointToPoint: iface.PointToPoint,
Multicast: iface.Multicast,
Addrs: iface.Addrs,
}) })
} }
}
return intfs
}
m.p2pMonolith.Multicast.RegisterNetworkCallback(callback)
}
func (m *DendriteMonolith) SetMulticastEnabled(enabled bool) { func (m *DendriteMonolith) SetMulticastEnabled(enabled bool) {
if enabled { if enabled {
m.PineconeMulticast.Start() m.p2pMonolith.Multicast.Start()
} else { } else {
m.PineconeMulticast.Stop() m.p2pMonolith.Multicast.Stop()
m.DisconnectType(int(pineconeRouter.PeerTypeMulticast)) m.DisconnectType(int(pineconeRouter.PeerTypeMulticast))
} }
} }
func (m *DendriteMonolith) SetStaticPeer(uri string) { func (m *DendriteMonolith) SetStaticPeer(uri string) {
m.PineconeManager.RemovePeers() m.p2pMonolith.ConnManager.RemovePeers()
for _, uri := range strings.Split(uri, ",") { for _, uri := range strings.Split(uri, ",") {
m.PineconeManager.AddPeer(strings.TrimSpace(uri)) m.p2pMonolith.ConnManager.AddPeer(strings.TrimSpace(uri))
} }
} }
func getServerKeyFromString(nodeID string) (spec.ServerName, error) {
var nodeKey spec.ServerName
if userID, err := spec.NewUserID(nodeID, false); err == nil {
hexKey, decodeErr := hex.DecodeString(string(userID.Domain()))
if decodeErr != nil || len(hexKey) != ed25519.PublicKeySize {
return "", fmt.Errorf("UserID domain is not a valid ed25519 public key: %v", userID.Domain())
} else {
nodeKey = userID.Domain()
}
} else {
hexKey, decodeErr := hex.DecodeString(nodeID)
if decodeErr != nil || len(hexKey) != ed25519.PublicKeySize {
return "", fmt.Errorf("Relay server uri is not a valid ed25519 public key: %v", nodeID)
} else {
nodeKey = spec.ServerName(nodeID)
}
}
return nodeKey, nil
}
func (m *DendriteMonolith) SetRelayServers(nodeID string, uris string) {
relays := []spec.ServerName{}
for _, uri := range strings.Split(uris, ",") {
uri = strings.TrimSpace(uri)
if len(uri) == 0 {
continue
}
nodeKey, err := getServerKeyFromString(uri)
if err != nil {
logrus.Errorf(err.Error())
continue
}
relays = append(relays, nodeKey)
}
nodeKey, err := getServerKeyFromString(nodeID)
if err != nil {
logrus.Errorf(err.Error())
return
}
if string(nodeKey) == m.PublicKey() {
logrus.Infof("Setting own relay servers to: %v", relays)
m.p2pMonolith.RelayRetriever.SetRelayServers(relays)
} else {
relay.UpdateNodeRelayServers(
spec.ServerName(nodeKey),
relays,
m.p2pMonolith.ProcessCtx.Context(),
m.p2pMonolith.GetFederationAPI(),
)
}
}
func (m *DendriteMonolith) GetRelayServers(nodeID string) string {
nodeKey, err := getServerKeyFromString(nodeID)
if err != nil {
logrus.Errorf(err.Error())
return ""
}
relaysString := ""
if string(nodeKey) == m.PublicKey() {
relays := m.p2pMonolith.RelayRetriever.GetRelayServers()
for i, relay := range relays {
if i != 0 {
// Append a comma to the previous entry if there is one.
relaysString += ","
}
relaysString += string(relay)
}
} else {
request := api.P2PQueryRelayServersRequest{Server: spec.ServerName(nodeKey)}
response := api.P2PQueryRelayServersResponse{}
err := m.p2pMonolith.GetFederationAPI().P2PQueryRelayServers(m.p2pMonolith.ProcessCtx.Context(), &request, &response)
if err != nil {
logrus.Warnf("Failed obtaining list of this node's relay servers: %s", err.Error())
return ""
}
for i, relay := range response.RelayServers {
if i != 0 {
// Append a comma to the previous entry if there is one.
relaysString += ","
}
relaysString += string(relay)
}
}
return relaysString
}
func (m *DendriteMonolith) RelayingEnabled() bool {
return m.p2pMonolith.GetRelayAPI().RelayingEnabled()
}
func (m *DendriteMonolith) SetRelayingEnabled(enabled bool) {
m.p2pMonolith.GetRelayAPI().SetRelayingEnabled(enabled)
}
func (m *DendriteMonolith) DisconnectType(peertype int) { func (m *DendriteMonolith) DisconnectType(peertype int) {
for _, p := range m.PineconeRouter.Peers() { for _, p := range m.p2pMonolith.Router.Peers() {
if int(peertype) == p.PeerType { if int(peertype) == p.PeerType {
m.PineconeRouter.Disconnect(types.SwitchPortID(p.Port), nil) m.p2pMonolith.Router.Disconnect(types.SwitchPortID(p.Port), nil)
} }
} }
} }
func (m *DendriteMonolith) DisconnectZone(zone string) { func (m *DendriteMonolith) DisconnectZone(zone string) {
for _, p := range m.PineconeRouter.Peers() { for _, p := range m.p2pMonolith.Router.Peers() {
if zone == p.Zone { if zone == p.Zone {
m.PineconeRouter.Disconnect(types.SwitchPortID(p.Port), nil) m.p2pMonolith.Router.Disconnect(types.SwitchPortID(p.Port), nil)
} }
} }
} }
func (m *DendriteMonolith) DisconnectPort(port int) { func (m *DendriteMonolith) DisconnectPort(port int) {
m.PineconeRouter.Disconnect(types.SwitchPortID(port), nil) m.p2pMonolith.Router.Disconnect(types.SwitchPortID(port), nil)
} }
func (m *DendriteMonolith) Conduit(zone string, peertype int) (*Conduit, error) { func (m *DendriteMonolith) Conduit(zone string, peertype int) (*Conduit, error) {
l, r := net.Pipe() l, r := net.Pipe()
conduit := &Conduit{conn: r, port: 0} newConduit := Conduit{conduit.NewConduit(r, 0)}
go func() { go func() {
conduit.portMutex.Lock()
defer conduit.portMutex.Unlock()
logrus.Errorf("Attempting authenticated connect") logrus.Errorf("Attempting authenticated connect")
var port types.SwitchPortID
var err error var err error
if conduit.port, err = m.PineconeRouter.Connect( if port, err = m.p2pMonolith.Router.Connect(
l, l,
pineconeRouter.ConnectionZone(zone), pineconeRouter.ConnectionZone(zone),
pineconeRouter.ConnectionPeerType(peertype), pineconeRouter.ConnectionPeerType(peertype),
@ -168,19 +279,20 @@ func (m *DendriteMonolith) Conduit(zone string, peertype int) (*Conduit, error)
logrus.Errorf("Authenticated connect failed: %s", err) logrus.Errorf("Authenticated connect failed: %s", err)
_ = l.Close() _ = l.Close()
_ = r.Close() _ = r.Close()
_ = conduit.Close() _ = newConduit.Close()
return return
} }
logrus.Infof("Authenticated connect succeeded (port %d)", conduit.port) newConduit.SetPort(port)
logrus.Infof("Authenticated connect succeeded (port %d)", newConduit.Port())
}() }()
return conduit, nil return &newConduit, nil
} }
func (m *DendriteMonolith) RegisterUser(localpart, password string) (string, error) { func (m *DendriteMonolith) RegisterUser(localpart, password string) (string, error) {
pubkey := m.PineconeRouter.PublicKey() pubkey := m.p2pMonolith.Router.PublicKey()
userID := userutil.MakeUserID( userID := userutil.MakeUserID(
localpart, localpart,
gomatrixserverlib.ServerName(hex.EncodeToString(pubkey[:])), spec.ServerName(hex.EncodeToString(pubkey[:])),
) )
userReq := &userapiAPI.PerformAccountCreationRequest{ userReq := &userapiAPI.PerformAccountCreationRequest{
AccountType: userapiAPI.AccountTypeUser, AccountType: userapiAPI.AccountTypeUser,
@ -188,7 +300,7 @@ func (m *DendriteMonolith) RegisterUser(localpart, password string) (string, err
Password: password, Password: password,
} }
userRes := &userapiAPI.PerformAccountCreationResponse{} userRes := &userapiAPI.PerformAccountCreationResponse{}
if err := m.userAPI.PerformAccountCreation(context.Background(), userReq, userRes); err != nil { if err := m.p2pMonolith.GetUserAPI().PerformAccountCreation(context.Background(), userReq, userRes); err != nil {
return userID, fmt.Errorf("userAPI.PerformAccountCreation: %w", err) return userID, fmt.Errorf("userAPI.PerformAccountCreation: %w", err)
} }
return userID, nil return userID, nil
@ -206,7 +318,7 @@ func (m *DendriteMonolith) RegisterDevice(localpart, deviceID string) (string, e
AccessToken: hex.EncodeToString(accessTokenBytes[:n]), AccessToken: hex.EncodeToString(accessTokenBytes[:n]),
} }
loginRes := &userapiAPI.PerformDeviceCreationResponse{} loginRes := &userapiAPI.PerformDeviceCreationResponse{}
if err := m.userAPI.PerformDeviceCreation(context.Background(), loginReq, loginRes); err != nil { if err := m.p2pMonolith.GetUserAPI().PerformDeviceCreation(context.Background(), loginReq, loginRes); err != nil {
return "", fmt.Errorf("userAPI.PerformDeviceCreation: %w", err) return "", fmt.Errorf("userAPI.PerformDeviceCreation: %w", err)
} }
if !loginRes.DeviceCreated { if !loginRes.DeviceCreated {
@ -215,51 +327,10 @@ func (m *DendriteMonolith) RegisterDevice(localpart, deviceID string) (string, e
return loginRes.Device.AccessToken, nil return loginRes.Device.AccessToken, nil
} }
// nolint:gocyclo
func (m *DendriteMonolith) Start() { func (m *DendriteMonolith) Start() {
var sk ed25519.PrivateKey
var pk ed25519.PublicKey
keyfile := filepath.Join(m.StorageDirectory, "p2p.pem") keyfile := filepath.Join(m.StorageDirectory, "p2p.pem")
if _, err := os.Stat(keyfile); os.IsNotExist(err) { oldKeyfile := filepath.Join(m.StorageDirectory, "p2p.key")
oldkeyfile := filepath.Join(m.StorageDirectory, "p2p.key") sk, pk := monolith.GetOrCreateKey(keyfile, oldKeyfile)
if _, err = os.Stat(oldkeyfile); os.IsNotExist(err) {
if err = test.NewMatrixKey(keyfile); err != nil {
panic("failed to generate a new PEM key: " + err.Error())
}
if _, sk, err = config.LoadMatrixKey(keyfile, os.ReadFile); err != nil {
panic("failed to load PEM key: " + err.Error())
}
if len(sk) != ed25519.PrivateKeySize {
panic("the private key is not long enough")
}
} else {
if sk, err = os.ReadFile(oldkeyfile); err != nil {
panic("failed to read the old private key: " + err.Error())
}
if len(sk) != ed25519.PrivateKeySize {
panic("the private key is not long enough")
}
if err = test.SaveMatrixKey(keyfile, sk); err != nil {
panic("failed to convert the private key to PEM format: " + err.Error())
}
}
} else {
if _, sk, err = config.LoadMatrixKey(keyfile, os.ReadFile); err != nil {
panic("failed to load PEM key: " + err.Error())
}
if len(sk) != ed25519.PrivateKeySize {
panic("the private key is not long enough")
}
}
pk = sk.Public().(ed25519.PublicKey)
var err error
m.listener, err = net.Listen("tcp", "localhost:65432")
if err != nil {
panic(err)
}
m.logger = logrus.Logger{ m.logger = logrus.Logger{
Out: BindLogger{}, Out: BindLogger{},
@ -267,190 +338,29 @@ func (m *DendriteMonolith) Start() {
m.logger.SetOutput(BindLogger{}) m.logger.SetOutput(BindLogger{})
logrus.SetOutput(BindLogger{}) logrus.SetOutput(BindLogger{})
m.PineconeRouter = pineconeRouter.NewRouter(logrus.WithField("pinecone", "router"), sk) m.p2pMonolith = monolith.P2PMonolith{}
m.PineconeQUIC = pineconeSessions.NewSessions(logrus.WithField("pinecone", "sessions"), m.PineconeRouter, []string{"matrix"}) m.p2pMonolith.SetupPinecone(sk)
m.PineconeMulticast = pineconeMulticast.NewMulticast(logrus.WithField("pinecone", "multicast"), m.PineconeRouter)
m.PineconeManager = pineconeConnections.NewConnectionManager(m.PineconeRouter, nil)
prefix := hex.EncodeToString(pk) prefix := hex.EncodeToString(pk)
cfg := &config.Dendrite{} cfg := monolith.GenerateDefaultConfig(sk, m.StorageDirectory, m.CacheDirectory, prefix)
cfg.Defaults(config.DefaultOpts{ cfg.Global.ServerName = spec.ServerName(hex.EncodeToString(pk))
Generate: true,
Monolithic: true,
})
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk))
cfg.Global.PrivateKey = sk
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID) cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
cfg.Global.JetStream.InMemory = false cfg.Global.JetStream.InMemory = false
cfg.Global.JetStream.StoragePath = config.Path(filepath.Join(m.CacheDirectory, prefix)) // NOTE : disabled for now since there is a 64 bit alignment panic on 32 bit systems
cfg.UserAPI.AccountDatabase.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-account.db", filepath.Join(m.StorageDirectory, prefix))) // This isn't actually fixed: https://github.com/blevesearch/zapx/pull/147
cfg.MediaAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-mediaapi.db", filepath.Join(m.StorageDirectory, prefix))) cfg.SyncAPI.Fulltext.Enabled = false
cfg.SyncAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-syncapi.db", filepath.Join(m.StorageDirectory, prefix)))
cfg.RoomServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-roomserver.db", filepath.Join(m.StorageDirectory, prefix)))
cfg.KeyServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-keyserver.db", filepath.Join(m.StorageDirectory, prefix)))
cfg.FederationAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-federationsender.db", filepath.Join(m.StorageDirectory, prefix)))
cfg.MediaAPI.BasePath = config.Path(filepath.Join(m.CacheDirectory, "media"))
cfg.MediaAPI.AbsBasePath = config.Path(filepath.Join(m.CacheDirectory, "media"))
cfg.MSCs.MSCs = []string{"msc2836", "msc2946"}
cfg.ClientAPI.RegistrationDisabled = false
cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled = true
cfg.SyncAPI.Fulltext.Enabled = true
cfg.SyncAPI.Fulltext.IndexPath = config.Path(filepath.Join(m.CacheDirectory, "search"))
if err = cfg.Derive(); err != nil {
panic(err)
}
base := base.NewBaseDendrite(cfg, "Monolith") processCtx := process.NewProcessContext()
defer base.Close() // nolint: errcheck cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
routers := httputil.NewRouters()
federation := conn.CreateFederationClient(base, m.PineconeQUIC) enableRelaying := false
enableMetrics := false
serverKeyAPI := &signing.YggdrasilKeys{} enableWebsockets := false
keyRing := serverKeyAPI.KeyRing() m.p2pMonolith.SetupDendrite(processCtx, cfg, cm, routers, 65432, enableRelaying, enableMetrics, enableWebsockets)
m.p2pMonolith.StartMonolith()
rsAPI := roomserver.NewInternalAPI(base)
fsAPI := federationapi.NewInternalAPI(
base, federation, rsAPI, base.Caches, keyRing, true,
)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
m.userAPI = userapi.NewInternalAPI(base, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI, rsAPI, base.PushGatewayHTTPClient())
keyAPI.SetUserAPI(m.userAPI)
asAPI := appservice.NewInternalAPI(base, m.userAPI, rsAPI)
// 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
rsAPI.SetFederationAPI(fsAPI, keyRing)
userProvider := users.NewPineconeUserProvider(m.PineconeRouter, m.PineconeQUIC, m.userAPI, federation)
roomProvider := rooms.NewPineconeRoomProvider(m.PineconeRouter, m.PineconeQUIC, fsAPI, federation)
monolith := setup.Monolith{
Config: base.Cfg,
Client: conn.CreateClient(base, m.PineconeQUIC),
FedClient: federation,
KeyRing: keyRing,
AppserviceAPI: asAPI,
FederationAPI: fsAPI,
RoomserverAPI: rsAPI,
UserAPI: m.userAPI,
KeyAPI: keyAPI,
ExtPublicRoomsProvider: roomProvider,
ExtUserDirectoryProvider: userProvider,
}
monolith.AddAllPublicRoutes(base)
httpRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux)
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
httpRouter.HandleFunc("/pinecone", m.PineconeRouter.ManholeHandler)
pMux := mux.NewRouter().SkipClean(true).UseEncodedPath()
pMux.PathPrefix(users.PublicURL).HandlerFunc(userProvider.FederatedUserProfiles)
pMux.PathPrefix(httputil.PublicFederationPathPrefix).Handler(base.PublicFederationAPIMux)
pMux.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
pHTTP := m.PineconeQUIC.Protocol("matrix").HTTP()
pHTTP.Mux().Handle(users.PublicURL, pMux)
pHTTP.Mux().Handle(httputil.PublicFederationPathPrefix, pMux)
pHTTP.Mux().Handle(httputil.PublicMediaPathPrefix, pMux)
// Build both ends of a HTTP multiplex.
h2s := &http2.Server{}
m.httpServer = &http.Server{
Addr: ":0",
TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
BaseContext: func(_ net.Listener) context.Context {
return context.Background()
},
Handler: h2c.NewHandler(pMux, h2s),
}
m.processContext = base.ProcessContext
go func() {
m.logger.Info("Listening on ", cfg.Global.ServerName)
switch m.httpServer.Serve(m.PineconeQUIC.Protocol("matrix")) {
case net.ErrClosed, http.ErrServerClosed:
m.logger.Info("Stopped listening on ", cfg.Global.ServerName)
default:
m.logger.Fatal(err)
}
}()
go func() {
logrus.Info("Listening on ", m.listener.Addr())
switch http.Serve(m.listener, httpRouter) {
case net.ErrClosed, http.ErrServerClosed:
m.logger.Info("Stopped listening on ", cfg.Global.ServerName)
default:
m.logger.Fatal(err)
}
}()
} }
func (m *DendriteMonolith) Stop() { func (m *DendriteMonolith) Stop() {
m.processContext.ShutdownDendrite() m.p2pMonolith.Stop()
_ = m.listener.Close()
m.PineconeMulticast.Stop()
_ = m.PineconeQUIC.Close()
_ = m.PineconeRouter.Close()
m.processContext.WaitForComponentsToFinish()
}
const MaxFrameSize = types.MaxFrameSize
type Conduit struct {
closed atomic.Bool
conn net.Conn
port types.SwitchPortID
portMutex sync.Mutex
}
func (c *Conduit) Port() int {
c.portMutex.Lock()
defer c.portMutex.Unlock()
return int(c.port)
}
func (c *Conduit) Read(b []byte) (int, error) {
if c.closed.Load() {
return 0, io.EOF
}
return c.conn.Read(b)
}
func (c *Conduit) ReadCopy() ([]byte, error) {
if c.closed.Load() {
return nil, io.EOF
}
var buf [65535 * 2]byte
n, err := c.conn.Read(buf[:])
if err != nil {
return nil, err
}
return buf[:n], nil
}
func (c *Conduit) Write(b []byte) (int, error) {
if c.closed.Load() {
return 0, io.EOF
}
return c.conn.Write(b)
}
func (c *Conduit) Close() error {
if c.closed.Load() {
return io.ErrClosedPipe
}
c.closed.Store(true)
return c.conn.Close()
} }

View file

@ -0,0 +1,158 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gobind
import (
"strings"
"testing"
"github.com/matrix-org/gomatrixserverlib/spec"
)
func TestMonolithStarts(t *testing.T) {
monolith := DendriteMonolith{
StorageDirectory: t.TempDir(),
CacheDirectory: t.TempDir(),
}
monolith.Start()
monolith.PublicKey()
monolith.Stop()
}
func TestMonolithSetRelayServers(t *testing.T) {
testCases := []struct {
name string
nodeID string
relays string
expectedRelays string
expectSelf bool
}{
{
name: "assorted valid, invalid, empty & self keys",
nodeID: "@valid:abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
relays: "@valid:123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd,@invalid:notakey,,",
expectedRelays: "123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
expectSelf: true,
},
{
name: "invalid node key",
nodeID: "@invalid:notakey",
relays: "@valid:123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd,@invalid:notakey,,",
expectedRelays: "",
expectSelf: false,
},
{
name: "node is self",
nodeID: "self",
relays: "@valid:123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd,@invalid:notakey,,",
expectedRelays: "123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
expectSelf: false,
},
}
for _, tc := range testCases {
monolith := DendriteMonolith{
StorageDirectory: t.TempDir(),
CacheDirectory: t.TempDir(),
}
monolith.Start()
inputRelays := tc.relays
expectedRelays := tc.expectedRelays
if tc.expectSelf {
inputRelays += "," + monolith.PublicKey()
expectedRelays += "," + monolith.PublicKey()
}
nodeID := tc.nodeID
if nodeID == "self" {
nodeID = monolith.PublicKey()
}
monolith.SetRelayServers(nodeID, inputRelays)
relays := monolith.GetRelayServers(nodeID)
monolith.Stop()
if !containSameKeys(strings.Split(relays, ","), strings.Split(expectedRelays, ",")) {
t.Fatalf("%s: expected %s got %s", tc.name, expectedRelays, relays)
}
}
}
func containSameKeys(expected []string, actual []string) bool {
if len(expected) != len(actual) {
return false
}
for _, expectedKey := range expected {
hasMatch := false
for _, actualKey := range actual {
if actualKey == expectedKey {
hasMatch = true
}
}
if !hasMatch {
return false
}
}
return true
}
func TestParseServerKey(t *testing.T) {
testCases := []struct {
name string
serverKey string
expectedErr bool
expectedKey spec.ServerName
}{
{
name: "valid userid as key",
serverKey: "@valid:abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
expectedErr: false,
expectedKey: "abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
},
{
name: "valid key",
serverKey: "abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
expectedErr: false,
expectedKey: "abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
},
{
name: "invalid userid key",
serverKey: "@invalid:notakey",
expectedErr: true,
expectedKey: "",
},
{
name: "invalid key",
serverKey: "@invalid:notakey",
expectedErr: true,
expectedKey: "",
},
}
for _, tc := range testCases {
key, err := getServerKeyFromString(tc.serverKey)
if tc.expectedErr && err == nil {
t.Fatalf("%s: expected an error", tc.name)
} else if !tc.expectedErr && err != nil {
t.Fatalf("%s: didn't expect an error: %s", tc.name, err.Error())
}
if tc.expectedKey != key {
t.Fatalf("%s: keys not equal. expected: %s got: %s", tc.name, tc.expectedKey, key)
}
}
}

View file

@ -12,6 +12,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/getsentry/sentry-go"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/matrix-org/dendrite/appservice" "github.com/matrix-org/dendrite/appservice"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing" "github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
@ -19,16 +20,20 @@ import (
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggrooms" "github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggrooms"
"github.com/matrix-org/dendrite/federationapi" "github.com/matrix-org/dendrite/federationapi"
"github.com/matrix-org/dendrite/federationapi/api" "github.com/matrix-org/dendrite/federationapi/api"
"github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/internal/caching"
"github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/keyserver" "github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/roomserver" "github.com/matrix-org/dendrite/roomserver"
"github.com/matrix-org/dendrite/setup" "github.com/matrix-org/dendrite/setup"
"github.com/matrix-org/dendrite/setup/base" basepkg "github.com/matrix-org/dendrite/setup/base"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/dendrite/setup/process" "github.com/matrix-org/dendrite/setup/process"
"github.com/matrix-org/dendrite/test" "github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/userapi" "github.com/matrix-org/dendrite/userapi"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
_ "golang.org/x/mobile/bind" _ "golang.org/x/mobile/bind"
@ -128,9 +133,9 @@ func (m *DendriteMonolith) Start() {
cfg := &config.Dendrite{} cfg := &config.Dendrite{}
cfg.Defaults(config.DefaultOpts{ cfg.Defaults(config.DefaultOpts{
Generate: true, Generate: true,
Monolithic: true, SingleDatabase: true,
}) })
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk)) cfg.Global.ServerName = spec.ServerName(hex.EncodeToString(pk))
cfg.Global.PrivateKey = sk cfg.Global.PrivateKey = sk
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID) cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
cfg.Global.JetStream.StoragePath = config.Path(fmt.Sprintf("%s/", m.StorageDirectory)) cfg.Global.JetStream.StoragePath = config.Path(fmt.Sprintf("%s/", m.StorageDirectory))
@ -149,26 +154,71 @@ func (m *DendriteMonolith) Start() {
panic(err) panic(err)
} }
base := base.NewBaseDendrite(cfg, "Monolith") configErrors := &config.ConfigErrors{}
m.processContext = base.ProcessContext cfg.Verify(configErrors)
defer base.Close() // nolint: errcheck if len(*configErrors) > 0 {
for _, err := range *configErrors {
logrus.Errorf("Configuration error: %s", err)
}
logrus.Fatalf("Failed to start due to configuration errors")
}
federation := ygg.CreateFederationClient(base) internal.SetupStdLogging()
internal.SetupHookLogging(cfg.Logging)
internal.SetupPprof()
logrus.Infof("Dendrite version %s", internal.VersionString())
if !cfg.ClientAPI.RegistrationDisabled && cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled {
logrus.Warn("Open registration is enabled")
}
closer, err := cfg.SetupTracing()
if err != nil {
logrus.WithError(err).Panicf("failed to start opentracing")
}
defer closer.Close()
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")
}
}
processCtx := process.NewProcessContext()
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
routers := httputil.NewRouters()
basepkg.ConfigureAdminEndpoints(processCtx, routers)
m.processContext = processCtx
defer func() {
processCtx.ShutdownDendrite()
processCtx.WaitForShutdown()
}() // nolint: errcheck
federation := ygg.CreateFederationClient(cfg)
serverKeyAPI := &signing.YggdrasilKeys{} serverKeyAPI := &signing.YggdrasilKeys{}
keyRing := serverKeyAPI.KeyRing() keyRing := serverKeyAPI.KeyRing()
rsAPI := roomserver.NewInternalAPI(base) 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( fsAPI := federationapi.NewInternalAPI(
base, federation, rsAPI, base.Caches, keyRing, true, processCtx, cfg, cm, &natsInstance, federation, rsAPI, caches, keyRing, true,
) )
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation) userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation, caching.EnableMetrics, fsAPI.IsBlacklistedOrBackingOff)
userAPI := userapi.NewInternalAPI(base, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI, rsAPI, base.PushGatewayHTTPClient())
keyAPI.SetUserAPI(userAPI)
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI) asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
rsAPI.SetAppserviceAPI(asAPI) rsAPI.SetAppserviceAPI(asAPI)
// The underlying roomserver implementation needs to be able to call the fedsender. // The underlying roomserver implementation needs to be able to call the fedsender.
@ -176,8 +226,8 @@ func (m *DendriteMonolith) Start() {
rsAPI.SetFederationAPI(fsAPI, keyRing) rsAPI.SetFederationAPI(fsAPI, keyRing)
monolith := setup.Monolith{ monolith := setup.Monolith{
Config: base.Cfg, Config: cfg,
Client: ygg.CreateClient(base), Client: ygg.CreateClient(),
FedClient: federation, FedClient: federation,
KeyRing: keyRing, KeyRing: keyRing,
@ -185,21 +235,21 @@ func (m *DendriteMonolith) Start() {
FederationAPI: fsAPI, FederationAPI: fsAPI,
RoomserverAPI: rsAPI, RoomserverAPI: rsAPI,
UserAPI: userAPI, UserAPI: userAPI,
KeyAPI: keyAPI,
ExtPublicRoomsProvider: yggrooms.NewYggdrasilRoomProvider( ExtPublicRoomsProvider: yggrooms.NewYggdrasilRoomProvider(
ygg, fsAPI, federation, ygg, fsAPI, federation,
), ),
} }
monolith.AddAllPublicRoutes(base) monolith.AddAllPublicRoutes(processCtx, cfg, routers, cm, &natsInstance, caches, caching.EnableMetrics)
httpRouter := mux.NewRouter() httpRouter := mux.NewRouter()
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux) httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(routers.Client)
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux) httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(routers.Media)
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux) httpRouter.PathPrefix(httputil.DendriteAdminPathPrefix).Handler(routers.DendriteAdmin)
httpRouter.PathPrefix(httputil.SynapseAdminPathPrefix).Handler(routers.SynapseAdmin)
yggRouter := mux.NewRouter() yggRouter := mux.NewRouter()
yggRouter.PathPrefix(httputil.PublicFederationPathPrefix).Handler(base.PublicFederationAPIMux) yggRouter.PathPrefix(httputil.PublicFederationPathPrefix).Handler(routers.Federation)
yggRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux) yggRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(routers.Media)
// Build both ends of a HTTP multiplex. // Build both ends of a HTTP multiplex.
m.httpServer = &http.Server{ m.httpServer = &http.Server{

View file

@ -1,6 +1,6 @@
#syntax=docker/dockerfile:1.2 #syntax=docker/dockerfile:1.2
FROM golang:1.18-stretch as build FROM golang:1.20-bullseye as build
RUN apt-get update && apt-get install -y sqlite3 RUN apt-get update && apt-get install -y sqlite3
WORKDIR /build WORKDIR /build
@ -10,18 +10,22 @@ RUN mkdir /dendrite
# Utilise Docker caching when downloading dependencies, this stops us needlessly # Utilise Docker caching when downloading dependencies, this stops us needlessly
# downloading dependencies every time. # downloading dependencies every time.
ARG CGO
RUN --mount=target=. \ RUN --mount=target=. \
--mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/root/.cache/go-build \
go build -o /dendrite ./cmd/generate-config && \ CGO_ENABLED=${CGO} go build -o /dendrite ./cmd/generate-config && \
go build -o /dendrite ./cmd/generate-keys && \ CGO_ENABLED=${CGO} go build -o /dendrite ./cmd/generate-keys && \
go build -o /dendrite ./cmd/dendrite-monolith-server CGO_ENABLED=${CGO} go build -o /dendrite/dendrite ./cmd/dendrite && \
CGO_ENABLED=${CGO} go build -cover -covermode=atomic -o /dendrite/dendrite-cover -coverpkg "github.com/matrix-org/..." ./cmd/dendrite && \
cp build/scripts/complement-cmd.sh /complement-cmd.sh
WORKDIR /dendrite WORKDIR /dendrite
RUN ./generate-keys --private-key matrix_key.pem RUN ./generate-keys --private-key matrix_key.pem
ENV SERVER_NAME=localhost ENV SERVER_NAME=localhost
ENV API=0 ENV API=0
ENV COVER=0
EXPOSE 8008 8448 EXPOSE 8008 8448
# At runtime, generate TLS cert based on the CA now mounted at /ca # At runtime, generate TLS cert based on the CA now mounted at /ca
@ -29,4 +33,4 @@ EXPOSE 8008 8448
CMD ./generate-keys -keysize 1024 --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key && \ CMD ./generate-keys -keysize 1024 --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key && \
./generate-config -server $SERVER_NAME --ci > dendrite.yaml && \ ./generate-config -server $SERVER_NAME --ci > dendrite.yaml && \
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \ cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates && \
exec ./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml -api=${API:-0} exec /complement-cmd.sh

View file

@ -12,18 +12,20 @@ FROM golang:1.18-stretch
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
ENV COVER=0
EXPOSE 8008 8448 EXPOSE 8008 8448
WORKDIR /runtime WORKDIR /runtime
# This script compiles Dendrite for us. # This script compiles Dendrite for us.
RUN echo '\ RUN echo '\
#!/bin/bash -eux \n\ #!/bin/bash -eux \n\
if test -f "/runtime/dendrite-monolith-server"; then \n\ if test -f "/runtime/dendrite" && test -f "/runtime/dendrite-cover"; then \n\
echo "Skipping compilation; binaries exist" \n\ echo "Skipping compilation; binaries exist" \n\
exit 0 \n\ exit 0 \n\
fi \n\ fi \n\
cd /dendrite \n\ cd /dendrite \n\
go build -v -o /runtime /dendrite/cmd/dendrite-monolith-server \n\ go build -v -o /runtime /dendrite/cmd/dendrite \n\
go test -c -cover -covermode=atomic -o /runtime/dendrite-cover -coverpkg "github.com/matrix-org/..." /dendrite/cmd/dendrite \n\
' > compile.sh && chmod +x compile.sh ' > compile.sh && chmod +x compile.sh
# This script runs Dendrite for us. Must be run in the /runtime directory. # This script runs Dendrite for us. Must be run in the /runtime directory.
@ -33,7 +35,8 @@ RUN echo '\
./generate-keys -keysize 1024 --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key \n\ ./generate-keys -keysize 1024 --server $SERVER_NAME --tls-cert server.crt --tls-key server.key --tls-authority-cert /complement/ca/ca.crt --tls-authority-key /complement/ca/ca.key \n\
./generate-config -server $SERVER_NAME --ci > dendrite.yaml \n\ ./generate-config -server $SERVER_NAME --ci > dendrite.yaml \n\
cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates \n\ cp /complement/ca/ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates \n\
exec ./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml \n\ [ ${COVER} -eq 1 ] && exec ./dendrite-cover --test.coverprofile=integrationcover.log --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml \n\
exec ./dendrite --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml \n\
' > run.sh && chmod +x run.sh ' > run.sh && chmod +x run.sh

View file

@ -1,19 +1,19 @@
#syntax=docker/dockerfile:1.2 #syntax=docker/dockerfile:1.2
FROM golang:1.18-stretch as build FROM golang:1.20-bullseye 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 over localhost
RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/postgresql/9.6/main/pg_hba.conf && \ RUN sed -i "s%127.0.0.1/32 md5%127.0.0.1/32 trust%g" /etc/postgresql/13/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/9.6/main/postgresql.conf sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/13/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 9.6 main start \n\ pg_ctlcluster 13 main start \n\
\n\ \n\
until pg_isready \n\ until pg_isready \n\
do \n\ do \n\
@ -28,18 +28,22 @@ RUN mkdir /dendrite
# Utilise Docker caching when downloading dependencies, this stops us needlessly # Utilise Docker caching when downloading dependencies, this stops us needlessly
# downloading dependencies every time. # downloading dependencies every time.
ARG CGO
RUN --mount=target=. \ RUN --mount=target=. \
--mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/root/.cache/go-build \
go build -o /dendrite ./cmd/generate-config && \ CGO_ENABLED=${CGO} go build -o /dendrite ./cmd/generate-config && \
go build -o /dendrite ./cmd/generate-keys && \ CGO_ENABLED=${CGO} go build -o /dendrite ./cmd/generate-keys && \
go build -o /dendrite ./cmd/dendrite-monolith-server CGO_ENABLED=${CGO} go build -o /dendrite/dendrite ./cmd/dendrite && \
CGO_ENABLED=${CGO} go build -cover -covermode=atomic -o /dendrite/dendrite-cover -coverpkg "github.com/matrix-org/..." ./cmd/dendrite && \
cp build/scripts/complement-cmd.sh /complement-cmd.sh
WORKDIR /dendrite WORKDIR /dendrite
RUN ./generate-keys --private-key matrix_key.pem RUN ./generate-keys --private-key matrix_key.pem
ENV SERVER_NAME=localhost ENV SERVER_NAME=localhost
ENV API=0 ENV API=0
ENV COVER=0
EXPOSE 8008 8448 EXPOSE 8008 8448
@ -50,4 +54,4 @@ CMD /build/run_postgres.sh && ./generate-keys --keysize 1024 --server $SERVER_NA
# 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 && \
exec ./dendrite-monolith-server --really-enable-open-registration --tls-cert server.crt --tls-key server.key --config dendrite.yaml -api=${API:-0} exec /complement-cmd.sh

21
build/scripts/complement-cmd.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/bash -e
# This script is intended to be used inside a docker container for Complement
export GOCOVERDIR=/tmp/covdatafiles
mkdir -p "${GOCOVERDIR}"
if [[ "${COVER}" -eq 1 ]]; then
echo "Running with coverage"
exec /dendrite/dendrite-cover \
--really-enable-open-registration \
--tls-cert server.crt \
--tls-key server.key \
--config dendrite.yaml
else
echo "Not running with coverage"
exec /dendrite/dendrite \
--really-enable-open-registration \
--tls-cert server.crt \
--tls-key server.key \
--config dendrite.yaml
fi

View file

@ -15,5 +15,5 @@ tar -xzf master.tar.gz
# Run the tests! # Run the tests!
cd complement-master cd complement-master
COMPLEMENT_BASE_IMAGE=complement-dendrite:latest go test -v -count=1 ./tests COMPLEMENT_BASE_IMAGE=complement-dendrite:latest go test -v -count=1 ./tests ./tests/csapi

1475
clientapi/admin_test.go Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,10 +14,18 @@
package api package api
import "github.com/matrix-org/gomatrixserverlib" import "github.com/matrix-org/gomatrixserverlib/fclient"
// ExtraPublicRoomsProvider provides a way to inject extra published rooms into /publicRooms requests. // ExtraPublicRoomsProvider provides a way to inject extra published rooms into /publicRooms requests.
type ExtraPublicRoomsProvider interface { type ExtraPublicRoomsProvider interface {
// Rooms returns the extra rooms. This is called on-demand by clients, so cache appropriately. // Rooms returns the extra rooms. This is called on-demand by clients, so cache appropriately.
Rooms() []gomatrixserverlib.PublicRoom Rooms() []fclient.PublicRoom
}
type RegistrationToken struct {
Token *string `json:"token"`
UsesAllowed *int32 `json:"uses_allowed"`
Pending *int32 `json:"pending"`
Completed *int32 `json:"completed"`
ExpiryTime *int64 `json:"expiry_time"`
} }

View file

@ -23,8 +23,8 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -58,7 +58,7 @@ func VerifyUserFromRequest(
if err != nil { if err != nil {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
JSON: jsonerror.MissingToken(err.Error()), JSON: spec.MissingToken(err.Error()),
} }
} }
var res api.QueryAccessTokenResponse var res api.QueryAccessTokenResponse
@ -68,21 +68,23 @@ func VerifyUserFromRequest(
}, &res) }, &res)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("userAPI.QueryAccessToken failed") util.GetLogger(req.Context()).WithError(err).Error("userAPI.QueryAccessToken failed")
jsonErr := jsonerror.InternalServerError() return nil, &util.JSONResponse{
return nil, &jsonErr Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
if res.Err != "" { if res.Err != "" {
if strings.HasPrefix(strings.ToLower(res.Err), "forbidden:") { // TODO: use actual error and no string comparison if strings.HasPrefix(strings.ToLower(res.Err), "forbidden:") { // TODO: use actual error and no string comparison
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden(res.Err), JSON: spec.Forbidden(res.Err),
} }
} }
} }
if res.Device == nil { if res.Device == nil {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
JSON: jsonerror.UnknownToken("Unknown token"), JSON: spec.UnknownToken("Unknown token"),
} }
} }
return res.Device, nil return res.Device, nil

View file

@ -18,4 +18,6 @@ package authtypes
type ThreePID struct { type ThreePID struct {
Address string `json:"address"` Address string `json:"address"`
Medium string `json:"medium"` Medium string `json:"medium"`
AddedAt int64 `json:"added_at"`
ValidatedAt int64 `json:"validated_at"`
} }

View file

@ -15,15 +15,14 @@
package auth package auth
import ( import (
"context"
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
uapi "github.com/matrix-org/dendrite/userapi/api" uapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -32,12 +31,17 @@ 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,
JSON: jsonerror.BadJSON("Reading request body failed: " + err.Error()), JSON: spec.BadJSON("Reading request body failed: " + err.Error()),
} }
return nil, nil, err return nil, nil, err
} }
@ -48,7 +52,7 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.U
if err := json.Unmarshal(reqBytes, &header); err != nil { if err := json.Unmarshal(reqBytes, &header); err != nil {
err := &util.JSONResponse{ err := &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("Reading request body failed: " + err.Error()), JSON: spec.BadJSON("Reading request body failed: " + err.Error()),
} }
return nil, nil, err return nil, nil, err
} }
@ -57,7 +61,7 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.U
switch header.Type { switch header.Type {
case authtypes.LoginTypePassword: case authtypes.LoginTypePassword:
typ = &LoginTypePassword{ typ = &LoginTypePassword{
GetAccountByPassword: useraccountAPI.QueryAccountByPassword, UserAPI: useraccountAPI,
Config: cfg, Config: cfg,
} }
case authtypes.LoginTypeToken: case authtypes.LoginTypeToken:
@ -65,15 +69,29 @@ 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,
JSON: jsonerror.InvalidArgumentValue("unhandled login type: " + header.Type), JSON: spec.InvalidParam("unhandled login type: " + header.Type),
} }
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.

View 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
}

View file

@ -17,13 +17,17 @@ package auth
import ( import (
"context" "context"
"net/http" "net/http"
"net/http/httptest"
"reflect" "reflect"
"regexp"
"strings" "strings"
"testing" "testing"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/userutil"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
uapi "github.com/matrix-org/dendrite/userapi/api" uapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -33,6 +37,7 @@ func TestLoginFromJSONReader(t *testing.T) {
tsts := []struct { tsts := []struct {
Name string Name string
Body string Body string
Token string
WantUsername string WantUsername string
WantDeviceID string WantDeviceID string
@ -46,7 +51,7 @@ func TestLoginFromJSONReader(t *testing.T) {
"password": "herpassword", "password": "herpassword",
"device_id": "adevice" "device_id": "adevice"
}`, }`,
WantUsername: "alice", WantUsername: "@alice:example.com",
WantDeviceID: "adevice", WantDeviceID: "adevice",
}, },
{ {
@ -60,19 +65,69 @@ 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) {
var userAPI fakeUserInternalAPI var userAPI fakeUserInternalAPI
cfg := &config.ClientAPI{ cfg := &config.ClientAPI{
Matrix: &config.Global{ Matrix: &config.Global{
SigningIdentity: fclient.SigningIdentity{
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 {
@ -102,14 +157,15 @@ func TestBadLoginFromJSONReader(t *testing.T) {
tsts := []struct { tsts := []struct {
Name string Name string
Body string Body string
Token string
WantErrCode string WantErrCode spec.MatrixErrorCode
}{ }{
{Name: "empty", WantErrCode: "M_BAD_JSON"}, {Name: "empty", WantErrCode: spec.ErrorBadJSON},
{ {
Name: "badUnmarshal", Name: "badUnmarshal",
Body: `badsyntaxJSON`, Body: `badsyntaxJSON`,
WantErrCode: "M_BAD_JSON", WantErrCode: spec.ErrorBadJSON,
}, },
{ {
Name: "badPassword", Name: "badPassword",
@ -119,7 +175,7 @@ func TestBadLoginFromJSONReader(t *testing.T) {
"password": "invalidpassword", "password": "invalidpassword",
"device_id": "adevice" "device_id": "adevice"
}`, }`,
WantErrCode: "M_FORBIDDEN", WantErrCode: spec.ErrorForbidden,
}, },
{ {
Name: "badToken", Name: "badToken",
@ -128,7 +184,7 @@ func TestBadLoginFromJSONReader(t *testing.T) {
"token": "invalidtoken", "token": "invalidtoken",
"device_id": "adevice" "device_id": "adevice"
}`, }`,
WantErrCode: "M_FORBIDDEN", WantErrCode: spec.ErrorForbidden,
}, },
{ {
Name: "badType", Name: "badType",
@ -136,7 +192,46 @@ func TestBadLoginFromJSONReader(t *testing.T) {
"type": "m.login.invalid", "type": "m.login.invalid",
"device_id": "adevice" "device_id": "adevice"
}`, }`,
WantErrCode: "M_INVALID_ARGUMENT_VALUE", 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 {
@ -144,14 +239,38 @@ func TestBadLoginFromJSONReader(t *testing.T) {
var userAPI fakeUserInternalAPI var userAPI fakeUserInternalAPI
cfg := &config.ClientAPI{ cfg := &config.ClientAPI{
Matrix: &config.Global{ Matrix: &config.Global{
SigningIdentity: fclient.SigningIdentity{
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)
} else if merr, ok := errRes.JSON.(*jsonerror.MatrixError); ok && merr.ErrCode != tst.WantErrCode { } else if merr, ok := errRes.JSON.(spec.MatrixError); ok && merr.ErrCode != tst.WantErrCode {
t.Fatalf("LoginFromJSONReader err: got %+v, want code %q", errRes, tst.WantErrCode) t.Fatalf("LoginFromJSONReader err: got %+v, want code %q", errRes, tst.WantErrCode)
} }
}) })
@ -169,7 +288,15 @@ func (ua *fakeUserInternalAPI) QueryAccountByPassword(ctx context.Context, req *
return nil return nil
} }
res.Exists = true res.Exists = true
res.Account = &uapi.Account{} res.Account = &uapi.Account{UserID: userutil.MakeUserID(req.Localpart, req.ServerName)}
return nil
}
func (ua *fakeUserInternalAPI) QueryAccountByLocalpart(ctx context.Context, req *uapi.QueryAccountByLocalpartRequest, res *uapi.QueryAccountByLocalpartResponse) error {
return nil
}
func (ua *fakeUserInternalAPI) PerformAccountCreation(ctx context.Context, req *uapi.PerformAccountCreationRequest, res *uapi.PerformAccountCreationResponse) error {
return nil return nil
} }

View file

@ -20,9 +20,9 @@ import (
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
uapi "github.com/matrix-org/dendrite/userapi/api" uapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -48,13 +48,15 @@ func (t *LoginTypeToken) LoginFromJSON(ctx context.Context, reqBytes []byte) (*L
var res uapi.QueryLoginTokenResponse var res uapi.QueryLoginTokenResponse
if err := t.UserAPI.QueryLoginToken(ctx, &uapi.QueryLoginTokenRequest{Token: r.Token}, &res); err != nil { if err := t.UserAPI.QueryLoginToken(ctx, &uapi.QueryLoginTokenRequest{Token: r.Token}, &res); err != nil {
util.GetLogger(ctx).WithError(err).Error("UserAPI.QueryLoginToken failed") util.GetLogger(ctx).WithError(err).Error("UserAPI.QueryLoginToken failed")
jsonErr := jsonerror.InternalServerError() return nil, nil, &util.JSONResponse{
return nil, nil, &jsonErr Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
if res.Data == nil { if res.Data == nil {
return nil, nil, &util.JSONResponse{ return nil, nil, &util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("invalid login token"), JSON: spec.Forbidden("invalid login token"),
} }
} }

View file

@ -16,20 +16,21 @@ package auth
import ( import (
"context" "context"
"database/sql"
"github.com/go-ldap/ldap/v3"
"github.com/google/uuid"
"net/http" "net/http"
"strings" "strings"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/userutil" "github.com/matrix-org/dendrite/clientapi/userutil"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
type GetAccountByPassword func(ctx context.Context, req *api.QueryAccountByPasswordRequest, res *api.QueryAccountByPasswordResponse) error
type PasswordRequest struct { type PasswordRequest struct {
Login Login
Password string `json:"password"` Password string `json:"password"`
@ -37,8 +38,8 @@ type PasswordRequest struct {
// LoginTypePassword implements https://matrix.org/docs/spec/client_server/r0.6.1#password-based // LoginTypePassword implements https://matrix.org/docs/spec/client_server/r0.6.1#password-based
type LoginTypePassword struct { type LoginTypePassword struct {
GetAccountByPassword GetAccountByPassword
Config *config.ClientAPI Config *config.ClientAPI
UserAPI api.UserLoginAPI
} }
func (t *LoginTypePassword) Name() string { func (t *LoginTypePassword) Name() string {
@ -59,57 +60,227 @@ func (t *LoginTypePassword) LoginFromJSON(ctx context.Context, reqBytes []byte)
return login, func(context.Context, *util.JSONResponse) {}, nil return login, func(context.Context, *util.JSONResponse) {}, nil
} }
func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, *util.JSONResponse) { func (t *LoginTypePassword) Login(ctx context.Context, request *PasswordRequest) (*Login, *util.JSONResponse) {
r := req.(*PasswordRequest) fullUsername := request.Username()
username := strings.ToLower(r.Username()) if fullUsername == "" {
if username == "" {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
JSON: jsonerror.BadJSON("A username must be supplied."), JSON: spec.BadJSON("A username must be supplied."),
} }
} }
if len(r.Password) == 0 { if len(request.Password) == 0 {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
JSON: jsonerror.BadJSON("A password must be supplied."), JSON: spec.BadJSON("A password must be supplied."),
} }
} }
localpart, err := userutil.ParseUsernameParam(username, &t.Config.Matrix.ServerName) username, domain, err := userutil.ParseUsernameParam(fullUsername, t.Config.Matrix)
if err != nil { if err != nil {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
JSON: jsonerror.InvalidUsername(err.Error()), JSON: spec.InvalidUsername(err.Error()),
} }
} }
// Squash username to all lowercase letters if !t.Config.Matrix.IsLocalServerName(domain) {
res := &api.QueryAccountByPasswordResponse{}
err = t.GetAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{Localpart: strings.ToLower(localpart), PlaintextPassword: r.Password}, res)
if err != nil {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusUnauthorized,
JSON: jsonerror.Unknown("unable to fetch account by password"), JSON: spec.InvalidUsername("The server name is not known."),
} }
} }
if !res.Exists { var account *api.Account
err = t.GetAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{ if t.Config.Ldap.Enabled {
Localpart: localpart, isAdmin, err := t.authenticateLdap(username, request.Password)
PlaintextPassword: r.Password, if err != nil {
return nil, err
}
acc, err := t.getOrCreateAccount(ctx, username, domain, isAdmin)
if err != nil {
return nil, err
}
account = acc
} else {
acc, err := t.authenticateDb(ctx, username, domain, request.Password)
if err != nil {
return nil, err
}
account = acc
}
// Set the user, so login.Username() can do the right thing
request.Identifier.User = account.UserID
request.User = account.UserID
return &request.Login, nil
}
func (t *LoginTypePassword) authenticateDb(ctx context.Context, username string, domain spec.ServerName, password string) (*api.Account, *util.JSONResponse) {
res := &api.QueryAccountByPasswordResponse{}
err := t.UserAPI.QueryAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{
Localpart: strings.ToLower(username),
ServerName: domain,
PlaintextPassword: password,
}, res) }, res)
if err != nil { if err != nil {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.Unknown("unable to fetch account by password"), JSON: spec.Unknown("Unable to fetch account by password."),
}
}
if !res.Exists {
err = t.UserAPI.QueryAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{
Localpart: username,
ServerName: domain,
PlaintextPassword: password,
}, res)
if err != nil {
return nil, &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("Unable to fetch account by password."),
} }
} }
// Technically we could tell them if the user does not exist by checking if err == sql.ErrNoRows
// but that would leak the existence of the user.
if !res.Exists { if !res.Exists {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("The username or password was incorrect or the account does not exist."), JSON: spec.Forbidden("The username or password was incorrect or the account does not exist."),
} }
} }
} }
return &r.Login, nil return res.Account, nil
}
func (t *LoginTypePassword) authenticateLdap(username, password string) (bool, *util.JSONResponse) {
var conn *ldap.Conn
conn, err := ldap.DialURL(t.Config.Ldap.Uri)
if err != nil {
return false, &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("unable to connect to ldap: " + err.Error()),
}
}
defer conn.Close()
if t.Config.Ldap.AdminBindEnabled {
err = conn.Bind(t.Config.Ldap.AdminBindDn, t.Config.Ldap.AdminBindPassword)
if err != nil {
return false, &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("unable to bind to ldap: " + err.Error()),
}
}
filter := strings.ReplaceAll(t.Config.Ldap.SearchFilter, "{username}", username)
searchRequest := ldap.NewSearchRequest(
t.Config.Ldap.BaseDn, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
0, 0, false, filter, []string{t.Config.Ldap.SearchAttribute}, nil,
)
result, err := conn.Search(searchRequest)
if err != nil {
return false, &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("unable to bind to search ldap: " + err.Error()),
}
}
if len(result.Entries) > 1 {
return false, &util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: spec.BadJSON("'user' must be duplicated."),
}
}
if len(result.Entries) < 1 {
return false, &util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: spec.BadJSON("'user' not found."),
}
}
userDN := result.Entries[0].DN
err = conn.Bind(userDN, password)
if err != nil {
return false, &util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: spec.InvalidUsername(err.Error()),
}
}
} else {
bindDn := strings.ReplaceAll(t.Config.Ldap.UserBindDn, "{username}", username)
err = conn.Bind(bindDn, password)
if err != nil {
return false, &util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: spec.InvalidUsername(err.Error()),
}
}
}
isAdmin, err := t.isLdapAdmin(conn, username)
if err != nil {
return false, &util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: spec.InvalidUsername(err.Error()),
}
}
return isAdmin, nil
}
func (t *LoginTypePassword) isLdapAdmin(conn *ldap.Conn, username string) (bool, error) {
searchRequest := ldap.NewSearchRequest(
t.Config.Ldap.AdminGroupDn,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
strings.ReplaceAll(t.Config.Ldap.AdminGroupFilter, "{username}", username),
[]string{t.Config.Ldap.AdminGroupAttribute},
nil)
sr, err := conn.Search(searchRequest)
if err != nil {
return false, err
}
if len(sr.Entries) < 1 {
return false, nil
}
return true, nil
}
func (t *LoginTypePassword) getOrCreateAccount(ctx context.Context, username string, domain spec.ServerName, admin bool) (*api.Account, *util.JSONResponse) {
var existing api.QueryAccountByLocalpartResponse
err := t.UserAPI.QueryAccountByLocalpart(ctx, &api.QueryAccountByLocalpartRequest{
Localpart: username,
ServerName: domain,
}, &existing)
if err == nil {
return existing.Account, nil
}
if err != sql.ErrNoRows {
return nil, &util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: spec.InvalidUsername(err.Error()),
}
}
accountType := api.AccountTypeUser
if admin {
accountType = api.AccountTypeAdmin
}
var created api.PerformAccountCreationResponse
err = t.UserAPI.PerformAccountCreation(ctx, &api.PerformAccountCreationRequest{
AppServiceID: "ldap",
Localpart: username,
Password: uuid.New().String(),
AccountType: accountType,
OnConflict: api.ConflictAbort,
}, &created)
if err != nil {
if _, ok := err.(*api.ErrorConflict); ok {
return nil, &util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.UserInUse("Desired user ID is already taken."),
}
}
return nil, &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("failed to create account: " + err.Error()),
}
}
return created.Account, nil
} }

View file

@ -20,9 +20,9 @@ import (
"net/http" "net/http"
"sync" "sync"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
@ -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"`
@ -113,7 +113,7 @@ type UserInteractive struct {
func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI) *UserInteractive { func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI) *UserInteractive {
typePassword := &LoginTypePassword{ typePassword := &LoginTypePassword{
GetAccountByPassword: userAccountAPI.QueryAccountByPassword, UserAPI: userAccountAPI,
Config: cfg, Config: cfg,
} }
return &UserInteractive{ return &UserInteractive{
@ -178,8 +178,10 @@ func (u *UserInteractive) NewSession() *util.JSONResponse {
sessionID, err := GenerateAccessToken() sessionID, err := GenerateAccessToken()
if err != nil { if err != nil {
logrus.WithError(err).Error("failed to generate session ID") logrus.WithError(err).Error("failed to generate session ID")
res := jsonerror.InternalServerError() return &util.JSONResponse{
return &res Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
u.Lock() u.Lock()
u.Sessions[sessionID] = []string{} u.Sessions[sessionID] = []string{}
@ -193,15 +195,19 @@ func (u *UserInteractive) ResponseWithChallenge(sessionID string, response inter
mixedObjects := make(map[string]interface{}) mixedObjects := make(map[string]interface{})
b, err := json.Marshal(response) b, err := json.Marshal(response)
if err != nil { if err != nil {
ise := jsonerror.InternalServerError() return &util.JSONResponse{
return &ise Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
_ = json.Unmarshal(b, &mixedObjects) _ = json.Unmarshal(b, &mixedObjects)
challenge := u.challenge(sessionID) challenge := u.challenge(sessionID)
b, err = json.Marshal(challenge.JSON) b, err = json.Marshal(challenge.JSON)
if err != nil { if err != nil {
ise := jsonerror.InternalServerError() return &util.JSONResponse{
return &ise Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
_ = json.Unmarshal(b, &mixedObjects) _ = json.Unmarshal(b, &mixedObjects)
@ -234,7 +240,7 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
if !ok { if !ok {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("Unknown auth.type: " + authType), JSON: spec.BadJSON("Unknown auth.type: " + authType),
} }
} }
@ -250,7 +256,7 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
if !u.IsSingleStageFlow(authType) { if !u.IsSingleStageFlow(authType) {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown("The auth.session is missing or unknown."), JSON: spec.Unknown("The auth.session is missing or unknown."),
} }
} }
} }

View file

@ -8,13 +8,14 @@ import (
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
var ( var (
ctx = context.Background() ctx = context.Background()
serverName = gomatrixserverlib.ServerName("example.com") serverName = spec.ServerName("example.com")
// space separated localpart+password -> account // space separated localpart+password -> account
lookup = make(map[string]*api.Account) lookup = make(map[string]*api.Account)
device = &api.Device{ device = &api.Device{
@ -44,11 +45,21 @@ func (d *fakeAccountDatabase) QueryAccountByPassword(ctx context.Context, req *a
return nil return nil
} }
func (d *fakeAccountDatabase) QueryAccountByLocalpart(ctx context.Context, req *api.QueryAccountByLocalpartRequest, res *api.QueryAccountByLocalpartResponse) error {
return nil
}
func (d *fakeAccountDatabase) PerformAccountCreation(ctx context.Context, req *api.PerformAccountCreationRequest, res *api.PerformAccountCreationResponse) error {
return nil
}
func setup() *UserInteractive { func setup() *UserInteractive {
cfg := &config.ClientAPI{ cfg := &config.ClientAPI{
Matrix: &config.Global{ Matrix: &config.Global{
SigningIdentity: fclient.SigningIdentity{
ServerName: serverName, ServerName: serverName,
}, },
},
} }
return NewUserInteractive(&fakeAccountDatabase{}, cfg) return NewUserInteractive(&fakeAccountDatabase{}, cfg)
} }

View file

@ -15,55 +15,54 @@
package clientapi package clientapi
import ( import (
"github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/process"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/fclient"
appserviceAPI "github.com/matrix-org/dendrite/appservice/api" appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/clientapi/api"
"github.com/matrix-org/dendrite/clientapi/producers" "github.com/matrix-org/dendrite/clientapi/producers"
"github.com/matrix-org/dendrite/clientapi/routing" "github.com/matrix-org/dendrite/clientapi/routing"
federationAPI "github.com/matrix-org/dendrite/federationapi/api" federationAPI "github.com/matrix-org/dendrite/federationapi/api"
"github.com/matrix-org/dendrite/internal/transactions" "github.com/matrix-org/dendrite/internal/transactions"
keyserverAPI "github.com/matrix-org/dendrite/keyserver/api"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/base"
"github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/jetstream"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
) )
// AddPublicRoutes sets up and registers HTTP handlers for the ClientAPI component. // AddPublicRoutes sets up and registers HTTP handlers for the ClientAPI component.
func AddPublicRoutes( func AddPublicRoutes(
base *base.BaseDendrite, processContext *process.ProcessContext,
federation *gomatrixserverlib.FederationClient, routers httputil.Routers,
cfg *config.Dendrite,
natsInstance *jetstream.NATSInstance,
federation fclient.FederationClient,
rsAPI roomserverAPI.ClientRoomserverAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
asAPI appserviceAPI.AppServiceInternalAPI, asAPI appserviceAPI.AppServiceInternalAPI,
transactionsCache *transactions.Cache, transactionsCache *transactions.Cache,
fsAPI federationAPI.ClientFederationAPI, fsAPI federationAPI.ClientFederationAPI,
userAPI userapi.ClientUserAPI, userAPI userapi.ClientUserAPI,
userDirectoryProvider userapi.QuerySearchProfilesAPI, userDirectoryProvider userapi.QuerySearchProfilesAPI,
keyAPI keyserverAPI.ClientKeyAPI, extRoomsProvider api.ExtraPublicRoomsProvider, enableMetrics bool,
extRoomsProvider api.ExtraPublicRoomsProvider,
) { ) {
cfg := &base.Cfg.ClientAPI js, natsClient := natsInstance.Prepare(processContext, &cfg.Global.JetStream)
mscCfg := &base.Cfg.MSCs
js, natsClient := base.NATS.Prepare(base.ProcessContext, &cfg.Matrix.JetStream)
syncProducer := &producers.SyncAPIProducer{ syncProducer := &producers.SyncAPIProducer{
JetStream: js, JetStream: js,
TopicReceiptEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputReceiptEvent), TopicReceiptEvent: cfg.Global.JetStream.Prefixed(jetstream.OutputReceiptEvent),
TopicSendToDeviceEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputSendToDeviceEvent), TopicSendToDeviceEvent: cfg.Global.JetStream.Prefixed(jetstream.OutputSendToDeviceEvent),
TopicTypingEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputTypingEvent), TopicTypingEvent: cfg.Global.JetStream.Prefixed(jetstream.OutputTypingEvent),
TopicPresenceEvent: cfg.Matrix.JetStream.Prefixed(jetstream.OutputPresenceEvent), TopicPresenceEvent: cfg.Global.JetStream.Prefixed(jetstream.OutputPresenceEvent),
UserAPI: userAPI, UserAPI: userAPI,
ServerName: cfg.Matrix.ServerName, ServerName: cfg.Global.ServerName,
} }
routing.Setup( routing.Setup(
base.PublicClientAPIMux, routers,
base.PublicWellKnownAPIMux,
base.SynapseAdminMux,
base.DendriteAdminMux,
cfg, rsAPI, asAPI, cfg, rsAPI, asAPI,
userAPI, userDirectoryProvider, federation, userAPI, userDirectoryProvider, federation,
syncProducer, transactionsCache, fsAPI, keyAPI, syncProducer, transactionsCache, fsAPI,
extRoomsProvider, mscCfg, natsClient, extRoomsProvider, natsClient, enableMetrics,
) )
} }

2437
clientapi/clientapi_test.go Normal file

File diff suppressed because it is too large Load diff

View file

@ -20,7 +20,7 @@ import (
"net/http" "net/http"
"unicode/utf8" "unicode/utf8"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -32,8 +32,10 @@ func UnmarshalJSONRequest(req *http.Request, iface interface{}) *util.JSONRespon
body, err := io.ReadAll(req.Body) body, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("io.ReadAll failed") util.GetLogger(req.Context()).WithError(err).Error("io.ReadAll failed")
resp := jsonerror.InternalServerError() return &util.JSONResponse{
return &resp Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return UnmarshalJSON(body, iface) return UnmarshalJSON(body, iface)
@ -43,7 +45,7 @@ func UnmarshalJSON(body []byte, iface interface{}) *util.JSONResponse {
if !utf8.Valid(body) { if !utf8.Valid(body) {
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.NotJSON("Body contains invalid UTF-8"), JSON: spec.NotJSON("Body contains invalid UTF-8"),
} }
} }
@ -53,7 +55,7 @@ func UnmarshalJSON(body []byte, iface interface{}) *util.JSONResponse {
// valid JSON with incorrect types for values. // valid JSON with incorrect types for values.
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("The request body could not be decoded into valid JSON. " + err.Error()), JSON: spec.BadJSON("The request body could not be decoded into valid JSON. " + err.Error()),
} }
} }
return nil return nil

View file

@ -1,229 +0,0 @@
// Copyright 2017 Vector Creations Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package jsonerror
import (
"context"
"fmt"
"net/http"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
"github.com/sirupsen/logrus"
)
// MatrixError represents the "standard error response" in Matrix.
// http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards
type MatrixError struct {
ErrCode string `json:"errcode"`
Err string `json:"error"`
}
func (e MatrixError) Error() string {
return fmt.Sprintf("%s: %s", e.ErrCode, e.Err)
}
// InternalServerError returns a 500 Internal Server Error in a matrix-compliant
// format.
func InternalServerError() util.JSONResponse {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: Unknown("Internal Server Error"),
}
}
// Unknown is an unexpected error
func Unknown(msg string) *MatrixError {
return &MatrixError{"M_UNKNOWN", msg}
}
// Forbidden is an error when the client tries to access a resource
// they are not allowed to access.
func Forbidden(msg string) *MatrixError {
return &MatrixError{"M_FORBIDDEN", msg}
}
// BadJSON is an error when the client supplies malformed JSON.
func BadJSON(msg string) *MatrixError {
return &MatrixError{"M_BAD_JSON", msg}
}
// BadAlias is an error when the client supplies a bad alias.
func BadAlias(msg string) *MatrixError {
return &MatrixError{"M_BAD_ALIAS", msg}
}
// NotJSON is an error when the client supplies something that is not JSON
// to a JSON endpoint.
func NotJSON(msg string) *MatrixError {
return &MatrixError{"M_NOT_JSON", msg}
}
// NotFound is an error when the client tries to access an unknown resource.
func NotFound(msg string) *MatrixError {
return &MatrixError{"M_NOT_FOUND", msg}
}
// MissingArgument is an error when the client tries to access a resource
// without providing an argument that is required.
func MissingArgument(msg string) *MatrixError {
return &MatrixError{"M_MISSING_ARGUMENT", msg}
}
// InvalidArgumentValue is an error when the client tries to provide an
// invalid value for a valid argument
func InvalidArgumentValue(msg string) *MatrixError {
return &MatrixError{"M_INVALID_ARGUMENT_VALUE", msg}
}
// MissingToken is an error when the client tries to access a resource which
// requires authentication without supplying credentials.
func MissingToken(msg string) *MatrixError {
return &MatrixError{"M_MISSING_TOKEN", msg}
}
// UnknownToken is an error when the client tries to access a resource which
// requires authentication and supplies an unrecognised token
func UnknownToken(msg string) *MatrixError {
return &MatrixError{"M_UNKNOWN_TOKEN", msg}
}
// WeakPassword is an error which is returned when the client tries to register
// using a weak password. http://matrix.org/docs/spec/client_server/r0.2.0.html#password-based
func WeakPassword(msg string) *MatrixError {
return &MatrixError{"M_WEAK_PASSWORD", msg}
}
// InvalidUsername is an error returned when the client tries to register an
// invalid username
func InvalidUsername(msg string) *MatrixError {
return &MatrixError{"M_INVALID_USERNAME", msg}
}
// UserInUse is an error returned when the client tries to register an
// username that already exists
func UserInUse(msg string) *MatrixError {
return &MatrixError{"M_USER_IN_USE", msg}
}
// RoomInUse is an error returned when the client tries to make a room
// that already exists
func RoomInUse(msg string) *MatrixError {
return &MatrixError{"M_ROOM_IN_USE", msg}
}
// ASExclusive is an error returned when an application service tries to
// register an username that is outside of its registered namespace, or if a
// user attempts to register a username or room alias within an exclusive
// namespace.
func ASExclusive(msg string) *MatrixError {
return &MatrixError{"M_EXCLUSIVE", msg}
}
// GuestAccessForbidden is an error which is returned when the client is
// forbidden from accessing a resource as a guest.
func GuestAccessForbidden(msg string) *MatrixError {
return &MatrixError{"M_GUEST_ACCESS_FORBIDDEN", msg}
}
// InvalidSignature is an error which is returned when the client tries
// to upload invalid signatures.
func InvalidSignature(msg string) *MatrixError {
return &MatrixError{"M_INVALID_SIGNATURE", msg}
}
// InvalidParam is an error that is returned when a parameter was invalid,
// traditionally with cross-signing.
func InvalidParam(msg string) *MatrixError {
return &MatrixError{"M_INVALID_PARAM", msg}
}
// MissingParam is an error that is returned when a parameter was incorrect,
// traditionally with cross-signing.
func MissingParam(msg string) *MatrixError {
return &MatrixError{"M_MISSING_PARAM", msg}
}
// UnableToAuthoriseJoin is an error that is returned when a server can't
// determine whether to allow a restricted join or not.
func UnableToAuthoriseJoin(msg string) *MatrixError {
return &MatrixError{"M_UNABLE_TO_AUTHORISE_JOIN", msg}
}
// LeaveServerNoticeError is an error returned when trying to reject an invite
// for a server notice room.
func LeaveServerNoticeError() *MatrixError {
return &MatrixError{
ErrCode: "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM",
Err: "You cannot reject this invite",
}
}
type IncompatibleRoomVersionError struct {
RoomVersion string `json:"room_version"`
Error string `json:"error"`
Code string `json:"errcode"`
}
// IncompatibleRoomVersion is an error which is returned when the client
// requests a room with a version that is unsupported.
func IncompatibleRoomVersion(roomVersion gomatrixserverlib.RoomVersion) *IncompatibleRoomVersionError {
return &IncompatibleRoomVersionError{
Code: "M_INCOMPATIBLE_ROOM_VERSION",
RoomVersion: string(roomVersion),
Error: "Your homeserver does not support the features required to join this room",
}
}
// UnsupportedRoomVersion is an error which is returned when the client
// requests a room with a version that is unsupported.
func UnsupportedRoomVersion(msg string) *MatrixError {
return &MatrixError{"M_UNSUPPORTED_ROOM_VERSION", msg}
}
// LimitExceededError is a rate-limiting error.
type LimitExceededError struct {
MatrixError
RetryAfterMS int64 `json:"retry_after_ms,omitempty"`
}
// LimitExceeded is an error when the client tries to send events too quickly.
func LimitExceeded(msg string, retryAfterMS int64) *LimitExceededError {
return &LimitExceededError{
MatrixError: MatrixError{"M_LIMIT_EXCEEDED", msg},
RetryAfterMS: retryAfterMS,
}
}
// NotTrusted is an error which is returned when the client asks the server to
// proxy a request (e.g. 3PID association) to a server that isn't trusted
func NotTrusted(serverName string) *MatrixError {
return &MatrixError{
ErrCode: "M_SERVER_NOT_TRUSTED",
Err: fmt.Sprintf("Untrusted server '%s'", serverName),
}
}
// InternalAPIError is returned when Dendrite failed to reach an internal API.
func InternalAPIError(ctx context.Context, err error) util.JSONResponse {
logrus.WithContext(ctx).WithError(err).Error("Error reaching an internal API")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: &MatrixError{
ErrCode: "M_INTERNAL_SERVER_ERROR",
Err: "Dendrite encountered an error reaching an internal API.",
},
}
}

View file

@ -1,44 +0,0 @@
// Copyright 2017 Vector Creations Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package jsonerror
import (
"encoding/json"
"testing"
)
func TestLimitExceeded(t *testing.T) {
e := LimitExceeded("too fast", 5000)
jsonBytes, err := json.Marshal(&e)
if err != nil {
t.Fatalf("TestLimitExceeded: Failed to marshal LimitExceeded error. %s", err.Error())
}
want := `{"errcode":"M_LIMIT_EXCEEDED","error":"too fast","retry_after_ms":5000}`
if string(jsonBytes) != want {
t.Errorf("TestLimitExceeded: want %s, got %s", want, string(jsonBytes))
}
}
func TestForbidden(t *testing.T) {
e := Forbidden("you shall not pass")
jsonBytes, err := json.Marshal(&e)
if err != nil {
t.Fatalf("TestForbidden: Failed to marshal Forbidden error. %s", err.Error())
}
want := `{"errcode":"M_FORBIDDEN","error":"you shall not pass"}`
if string(jsonBytes) != want {
t.Errorf("TestForbidden: want %s, got %s", want, string(jsonBytes))
}
}

View file

@ -22,6 +22,7 @@ import (
"time" "time"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -37,13 +38,13 @@ type SyncAPIProducer struct {
TopicTypingEvent string TopicTypingEvent string
TopicPresenceEvent string TopicPresenceEvent string
JetStream nats.JetStreamContext JetStream nats.JetStreamContext
ServerName gomatrixserverlib.ServerName ServerName spec.ServerName
UserAPI userapi.ClientUserAPI UserAPI userapi.ClientUserAPI
} }
func (p *SyncAPIProducer) SendReceipt( func (p *SyncAPIProducer) SendReceipt(
ctx context.Context, ctx context.Context,
userID, roomID, eventID, receiptType string, timestamp gomatrixserverlib.Timestamp, userID, roomID, eventID, receiptType string, timestamp spec.Timestamp,
) error { ) error {
m := &nats.Msg{ m := &nats.Msg{
Subject: p.TopicReceiptEvent, Subject: p.TopicReceiptEvent,
@ -154,7 +155,7 @@ func (p *SyncAPIProducer) SendPresence(
m.Header.Set("status_msg", *statusMsg) m.Header.Set("status_msg", *statusMsg)
} }
m.Header.Set("last_active_ts", strconv.Itoa(int(gomatrixserverlib.AsTimestamp(time.Now())))) m.Header.Set("last_active_ts", strconv.Itoa(int(spec.AsTimestamp(time.Now()))))
_, err := p.JetStream.PublishMsg(m, nats.Context(ctx)) _, err := p.JetStream.PublishMsg(m, nats.Context(ctx))
return err return err

View file

@ -21,11 +21,11 @@ import (
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/producers" "github.com/matrix-org/dendrite/clientapi/producers"
"github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/dendrite/internal/eventutil"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -38,7 +38,7 @@ func GetAccountData(
if userID != device.UserID { if userID != device.UserID {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("userID does not match the current user"), JSON: spec.Forbidden("userID does not match the current user"),
} }
} }
@ -69,7 +69,7 @@ func GetAccountData(
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
JSON: jsonerror.NotFound("data not found"), JSON: spec.NotFound("data not found"),
} }
} }
@ -81,7 +81,7 @@ func SaveAccountData(
if userID != device.UserID { if userID != device.UserID {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("userID does not match the current user"), JSON: spec.Forbidden("userID does not match the current user"),
} }
} }
@ -90,27 +90,30 @@ func SaveAccountData(
if req.Body == http.NoBody { if req.Body == http.NoBody {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.NotJSON("Content not JSON"), JSON: spec.NotJSON("Content not JSON"),
} }
} }
if dataType == "m.fully_read" || dataType == "m.push_rules" { if dataType == "m.fully_read" || dataType == "m.push_rules" {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden(fmt.Sprintf("Unable to modify %q using this API", dataType)), JSON: spec.Forbidden(fmt.Sprintf("Unable to modify %q using this API", dataType)),
} }
} }
body, err := io.ReadAll(req.Body) body, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("io.ReadAll failed") util.GetLogger(req.Context()).WithError(err).Error("io.ReadAll failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
if !json.Valid(body) { if !json.Valid(body) {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("Bad JSON content"), JSON: spec.BadJSON("Bad JSON content"),
} }
} }
@ -142,8 +145,16 @@ func SaveReadMarker(
userAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI, userAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
syncProducer *producers.SyncAPIProducer, device *api.Device, roomID string, syncProducer *producers.SyncAPIProducer, device *api.Device, roomID string,
) util.JSONResponse { ) util.JSONResponse {
deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("userID for this device is invalid"),
}
}
// Verify that the user is a member of this room // Verify that the user is a member of this room
resErr := checkMemberInRoom(req.Context(), rsAPI, device.UserID, roomID) resErr := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID)
if resErr != nil { if resErr != nil {
return *resErr return *resErr
} }
@ -154,16 +165,13 @@ func SaveReadMarker(
return *resErr return *resErr
} }
if r.FullyRead == "" { if r.FullyRead != "" {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("Missing m.fully_read mandatory field"),
}
}
data, err := json.Marshal(fullyReadEvent{EventID: r.FullyRead}) data, err := json.Marshal(fullyReadEvent{EventID: r.FullyRead})
if err != nil { if err != nil {
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
dataReq := api.InputAccountDataRequest{ dataReq := api.InputAccountDataRequest{
@ -177,10 +185,14 @@ func SaveReadMarker(
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)
} }
}
// Handle the read receipt that may be included in the read marker // Handle the read receipts that may be included in the read marker.
if r.Read != "" { if r.Read != "" {
return SetReceipt(req, syncProducer, device, roomID, "m.read", r.Read) return SetReceipt(req, userAPI, syncProducer, device, roomID, "m.read", r.Read)
}
if r.ReadPrivate != "" {
return SetReceipt(req, userAPI, syncProducer, device, roomID, "m.read.private", r.ReadPrivate)
} }
return util.JSONResponse{ return util.JSONResponse{

View file

@ -1,139 +1,401 @@
package routing package routing
import ( import (
"context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"regexp"
"strconv"
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/internal/eventutil"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/exp/constraints"
"github.com/matrix-org/dendrite/clientapi/jsonerror" clientapi "github.com/matrix-org/dendrite/clientapi/api"
"github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/keyserver/api"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/dendrite/userapi/api"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
) )
func AdminEvacuateRoom(req *http.Request, cfg *config.ClientAPI, device *userapi.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse { var validRegistrationTokenRegex = regexp.MustCompile("^[[:ascii:][:digit:]_]*$")
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil { func AdminCreateNewRegistrationToken(req *http.Request, cfg *config.ClientAPI, userAPI userapi.ClientUserAPI) util.JSONResponse {
return util.ErrorResponse(err) if !cfg.RegistrationRequiresToken {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("Registration via tokens is not enabled on this homeserver"),
} }
roomID, ok := vars["roomID"] }
if !ok { request := struct {
Token string `json:"token"`
UsesAllowed *int32 `json:"uses_allowed,omitempty"`
ExpiryTime *int64 `json:"expiry_time,omitempty"`
Length int32 `json:"length"`
}{}
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.MissingArgument("Expecting room ID."), JSON: spec.BadJSON(fmt.Sprintf("Failed to decode request body: %s", err)),
} }
} }
res := &roomserverAPI.PerformAdminEvacuateRoomResponse{}
if err := rsAPI.PerformAdminEvacuateRoom( token := request.Token
req.Context(), usesAllowed := request.UsesAllowed
&roomserverAPI.PerformAdminEvacuateRoomRequest{ expiryTime := request.ExpiryTime
RoomID: roomID, length := request.Length
if len(token) == 0 {
if length == 0 {
// length not provided in request. Assign default value of 16.
length = 16
}
// token not present in request body. Hence, generate a random token.
if length <= 0 || length > 64 {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("length must be greater than zero and not greater than 64"),
}
}
token = util.RandomString(int(length))
}
if len(token) > 64 {
//Token present in request body, but is too long.
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("token must not be longer than 64"),
}
}
isTokenValid := validRegistrationTokenRegex.Match([]byte(token))
if !isTokenValid {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("token must consist only of characters matched by the regex [A-Za-z0-9-_]"),
}
}
// At this point, we have a valid token, either through request body or through random generation.
if usesAllowed != nil && *usesAllowed < 0 {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("uses_allowed must be a non-negative integer or null"),
}
}
if expiryTime != nil && spec.Timestamp(*expiryTime).Time().Before(time.Now()) {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("expiry_time must not be in the past"),
}
}
pending := int32(0)
completed := int32(0)
// If usesAllowed or expiryTime is 0, it means they are not present in the request. NULL (indicating unlimited uses / no expiration will be persisted in DB)
registrationToken := &clientapi.RegistrationToken{
Token: &token,
UsesAllowed: usesAllowed,
Pending: &pending,
Completed: &completed,
ExpiryTime: expiryTime,
}
created, err := userAPI.PerformAdminCreateRegistrationToken(req.Context(), registrationToken)
if !created {
return util.JSONResponse{
Code: http.StatusConflict,
JSON: map[string]string{
"error": fmt.Sprintf("token: %s already exists", token),
}, },
res,
); err != nil {
return util.ErrorResponse(err)
} }
if err := res.Error; err != nil { }
return err.JSONResponse() if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: err,
}
} }
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,
JSON: map[string]interface{}{ JSON: map[string]interface{}{
"affected": res.Affected, "token": token,
"uses_allowed": getReturnValue(usesAllowed),
"pending": pending,
"completed": completed,
"expiry_time": getReturnValue(expiryTime),
}, },
} }
} }
func AdminEvacuateUser(req *http.Request, cfg *config.ClientAPI, device *userapi.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse { func getReturnValue[t constraints.Integer](in *t) any {
if in == nil {
return nil
}
return *in
}
func AdminListRegistrationTokens(req *http.Request, cfg *config.ClientAPI, userAPI userapi.ClientUserAPI) util.JSONResponse {
queryParams := req.URL.Query()
returnAll := true
valid := true
validQuery, ok := queryParams["valid"]
if ok {
returnAll = false
validValue, err := strconv.ParseBool(validQuery[0])
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("invalid 'valid' query parameter"),
}
}
valid = validValue
}
tokens, err := userAPI.PerformAdminListRegistrationTokens(req.Context(), returnAll, valid)
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.ErrorUnknown,
}
}
return util.JSONResponse{
Code: 200,
JSON: map[string]interface{}{
"registration_tokens": tokens,
},
}
}
func AdminGetRegistrationToken(req *http.Request, cfg *config.ClientAPI, userAPI userapi.ClientUserAPI) 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)
} }
userID, ok := vars["userID"] tokenText := vars["token"]
if !ok { token, err := userAPI.PerformAdminGetRegistrationToken(req.Context(), tokenText)
if err != nil {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.NotFound(fmt.Sprintf("token: %s not found", tokenText)),
}
}
return util.JSONResponse{
Code: 200,
JSON: token,
}
}
func AdminDeleteRegistrationToken(req *http.Request, cfg *config.ClientAPI, userAPI userapi.ClientUserAPI) util.JSONResponse {
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {
return util.ErrorResponse(err)
}
tokenText := vars["token"]
err = userAPI.PerformAdminDeleteRegistrationToken(req.Context(), tokenText)
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: err,
}
}
return util.JSONResponse{
Code: 200,
JSON: map[string]interface{}{},
}
}
func AdminUpdateRegistrationToken(req *http.Request, cfg *config.ClientAPI, userAPI userapi.ClientUserAPI) util.JSONResponse {
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {
return util.ErrorResponse(err)
}
tokenText := vars["token"]
request := make(map[string]*int64)
if err = json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.MissingArgument("Expecting user ID."), JSON: spec.BadJSON(fmt.Sprintf("Failed to decode request body: %s", err)),
} }
} }
_, domain, err := gomatrixserverlib.SplitID('@', userID) newAttributes := make(map[string]interface{})
usesAllowed, ok := request["uses_allowed"]
if ok {
// Only add usesAllowed to newAtrributes if it is present and valid
if usesAllowed != nil && *usesAllowed < 0 {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("uses_allowed must be a non-negative integer or null"),
}
}
newAttributes["usesAllowed"] = usesAllowed
}
expiryTime, ok := request["expiry_time"]
if ok {
// Only add expiryTime to newAtrributes if it is present and valid
if expiryTime != nil && spec.Timestamp(*expiryTime).Time().Before(time.Now()) {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("expiry_time must not be in the past"),
}
}
newAttributes["expiryTime"] = expiryTime
}
if len(newAttributes) == 0 {
// No attributes to update. Return existing token
return AdminGetRegistrationToken(req, cfg, userAPI)
}
updatedToken, err := userAPI.PerformAdminUpdateRegistrationToken(req.Context(), tokenText, newAttributes)
if err != nil { if err != nil {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.NotFound(fmt.Sprintf("token: %s not found", tokenText)),
}
}
return util.JSONResponse{
Code: 200,
JSON: *updatedToken,
}
}
func AdminEvacuateRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {
return util.ErrorResponse(err)
}
affected, err := rsAPI.PerformAdminEvacuateRoom(req.Context(), vars["roomID"])
switch err.(type) {
case nil:
case eventutil.ErrRoomNoExists:
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.NotFound(err.Error()),
}
default:
logrus.WithError(err).WithField("roomID", vars["roomID"]).Error("Failed to evacuate room")
return util.ErrorResponse(err)
}
return util.JSONResponse{
Code: 200,
JSON: map[string]interface{}{
"affected": affected,
},
}
}
func AdminEvacuateUser(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {
return util.ErrorResponse(err)
}
affected, err := rsAPI.PerformAdminEvacuateUser(req.Context(), vars["userID"])
if err != nil {
logrus.WithError(err).WithField("userID", vars["userID"]).Error("Failed to evacuate user")
return util.MessageResponse(http.StatusBadRequest, err.Error()) return util.MessageResponse(http.StatusBadRequest, err.Error())
} }
if domain != cfg.Matrix.ServerName {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.MissingArgument("User ID must belong to this server."),
}
}
res := &roomserverAPI.PerformAdminEvacuateUserResponse{}
if err := rsAPI.PerformAdminEvacuateUser(
req.Context(),
&roomserverAPI.PerformAdminEvacuateUserRequest{
UserID: userID,
},
res,
); err != nil {
return jsonerror.InternalAPIError(req.Context(), err)
}
if err := res.Error; err != nil {
return err.JSONResponse()
}
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,
JSON: map[string]interface{}{ JSON: map[string]interface{}{
"affected": res.Affected, "affected": affected,
}, },
} }
} }
func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { func AdminPurgeRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) 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)
} }
localpart, ok := vars["localpart"]
if !ok { if err = rsAPI.PerformAdminPurgeRoom(context.Background(), vars["roomID"]); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{
Code: 200,
JSON: struct{}{},
}
}
func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI api.ClientUserAPI) util.JSONResponse {
if req.Body == nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.MissingArgument("Expecting user localpart."), JSON: spec.Unknown("Missing request body"),
}
}
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {
return util.ErrorResponse(err)
}
var localpart string
userID := vars["userID"]
localpart, serverName, err := cfg.Matrix.SplitLocalID('@', userID)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidParam(err.Error()),
}
}
accAvailableResp := &api.QueryAccountAvailabilityResponse{}
if err = userAPI.QueryAccountAvailability(req.Context(), &api.QueryAccountAvailabilityRequest{
Localpart: localpart,
ServerName: serverName,
}, accAvailableResp); err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
if accAvailableResp.Available {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.Unknown("User does not exist"),
} }
} }
request := struct { request := struct {
Password string `json:"password"` Password string `json:"password"`
LogoutDevices bool `json:"logout_devices"`
}{} }{}
if err := json.NewDecoder(req.Body).Decode(&request); err != nil { if err = json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown("Failed to decode request body: " + err.Error()), JSON: spec.Unknown("Failed to decode request body: " + err.Error()),
} }
} }
if request.Password == "" { if request.Password == "" {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.MissingArgument("Expecting non-empty password."), JSON: spec.MissingParam("Expecting non-empty password."),
} }
} }
updateReq := &userapi.PerformPasswordUpdateRequest{
if err = internal.ValidatePassword(request.Password); err != nil {
return *internal.PasswordResponse(err)
}
updateReq := &api.PerformPasswordUpdateRequest{
Localpart: localpart, Localpart: localpart,
ServerName: serverName,
Password: request.Password, Password: request.Password,
LogoutDevices: true, LogoutDevices: request.LogoutDevices,
} }
updateRes := &userapi.PerformPasswordUpdateResponse{} updateRes := &api.PerformPasswordUpdateResponse{}
if err := userAPI.PerformPasswordUpdate(req.Context(), updateReq, updateRes); err != nil { if err := userAPI.PerformPasswordUpdate(req.Context(), updateReq, updateRes); err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown("Failed to perform password update: " + err.Error()), JSON: spec.Unknown("Failed to perform password update: " + err.Error()),
} }
} }
return util.JSONResponse{ return util.JSONResponse{
@ -146,11 +408,14 @@ func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *userap
} }
} }
func AdminReindex(req *http.Request, cfg *config.ClientAPI, device *userapi.Device, natsClient *nats.Conn) util.JSONResponse { func AdminReindex(req *http.Request, cfg *config.ClientAPI, device *api.Device, natsClient *nats.Conn) util.JSONResponse {
_, err := natsClient.RequestMsg(nats.NewMsg(cfg.Matrix.JetStream.Prefixed(jetstream.InputFulltextReindex)), time.Second*10) _, err := natsClient.RequestMsg(nats.NewMsg(cfg.Matrix.JetStream.Prefixed(jetstream.InputFulltextReindex)), time.Second*10)
if err != nil { if err != nil {
logrus.WithError(err).Error("failed to publish nats message") logrus.WithError(err).Error("failed to publish nats message")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
@ -169,10 +434,10 @@ func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI api.Clien
if err != nil { if err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error()) return util.MessageResponse(http.StatusBadRequest, err.Error())
} }
if domain == cfg.Matrix.ServerName { if cfg.Matrix.IsLocalServerName(domain) {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidParam("Can not mark local device list as stale"), JSON: spec.InvalidParam("Can not mark local device list as stale"),
} }
} }
@ -183,7 +448,7 @@ func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI api.Clien
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.Unknown(fmt.Sprintf("Failed to mark device list as stale: %s", err)), JSON: spec.Unknown(fmt.Sprintf("Failed to mark device list as stale: %s", err)),
} }
} }
return util.JSONResponse{ return util.JSONResponse{
@ -192,7 +457,7 @@ func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI api.Clien
} }
} }
func AdminDownloadState(req *http.Request, cfg *config.ClientAPI, device *userapi.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse { func AdminDownloadState(req *http.Request, device *api.Device, rsAPI roomserverAPI.ClientRoomserverAPI) 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)
@ -201,33 +466,122 @@ func AdminDownloadState(req *http.Request, cfg *config.ClientAPI, device *userap
if !ok { if !ok {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.MissingArgument("Expecting room ID."), JSON: spec.MissingParam("Expecting room ID."),
} }
} }
serverName, ok := vars["serverName"] serverName, ok := vars["serverName"]
if !ok { if !ok {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.MissingArgument("Expecting remote server name."), JSON: spec.MissingParam("Expecting remote server name."),
} }
} }
res := &roomserverAPI.PerformAdminDownloadStateResponse{} if err = rsAPI.PerformAdminDownloadState(req.Context(), roomID, device.UserID, spec.ServerName(serverName)); err != nil {
if err := rsAPI.PerformAdminDownloadState( if errors.Is(err, eventutil.ErrRoomNoExists{}) {
req.Context(), return util.JSONResponse{
&roomserverAPI.PerformAdminDownloadStateRequest{ Code: 200,
UserID: device.UserID, JSON: spec.NotFound(err.Error()),
RoomID: roomID,
ServerName: gomatrixserverlib.ServerName(serverName),
},
res,
); err != nil {
return jsonerror.InternalAPIError(req.Context(), err)
} }
if err := res.Error; err != nil { }
return err.JSONResponse() logrus.WithError(err).WithFields(logrus.Fields{
"userID": device.UserID,
"serverName": serverName,
"roomID": roomID,
}).Error("failed to download state")
return util.ErrorResponse(err)
} }
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,
JSON: map[string]interface{}{}, 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
}

View file

@ -17,8 +17,8 @@ package routing
import ( import (
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -51,7 +51,7 @@ func GetAdminWhois(
if !allowed { if !allowed {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("userID does not match the current user"), JSON: spec.Forbidden("userID does not match the current user"),
} }
} }
@ -61,7 +61,10 @@ func GetAdminWhois(
}, &queryRes) }, &queryRes)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("GetAdminWhois failed to query user devices") util.GetLogger(req.Context()).WithError(err).Error("GetAdminWhois failed to query user devices")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
devices := make(map[string]deviceInfo) devices := make(map[string]deviceInfo)

View file

@ -15,14 +15,14 @@
package routing package routing
import ( import (
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -31,7 +31,7 @@ func GetAliases(
req *http.Request, rsAPI api.ClientRoomserverAPI, device *userapi.Device, roomID string, req *http.Request, rsAPI api.ClientRoomserverAPI, device *userapi.Device, roomID string,
) util.JSONResponse { ) util.JSONResponse {
stateTuple := gomatrixserverlib.StateKeyTuple{ stateTuple := gomatrixserverlib.StateKeyTuple{
EventType: gomatrixserverlib.MRoomHistoryVisibility, EventType: spec.MRoomHistoryVisibility,
StateKey: "", StateKey: "",
} }
stateReq := &api.QueryCurrentStateRequest{ stateReq := &api.QueryCurrentStateRequest{
@ -47,26 +47,37 @@ func GetAliases(
visibility := gomatrixserverlib.HistoryVisibilityInvited visibility := gomatrixserverlib.HistoryVisibilityInvited
if historyVisEvent, ok := stateRes.StateEvents[stateTuple]; ok { if historyVisEvent, ok := stateRes.StateEvents[stateTuple]; ok {
var err error var err error
visibility, err = historyVisEvent.HistoryVisibility() var content gomatrixserverlib.HistoryVisibilityContent
if err != nil { if err = json.Unmarshal(historyVisEvent.Content(), &content); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("historyVisEvent.HistoryVisibility failed") util.GetLogger(req.Context()).WithError(err).Error("historyVisEvent.HistoryVisibility failed")
return util.ErrorResponse(fmt.Errorf("historyVisEvent.HistoryVisibility: %w", err)) return util.ErrorResponse(fmt.Errorf("historyVisEvent.HistoryVisibility: %w", err))
} }
visibility = content.HistoryVisibility
}
if visibility != spec.WorldReadable {
deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("userID doesn't have power level to change visibility"),
}
} }
if visibility != gomatrixserverlib.WorldReadable {
queryReq := api.QueryMembershipForUserRequest{ queryReq := api.QueryMembershipForUserRequest{
RoomID: roomID, RoomID: roomID,
UserID: device.UserID, UserID: *deviceUserID,
} }
var queryRes api.QueryMembershipForUserResponse var queryRes api.QueryMembershipForUserResponse
if err := rsAPI.QueryMembershipForUser(req.Context(), &queryReq, &queryRes); err != nil { if err := rsAPI.QueryMembershipForUser(req.Context(), &queryReq, &queryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryMembershipsForRoom failed") util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryMembershipsForRoom failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
if !queryRes.IsInRoom { if !queryRes.IsInRoom {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You aren't a member of this room."), JSON: spec.Forbidden("You aren't a member of this room."),
} }
} }
} }

View file

@ -15,11 +15,11 @@
package routing package routing
import ( import (
"fmt"
"html/template" "html/template"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -31,8 +31,7 @@ const recaptchaTemplate = `
<title>Authentication</title> <title>Authentication</title>
<meta name='viewport' content='width=device-width, initial-scale=1, <meta name='viewport' content='width=device-width, initial-scale=1,
user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'> user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
<script src="https://www.google.com/recaptcha/api.js" <script src="{{.apiJsUrl}}" async defer></script>
async defer></script>
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script> <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<script> <script>
function captchaDone() { function captchaDone() {
@ -51,8 +50,8 @@ function captchaDone() {
Please verify that you're not a robot. Please verify that you're not a robot.
</p> </p>
<input type="hidden" name="session" value="{{.session}}" /> <input type="hidden" name="session" value="{{.session}}" />
<div class="g-recaptcha" <div class="{{.sitekeyClass}}"
data-sitekey="{{.siteKey}}" data-sitekey="{{.sitekey}}"
data-callback="captchaDone"> data-callback="captchaDone">
</div> </div>
<noscript> <noscript>
@ -102,21 +101,38 @@ func serveTemplate(w http.ResponseWriter, templateHTML string, data map[string]s
func AuthFallback( func AuthFallback(
w http.ResponseWriter, req *http.Request, authType string, w http.ResponseWriter, req *http.Request, authType string,
cfg *config.ClientAPI, cfg *config.ClientAPI,
) *util.JSONResponse { ) {
sessionID := req.URL.Query().Get("session") // We currently only support "m.login.recaptcha", so fail early if that's not requested
if authType == authtypes.LoginTypeRecaptcha {
if !cfg.RecaptchaEnabled {
writeHTTPMessage(w, req,
"Recaptcha login is disabled on this Homeserver",
http.StatusBadRequest,
)
return
}
} else {
writeHTTPMessage(w, req, fmt.Sprintf("Unknown authtype %q", authType), http.StatusNotImplemented)
return
}
sessionID := req.URL.Query().Get("session")
if sessionID == "" { if sessionID == "" {
return writeHTTPMessage(w, req, writeHTTPMessage(w, req,
"Session ID not provided", "Session ID not provided",
http.StatusBadRequest, http.StatusBadRequest,
) )
return
} }
serveRecaptcha := func() { serveRecaptcha := func() {
data := map[string]string{ data := map[string]string{
"myUrl": req.URL.String(), "myUrl": req.URL.String(),
"session": sessionID, "session": sessionID,
"siteKey": cfg.RecaptchaPublicKey, "apiJsUrl": cfg.RecaptchaApiJsUrl,
"sitekey": cfg.RecaptchaPublicKey,
"sitekeyClass": cfg.RecaptchaSitekeyClass,
"formField": cfg.RecaptchaFormField,
} }
serveTemplate(w, recaptchaTemplate, data) serveTemplate(w, recaptchaTemplate, data)
} }
@ -128,70 +144,44 @@ func AuthFallback(
if req.Method == http.MethodGet { if req.Method == http.MethodGet {
// Handle Recaptcha // Handle Recaptcha
if authType == authtypes.LoginTypeRecaptcha {
if err := checkRecaptchaEnabled(cfg, w, req); err != nil {
return err
}
serveRecaptcha() serveRecaptcha()
return nil return
}
return &util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound("Unknown auth stage type"),
}
} else if req.Method == http.MethodPost { } else if req.Method == http.MethodPost {
// Handle Recaptcha // Handle Recaptcha
if authType == authtypes.LoginTypeRecaptcha {
if err := checkRecaptchaEnabled(cfg, w, req); err != nil {
return err
}
clientIP := req.RemoteAddr clientIP := req.RemoteAddr
err := req.ParseForm() err := req.ParseForm()
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("req.ParseForm failed") util.GetLogger(req.Context()).WithError(err).Error("req.ParseForm failed")
res := jsonerror.InternalServerError() w.WriteHeader(http.StatusBadRequest)
return &res serveRecaptcha()
return
} }
response := req.Form.Get("g-recaptcha-response") response := req.Form.Get(cfg.RecaptchaFormField)
if err := validateRecaptcha(cfg, response, clientIP); err != nil { err = validateRecaptcha(cfg, response, clientIP)
util.GetLogger(req.Context()).Error(err) switch err {
return err case ErrMissingResponse:
w.WriteHeader(http.StatusBadRequest)
serveRecaptcha() // serve the initial page again, instead of nothing
return
case ErrInvalidCaptcha:
w.WriteHeader(http.StatusUnauthorized)
serveRecaptcha()
return
case nil:
default: // something else failed
util.GetLogger(req.Context()).WithError(err).Error("failed to validate recaptcha")
serveRecaptcha()
return
} }
// Success. Add recaptcha as a completed login flow // Success. Add recaptcha as a completed login flow
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeRecaptcha) sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeRecaptcha)
serveSuccess() serveSuccess()
return nil return
} }
writeHTTPMessage(w, req, "Bad method", http.StatusMethodNotAllowed)
return &util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound("Unknown auth stage type"),
}
}
return &util.JSONResponse{
Code: http.StatusMethodNotAllowed,
JSON: jsonerror.NotFound("Bad method"),
}
}
// checkRecaptchaEnabled creates an error response if recaptcha is not usable on homeserver.
func checkRecaptchaEnabled(
cfg *config.ClientAPI,
w http.ResponseWriter,
req *http.Request,
) *util.JSONResponse {
if !cfg.RecaptchaEnabled {
return writeHTTPMessage(w, req,
"Recaptcha login is disabled on this Homeserver",
http.StatusBadRequest,
)
}
return nil
} }
// writeHTTPMessage writes the given header and message to the HTTP response writer. // writeHTTPMessage writes the given header and message to the HTTP response writer.
@ -199,13 +189,10 @@ func checkRecaptchaEnabled(
func writeHTTPMessage( func writeHTTPMessage(
w http.ResponseWriter, req *http.Request, w http.ResponseWriter, req *http.Request,
message string, header int, message string, header int,
) *util.JSONResponse { ) {
w.WriteHeader(header) w.WriteHeader(header)
_, err := w.Write([]byte(message)) _, err := w.Write([]byte(message))
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("w.Write failed") util.GetLogger(req.Context()).WithError(err).Error("w.Write failed")
res := jsonerror.InternalServerError()
return &res
} }
return nil
} }

View file

@ -0,0 +1,147 @@
package routing
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/setup/config"
)
func Test_AuthFallback(t *testing.T) {
cfg := config.Dendrite{}
cfg.Defaults(config.DefaultOpts{Generate: true, SingleDatabase: true})
for _, useHCaptcha := range []bool{false, true} {
for _, recaptchaEnabled := range []bool{false, true} {
for _, wantErr := range []bool{false, true} {
t.Run(fmt.Sprintf("useHCaptcha(%v) - recaptchaEnabled(%v) - wantErr(%v)", useHCaptcha, recaptchaEnabled, wantErr), func(t *testing.T) {
// Set the defaults for each test
cfg.ClientAPI.Defaults(config.DefaultOpts{Generate: true, SingleDatabase: true})
cfg.ClientAPI.RecaptchaEnabled = recaptchaEnabled
cfg.ClientAPI.RecaptchaPublicKey = "pub"
cfg.ClientAPI.RecaptchaPrivateKey = "priv"
if useHCaptcha {
cfg.ClientAPI.RecaptchaSiteVerifyAPI = "https://hcaptcha.com/siteverify"
cfg.ClientAPI.RecaptchaApiJsUrl = "https://js.hcaptcha.com/1/api.js"
cfg.ClientAPI.RecaptchaFormField = "h-captcha-response"
cfg.ClientAPI.RecaptchaSitekeyClass = "h-captcha"
}
cfgErrs := &config.ConfigErrors{}
cfg.ClientAPI.Verify(cfgErrs)
if len(*cfgErrs) > 0 {
t.Fatalf("(hCaptcha=%v) unexpected config errors: %s", useHCaptcha, cfgErrs.Error())
}
req := httptest.NewRequest(http.MethodGet, "/?session=1337", nil)
rec := httptest.NewRecorder()
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &cfg.ClientAPI)
if !recaptchaEnabled {
if rec.Code != http.StatusBadRequest {
t.Fatalf("unexpected response code: %d, want %d", rec.Code, http.StatusBadRequest)
}
if rec.Body.String() != "Recaptcha login is disabled on this Homeserver" {
t.Fatalf("unexpected response body: %s", rec.Body.String())
}
} else {
if !strings.Contains(rec.Body.String(), cfg.ClientAPI.RecaptchaSitekeyClass) {
t.Fatalf("body does not contain %s: %s", cfg.ClientAPI.RecaptchaSitekeyClass, rec.Body.String())
}
}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if wantErr {
_, _ = w.Write([]byte(`{"success":false}`))
return
}
_, _ = w.Write([]byte(`{"success":true}`))
}))
defer srv.Close() // nolint: errcheck
cfg.ClientAPI.RecaptchaSiteVerifyAPI = srv.URL
// check the result after sending the captcha
req = httptest.NewRequest(http.MethodPost, "/?session=1337", nil)
req.Form = url.Values{}
req.Form.Add(cfg.ClientAPI.RecaptchaFormField, "someRandomValue")
rec = httptest.NewRecorder()
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &cfg.ClientAPI)
if recaptchaEnabled {
if !wantErr {
if rec.Code != http.StatusOK {
t.Fatalf("unexpected response code: %d, want %d", rec.Code, http.StatusOK)
}
if rec.Body.String() != successTemplate {
t.Fatalf("unexpected response: %s, want %s", rec.Body.String(), successTemplate)
}
} else {
if rec.Code != http.StatusUnauthorized {
t.Fatalf("unexpected response code: %d, want %d", rec.Code, http.StatusUnauthorized)
}
wantString := "Authentication"
if !strings.Contains(rec.Body.String(), wantString) {
t.Fatalf("expected response to contain '%s', but didn't: %s", wantString, rec.Body.String())
}
}
} else {
if rec.Code != http.StatusBadRequest {
t.Fatalf("unexpected response code: %d, want %d", rec.Code, http.StatusBadRequest)
}
if rec.Body.String() != "Recaptcha login is disabled on this Homeserver" {
t.Fatalf("unexpected response: %s, want %s", rec.Body.String(), "successTemplate")
}
}
})
}
}
}
t.Run("unknown fallbacks are handled correctly", func(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/?session=1337", nil)
rec := httptest.NewRecorder()
AuthFallback(rec, req, "DoesNotExist", &cfg.ClientAPI)
if rec.Code != http.StatusNotImplemented {
t.Fatalf("unexpected http status: %d, want %d", rec.Code, http.StatusNotImplemented)
}
})
t.Run("unknown methods are handled correctly", func(t *testing.T) {
req := httptest.NewRequest(http.MethodDelete, "/?session=1337", nil)
rec := httptest.NewRecorder()
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &cfg.ClientAPI)
if rec.Code != http.StatusMethodNotAllowed {
t.Fatalf("unexpected http status: %d, want %d", rec.Code, http.StatusMethodNotAllowed)
}
})
t.Run("missing session parameter is handled correctly", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &cfg.ClientAPI)
if rec.Code != http.StatusBadRequest {
t.Fatalf("unexpected http status: %d, want %d", rec.Code, http.StatusBadRequest)
}
})
t.Run("missing session parameter is handled correctly", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &cfg.ClientAPI)
if rec.Code != http.StatusBadRequest {
t.Fatalf("unexpected http status: %d, want %d", rec.Code, http.StatusBadRequest)
}
})
t.Run("missing 'response' is handled correctly", func(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/?session=1337", nil)
rec := httptest.NewRecorder()
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &cfg.ClientAPI)
if rec.Code != http.StatusBadRequest {
t.Fatalf("unexpected http status: %d, want %d", rec.Code, http.StatusBadRequest)
}
})
}

View file

@ -17,26 +17,22 @@ package routing
import ( import (
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/roomserver/version"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
// GetCapabilities returns information about the server's supported feature set // GetCapabilities returns information about the server's supported feature set
// and other relevant capabilities to an authenticated user. // and other relevant capabilities to an authenticated user.
func GetCapabilities( func GetCapabilities(rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, versionsMap := map[gomatrixserverlib.RoomVersion]string{}
) util.JSONResponse { for v, desc := range version.SupportedRoomVersions() {
roomVersionsQueryReq := roomserverAPI.QueryRoomVersionCapabilitiesRequest{} if desc.Stable() {
roomVersionsQueryRes := roomserverAPI.QueryRoomVersionCapabilitiesResponse{} versionsMap[v] = "stable"
if err := rsAPI.QueryRoomVersionCapabilities( } else {
req.Context(), versionsMap[v] = "unstable"
&roomVersionsQueryReq, }
&roomVersionsQueryRes,
); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("queryAPI.QueryRoomVersionCapabilities failed")
return jsonerror.InternalServerError()
} }
response := map[string]interface{}{ response := map[string]interface{}{
@ -44,7 +40,10 @@ func GetCapabilities(
"m.change_password": map[string]bool{ "m.change_password": map[string]bool{
"enabled": true, "enabled": true,
}, },
"m.room_versions": roomVersionsQueryRes, "m.room_versions": map[string]interface{}{
"default": rsAPI.DefaultRoomVersion(),
"available": versionsMap,
},
}, },
} }

View file

@ -26,10 +26,9 @@ import (
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
roomserverVersion "github.com/matrix-org/dendrite/roomserver/version" roomserverVersion "github.com/matrix-org/dendrite/roomserver/version"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/internal/eventutil"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "github.com/matrix-org/util"
@ -44,27 +43,13 @@ type createRoomRequest struct {
Topic string `json:"topic"` Topic string `json:"topic"`
Preset string `json:"preset"` Preset string `json:"preset"`
CreationContent json.RawMessage `json:"creation_content"` CreationContent json.RawMessage `json:"creation_content"`
InitialState []fledglingEvent `json:"initial_state"` InitialState []gomatrixserverlib.FledglingEvent `json:"initial_state"`
RoomAliasName string `json:"room_alias_name"` RoomAliasName string `json:"room_alias_name"`
GuestCanJoin bool `json:"guest_can_join"`
RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"` RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"` PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"`
IsDirect bool `json:"is_direct"` IsDirect bool `json:"is_direct"`
} }
const (
presetPrivateChat = "private_chat"
presetTrustedPrivateChat = "trusted_private_chat"
presetPublicChat = "public_chat"
)
const (
historyVisibilityShared = "shared"
// TODO: These should be implemented once history visibility is implemented
// historyVisibilityWorldReadable = "world_readable"
// historyVisibilityInvited = "invited"
)
func (r createRoomRequest) Validate() *util.JSONResponse { func (r createRoomRequest) Validate() *util.JSONResponse {
whitespace := "\t\n\x0b\x0c\r " // https://docs.python.org/2/library/string.html#string.whitespace whitespace := "\t\n\x0b\x0c\r " // https://docs.python.org/2/library/string.html#string.whitespace
// https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/handlers/room.py#L81 // https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/handlers/room.py#L81
@ -72,28 +57,23 @@ func (r createRoomRequest) Validate() *util.JSONResponse {
if strings.ContainsAny(r.RoomAliasName, whitespace+":") { if strings.ContainsAny(r.RoomAliasName, whitespace+":") {
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("room_alias_name cannot contain whitespace or ':'"), JSON: spec.BadJSON("room_alias_name cannot contain whitespace or ':'"),
} }
} }
for _, userID := range r.Invite { for _, userID := range r.Invite {
// TODO: We should put user ID parsing code into gomatrixserverlib and use that instead if _, err := spec.NewUserID(userID, true); err != nil {
// (see https://github.com/matrix-org/gomatrixserverlib/blob/3394e7c7003312043208aa73727d2256eea3d1f6/eventcontent.go#L347 )
// It should be a struct (with pointers into a single string to avoid copying) and
// we should update all refs to use UserID types rather than strings.
// https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/types.py#L92
if _, _, err := gomatrixserverlib.SplitID('@', userID); err != nil {
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("user id must be in the form @localpart:domain"), JSON: spec.BadJSON("user id must be in the form @localpart:domain"),
} }
} }
} }
switch r.Preset { switch r.Preset {
case presetPrivateChat, presetTrustedPrivateChat, presetPublicChat, "": case spec.PresetPrivateChat, spec.PresetTrustedPrivateChat, spec.PresetPublicChat, "":
default: default:
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("preset must be any of 'private_chat', 'trusted_private_chat', 'public_chat'"), JSON: spec.BadJSON("preset must be any of 'private_chat', 'trusted_private_chat', 'public_chat'"),
} }
} }
@ -105,7 +85,7 @@ func (r createRoomRequest) Validate() *util.JSONResponse {
if err != nil { if err != nil {
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("malformed creation_content"), JSON: spec.BadJSON("malformed creation_content"),
} }
} }
@ -114,7 +94,7 @@ func (r createRoomRequest) Validate() *util.JSONResponse {
if err != nil { if err != nil {
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("malformed creation_content"), JSON: spec.BadJSON("malformed creation_content"),
} }
} }
@ -127,13 +107,6 @@ type createRoomResponse struct {
RoomAlias string `json:"room_alias,omitempty"` // in synapse not spec RoomAlias string `json:"room_alias,omitempty"` // in synapse not spec
} }
// fledglingEvent is a helper representation of an event used when creating many events in succession.
type fledglingEvent struct {
Type string `json:"type"`
StateKey string `json:"state_key"`
Content interface{} `json:"content"`
}
// CreateRoom implements /createRoom // CreateRoom implements /createRoom
func CreateRoom( func CreateRoom(
req *http.Request, device *api.Device, req *http.Request, device *api.Device,
@ -141,444 +114,124 @@ func CreateRoom(
profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI, profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
asAPI appserviceAPI.AppServiceInternalAPI, asAPI appserviceAPI.AppServiceInternalAPI,
) util.JSONResponse { ) util.JSONResponse {
var r createRoomRequest var createRequest createRoomRequest
resErr := httputil.UnmarshalJSONRequest(req, &r) resErr := httputil.UnmarshalJSONRequest(req, &createRequest)
if resErr != nil { if resErr != nil {
return *resErr return *resErr
} }
if resErr = r.Validate(); resErr != nil { if resErr = createRequest.Validate(); resErr != nil {
return *resErr return *resErr
} }
evTime, err := httputil.ParseTSParam(req) evTime, err := httputil.ParseTSParam(req)
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidArgumentValue(err.Error()), JSON: spec.InvalidParam(err.Error()),
} }
} }
return createRoom(req.Context(), r, device, cfg, profileAPI, rsAPI, asAPI, evTime) return createRoom(req.Context(), createRequest, device, cfg, profileAPI, rsAPI, asAPI, evTime)
} }
// createRoom implements /createRoom // createRoom implements /createRoom
// nolint: gocyclo
func createRoom( func createRoom(
ctx context.Context, ctx context.Context,
r createRoomRequest, device *api.Device, createRequest createRoomRequest, device *api.Device,
cfg *config.ClientAPI, cfg *config.ClientAPI,
profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI, profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
asAPI appserviceAPI.AppServiceInternalAPI, asAPI appserviceAPI.AppServiceInternalAPI,
evTime time.Time, evTime time.Time,
) util.JSONResponse { ) util.JSONResponse {
// TODO (#267): Check room ID doesn't clash with an existing one, and we userID, err := spec.NewUserID(device.UserID, true)
// probably shouldn't be using pseudo-random strings, maybe GUIDs? if err != nil {
roomID := fmt.Sprintf("!%s:%s", util.RandomString(16), cfg.Matrix.ServerName) util.GetLogger(ctx).WithError(err).Error("invalid userID")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
if !cfg.Matrix.IsLocalServerName(userID.Domain()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden(fmt.Sprintf("User domain %q not configured locally", userID.Domain())),
}
}
logger := util.GetLogger(ctx) logger := util.GetLogger(ctx)
userID := device.UserID
// TODO: Check room ID doesn't clash with an existing one, and we
// probably shouldn't be using pseudo-random strings, maybe GUIDs?
roomID, err := spec.NewRoomID(fmt.Sprintf("!%s:%s", util.RandomString(16), userID.Domain()))
if err != nil {
util.GetLogger(ctx).WithError(err).Error("invalid roomID")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
// Clobber keys: creator, room_version // Clobber keys: creator, room_version
roomVersion := roomserverVersion.DefaultRoomVersion() roomVersion := rsAPI.DefaultRoomVersion()
if r.RoomVersion != "" { if createRequest.RoomVersion != "" {
candidateVersion := gomatrixserverlib.RoomVersion(r.RoomVersion) candidateVersion := gomatrixserverlib.RoomVersion(createRequest.RoomVersion)
_, roomVersionError := roomserverVersion.SupportedRoomVersion(candidateVersion) _, roomVersionError := roomserverVersion.SupportedRoomVersion(candidateVersion)
if roomVersionError != nil { if roomVersionError != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.UnsupportedRoomVersion(roomVersionError.Error()), JSON: spec.UnsupportedRoomVersion(roomVersionError.Error()),
} }
} }
roomVersion = candidateVersion roomVersion = candidateVersion
} }
// TODO: visibility/presets/raw initial state
// TODO: Create room alias association
// Make sure this doesn't fall into an application service's namespace though!
logger.WithFields(log.Fields{ logger.WithFields(log.Fields{
"userID": userID, "userID": userID.String(),
"roomID": roomID, "roomID": roomID.String(),
"roomVersion": roomVersion, "roomVersion": roomVersion,
}).Info("Creating new room") }).Info("Creating new room")
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, profileAPI) profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID.String(), asAPI, profileAPI)
if err != nil { if err != nil {
util.GetLogger(ctx).WithError(err).Error("appserviceAPI.RetrieveUserProfile failed") util.GetLogger(ctx).WithError(err).Error("appserviceAPI.RetrieveUserProfile failed")
return jsonerror.InternalServerError()
}
createContent := map[string]interface{}{}
if len(r.CreationContent) > 0 {
if err = json.Unmarshal(r.CreationContent, &createContent); err != nil {
util.GetLogger(ctx).WithError(err).Error("json.Unmarshal for creation_content failed")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("invalid create content"),
}
}
}
createContent["creator"] = userID
createContent["room_version"] = roomVersion
powerLevelContent := eventutil.InitialPowerLevelsContent(userID)
joinRuleContent := gomatrixserverlib.JoinRuleContent{
JoinRule: gomatrixserverlib.Invite,
}
historyVisibilityContent := gomatrixserverlib.HistoryVisibilityContent{
HistoryVisibility: historyVisibilityShared,
}
if r.PowerLevelContentOverride != nil {
// Merge powerLevelContentOverride fields by unmarshalling it atop the defaults
err = json.Unmarshal(r.PowerLevelContentOverride, &powerLevelContent)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("json.Unmarshal for power_level_content_override failed")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("malformed power_level_content_override"),
}
}
}
switch r.Preset {
case presetPrivateChat:
joinRuleContent.JoinRule = gomatrixserverlib.Invite
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
case presetTrustedPrivateChat:
joinRuleContent.JoinRule = gomatrixserverlib.Invite
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
for _, invitee := range r.Invite {
powerLevelContent.Users[invitee] = 100
}
case presetPublicChat:
joinRuleContent.JoinRule = gomatrixserverlib.Public
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
}
createEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomCreate,
Content: createContent,
}
powerLevelEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomPowerLevels,
Content: powerLevelContent,
}
joinRuleEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomJoinRules,
Content: joinRuleContent,
}
historyVisibilityEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomHistoryVisibility,
Content: historyVisibilityContent,
}
membershipEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomMember,
StateKey: userID,
Content: gomatrixserverlib.MemberContent{
Membership: gomatrixserverlib.Join,
DisplayName: profile.DisplayName,
AvatarURL: profile.AvatarURL,
},
}
var nameEvent *fledglingEvent
var topicEvent *fledglingEvent
var guestAccessEvent *fledglingEvent
var aliasEvent *fledglingEvent
if r.Name != "" {
nameEvent = &fledglingEvent{
Type: gomatrixserverlib.MRoomName,
Content: eventutil.NameContent{
Name: r.Name,
},
}
}
if r.Topic != "" {
topicEvent = &fledglingEvent{
Type: gomatrixserverlib.MRoomTopic,
Content: eventutil.TopicContent{
Topic: r.Topic,
},
}
}
if r.GuestCanJoin {
guestAccessEvent = &fledglingEvent{
Type: gomatrixserverlib.MRoomGuestAccess,
Content: eventutil.GuestAccessContent{
GuestAccess: "can_join",
},
}
}
var roomAlias string
if r.RoomAliasName != "" {
roomAlias = fmt.Sprintf("#%s:%s", r.RoomAliasName, cfg.Matrix.ServerName)
// check it's free TODO: This races but is better than nothing
hasAliasReq := roomserverAPI.GetRoomIDForAliasRequest{
Alias: roomAlias,
IncludeAppservices: false,
}
var aliasResp roomserverAPI.GetRoomIDForAliasResponse
err = rsAPI.GetRoomIDForAlias(ctx, &hasAliasReq, &aliasResp)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("aliasAPI.GetRoomIDForAlias failed")
return jsonerror.InternalServerError()
}
if aliasResp.RoomID != "" {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.RoomInUse("Room ID already exists."),
}
}
aliasEvent = &fledglingEvent{
Type: gomatrixserverlib.MRoomCanonicalAlias,
Content: eventutil.CanonicalAlias{
Alias: roomAlias,
},
}
}
var initialStateEvents []fledglingEvent
for i := range r.InitialState {
if r.InitialState[i].StateKey != "" {
initialStateEvents = append(initialStateEvents, r.InitialState[i])
continue
}
switch r.InitialState[i].Type {
case gomatrixserverlib.MRoomCreate:
continue
case gomatrixserverlib.MRoomPowerLevels:
powerLevelEvent = r.InitialState[i]
case gomatrixserverlib.MRoomJoinRules:
joinRuleEvent = r.InitialState[i]
case gomatrixserverlib.MRoomHistoryVisibility:
historyVisibilityEvent = r.InitialState[i]
case gomatrixserverlib.MRoomGuestAccess:
guestAccessEvent = &r.InitialState[i]
case gomatrixserverlib.MRoomName:
nameEvent = &r.InitialState[i]
case gomatrixserverlib.MRoomTopic:
topicEvent = &r.InitialState[i]
default:
initialStateEvents = append(initialStateEvents, r.InitialState[i])
}
}
// send events into the room in order of:
// 1- m.room.create
// 2- room creator join member
// 3- m.room.power_levels
// 4- m.room.join_rules
// 5- m.room.history_visibility
// 6- m.room.canonical_alias (opt)
// 7- m.room.guest_access (opt)
// 8- other initial state items
// 9- m.room.name (opt)
// 10- m.room.topic (opt)
// 11- invite events (opt) - with is_direct flag if applicable TODO
// 12- 3pid invite events (opt) TODO
// This differs from Synapse slightly. Synapse would vary the ordering of 3-7
// depending on if those events were in "initial_state" or not. This made it
// harder to reason about, hence sticking to a strict static ordering.
// TODO: Synapse has txn/token ID on each event. Do we need to do this here?
eventsToMake := []fledglingEvent{
createEvent, membershipEvent, powerLevelEvent, joinRuleEvent, historyVisibilityEvent,
}
if guestAccessEvent != nil {
eventsToMake = append(eventsToMake, *guestAccessEvent)
}
eventsToMake = append(eventsToMake, initialStateEvents...)
if nameEvent != nil {
eventsToMake = append(eventsToMake, *nameEvent)
}
if topicEvent != nil {
eventsToMake = append(eventsToMake, *topicEvent)
}
if aliasEvent != nil {
// TODO: bit of a chicken and egg problem here as the alias doesn't exist and cannot until we have made the room.
// This means we might fail creating the alias but say the canonical alias is something that doesn't exist.
eventsToMake = append(eventsToMake, *aliasEvent)
}
// TODO: invite events
// TODO: 3pid invite events
var builtEvents []*gomatrixserverlib.HeaderedEvent
authEvents := gomatrixserverlib.NewAuthEvents(nil)
for i, e := range eventsToMake {
depth := i + 1 // depth starts at 1
builder := gomatrixserverlib.EventBuilder{
Sender: userID,
RoomID: roomID,
Type: e.Type,
StateKey: &e.StateKey,
Depth: int64(depth),
}
err = builder.SetContent(e.Content)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("builder.SetContent failed")
return jsonerror.InternalServerError()
}
if i > 0 {
builder.PrevEvents = []gomatrixserverlib.EventReference{builtEvents[i-1].EventReference()}
}
var ev *gomatrixserverlib.Event
ev, err = buildEvent(&builder, &authEvents, cfg, evTime, roomVersion)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("buildEvent failed")
return jsonerror.InternalServerError()
}
if err = gomatrixserverlib.Allowed(ev, &authEvents); err != nil {
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.Allowed failed")
return jsonerror.InternalServerError()
}
// Add the event to the list of auth events
builtEvents = append(builtEvents, ev.Headered(roomVersion))
err = authEvents.AddEvent(ev)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("authEvents.AddEvent failed")
return jsonerror.InternalServerError()
}
}
inputs := make([]roomserverAPI.InputRoomEvent, 0, len(builtEvents))
for _, event := range builtEvents {
inputs = append(inputs, roomserverAPI.InputRoomEvent{
Kind: roomserverAPI.KindNew,
Event: event,
Origin: cfg.Matrix.ServerName,
SendAsServer: roomserverAPI.DoNotSendToOtherServers,
})
}
if err = roomserverAPI.SendInputRoomEvents(ctx, rsAPI, inputs, false); err != nil {
util.GetLogger(ctx).WithError(err).Error("roomserverAPI.SendInputRoomEvents failed")
return jsonerror.InternalServerError()
}
// TODO(#269): Reserve room alias while we create the room. This stops us
// from creating the room but still failing due to the alias having already
// been taken.
if roomAlias != "" {
aliasReq := roomserverAPI.SetRoomAliasRequest{
Alias: roomAlias,
RoomID: roomID,
UserID: userID,
}
var aliasResp roomserverAPI.SetRoomAliasResponse
err = rsAPI.SetRoomAlias(ctx, &aliasReq, &aliasResp)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("aliasAPI.SetRoomAlias failed")
return jsonerror.InternalServerError()
}
if aliasResp.AliasExists {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.RoomInUse("Room alias already exists."),
}
}
}
// If this is a direct message then we should invite the participants.
if len(r.Invite) > 0 {
// Build some stripped state for the invite.
var globalStrippedState []gomatrixserverlib.InviteV2StrippedState
for _, event := range builtEvents {
// Chosen events from the spec:
// https://spec.matrix.org/v1.3/client-server-api/#stripped-state
switch event.Type() {
case gomatrixserverlib.MRoomCreate:
fallthrough
case gomatrixserverlib.MRoomName:
fallthrough
case gomatrixserverlib.MRoomAvatar:
fallthrough
case gomatrixserverlib.MRoomTopic:
fallthrough
case gomatrixserverlib.MRoomCanonicalAlias:
fallthrough
case gomatrixserverlib.MRoomEncryption:
fallthrough
case gomatrixserverlib.MRoomMember:
fallthrough
case gomatrixserverlib.MRoomJoinRules:
ev := event.Event
globalStrippedState = append(
globalStrippedState,
gomatrixserverlib.NewInviteV2StrippedState(ev),
)
}
}
// Process the invites.
for _, invitee := range r.Invite {
// Build the invite event.
inviteEvent, err := buildMembershipEvent(
ctx, invitee, "", profileAPI, device, gomatrixserverlib.Invite,
roomID, r.IsDirect, cfg, evTime, rsAPI, asAPI,
)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed")
continue
}
inviteStrippedState := append(
globalStrippedState,
gomatrixserverlib.NewInviteV2StrippedState(inviteEvent.Event),
)
// Send the invite event to the roomserver.
var inviteRes roomserverAPI.PerformInviteResponse
event := inviteEvent.Headered(roomVersion)
if err := rsAPI.PerformInvite(ctx, &roomserverAPI.PerformInviteRequest{
Event: event,
InviteRoomState: inviteStrippedState,
RoomVersion: event.RoomVersion,
SendAsServer: string(cfg.Matrix.ServerName),
}, &inviteRes); err != nil {
util.GetLogger(ctx).WithError(err).Error("PerformInvite failed")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(), JSON: spec.InternalServerError{},
}
}
if inviteRes.Error != nil {
return inviteRes.Error.JSONResponse()
}
} }
} }
if r.Visibility == "public" { userDisplayName := profile.DisplayName
// expose this room in the published room list userAvatarURL := profile.AvatarURL
var pubRes roomserverAPI.PerformPublishResponse
if err := rsAPI.PerformPublish(ctx, &roomserverAPI.PerformPublishRequest{ keyID := cfg.Matrix.KeyID
RoomID: roomID, privateKey := cfg.Matrix.PrivateKey
Visibility: "public",
}, &pubRes); err != nil { req := roomserverAPI.PerformCreateRoomRequest{
return jsonerror.InternalAPIError(ctx, err) InvitedUsers: createRequest.Invite,
} RoomName: createRequest.Name,
if pubRes.Error != nil { Visibility: createRequest.Visibility,
// treat as non-fatal since the room is already made by this point Topic: createRequest.Topic,
util.GetLogger(ctx).WithError(pubRes.Error).Error("failed to visibility:public") StatePreset: createRequest.Preset,
CreationContent: createRequest.CreationContent,
InitialState: createRequest.InitialState,
RoomAliasName: createRequest.RoomAliasName,
RoomVersion: roomVersion,
PowerLevelContentOverride: createRequest.PowerLevelContentOverride,
IsDirect: createRequest.IsDirect,
UserDisplayName: userDisplayName,
UserAvatarURL: userAvatarURL,
KeyID: keyID,
PrivateKey: privateKey,
EventTime: evTime,
} }
roomAlias, createRes := rsAPI.PerformCreateRoom(ctx, *userID, *roomID, &req)
if createRes != nil {
return *createRes
} }
response := createRoomResponse{ response := createRoomResponse{
RoomID: roomID, RoomID: roomID.String(),
RoomAlias: roomAlias, RoomAlias: roomAlias,
} }
@ -587,30 +240,3 @@ func createRoom(
JSON: response, JSON: response,
} }
} }
// buildEvent fills out auth_events for the builder then builds the event
func buildEvent(
builder *gomatrixserverlib.EventBuilder,
provider gomatrixserverlib.AuthEventProvider,
cfg *config.ClientAPI,
evTime time.Time,
roomVersion gomatrixserverlib.RoomVersion,
) (*gomatrixserverlib.Event, error) {
eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder)
if err != nil {
return nil, err
}
refs, err := eventsNeeded.AuthEventReferences(provider)
if err != nil {
return nil, err
}
builder.AuthEvents = refs
event, err := builder.Build(
evTime, cfg.Matrix.ServerName, cfg.Matrix.KeyID,
cfg.Matrix.PrivateKey, roomVersion,
)
if err != nil {
return nil, fmt.Errorf("cannot build event %s : Builder failed to build. %w", builder.Type, err)
}
return event, nil
}

View file

@ -5,9 +5,9 @@ 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/jsonerror"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -24,7 +24,7 @@ func Deactivate(
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()), JSON: spec.BadJSON("The request body could not be read: " + err.Error()),
} }
} }
@ -33,19 +33,26 @@ func Deactivate(
return *errRes return *errRes
} }
localpart, _, err := gomatrixserverlib.SplitID('@', login.Username()) localpart, serverName, err := gomatrixserverlib.SplitID('@', login.Username())
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed") util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
var res api.PerformAccountDeactivationResponse var res api.PerformAccountDeactivationResponse
err = accountAPI.PerformAccountDeactivation(ctx, &api.PerformAccountDeactivationRequest{ err = accountAPI.PerformAccountDeactivation(ctx, &api.PerformAccountDeactivationRequest{
Localpart: localpart, Localpart: localpart,
ServerName: serverName,
}, &res) }, &res)
if err != nil { if err != nil {
util.GetLogger(ctx).WithError(err).Error("userAPI.PerformAccountDeactivation failed") util.GetLogger(ctx).WithError(err).Error("userAPI.PerformAccountDeactivation failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{

View file

@ -15,15 +15,16 @@
package routing package routing
import ( import (
"encoding/json"
"io" "io"
"net" "net"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/auth" "github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
@ -59,7 +60,10 @@ func GetDeviceByID(
}, &queryRes) }, &queryRes)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryDevices failed") util.GetLogger(req.Context()).WithError(err).Error("QueryDevices failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
var targetDevice *api.Device var targetDevice *api.Device
for _, device := range queryRes.Devices { for _, device := range queryRes.Devices {
@ -71,7 +75,7 @@ func GetDeviceByID(
if targetDevice == nil { if targetDevice == nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
JSON: jsonerror.NotFound("Unknown device"), JSON: spec.NotFound("Unknown device"),
} }
} }
@ -96,7 +100,10 @@ func GetDevicesByLocalpart(
}, &queryRes) }, &queryRes)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryDevices failed") util.GetLogger(req.Context()).WithError(err).Error("QueryDevices failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
res := devicesJSON{} res := devicesJSON{}
@ -138,18 +145,15 @@ func UpdateDeviceByID(
}, &performRes) }, &performRes)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("PerformDeviceUpdate failed") util.GetLogger(req.Context()).WithError(err).Error("PerformDeviceUpdate failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
if !performRes.DeviceExists { if !performRes.DeviceExists {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
JSON: jsonerror.Forbidden("device does not exist"), JSON: spec.Forbidden("device does not exist"),
}
}
if performRes.Forbidden {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("device not owned by current user"),
} }
} }
@ -179,7 +183,7 @@ func DeleteDeviceById(
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()), JSON: spec.BadJSON("The request body could not be read: " + err.Error()),
} }
} }
@ -189,7 +193,7 @@ func DeleteDeviceById(
if dev != deviceID { if dev != deviceID {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("session & device mismatch"), JSON: spec.Forbidden("session and device mismatch"),
} }
} }
} }
@ -211,7 +215,10 @@ func DeleteDeviceById(
localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID)
if err != nil { if err != nil {
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.SplitID failed") util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.SplitID failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
// make sure that the access token being used matches the login creds used for user interactive auth, else // make sure that the access token being used matches the login creds used for user interactive auth, else
@ -219,7 +226,7 @@ func DeleteDeviceById(
if login.Username() != localpart && login.Username() != device.UserID { if login.Username() != localpart && login.Username() != device.UserID {
return util.JSONResponse{ return util.JSONResponse{
Code: 403, Code: 403,
JSON: jsonerror.Forbidden("Cannot delete another user's device"), JSON: spec.Forbidden("Cannot delete another user's device"),
} }
} }
@ -229,7 +236,10 @@ func DeleteDeviceById(
DeviceIDs: []string{deviceID}, DeviceIDs: []string{deviceID},
}, &res); err != nil { }, &res); err != nil {
util.GetLogger(ctx).WithError(err).Error("userAPI.PerformDeviceDeletion failed") util.GetLogger(ctx).WithError(err).Error("userAPI.PerformDeviceDeletion failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
deleteOK = true deleteOK = true
@ -242,16 +252,40 @@ func DeleteDeviceById(
// DeleteDevices handles POST requests to /delete_devices // DeleteDevices handles POST requests to /delete_devices
func DeleteDevices( func DeleteDevices(
req *http.Request, userAPI api.ClientUserAPI, device *api.Device, req *http.Request, userInteractiveAuth *auth.UserInteractive, userAPI api.ClientUserAPI, device *api.Device,
) util.JSONResponse { ) util.JSONResponse {
ctx := req.Context() ctx := req.Context()
payload := devicesDeleteJSON{}
if resErr := httputil.UnmarshalJSONRequest(req, &payload); resErr != nil { bodyBytes, err := io.ReadAll(req.Body)
return *resErr if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("The request body could not be read: " + err.Error()),
}
}
defer req.Body.Close() // nolint:errcheck
// initiate UIA
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes, device)
if errRes != nil {
return *errRes
} }
defer req.Body.Close() // nolint: errcheck if login.Username() != device.UserID {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("unable to delete devices for other user"),
}
}
payload := devicesDeleteJSON{}
if err = json.Unmarshal(bodyBytes, &payload); err != nil {
util.GetLogger(ctx).WithError(err).Error("unable to unmarshal device deletion request")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
var res api.PerformDeviceDeletionResponse var res api.PerformDeviceDeletionResponse
if err := userAPI.PerformDeviceDeletion(ctx, &api.PerformDeviceDeletionRequest{ if err := userAPI.PerformDeviceDeletion(ctx, &api.PerformDeviceDeletionRequest{
@ -259,7 +293,10 @@ func DeleteDevices(
DeviceIDs: payload.Devices, DeviceIDs: payload.Devices,
}, &res); err != nil { }, &res); err != nil {
util.GetLogger(ctx).WithError(err).Error("userAPI.PerformDeviceDeletion failed") util.GetLogger(ctx).WithError(err).Error("userAPI.PerformDeviceDeletion failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{

View file

@ -18,14 +18,16 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
federationAPI "github.com/matrix-org/dendrite/federationapi/api" federationAPI "github.com/matrix-org/dendrite/federationapi/api"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
) )
type roomDirectoryResponse struct { type roomDirectoryResponse struct {
@ -33,7 +35,7 @@ type roomDirectoryResponse struct {
Servers []string `json:"servers"` Servers []string `json:"servers"`
} }
func (r *roomDirectoryResponse) fillServers(servers []gomatrixserverlib.ServerName) { func (r *roomDirectoryResponse) fillServers(servers []spec.ServerName) {
r.Servers = make([]string, len(servers)) r.Servers = make([]string, len(servers))
for i, s := range servers { for i, s := range servers {
r.Servers[i] = string(s) r.Servers[i] = string(s)
@ -44,7 +46,7 @@ func (r *roomDirectoryResponse) fillServers(servers []gomatrixserverlib.ServerNa
func DirectoryRoom( func DirectoryRoom(
req *http.Request, req *http.Request,
roomAlias string, roomAlias string,
federation *gomatrixserverlib.FederationClient, federation fclient.FederationClient,
cfg *config.ClientAPI, cfg *config.ClientAPI,
rsAPI roomserverAPI.ClientRoomserverAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
fedSenderAPI federationAPI.ClientFederationAPI, fedSenderAPI federationAPI.ClientFederationAPI,
@ -53,7 +55,7 @@ func DirectoryRoom(
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("Room alias must be in the form '#localpart:domain'"), JSON: spec.InvalidParam("Room alias must be in the form '#localpart:domain'"),
} }
} }
@ -67,7 +69,10 @@ func DirectoryRoom(
queryRes := &roomserverAPI.GetRoomIDForAliasResponse{} queryRes := &roomserverAPI.GetRoomIDForAliasResponse{}
if err = rsAPI.GetRoomIDForAlias(req.Context(), queryReq, queryRes); err != nil { if err = rsAPI.GetRoomIDForAlias(req.Context(), queryReq, queryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.GetRoomIDForAlias failed") util.GetLogger(req.Context()).WithError(err).Error("rsAPI.GetRoomIDForAlias failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
res.RoomID = queryRes.RoomID res.RoomID = queryRes.RoomID
@ -75,13 +80,16 @@ func DirectoryRoom(
if res.RoomID == "" { if res.RoomID == "" {
// If we don't know it locally, do a federation query. // If we don't know it locally, do a federation query.
// But don't send the query to ourselves. // But don't send the query to ourselves.
if domain != cfg.Matrix.ServerName { if !cfg.Matrix.IsLocalServerName(domain) {
fedRes, fedErr := federation.LookupRoomAlias(req.Context(), domain, roomAlias) fedRes, fedErr := federation.LookupRoomAlias(req.Context(), cfg.Matrix.ServerName, domain, roomAlias)
if fedErr != nil { if fedErr != nil {
// TODO: Return 502 if the remote server errored. // TODO: Return 502 if the remote server errored.
// TODO: Return 504 if the remote server timed out. // TODO: Return 504 if the remote server timed out.
util.GetLogger(req.Context()).WithError(fedErr).Error("federation.LookupRoomAlias failed") util.GetLogger(req.Context()).WithError(fedErr).Error("federation.LookupRoomAlias failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
res.RoomID = fedRes.RoomID res.RoomID = fedRes.RoomID
res.fillServers(fedRes.Servers) res.fillServers(fedRes.Servers)
@ -90,7 +98,7 @@ func DirectoryRoom(
if res.RoomID == "" { if res.RoomID == "" {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
JSON: jsonerror.NotFound( JSON: spec.NotFound(
fmt.Sprintf("Room alias %s not found", roomAlias), fmt.Sprintf("Room alias %s not found", roomAlias),
), ),
} }
@ -100,7 +108,10 @@ func DirectoryRoom(
var joinedHostsRes federationAPI.QueryJoinedHostServerNamesInRoomResponse var joinedHostsRes federationAPI.QueryJoinedHostServerNamesInRoomResponse
if err = fedSenderAPI.QueryJoinedHostServerNamesInRoom(req.Context(), &joinedHostsReq, &joinedHostsRes); err != nil { if err = fedSenderAPI.QueryJoinedHostServerNamesInRoom(req.Context(), &joinedHostsReq, &joinedHostsRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("fedSenderAPI.QueryJoinedHostServerNamesInRoom failed") util.GetLogger(req.Context()).WithError(err).Error("fedSenderAPI.QueryJoinedHostServerNamesInRoom failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
res.fillServers(joinedHostsRes.ServerNames) res.fillServers(joinedHostsRes.ServerNames)
} }
@ -123,14 +134,14 @@ func SetLocalAlias(
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("Room alias must be in the form '#localpart:domain'"), JSON: spec.InvalidParam("Room alias must be in the form '#localpart:domain'"),
} }
} }
if domain != cfg.Matrix.ServerName { if !cfg.Matrix.IsLocalServerName(domain) {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Alias must be on local homeserver"), JSON: spec.Forbidden("Alias must be on local homeserver"),
} }
} }
@ -143,7 +154,7 @@ func SetLocalAlias(
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("User ID must be in the form '@localpart:domain'"), JSON: spec.BadJSON("User ID must be in the form '@localpart:domain'"),
} }
} }
for _, appservice := range cfg.Derived.ApplicationServices { for _, appservice := range cfg.Derived.ApplicationServices {
@ -155,7 +166,7 @@ func SetLocalAlias(
if namespace.Exclusive && namespace.RegexpObject.MatchString(alias) { if namespace.Exclusive && namespace.RegexpObject.MatchString(alias) {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.ASExclusive("Alias is reserved by an application service"), JSON: spec.ASExclusive("Alias is reserved by an application service"),
} }
} }
} }
@ -170,21 +181,50 @@ func SetLocalAlias(
return *resErr return *resErr
} }
queryReq := roomserverAPI.SetRoomAliasRequest{ roomID, err := spec.NewRoomID(r.RoomID)
UserID: device.UserID, if err != nil {
RoomID: r.RoomID, return util.JSONResponse{
Alias: alias, Code: http.StatusBadRequest,
JSON: spec.InvalidParam("invalid room ID"),
} }
var queryRes roomserverAPI.SetRoomAliasResponse
if err := rsAPI.SetRoomAlias(req.Context(), &queryReq, &queryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("aliasAPI.SetRoomAlias failed")
return jsonerror.InternalServerError()
} }
if queryRes.AliasExists { userID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
senderID, err := rsAPI.QuerySenderIDForUser(req.Context(), *roomID, *userID)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QuerySenderIDForUser failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
} else if senderID == nil {
util.GetLogger(req.Context()).WithField("roomID", *roomID).WithField("userID", *userID).Error("Sender ID not found")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
aliasAlreadyExists, err := rsAPI.SetRoomAlias(req.Context(), *senderID, *roomID, alias)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("aliasAPI.SetRoomAlias failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
if aliasAlreadyExists {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusConflict, Code: http.StatusConflict,
JSON: jsonerror.Unknown("The alias " + alias + " already exists."), JSON: spec.Unknown("The alias " + alias + " already exists."),
} }
} }
@ -201,27 +241,91 @@ func RemoveLocalAlias(
alias string, alias string,
rsAPI roomserverAPI.ClientRoomserverAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
) util.JSONResponse { ) util.JSONResponse {
queryReq := roomserverAPI.RemoveRoomAliasRequest{ userID, err := spec.NewUserID(device.UserID, true)
Alias: alias, if err != nil {
UserID: device.UserID, return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{Err: "UserID for device is invalid"},
} }
var queryRes roomserverAPI.RemoveRoomAliasResponse
if err := rsAPI.RemoveRoomAlias(req.Context(), &queryReq, &queryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("aliasAPI.RemoveRoomAlias failed")
return jsonerror.InternalServerError()
} }
if !queryRes.Found { roomIDReq := roomserverAPI.GetRoomIDForAliasRequest{Alias: alias}
roomIDRes := roomserverAPI.GetRoomIDForAliasResponse{}
err = rsAPI.GetRoomIDForAlias(req.Context(), &roomIDReq, &roomIDRes)
if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
JSON: jsonerror.NotFound("The alias does not exist."), JSON: spec.NotFound("The alias does not exist."),
} }
} }
if !queryRes.Removed { validRoomID, err := spec.NewRoomID(roomIDRes.RoomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.NotFound("The alias does not exist."),
}
}
// This seems like the kind of auth check that should be done in the roomserver, but
// if this check fails (user is not in the room), then there will be no SenderID for the user
// for pseudo-ID rooms - it will just return "". However, we can't use lack of a sender ID
// as meaning they are not in the room, since lacking a sender ID could be caused by other bugs.
// TODO: maybe have QuerySenderIDForUser return richer errors?
var queryResp roomserverAPI.QueryMembershipForUserResponse
err = rsAPI.QueryMembershipForUser(req.Context(), &roomserverAPI.QueryMembershipForUserRequest{
RoomID: validRoomID.String(),
UserID: *userID,
}, &queryResp)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("roomserverAPI.QueryMembershipForUser failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
if !queryResp.IsInRoom {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You do not have permission to remove this alias."), JSON: spec.Forbidden("You do not have permission to remove this alias."),
}
}
deviceSenderID, err := rsAPI.QuerySenderIDForUser(req.Context(), *validRoomID, *userID)
if err != nil {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.NotFound("The alias does not exist."),
}
}
// TODO: how to handle this case? missing user/room keys seem to be a whole new class of errors
if deviceSenderID == nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
aliasFound, aliasRemoved, err := rsAPI.RemoveRoomAlias(req.Context(), *deviceSenderID, alias)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("aliasAPI.RemoveRoomAlias failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
if !aliasFound {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.NotFound("The alias does not exist."),
}
}
if !aliasRemoved {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You do not have permission to remove this alias."),
} }
} }
@ -246,12 +350,15 @@ func GetVisibility(
}, &res) }, &res)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryPublishedRooms failed") util.GetLogger(req.Context()).WithError(err).Error("QueryPublishedRooms failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
var v roomVisibility var v roomVisibility
if len(res.RoomIDs) == 1 { if len(res.RoomIDs) == 1 {
v.Visibility = gomatrixserverlib.Public v.Visibility = spec.Public
} else { } else {
v.Visibility = "private" v.Visibility = "private"
} }
@ -268,7 +375,30 @@ func SetVisibility(
req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, dev *userapi.Device, req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, dev *userapi.Device,
roomID string, roomID string,
) util.JSONResponse { ) util.JSONResponse {
resErr := checkMemberInRoom(req.Context(), rsAPI, dev.UserID, roomID) deviceUserID, err := spec.NewUserID(dev.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("userID for this device is invalid"),
}
}
validRoomID, err := spec.NewRoomID(roomID)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("roomID is invalid")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("RoomID is invalid"),
}
}
senderID, err := rsAPI.QuerySenderIDForUser(req.Context(), *validRoomID, *deviceUserID)
if err != nil || senderID == nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown("failed to find senderID for this user"),
}
}
resErr := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID)
if resErr != nil { if resErr != nil {
return *resErr return *resErr
} }
@ -276,23 +406,26 @@ func SetVisibility(
queryEventsReq := roomserverAPI.QueryLatestEventsAndStateRequest{ queryEventsReq := roomserverAPI.QueryLatestEventsAndStateRequest{
RoomID: roomID, RoomID: roomID,
StateToFetch: []gomatrixserverlib.StateKeyTuple{{ StateToFetch: []gomatrixserverlib.StateKeyTuple{{
EventType: gomatrixserverlib.MRoomPowerLevels, EventType: spec.MRoomPowerLevels,
StateKey: "", StateKey: "",
}}, }},
} }
var queryEventsRes roomserverAPI.QueryLatestEventsAndStateResponse var queryEventsRes roomserverAPI.QueryLatestEventsAndStateResponse
err := rsAPI.QueryLatestEventsAndState(req.Context(), &queryEventsReq, &queryEventsRes) err = rsAPI.QueryLatestEventsAndState(req.Context(), &queryEventsReq, &queryEventsRes)
if err != nil || len(queryEventsRes.StateEvents) == 0 { if err != nil || len(queryEventsRes.StateEvents) == 0 {
util.GetLogger(req.Context()).WithError(err).Error("could not query events from room") util.GetLogger(req.Context()).WithError(err).Error("could not query events from room")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
// NOTSPEC: Check if the user's power is greater than power required to change m.room.canonical_alias event // NOTSPEC: Check if the user's power is greater than power required to change m.room.canonical_alias event
power, _ := gomatrixserverlib.NewPowerLevelContentFromEvent(queryEventsRes.StateEvents[0].Event) power, _ := gomatrixserverlib.NewPowerLevelContentFromEvent(queryEventsRes.StateEvents[0].PDU)
if power.UserLevel(dev.UserID) < power.EventLevel(gomatrixserverlib.MRoomCanonicalAlias, true) { if power.UserLevel(*senderID) < power.EventLevel(spec.MRoomCanonicalAlias, true) {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("userID doesn't have power level to change visibility"), JSON: spec.Forbidden("userID doesn't have power level to change visibility"),
} }
} }
@ -301,16 +434,54 @@ func SetVisibility(
return *reqErr return *reqErr
} }
var publishRes roomserverAPI.PerformPublishResponse if err = rsAPI.PerformPublish(req.Context(), &roomserverAPI.PerformPublishRequest{
if err := rsAPI.PerformPublish(req.Context(), &roomserverAPI.PerformPublishRequest{
RoomID: roomID, RoomID: roomID,
Visibility: v.Visibility, Visibility: v.Visibility,
}, &publishRes); err != nil { }); err != nil {
return jsonerror.InternalAPIError(req.Context(), err) util.GetLogger(req.Context()).WithError(err).Error("failed to publish room")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}
func SetVisibilityAS(
req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, dev *userapi.Device,
networkID, roomID string,
) util.JSONResponse {
if dev.AccountType != userapi.AccountTypeAppService {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("Only appservice may use this endpoint"),
}
}
var v roomVisibility
// If the method is delete, we simply mark the visibility as private
if req.Method == http.MethodDelete {
v.Visibility = "private"
} else {
if reqErr := httputil.UnmarshalJSONRequest(req, &v); reqErr != nil {
return *reqErr
}
}
if err := rsAPI.PerformPublish(req.Context(), &roomserverAPI.PerformPublishRequest{
RoomID: roomID,
Visibility: v.Visibility,
NetworkID: networkID,
AppserviceID: dev.AppserviceID,
}); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("failed to publish room")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
} }
if publishRes.Error != nil {
util.GetLogger(req.Context()).WithError(publishRes.Error).Error("PerformPublish failed")
return publishRes.Error.JSONResponse()
} }
return util.JSONResponse{ return util.JSONResponse{

View file

@ -23,37 +23,40 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/clientapi/api"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
) )
var ( var (
cacheMu sync.Mutex cacheMu sync.Mutex
publicRoomsCache []gomatrixserverlib.PublicRoom publicRoomsCache []fclient.PublicRoom
) )
type PublicRoomReq struct { type PublicRoomReq struct {
Since string `json:"since,omitempty"` Since string `json:"since,omitempty"`
Limit int16 `json:"limit,omitempty"` Limit int64 `json:"limit,omitempty"`
Filter filter `json:"filter,omitempty"` Filter filter `json:"filter,omitempty"`
Server string `json:"server,omitempty"` Server string `json:"server,omitempty"`
IncludeAllNetworks bool `json:"include_all_networks,omitempty"`
NetworkID string `json:"third_party_instance_id,omitempty"`
} }
type filter struct { type filter struct {
SearchTerms string `json:"generic_search_term,omitempty"` SearchTerms string `json:"generic_search_term,omitempty"`
RoomTypes []string `json:"room_types,omitempty"` // TODO: Implement filter on this
} }
// GetPostPublicRooms implements GET and POST /publicRooms // GetPostPublicRooms implements GET and POST /publicRooms
func GetPostPublicRooms( func GetPostPublicRooms(
req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI,
extRoomsProvider api.ExtraPublicRoomsProvider, extRoomsProvider api.ExtraPublicRoomsProvider,
federation *gomatrixserverlib.FederationClient, federation fclient.FederationClient,
cfg *config.ClientAPI, cfg *config.ClientAPI,
) util.JSONResponse { ) util.JSONResponse {
var request PublicRoomReq var request PublicRoomReq
@ -61,18 +64,27 @@ func GetPostPublicRooms(
return *fillErr return *fillErr
} }
serverName := gomatrixserverlib.ServerName(request.Server) if request.IncludeAllNetworks && request.NetworkID != "" {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidParam("include_all_networks and third_party_instance_id can not be used together"),
}
}
if serverName != "" && serverName != cfg.Matrix.ServerName { serverName := spec.ServerName(request.Server)
if serverName != "" && !cfg.Matrix.IsLocalServerName(serverName) {
res, err := federation.GetPublicRoomsFiltered( res, err := federation.GetPublicRoomsFiltered(
req.Context(), serverName, req.Context(), cfg.Matrix.ServerName, serverName,
int(request.Limit), request.Since, int(request.Limit), request.Since,
request.Filter.SearchTerms, false, request.Filter.SearchTerms, false,
"", "",
) )
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("failed to get public rooms") util.GetLogger(req.Context()).WithError(err).Error("failed to get public rooms")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
@ -83,7 +95,10 @@ func GetPostPublicRooms(
response, err := publicRooms(req.Context(), request, rsAPI, extRoomsProvider) response, err := publicRooms(req.Context(), request, rsAPI, extRoomsProvider)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Errorf("failed to work out public rooms") util.GetLogger(req.Context()).WithError(err).Errorf("failed to work out public rooms")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
@ -93,12 +108,12 @@ func GetPostPublicRooms(
func publicRooms( func publicRooms(
ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider, ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider,
) (*gomatrixserverlib.RespPublicRooms, error) { ) (*fclient.RespPublicRooms, error) {
response := gomatrixserverlib.RespPublicRooms{ response := fclient.RespPublicRooms{
Chunk: []gomatrixserverlib.PublicRoom{}, Chunk: []fclient.PublicRoom{},
} }
var limit int16 var limit int64
var offset int64 var offset int64
limit = request.Limit limit = request.Limit
if limit == 0 { if limit == 0 {
@ -113,9 +128,9 @@ func publicRooms(
} }
err = nil err = nil
var rooms []gomatrixserverlib.PublicRoom var rooms []fclient.PublicRoom
if request.Since == "" { if request.Since == "" {
rooms = refreshPublicRoomCache(ctx, rsAPI, extRoomsProvider) rooms = refreshPublicRoomCache(ctx, rsAPI, extRoomsProvider, request)
} else { } else {
rooms = getPublicRoomsFromCache() rooms = getPublicRoomsFromCache()
} }
@ -137,14 +152,14 @@ func publicRooms(
return &response, err return &response, err
} }
func filterRooms(rooms []gomatrixserverlib.PublicRoom, searchTerm string) []gomatrixserverlib.PublicRoom { func filterRooms(rooms []fclient.PublicRoom, searchTerm string) []fclient.PublicRoom {
if searchTerm == "" { if searchTerm == "" {
return rooms return rooms
} }
normalizedTerm := strings.ToLower(searchTerm) normalizedTerm := strings.ToLower(searchTerm)
result := make([]gomatrixserverlib.PublicRoom, 0) result := make([]fclient.PublicRoom, 0)
for _, room := range rooms { for _, room := range rooms {
if strings.Contains(strings.ToLower(room.Name), normalizedTerm) || if strings.Contains(strings.ToLower(room.Name), normalizedTerm) ||
strings.Contains(strings.ToLower(room.Topic), normalizedTerm) || strings.Contains(strings.ToLower(room.Topic), normalizedTerm) ||
@ -163,7 +178,7 @@ func fillPublicRoomsReq(httpReq *http.Request, request *PublicRoomReq) *util.JSO
if httpReq.Method != "GET" && httpReq.Method != "POST" { if httpReq.Method != "GET" && httpReq.Method != "POST" {
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusMethodNotAllowed, Code: http.StatusMethodNotAllowed,
JSON: jsonerror.NotFound("Bad method"), JSON: spec.NotFound("Bad method"),
} }
} }
if httpReq.Method == "GET" { if httpReq.Method == "GET" {
@ -174,10 +189,10 @@ func fillPublicRoomsReq(httpReq *http.Request, request *PublicRoomReq) *util.JSO
util.GetLogger(httpReq.Context()).WithError(err).Error("strconv.Atoi failed") util.GetLogger(httpReq.Context()).WithError(err).Error("strconv.Atoi failed")
return &util.JSONResponse{ return &util.JSONResponse{
Code: 400, Code: 400,
JSON: jsonerror.BadJSON("limit param is not a number"), JSON: spec.BadJSON("limit param is not a number"),
} }
} }
request.Limit = int16(limit) request.Limit = int64(limit)
request.Since = httpReq.FormValue("since") request.Since = httpReq.FormValue("since")
request.Server = httpReq.FormValue("server") request.Server = httpReq.FormValue("server")
} else { } else {
@ -205,7 +220,7 @@ func fillPublicRoomsReq(httpReq *http.Request, request *PublicRoomReq) *util.JSO
// limit=3&since=6 => G (prev='3', next='') // limit=3&since=6 => G (prev='3', next='')
// //
// A value of '-1' for prev/next indicates no position. // A value of '-1' for prev/next indicates no position.
func sliceInto(slice []gomatrixserverlib.PublicRoom, since int64, limit int16) (subset []gomatrixserverlib.PublicRoom, prev, next int) { func sliceInto(slice []fclient.PublicRoom, since int64, limit int64) (subset []fclient.PublicRoom, prev, next int) {
prev = -1 prev = -1
next = -1 next = -1
@ -231,16 +246,26 @@ func sliceInto(slice []gomatrixserverlib.PublicRoom, since int64, limit int16) (
func refreshPublicRoomCache( func refreshPublicRoomCache(
ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider, ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider,
) []gomatrixserverlib.PublicRoom { request PublicRoomReq,
) []fclient.PublicRoom {
cacheMu.Lock() cacheMu.Lock()
defer cacheMu.Unlock() defer cacheMu.Unlock()
var extraRooms []gomatrixserverlib.PublicRoom var extraRooms []fclient.PublicRoom
if extRoomsProvider != nil { if extRoomsProvider != nil {
extraRooms = extRoomsProvider.Rooms() extraRooms = extRoomsProvider.Rooms()
} }
// TODO: this is only here to make Sytest happy, for now.
ns := strings.Split(request.NetworkID, "|")
if len(ns) == 2 {
request.NetworkID = ns[1]
}
var queryRes roomserverAPI.QueryPublishedRoomsResponse var queryRes roomserverAPI.QueryPublishedRoomsResponse
err := rsAPI.QueryPublishedRooms(ctx, &roomserverAPI.QueryPublishedRoomsRequest{}, &queryRes) err := rsAPI.QueryPublishedRooms(ctx, &roomserverAPI.QueryPublishedRoomsRequest{
NetworkID: request.NetworkID,
IncludeAllNetworks: request.IncludeAllNetworks,
}, &queryRes)
if err != nil { if err != nil {
util.GetLogger(ctx).WithError(err).Error("QueryPublishedRooms failed") util.GetLogger(ctx).WithError(err).Error("QueryPublishedRooms failed")
return publicRoomsCache return publicRoomsCache
@ -250,7 +275,7 @@ func refreshPublicRoomCache(
util.GetLogger(ctx).WithError(err).Error("PopulatePublicRooms failed") util.GetLogger(ctx).WithError(err).Error("PopulatePublicRooms failed")
return publicRoomsCache return publicRoomsCache
} }
publicRoomsCache = []gomatrixserverlib.PublicRoom{} publicRoomsCache = []fclient.PublicRoom{}
publicRoomsCache = append(publicRoomsCache, pubRooms...) publicRoomsCache = append(publicRoomsCache, pubRooms...)
publicRoomsCache = append(publicRoomsCache, extraRooms...) publicRoomsCache = append(publicRoomsCache, extraRooms...)
publicRoomsCache = dedupeAndShuffle(publicRoomsCache) publicRoomsCache = dedupeAndShuffle(publicRoomsCache)
@ -262,16 +287,16 @@ func refreshPublicRoomCache(
return publicRoomsCache return publicRoomsCache
} }
func getPublicRoomsFromCache() []gomatrixserverlib.PublicRoom { func getPublicRoomsFromCache() []fclient.PublicRoom {
cacheMu.Lock() cacheMu.Lock()
defer cacheMu.Unlock() defer cacheMu.Unlock()
return publicRoomsCache return publicRoomsCache
} }
func dedupeAndShuffle(in []gomatrixserverlib.PublicRoom) []gomatrixserverlib.PublicRoom { func dedupeAndShuffle(in []fclient.PublicRoom) []fclient.PublicRoom {
// de-duplicate rooms with the same room ID. We can join the room via any of these aliases as we know these servers // de-duplicate rooms with the same room ID. We can join the room via any of these aliases as we know these servers
// are alive and well, so we arbitrarily pick one (purposefully shuffling them to spread the load a bit) // are alive and well, so we arbitrarily pick one (purposefully shuffling them to spread the load a bit)
var publicRooms []gomatrixserverlib.PublicRoom var publicRooms []fclient.PublicRoom
haveRoomIDs := make(map[string]bool) haveRoomIDs := make(map[string]bool)
rand.Shuffle(len(in), func(i, j int) { rand.Shuffle(len(in), func(i, j int) {
in[i], in[j] = in[j], in[i] in[i], in[j] = in[j], in[i]

View file

@ -4,25 +4,25 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/fclient"
) )
func pubRoom(name string) gomatrixserverlib.PublicRoom { func pubRoom(name string) fclient.PublicRoom {
return gomatrixserverlib.PublicRoom{ return fclient.PublicRoom{
Name: name, Name: name,
} }
} }
func TestSliceInto(t *testing.T) { func TestSliceInto(t *testing.T) {
slice := []gomatrixserverlib.PublicRoom{ slice := []fclient.PublicRoom{
pubRoom("a"), pubRoom("b"), pubRoom("c"), pubRoom("d"), pubRoom("e"), pubRoom("f"), pubRoom("g"), pubRoom("a"), pubRoom("b"), pubRoom("c"), pubRoom("d"), pubRoom("e"), pubRoom("f"), pubRoom("g"),
} }
limit := int16(3) limit := int64(3)
testCases := []struct { testCases := []struct {
since int64 since int64
wantPrev int wantPrev int
wantNext int wantNext int
wantSubset []gomatrixserverlib.PublicRoom wantSubset []fclient.PublicRoom
}{ }{
{ {
since: 0, since: 0,

View file

@ -0,0 +1,68 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package routing
import (
"net/http"
"github.com/matrix-org/util"
"github.com/matrix-org/dendrite/roomserver/api"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
)
type getJoinedRoomsResponse struct {
JoinedRooms []string `json:"joined_rooms"`
}
func GetJoinedRooms(
req *http.Request,
device *userapi.Device,
rsAPI api.ClientRoomserverAPI,
) util.JSONResponse {
deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("Invalid device user ID")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
rooms, err := rsAPI.QueryRoomsForUser(req.Context(), *deviceUserID, "join")
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryRoomsForUser failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
var roomIDStrs []string
if rooms == nil {
roomIDStrs = []string{}
} else {
roomIDStrs = make([]string, len(rooms))
for i, roomID := range rooms {
roomIDStrs[i] = roomID.String()
}
}
return util.JSONResponse{
Code: http.StatusOK,
JSON: getJoinedRoomsResponse{roomIDStrs},
}
}

View file

@ -15,14 +15,17 @@
package routing package routing
import ( import (
"encoding/json"
"net/http" "net/http"
"time" "time"
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/internal/eventutil"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrix"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -37,9 +40,9 @@ func JoinRoomByIDOrAlias(
joinReq := roomserverAPI.PerformJoinRequest{ joinReq := roomserverAPI.PerformJoinRequest{
RoomIDOrAlias: roomIDOrAlias, RoomIDOrAlias: roomIDOrAlias,
UserID: device.UserID, UserID: device.UserID,
IsGuest: device.AccountType == api.AccountTypeGuest,
Content: map[string]interface{}{}, Content: map[string]interface{}{},
} }
joinRes := roomserverAPI.PerformJoinResponse{}
// Check to see if any ?server_name= query parameters were // Check to see if any ?server_name= query parameters were
// given in the request. // given in the request.
@ -47,7 +50,7 @@ func JoinRoomByIDOrAlias(
for _, serverName := range serverNames { for _, serverName := range serverNames {
joinReq.ServerNames = append( joinReq.ServerNames = append(
joinReq.ServerNames, joinReq.ServerNames,
gomatrixserverlib.ServerName(serverName), spec.ServerName(serverName),
) )
} }
} }
@ -60,51 +63,84 @@ func JoinRoomByIDOrAlias(
// Work out our localpart for the client profile request. // Work out our localpart for the client profile request.
// Request our profile content to populate the request content with. // Request our profile content to populate the request content with.
res := &api.QueryProfileResponse{} profile, err := profileAPI.QueryProfile(req.Context(), device.UserID)
err := profileAPI.QueryProfile(req.Context(), &api.QueryProfileRequest{UserID: device.UserID}, res)
if err != nil || !res.UserExists { switch err {
if !res.UserExists { case nil:
joinReq.Content["displayname"] = profile.DisplayName
joinReq.Content["avatar_url"] = profile.AvatarURL
case appserviceAPI.ErrProfileNotExists:
util.GetLogger(req.Context()).Error("Unable to query user profile, no profile found.") util.GetLogger(req.Context()).Error("Unable to query user profile, no profile found.")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.Unknown("Unable to query user profile, no profile found."), JSON: spec.Unknown("Unable to query user profile, no profile found."),
} }
} default:
util.GetLogger(req.Context()).WithError(err).Error("UserProfileAPI.QueryProfile failed")
} else {
joinReq.Content["displayname"] = res.DisplayName
joinReq.Content["avatar_url"] = res.AvatarURL
} }
// Ask the roomserver to perform the join. // Ask the roomserver to perform the join.
done := make(chan util.JSONResponse, 1) done := make(chan util.JSONResponse, 1)
go func() { go func() {
defer close(done) defer close(done)
if err := rsAPI.PerformJoin(req.Context(), &joinReq, &joinRes); err != nil { roomID, _, err := rsAPI.PerformJoin(req.Context(), &joinReq)
done <- jsonerror.InternalAPIError(req.Context(), err) var response util.JSONResponse
} else if joinRes.Error != nil {
done <- joinRes.Error.JSONResponse() switch e := err.(type) {
} else { case nil: // success case
done <- util.JSONResponse{ response = util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
// TODO: Put the response struct somewhere internal. // TODO: Put the response struct somewhere internal.
JSON: struct { JSON: struct {
RoomID string `json:"room_id"` RoomID string `json:"room_id"`
}{joinRes.RoomID}, }{roomID},
}
case roomserverAPI.ErrInvalidID:
response = util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown(e.Error()),
}
case roomserverAPI.ErrNotAllowed:
jsonErr := spec.Forbidden(e.Error())
if device.AccountType == api.AccountTypeGuest {
jsonErr = spec.GuestAccessForbidden(e.Error())
}
response = util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonErr,
}
case *gomatrix.HTTPError: // this ensures we proxy responses over federation to the client
response = util.JSONResponse{
Code: e.Code,
JSON: json.RawMessage(e.Message),
}
case eventutil.ErrRoomNoExists:
response = util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.NotFound(e.Error()),
}
default:
response = util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
} }
} }
done <- response
}() }()
// Wait either for the join to finish, or for us to hit a reasonable // Wait either for the join to finish, or for us to hit a reasonable
// timeout, at which point we'll just return a 200 to placate clients. // timeout, at which point we'll just return a 200 to placate clients.
timer := time.NewTimer(time.Second * 20)
select { select {
case <-time.After(time.Second * 20): case <-timer.C:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusAccepted, Code: http.StatusAccepted,
JSON: jsonerror.Unknown("The room join will continue in the background."), JSON: spec.Unknown("The room join will continue in the background."),
} }
case result := <-done: case result := <-done:
// Stop and drain the timer
if !timer.Stop() {
<-timer.C
}
return result return result
} }
} }

View file

@ -0,0 +1,166 @@
package routing
import (
"bytes"
"context"
"net/http"
"testing"
"time"
"github.com/matrix-org/dendrite/federationapi/statistics"
"github.com/matrix-org/dendrite/internal/caching"
"github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/dendrite/appservice"
"github.com/matrix-org/dendrite/roomserver"
"github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/test/testrig"
"github.com/matrix-org/dendrite/userapi"
uapi "github.com/matrix-org/dendrite/userapi/api"
)
var testIsBlacklistedOrBackingOff = func(s spec.ServerName) (*statistics.ServerStatistics, error) {
return &statistics.ServerStatistics{}, nil
}
func TestJoinRoomByIDOrAlias(t *testing.T) {
alice := test.NewUser(t)
bob := test.NewUser(t)
charlie := test.NewUser(t, test.WithAccountType(uapi.AccountTypeGuest))
ctx := context.Background()
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
defer close()
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
natsInstance := jetstream.NATSInstance{}
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
rsAPI.SetFederationAPI(nil, nil) // creates the rs.Inputer etc
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
// Create the users in the userapi
for _, u := range []*test.User{alice, bob, charlie} {
localpart, serverName, _ := gomatrixserverlib.SplitID('@', u.ID)
userRes := &uapi.PerformAccountCreationResponse{}
if err := userAPI.PerformAccountCreation(ctx, &uapi.PerformAccountCreationRequest{
AccountType: u.AccountType,
Localpart: localpart,
ServerName: serverName,
Password: "someRandomPassword",
}, userRes); err != nil {
t.Errorf("failed to create account: %s", err)
}
}
aliceDev := &uapi.Device{UserID: alice.ID}
bobDev := &uapi.Device{UserID: bob.ID}
charlieDev := &uapi.Device{UserID: charlie.ID, AccountType: uapi.AccountTypeGuest}
// create a room with disabled guest access and invite Bob
resp := createRoom(ctx, createRoomRequest{
Name: "testing",
IsDirect: true,
Topic: "testing",
Visibility: "public",
Preset: spec.PresetPublicChat,
RoomAliasName: "alias",
Invite: []string{bob.ID},
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
crResp, ok := resp.JSON.(createRoomResponse)
if !ok {
t.Fatalf("response is not a createRoomResponse: %+v", resp)
}
// create a room with guest access enabled and invite Charlie
resp = createRoom(ctx, createRoomRequest{
Name: "testing",
IsDirect: true,
Topic: "testing",
Visibility: "public",
Preset: spec.PresetPublicChat,
Invite: []string{charlie.ID},
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
crRespWithGuestAccess, ok := resp.JSON.(createRoomResponse)
if !ok {
t.Fatalf("response is not a createRoomResponse: %+v", resp)
}
// Dummy request
body := &bytes.Buffer{}
req, err := http.NewRequest(http.MethodPost, "/?server_name=test", body)
if err != nil {
t.Fatal(err)
}
testCases := []struct {
name string
device *uapi.Device
roomID string
wantHTTP200 bool
}{
{
name: "User can join successfully by alias",
device: bobDev,
roomID: crResp.RoomAlias,
wantHTTP200: true,
},
{
name: "User can join successfully by roomID",
device: bobDev,
roomID: crResp.RoomID,
wantHTTP200: true,
},
{
name: "join is forbidden if user is guest",
device: charlieDev,
roomID: crResp.RoomID,
},
{
name: "room does not exist",
device: aliceDev,
roomID: "!doesnotexist:test",
},
{
name: "user from different server",
device: &uapi.Device{UserID: "@wrong:server"},
roomID: crResp.RoomAlias,
},
{
name: "user doesn't exist locally",
device: &uapi.Device{UserID: "@doesnotexist:test"},
roomID: crResp.RoomAlias,
},
{
name: "invalid room ID",
device: aliceDev,
roomID: "invalidRoomID",
},
{
name: "roomAlias does not exist",
device: aliceDev,
roomID: "#doesnotexist:test",
},
{
name: "room with guest_access event",
device: charlieDev,
roomID: crRespWithGuestAccess.RoomID,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
joinResp := JoinRoomByIDOrAlias(req, tc.device, rsAPI, userAPI, tc.roomID)
if tc.wantHTTP200 && !joinResp.Is2xx() {
t.Fatalf("expected join room to succeed, but didn't: %+v", joinResp)
}
})
}
})
}

View file

@ -20,8 +20,8 @@ import (
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -61,28 +61,26 @@ func CreateKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, de
if resErr != nil { if resErr != nil {
return *resErr return *resErr
} }
var performKeyBackupResp userapi.PerformKeyBackupResponse if len(kb.AuthData) == 0 {
if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{ return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("missing auth_data"),
}
}
version, err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: "", Version: "",
AuthData: kb.AuthData, AuthData: kb.AuthData,
Algorithm: kb.Algorithm, Algorithm: kb.Algorithm,
}, &performKeyBackupResp); err != nil { })
return jsonerror.InternalServerError() if err != nil {
} return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %w", err))
if performKeyBackupResp.Error != "" {
if performKeyBackupResp.BadInput {
return util.JSONResponse{
Code: 400,
JSON: jsonerror.InvalidArgumentValue(performKeyBackupResp.Error),
}
}
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %s", performKeyBackupResp.Error))
} }
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,
JSON: keyBackupVersionCreateResponse{ JSON: keyBackupVersionCreateResponse{
Version: performKeyBackupResp.Version, Version: version,
}, },
} }
} }
@ -90,20 +88,17 @@ func CreateKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, de
// KeyBackupVersion returns the key backup version specified. If `version` is empty, the latest `keyBackupVersionResponse` is returned. // KeyBackupVersion returns the key backup version specified. If `version` is empty, the latest `keyBackupVersionResponse` is returned.
// Implements GET /_matrix/client/r0/room_keys/version and GET /_matrix/client/r0/room_keys/version/{version} // Implements GET /_matrix/client/r0/room_keys/version and GET /_matrix/client/r0/room_keys/version/{version}
func KeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse { func KeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse {
var queryResp userapi.QueryKeyBackupResponse queryResp, err := userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
if err := userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: version, Version: version,
}, &queryResp); err != nil { })
return jsonerror.InternalAPIError(req.Context(), err) if err != nil {
} return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", err))
if queryResp.Error != "" {
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", queryResp.Error))
} }
if !queryResp.Exists { if !queryResp.Exists {
return util.JSONResponse{ return util.JSONResponse{
Code: 404, Code: 404,
JSON: jsonerror.NotFound("version not found"), JSON: spec.NotFound("version not found"),
} }
} }
return util.JSONResponse{ return util.JSONResponse{
@ -126,31 +121,29 @@ func ModifyKeyBackupVersionAuthData(req *http.Request, userAPI userapi.ClientUse
if resErr != nil { if resErr != nil {
return *resErr return *resErr
} }
var performKeyBackupResp userapi.PerformKeyBackupResponse performKeyBackupResp, err := userAPI.UpdateBackupKeyAuthData(req.Context(), &userapi.PerformKeyBackupRequest{
if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: version, Version: version,
AuthData: kb.AuthData, AuthData: kb.AuthData,
Algorithm: kb.Algorithm, Algorithm: kb.Algorithm,
}, &performKeyBackupResp); err != nil { })
return jsonerror.InternalServerError() switch e := err.(type) {
} case spec.ErrRoomKeysVersion:
if performKeyBackupResp.Error != "" {
if performKeyBackupResp.BadInput {
return util.JSONResponse{ return util.JSONResponse{
Code: 400, Code: http.StatusForbidden,
JSON: jsonerror.InvalidArgumentValue(performKeyBackupResp.Error), JSON: e,
} }
case nil:
default:
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %w", e))
} }
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %s", performKeyBackupResp.Error))
}
if !performKeyBackupResp.Exists { if !performKeyBackupResp.Exists {
return util.JSONResponse{ return util.JSONResponse{
Code: 404, Code: 404,
JSON: jsonerror.NotFound("backup version not found"), JSON: spec.NotFound("backup version not found"),
} }
} }
// Unclear what the 200 body should be
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,
JSON: keyBackupVersionCreateResponse{ JSON: keyBackupVersionCreateResponse{
@ -162,35 +155,19 @@ func ModifyKeyBackupVersionAuthData(req *http.Request, userAPI userapi.ClientUse
// Delete a version of key backup. Version must not be empty. If the key backup was previously deleted, will return 200 OK. // Delete a version of key backup. Version must not be empty. If the key backup was previously deleted, will return 200 OK.
// Implements DELETE /_matrix/client/r0/room_keys/version/{version} // Implements DELETE /_matrix/client/r0/room_keys/version/{version}
func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse { func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse {
var performKeyBackupResp userapi.PerformKeyBackupResponse exists, err := userAPI.DeleteKeyBackup(req.Context(), device.UserID, version)
if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{ if err != nil {
UserID: device.UserID, return util.ErrorResponse(fmt.Errorf("DeleteKeyBackup: %s", err))
Version: version,
DeleteBackup: true,
}, &performKeyBackupResp); err != nil {
return jsonerror.InternalServerError()
} }
if performKeyBackupResp.Error != "" { if !exists {
if performKeyBackupResp.BadInput {
return util.JSONResponse{
Code: 400,
JSON: jsonerror.InvalidArgumentValue(performKeyBackupResp.Error),
}
}
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %s", performKeyBackupResp.Error))
}
if !performKeyBackupResp.Exists {
return util.JSONResponse{ return util.JSONResponse{
Code: 404, Code: 404,
JSON: jsonerror.NotFound("backup version not found"), JSON: spec.NotFound("backup version not found"),
} }
} }
// Unclear what the 200 body should be
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,
JSON: keyBackupVersionCreateResponse{ JSON: struct{}{},
Version: performKeyBackupResp.Version,
},
} }
} }
@ -198,27 +175,26 @@ func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, de
func UploadBackupKeys( func UploadBackupKeys(
req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string, keys *keyBackupSessionRequest, req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string, keys *keyBackupSessionRequest,
) util.JSONResponse { ) util.JSONResponse {
var performKeyBackupResp userapi.PerformKeyBackupResponse performKeyBackupResp, err := userAPI.UpdateBackupKeyAuthData(req.Context(), &userapi.PerformKeyBackupRequest{
if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: version, Version: version,
Keys: *keys, Keys: *keys,
}, &performKeyBackupResp); err != nil && performKeyBackupResp.Error == "" { })
return jsonerror.InternalServerError()
} switch e := err.(type) {
if performKeyBackupResp.Error != "" { case spec.ErrRoomKeysVersion:
if performKeyBackupResp.BadInput {
return util.JSONResponse{ return util.JSONResponse{
Code: 400, Code: http.StatusForbidden,
JSON: jsonerror.InvalidArgumentValue(performKeyBackupResp.Error), JSON: e,
} }
} case nil:
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %s", performKeyBackupResp.Error)) default:
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %w", e))
} }
if !performKeyBackupResp.Exists { if !performKeyBackupResp.Exists {
return util.JSONResponse{ return util.JSONResponse{
Code: 404, Code: 404,
JSON: jsonerror.NotFound("backup version not found"), JSON: spec.NotFound("backup version not found"),
} }
} }
return util.JSONResponse{ return util.JSONResponse{
@ -234,23 +210,20 @@ func UploadBackupKeys(
func GetBackupKeys( func GetBackupKeys(
req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version, roomID, sessionID string, req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version, roomID, sessionID string,
) util.JSONResponse { ) util.JSONResponse {
var queryResp userapi.QueryKeyBackupResponse queryResp, err := userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
if err := userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: version, Version: version,
ReturnKeys: true, ReturnKeys: true,
KeysForRoomID: roomID, KeysForRoomID: roomID,
KeysForSessionID: sessionID, KeysForSessionID: sessionID,
}, &queryResp); err != nil { })
return jsonerror.InternalAPIError(req.Context(), err) if err != nil {
} return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %w", err))
if queryResp.Error != "" {
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", queryResp.Error))
} }
if !queryResp.Exists { if !queryResp.Exists {
return util.JSONResponse{ return util.JSONResponse{
Code: 404, Code: 404,
JSON: jsonerror.NotFound("version not found"), JSON: spec.NotFound("version not found"),
} }
} }
if sessionID != "" { if sessionID != "" {
@ -267,7 +240,10 @@ func GetBackupKeys(
} }
} else if roomID != "" { } else if roomID != "" {
roomData, ok := queryResp.Keys[roomID] roomData, ok := queryResp.Keys[roomID]
if ok { if !ok {
// If no keys are found, then an object with an empty sessions property will be returned
roomData = make(map[string]userapi.KeyBackupSession)
}
// wrap response in "sessions" // wrap response in "sessions"
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,
@ -277,7 +253,7 @@ func GetBackupKeys(
Sessions: roomData, Sessions: roomData,
}, },
} }
}
} else { } else {
// response is the same as the upload request // response is the same as the upload request
var resp keyBackupSessionRequest var resp keyBackupSessionRequest
@ -298,6 +274,6 @@ func GetBackupKeys(
} }
return util.JSONResponse{ return util.JSONResponse{
Code: 404, Code: 404,
JSON: jsonerror.NotFound("keys not found"), JSON: spec.NotFound("keys not found"),
} }
} }

View file

@ -20,10 +20,9 @@ import (
"github.com/matrix-org/dendrite/clientapi/auth" "github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/keyserver/api"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -33,9 +32,9 @@ type crossSigningRequest struct {
} }
func UploadCrossSigningDeviceKeys( func UploadCrossSigningDeviceKeys(
req *http.Request, userInteractiveAuth *auth.UserInteractive, req *http.Request,
keyserverAPI api.ClientKeyAPI, device *userapi.Device, keyserverAPI api.ClientKeyAPI, device *api.Device,
accountAPI userapi.ClientUserAPI, cfg *config.ClientAPI, accountAPI api.ClientUserAPI, cfg *config.ClientAPI,
) util.JSONResponse { ) util.JSONResponse {
uploadReq := &crossSigningRequest{} uploadReq := &crossSigningRequest{}
uploadRes := &api.PerformUploadDeviceKeysResponse{} uploadRes := &api.PerformUploadDeviceKeysResponse{}
@ -63,7 +62,7 @@ func UploadCrossSigningDeviceKeys(
} }
} }
typePassword := auth.LoginTypePassword{ typePassword := auth.LoginTypePassword{
GetAccountByPassword: accountAPI.QueryAccountByPassword, UserAPI: accountAPI,
Config: cfg, Config: cfg,
} }
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil { if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
@ -72,31 +71,29 @@ func UploadCrossSigningDeviceKeys(
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword) sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
uploadReq.UserID = device.UserID uploadReq.UserID = device.UserID
if err := keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes); err != nil { keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes)
return jsonerror.InternalAPIError(req.Context(), err)
}
if err := uploadRes.Error; err != nil { if err := uploadRes.Error; err != nil {
switch { switch {
case err.IsInvalidSignature: case err.IsInvalidSignature:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidSignature(err.Error()), JSON: spec.InvalidSignature(err.Error()),
} }
case err.IsMissingParam: case err.IsMissingParam:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.MissingParam(err.Error()), JSON: spec.MissingParam(err.Error()),
} }
case err.IsInvalidParam: case err.IsInvalidParam:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidParam(err.Error()), JSON: spec.InvalidParam(err.Error()),
} }
default: default:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown(err.Error()), JSON: spec.Unknown(err.Error()),
} }
} }
} }
@ -107,7 +104,7 @@ func UploadCrossSigningDeviceKeys(
} }
} }
func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.ClientKeyAPI, device *userapi.Device) util.JSONResponse { func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.ClientKeyAPI, device *api.Device) util.JSONResponse {
uploadReq := &api.PerformUploadDeviceSignaturesRequest{} uploadReq := &api.PerformUploadDeviceSignaturesRequest{}
uploadRes := &api.PerformUploadDeviceSignaturesResponse{} uploadRes := &api.PerformUploadDeviceSignaturesResponse{}
@ -116,31 +113,29 @@ func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.Clie
} }
uploadReq.UserID = device.UserID uploadReq.UserID = device.UserID
if err := keyserverAPI.PerformUploadDeviceSignatures(req.Context(), uploadReq, uploadRes); err != nil { keyserverAPI.PerformUploadDeviceSignatures(req.Context(), uploadReq, uploadRes)
return jsonerror.InternalAPIError(req.Context(), err)
}
if err := uploadRes.Error; err != nil { if err := uploadRes.Error; err != nil {
switch { switch {
case err.IsInvalidSignature: case err.IsInvalidSignature:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidSignature(err.Error()), JSON: spec.InvalidSignature(err.Error()),
} }
case err.IsMissingParam: case err.IsMissingParam:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.MissingParam(err.Error()), JSON: spec.MissingParam(err.Error()),
} }
case err.IsInvalidParam: case err.IsInvalidParam:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidParam(err.Error()), JSON: spec.InvalidParam(err.Error()),
} }
default: default:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown(err.Error()), JSON: spec.Unknown(err.Error()),
} }
} }
} }

View file

@ -22,9 +22,8 @@ import (
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/dendrite/keyserver/api" "github.com/matrix-org/gomatrixserverlib/spec"
userapi "github.com/matrix-org/dendrite/userapi/api"
) )
type uploadKeysRequest struct { type uploadKeysRequest struct {
@ -32,7 +31,7 @@ type uploadKeysRequest struct {
OneTimeKeys map[string]json.RawMessage `json:"one_time_keys"` OneTimeKeys map[string]json.RawMessage `json:"one_time_keys"`
} }
func UploadKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *userapi.Device) util.JSONResponse { func UploadKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *api.Device) util.JSONResponse {
var r uploadKeysRequest var r uploadKeysRequest
resErr := httputil.UnmarshalJSONRequest(req, &r) resErr := httputil.UnmarshalJSONRequest(req, &r)
if resErr != nil { if resErr != nil {
@ -68,7 +67,10 @@ func UploadKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *userapi.Devi
} }
if uploadRes.Error != nil { if uploadRes.Error != nil {
util.GetLogger(req.Context()).WithError(uploadRes.Error).Error("Failed to PerformUploadKeys") util.GetLogger(req.Context()).WithError(uploadRes.Error).Error("Failed to PerformUploadKeys")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
if len(uploadRes.KeyErrors) > 0 { if len(uploadRes.KeyErrors) > 0 {
util.GetLogger(req.Context()).WithField("key_errors", uploadRes.KeyErrors).Error("Failed to upload one or more keys") util.GetLogger(req.Context()).WithField("key_errors", uploadRes.KeyErrors).Error("Failed to upload one or more keys")
@ -91,7 +93,6 @@ func UploadKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *userapi.Devi
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"`
} }
@ -99,24 +100,25 @@ func (r *queryKeysRequest) GetTimeout() time.Duration {
if r.Timeout == 0 { if r.Timeout == 0 {
return 10 * time.Second return 10 * time.Second
} }
return time.Duration(r.Timeout) * time.Millisecond timeout := time.Duration(r.Timeout) * time.Millisecond
if timeout > time.Second*20 {
timeout = time.Second * 20
}
return timeout
} }
func QueryKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *userapi.Device) util.JSONResponse { func QueryKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *api.Device) util.JSONResponse {
var r queryKeysRequest var r queryKeysRequest
resErr := httputil.UnmarshalJSONRequest(req, &r) resErr := httputil.UnmarshalJSONRequest(req, &r)
if resErr != nil { if resErr != nil {
return *resErr return *resErr
} }
queryRes := api.QueryKeysResponse{} queryRes := api.QueryKeysResponse{}
if err := keyAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{ keyAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
UserID: device.UserID, UserID: device.UserID,
UserToDevices: r.DeviceKeys, UserToDevices: r.DeviceKeys,
Timeout: r.GetTimeout(), Timeout: r.GetTimeout(),
// TODO: Token? }, &queryRes)
}, &queryRes); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,
JSON: map[string]interface{}{ JSON: map[string]interface{}{
@ -149,15 +151,16 @@ func ClaimKeys(req *http.Request, keyAPI api.ClientKeyAPI) util.JSONResponse {
return *resErr return *resErr
} }
claimRes := api.PerformClaimKeysResponse{} claimRes := api.PerformClaimKeysResponse{}
if err := keyAPI.PerformClaimKeys(req.Context(), &api.PerformClaimKeysRequest{ keyAPI.PerformClaimKeys(req.Context(), &api.PerformClaimKeysRequest{
OneTimeKeys: r.OneTimeKeys, OneTimeKeys: r.OneTimeKeys,
Timeout: r.GetTimeout(), Timeout: r.GetTimeout(),
}, &claimRes); err != nil { }, &claimRes)
return jsonerror.InternalAPIError(req.Context(), err)
}
if claimRes.Error != nil { if claimRes.Error != nil {
util.GetLogger(req.Context()).WithError(claimRes.Error).Error("failed to PerformClaimKeys") util.GetLogger(req.Context()).WithError(claimRes.Error).Error("failed to PerformClaimKeys")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
Code: 200, Code: 200,

View file

@ -17,9 +17,9 @@ package routing
import ( import (
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -29,10 +29,18 @@ func LeaveRoomByID(
rsAPI roomserverAPI.ClientRoomserverAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
roomID string, roomID string,
) util.JSONResponse { ) util.JSONResponse {
userID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown("device userID is invalid"),
}
}
// Prepare to ask the roomserver to perform the room join. // Prepare to ask the roomserver to perform the room join.
leaveReq := roomserverAPI.PerformLeaveRequest{ leaveReq := roomserverAPI.PerformLeaveRequest{
RoomID: roomID, RoomID: roomID,
UserID: device.UserID, Leaver: *userID,
} }
leaveRes := roomserverAPI.PerformLeaveResponse{} leaveRes := roomserverAPI.PerformLeaveResponse{}
@ -41,12 +49,12 @@ func LeaveRoomByID(
if leaveRes.Code != 0 { if leaveRes.Code != 0 {
return util.JSONResponse{ return util.JSONResponse{
Code: leaveRes.Code, Code: leaveRes.Code,
JSON: jsonerror.LeaveServerNoticeError(), JSON: spec.LeaveServerNoticeError(),
} }
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown(err.Error()), JSON: spec.Unknown(err.Error()),
} }
} }

View file

@ -19,18 +19,17 @@ 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/jsonerror" "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"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
type loginResponse struct { type loginResponse struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
HomeServer gomatrixserverlib.ServerName `json:"home_server"`
DeviceID string `json:"device_id"` DeviceID string `json:"device_id"`
} }
@ -42,56 +41,59 @@ 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
} }
// make a device/access token // make a device/access token
authErr2 := completeAuth(req.Context(), cfg.Matrix.ServerName, userAPI, login, req.RemoteAddr, req.UserAgent()) authErr2 := completeAuth(req.Context(), cfg.Matrix, userAPI, login, req.RemoteAddr, req.UserAgent())
cleanup(req.Context(), &authErr2) cleanup(req.Context(), &authErr2)
return authErr2 return authErr2
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusMethodNotAllowed, Code: http.StatusMethodNotAllowed,
JSON: jsonerror.NotFound("Bad method"), JSON: spec.NotFound("Bad method"),
} }
} }
func completeAuth( func completeAuth(
ctx context.Context, serverName gomatrixserverlib.ServerName, userAPI userapi.ClientUserAPI, login *auth.Login, ctx context.Context, cfg *config.Global, userAPI userapi.ClientUserAPI, login *auth.Login,
ipAddr, userAgent string, ipAddr, userAgent string,
) util.JSONResponse { ) util.JSONResponse {
token, err := auth.GenerateAccessToken() token, err := auth.GenerateAccessToken()
if err != nil { if err != nil {
util.GetLogger(ctx).WithError(err).Error("auth.GenerateAccessToken failed") util.GetLogger(ctx).WithError(err).Error("auth.GenerateAccessToken failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
localpart, err := userutil.ParseUsernameParam(login.Username(), &serverName) localpart, serverName, err := userutil.ParseUsernameParam(login.Username(), cfg)
if err != nil { if err != nil {
util.GetLogger(ctx).WithError(err).Error("auth.ParseUsernameParam failed") util.GetLogger(ctx).WithError(err).Error("auth.ParseUsernameParam failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
var performRes userapi.PerformDeviceCreationResponse var performRes userapi.PerformDeviceCreationResponse
@ -100,13 +102,14 @@ func completeAuth(
DeviceID: login.DeviceID, DeviceID: login.DeviceID,
AccessToken: token, AccessToken: token,
Localpart: localpart, Localpart: localpart,
ServerName: serverName,
IPAddr: ipAddr, IPAddr: ipAddr,
UserAgent: userAgent, UserAgent: userAgent,
}, &performRes) }, &performRes)
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.Unknown("failed to create device: " + err.Error()), JSON: spec.Unknown("failed to create device: " + err.Error()),
} }
} }
@ -115,7 +118,6 @@ func completeAuth(
JSON: loginResponse{ JSON: loginResponse{
UserID: performRes.Device.UserID, UserID: performRes.Device.UserID,
AccessToken: performRes.Device.AccessToken, AccessToken: performRes.Device.AccessToken,
HomeServer: serverName,
DeviceID: performRes.Device.ID, DeviceID: performRes.Device.ID,
}, },
} }

View file

@ -0,0 +1,198 @@
package routing
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"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/roomserver"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/util"
"github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/test/testrig"
"github.com/matrix-org/dendrite/userapi"
uapi "github.com/matrix-org/dendrite/userapi/api"
)
func TestLogin(t *testing.T) {
aliceAdmin := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin))
bobUser := &test.User{ID: "@bob:test", AccountType: uapi.AccountTypeUser}
charlie := &test.User{ID: "@Charlie:test", AccountType: uapi.AccountTypeUser}
vhUser := &test.User{ID: "@vhuser:vh1"}
ctx := context.Background()
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
defer close()
cfg.ClientAPI.RateLimiting.Enabled = false
natsInstance := jetstream.NATSInstance{}
// add a vhost
cfg.Global.VirtualHosts = append(cfg.Global.VirtualHosts, &config.VirtualHost{
SigningIdentity: fclient.SigningIdentity{ServerName: "vh1"},
})
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
routers := httputil.NewRouters()
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
rsAPI.SetFederationAPI(nil, nil)
// Needed for /login
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
// We mostly need the userAPI for this test, so nil for other APIs/caches etc.
Setup(routers, cfg, nil, nil, userAPI, nil, nil, nil, nil, nil, nil, nil, caching.DisableMetrics)
// Create password
password := util.RandomString(8)
// create the users
for _, u := range []*test.User{aliceAdmin, bobUser, vhUser, charlie} {
localpart, serverName, _ := gomatrixserverlib.SplitID('@', u.ID)
userRes := &uapi.PerformAccountCreationResponse{}
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)
}
if !userRes.AccountCreated {
t.Fatalf("account not created")
}
}
testCases := []struct {
name string
userID string
wantOK bool
}{
{
name: "aliceAdmin can login",
userID: aliceAdmin.ID,
wantOK: true,
},
{
name: "bobUser can login",
userID: bobUser.ID,
wantOK: true,
},
{
name: "vhuser can login",
userID: vhUser.ID,
wantOK: true,
},
{
name: "bob with uppercase can login",
userID: "@Bob:test",
wantOK: true,
},
{
name: "Charlie can login (existing uppercase)",
userID: charlie.ID,
wantOK: true,
},
{
name: "Charlie can not login with lowercase userID",
userID: strings.ToLower(charlie.ID),
wantOK: false,
},
}
ctx := context.Background()
// Inject a dummy application service, so we have a "m.login.application_service"
// in the login flows
as := &config.ApplicationService{}
cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{*as}
t.Run("Supported log-in flows are returned", func(t *testing.T) {
req := test.NewRequest(t, http.MethodGet, "/_matrix/client/v3/login")
rec := httptest.NewRecorder()
routers.Client.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("failed to get log-in flows: %s", rec.Body.String())
}
t.Logf("response: %s", rec.Body.String())
resp := flows{}
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
t.Fatal(err)
}
appServiceFound := false
passwordFound := false
for _, flow := range resp.Flows {
if flow.Type == "m.login.password" {
passwordFound = true
} else if flow.Type == "m.login.application_service" {
appServiceFound = true
} else {
t.Fatalf("got unknown login flow: %s", flow.Type)
}
}
if !appServiceFound {
t.Fatal("m.login.application_service missing from login flows")
}
if !passwordFound {
t.Fatal("m.login.password missing from login flows")
}
})
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := test.NewRequest(t, http.MethodPost, "/_matrix/client/v3/login", test.WithJSONBody(t, map[string]interface{}{
"type": authtypes.LoginTypePassword,
"identifier": map[string]interface{}{
"type": "m.id.user",
"user": tc.userID,
},
"password": password,
}))
rec := httptest.NewRecorder()
routers.Client.ServeHTTP(rec, req)
if tc.wantOK && rec.Code != http.StatusOK {
t.Fatalf("failed to login: %s", rec.Body.String())
}
t.Logf("Response: %s", rec.Body.String())
// get the response
resp := loginResponse{}
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
t.Fatal(err)
}
// everything OK
if !tc.wantOK && resp.AccessToken == "" {
return
}
if tc.wantOK && resp.AccessToken == "" {
t.Fatalf("expected accessToken after successful login but got none: %+v", resp)
}
devicesResp := &uapi.QueryDevicesResponse{}
if err := userAPI.QueryDevices(ctx, &uapi.QueryDevicesRequest{UserID: resp.UserID}, devicesResp); err != nil {
t.Fatal(err)
}
for _, dev := range devicesResp.Devices {
// We expect the userID on the device to be the same as resp.UserID
if dev.UserID != resp.UserID {
t.Fatalf("unexpected userID on device: %s", dev.UserID)
}
}
})
}
})
}

View file

@ -17,8 +17,8 @@ package routing
import ( import (
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -33,7 +33,10 @@ func Logout(
}, &performRes) }, &performRes)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("PerformDeviceDeletion failed") util.GetLogger(req.Context()).WithError(err).Error("PerformDeviceDeletion failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
@ -53,7 +56,10 @@ func LogoutAll(
}, &performRes) }, &performRes)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("PerformDeviceDeletion failed") util.GetLogger(req.Context()).WithError(err).Error("PerformDeviceDeletion failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{

View file

@ -16,106 +16,120 @@ package routing
import ( import (
"context" "context"
"errors" "crypto/ed25519"
"fmt" "fmt"
"net/http" "net/http"
"time" "time"
"github.com/getsentry/sentry-go"
appserviceAPI "github.com/matrix-org/dendrite/appservice/api" appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/threepid" "github.com/matrix-org/dendrite/clientapi/threepid"
"github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/dendrite/internal/eventutil"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/roomserver/types"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
var errMissingUserID = errors.New("'user_id' must be supplied")
func SendBan( func SendBan(
req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device,
roomID string, cfg *config.ClientAPI, roomID string, cfg *config.ClientAPI,
rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI,
) util.JSONResponse { ) util.JSONResponse {
body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) body, evTime, reqErr := extractRequestData(req)
if reqErr != nil { if reqErr != nil {
return *reqErr return *reqErr
} }
errRes := checkMemberInRoom(req.Context(), rsAPI, device.UserID, roomID) if body.UserID == "" {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("missing user_id"),
}
}
deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to ban this user, bad userID"),
}
}
validRoomID, err := spec.NewRoomID(roomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("RoomID is invalid"),
}
}
senderID, err := rsAPI.QuerySenderIDForUser(req.Context(), *validRoomID, *deviceUserID)
if err != nil || senderID == nil {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to ban this user, unknown senderID"),
}
}
errRes := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID)
if errRes != nil { if errRes != nil {
return *errRes return *errRes
} }
plEvent := roomserverAPI.GetStateEvent(req.Context(), rsAPI, roomID, gomatrixserverlib.StateKeyTuple{ pl, errRes := getPowerlevels(req, rsAPI, roomID)
EventType: gomatrixserverlib.MRoomPowerLevels, if errRes != nil {
StateKey: "", return *errRes
})
if plEvent == nil {
return util.JSONResponse{
Code: 403,
JSON: jsonerror.Forbidden("You don't have permission to ban this user, no power_levels event in this room."),
} }
} allowedToBan := pl.UserLevel(*senderID) >= pl.Ban
pl, err := plEvent.PowerLevels()
if err != nil {
return util.JSONResponse{
Code: 403,
JSON: jsonerror.Forbidden("You don't have permission to ban this user, the power_levels event for this room is malformed so auth checks cannot be performed."),
}
}
allowedToBan := pl.UserLevel(device.UserID) >= pl.Ban
if !allowedToBan { if !allowedToBan {
return util.JSONResponse{ return util.JSONResponse{
Code: 403, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You don't have permission to ban this user, power level too low."), JSON: spec.Forbidden("You don't have permission to ban this user, power level too low."),
} }
} }
return sendMembership(req.Context(), profileAPI, device, roomID, "ban", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI) return sendMembership(req.Context(), profileAPI, device, roomID, spec.Ban, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI)
} }
func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, device *userapi.Device, func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, device *userapi.Device,
roomID, membership, reason string, cfg *config.ClientAPI, targetUserID string, evTime time.Time, roomID, membership, reason string, cfg *config.ClientAPI, targetUserID string, evTime time.Time,
roomVer gomatrixserverlib.RoomVersion,
rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI) util.JSONResponse { rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI) util.JSONResponse {
event, err := buildMembershipEvent( event, err := buildMembershipEvent(
ctx, targetUserID, reason, profileAPI, device, membership, ctx, targetUserID, reason, profileAPI, device, membership,
roomID, false, cfg, evTime, rsAPI, asAPI, roomID, false, cfg, evTime, rsAPI, asAPI,
) )
if err == errMissingUserID { if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON(err.Error()),
}
} else if err == eventutil.ErrRoomNoExists {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound(err.Error()),
}
} else if err != nil {
util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed") util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
serverName := device.UserDomain()
if err = roomserverAPI.SendEvents( if err = roomserverAPI.SendEvents(
ctx, rsAPI, ctx, rsAPI,
roomserverAPI.KindNew, roomserverAPI.KindNew,
[]*gomatrixserverlib.HeaderedEvent{event.Event.Headered(roomVer)}, []*types.HeaderedEvent{event},
cfg.Matrix.ServerName, device.UserDomain(),
cfg.Matrix.ServerName, serverName,
serverName,
nil, nil,
false, false,
); err != nil { ); err != nil {
util.GetLogger(ctx).WithError(err).Error("SendEvents failed") util.GetLogger(ctx).WithError(err).Error("SendEvents failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
@ -129,39 +143,81 @@ func SendKick(
roomID string, cfg *config.ClientAPI, roomID string, cfg *config.ClientAPI,
rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI,
) util.JSONResponse { ) util.JSONResponse {
body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) body, evTime, reqErr := extractRequestData(req)
if reqErr != nil { if reqErr != nil {
return *reqErr return *reqErr
} }
if body.UserID == "" { if body.UserID == "" {
return util.JSONResponse{ return util.JSONResponse{
Code: 400, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("missing user_id"), JSON: spec.BadJSON("missing user_id"),
} }
} }
errRes := checkMemberInRoom(req.Context(), rsAPI, device.UserID, roomID) deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to kick this user, bad userID"),
}
}
validRoomID, err := spec.NewRoomID(roomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("RoomID is invalid"),
}
}
senderID, err := rsAPI.QuerySenderIDForUser(req.Context(), *validRoomID, *deviceUserID)
if err != nil || senderID == nil {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to kick this user, unknown senderID"),
}
}
errRes := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID)
if errRes != nil { if errRes != nil {
return *errRes return *errRes
} }
bodyUserID, err := spec.NewUserID(body.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
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,
UserID: body.UserID, UserID: *bodyUserID,
}, &queryRes) }, &queryRes)
if err != nil { if err != nil {
return util.ErrorResponse(err) return util.ErrorResponse(err)
} }
// kick is only valid if the user is not currently banned or left (that is, they are joined or invited) // kick is only valid if the user is not currently banned or left (that is, they are joined or invited)
if queryRes.Membership != "join" && queryRes.Membership != "invite" { if queryRes.Membership != spec.Join && queryRes.Membership != spec.Invite {
return util.JSONResponse{ return util.JSONResponse{
Code: 403, Code: http.StatusForbidden,
JSON: jsonerror.Unknown("cannot /kick banned or left users"), JSON: spec.Unknown("cannot /kick banned or left users"),
} }
} }
// TODO: should we be using SendLeave instead? // TODO: should we be using SendLeave instead?
return sendMembership(req.Context(), profileAPI, device, roomID, "leave", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI) return sendMembership(req.Context(), profileAPI, device, roomID, spec.Leave, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI)
} }
func SendUnban( func SendUnban(
@ -169,40 +225,55 @@ func SendUnban(
roomID string, cfg *config.ClientAPI, roomID string, cfg *config.ClientAPI,
rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI,
) util.JSONResponse { ) util.JSONResponse {
body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) body, evTime, reqErr := extractRequestData(req)
if reqErr != nil { if reqErr != nil {
return *reqErr return *reqErr
} }
if body.UserID == "" { if body.UserID == "" {
return util.JSONResponse{ return util.JSONResponse{
Code: 400, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("missing user_id"), JSON: spec.BadJSON("missing user_id"),
} }
} }
deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to kick this user, bad userID"),
}
}
errRes := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID)
if errRes != nil {
return *errRes
}
bodyUserID, err := spec.NewUserID(body.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("body userID is invalid"),
}
}
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,
UserID: body.UserID, UserID: *bodyUserID,
}, &queryRes) }, &queryRes)
if err != nil { if err != nil {
return util.ErrorResponse(err) return util.ErrorResponse(err)
} }
if !queryRes.RoomExists {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("room does not exist"),
}
}
// unban is only valid if the user is currently banned // unban is only valid if the user is currently banned
if queryRes.Membership != "ban" { if queryRes.Membership != spec.Ban {
return util.JSONResponse{ return util.JSONResponse{
Code: 400, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown("can only /unban users that are banned"), JSON: spec.Unknown("can only /unban users that are banned"),
} }
} }
// TODO: should we be using SendLeave instead? // TODO: should we be using SendLeave instead?
return sendMembership(req.Context(), profileAPI, device, roomID, "leave", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI) return sendMembership(req.Context(), profileAPI, device, roomID, spec.Leave, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI)
} }
func SendInvite( func SendInvite(
@ -210,7 +281,7 @@ func SendInvite(
roomID string, cfg *config.ClientAPI, roomID string, cfg *config.ClientAPI,
rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI,
) util.JSONResponse { ) util.JSONResponse {
body, evTime, _, reqErr := extractRequestData(req, roomID, rsAPI) body, evTime, reqErr := extractRequestData(req)
if reqErr != nil { if reqErr != nil {
return *reqErr return *reqErr
} }
@ -232,55 +303,103 @@ func SendInvite(
} }
} }
if body.UserID == "" {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON("missing user_id"),
}
}
deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to kick this user, bad userID"),
}
}
errRes := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID)
if errRes != nil {
return *errRes
}
// 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) {
event, err := buildMembershipEvent( validRoomID, err := spec.NewRoomID(roomID)
ctx, userID, reason, profileAPI, device, "invite", if err != nil {
roomID, false, cfg, evTime, rsAPI, asAPI,
)
if err == errMissingUserID {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON(err.Error()), JSON: spec.InvalidParam("RoomID is invalid"),
}, err }, err
} else if err == eventutil.ErrRoomNoExists {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound(err.Error()),
}, err
} else if err != nil {
util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed")
return jsonerror.InternalServerError(), err
} }
inviter, err := spec.NewUserID(device.UserID, true)
var inviteRes api.PerformInviteResponse if err != nil {
if err := rsAPI.PerformInvite(ctx, &api.PerformInviteRequest{
Event: event,
InviteRoomState: nil, // ask the roomserver to draw up invite room state for us
RoomVersion: event.RoomVersion,
SendAsServer: string(cfg.Matrix.ServerName),
}, &inviteRes); err != nil {
util.GetLogger(ctx).WithError(err).Error("PerformInvite failed")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(), JSON: spec.InternalServerError{},
}, err }, err
} }
if inviteRes.Error != nil { invitee, err := spec.NewUserID(userID, true)
return inviteRes.Error.JSONResponse(), inviteRes.Error if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidParam("UserID is invalid"),
}, err
}
identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain())
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}, err
}
err = rsAPI.PerformInvite(ctx, &api.PerformInviteRequest{
InviteInput: roomserverAPI.InviteInput{
RoomID: *validRoomID,
Inviter: *inviter,
Invitee: *invitee,
Reason: reason,
IsDirect: false,
KeyID: identity.KeyID,
PrivateKey: identity.PrivateKey,
EventTime: evTime,
},
InviteRoomState: nil, // ask the roomserver to draw up invite room state for us
SendAsServer: string(device.UserDomain()),
})
switch e := err.(type) {
case roomserverAPI.ErrInvalidID:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown(e.Error()),
}, e
case roomserverAPI.ErrNotAllowed:
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden(e.Error()),
}, e
case nil:
default:
util.GetLogger(ctx).WithError(err).Error("PerformInvite failed")
sentry.CaptureException(err)
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}, err
} }
return util.JSONResponse{ return util.JSONResponse{
@ -289,6 +408,42 @@ func sendInvite(
}, nil }, nil
} }
func buildMembershipEventDirect(
ctx context.Context,
targetSenderID spec.SenderID, reason string, userDisplayName, userAvatarURL string,
sender spec.SenderID, senderDomain spec.ServerName,
membership, roomID string, isDirect bool,
keyID gomatrixserverlib.KeyID, privateKey ed25519.PrivateKey, evTime time.Time,
rsAPI roomserverAPI.ClientRoomserverAPI,
) (*types.HeaderedEvent, error) {
targetSenderString := string(targetSenderID)
proto := gomatrixserverlib.ProtoEvent{
SenderID: string(sender),
RoomID: roomID,
Type: "m.room.member",
StateKey: &targetSenderString,
}
content := gomatrixserverlib.MemberContent{
Membership: membership,
DisplayName: userDisplayName,
AvatarURL: userAvatarURL,
Reason: reason,
IsDirect: isDirect,
}
if err := proto.SetContent(content); err != nil {
return nil, err
}
identity := &fclient.SigningIdentity{
ServerName: senderDomain,
KeyID: keyID,
PrivateKey: privateKey,
}
return eventutil.QueryAndBuildEvent(ctx, &proto, identity, evTime, rsAPI, nil)
}
func buildMembershipEvent( func buildMembershipEvent(
ctx context.Context, ctx context.Context,
targetUserID, reason string, profileAPI userapi.ClientUserAPI, targetUserID, reason string, profileAPI userapi.ClientUserAPI,
@ -296,32 +451,45 @@ func buildMembershipEvent(
membership, roomID string, isDirect bool, membership, roomID string, isDirect bool,
cfg *config.ClientAPI, evTime time.Time, cfg *config.ClientAPI, evTime time.Time,
rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI,
) (*gomatrixserverlib.HeaderedEvent, error) { ) (*types.HeaderedEvent, error) {
profile, err := loadProfile(ctx, targetUserID, cfg, profileAPI, asAPI) profile, err := loadProfile(ctx, targetUserID, cfg, profileAPI, asAPI)
if err != nil { if err != nil {
return nil, err return nil, err
} }
builder := gomatrixserverlib.EventBuilder{ userID, err := spec.NewUserID(device.UserID, true)
Sender: device.UserID, if err != nil {
RoomID: roomID, return nil, err
Type: "m.room.member", }
StateKey: &targetUserID, validRoomID, err := spec.NewRoomID(roomID)
if err != nil {
return nil, err
}
senderID, err := rsAPI.QuerySenderIDForUser(ctx, *validRoomID, *userID)
if err != nil {
return nil, err
} else if senderID == nil {
return nil, fmt.Errorf("no sender ID for %s in %s", *userID, *validRoomID)
} }
content := gomatrixserverlib.MemberContent{ targetID, err := spec.NewUserID(targetUserID, true)
Membership: membership, if err != nil {
DisplayName: profile.DisplayName, return nil, err
AvatarURL: profile.AvatarURL, }
Reason: reason, targetSenderID, err := rsAPI.QuerySenderIDForUser(ctx, *validRoomID, *targetID)
IsDirect: isDirect, if err != nil {
return nil, err
} else if targetSenderID == nil {
return nil, fmt.Errorf("no sender ID for %s in %s", *targetID, *validRoomID)
} }
if err = builder.SetContent(content); err != nil { identity, err := rsAPI.SigningIdentityFor(ctx, *validRoomID, *userID)
if err != nil {
return nil, err return nil, err
} }
return eventutil.QueryAndBuildEvent(ctx, &builder, cfg.Matrix, evTime, rsAPI, nil) return buildMembershipEventDirect(ctx, *targetSenderID, reason, profile.DisplayName, profile.AvatarURL,
*senderID, device.UserDomain(), membership, roomID, isDirect, identity.KeyID, identity.PrivateKey, evTime, rsAPI)
} }
// loadProfile lookups the profile of a given user from the database and returns // loadProfile lookups the profile of a given user from the database and returns
@ -341,7 +509,7 @@ func loadProfile(
} }
var profile *authtypes.Profile var profile *authtypes.Profile
if serverName == cfg.Matrix.ServerName { if cfg.Matrix.IsLocalServerName(serverName) {
profile, err = appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, profileAPI) profile, err = appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, profileAPI)
} else { } else {
profile = &authtypes.Profile{} profile = &authtypes.Profile{}
@ -350,19 +518,7 @@ func loadProfile(
return profile, err return profile, err
} }
func extractRequestData(req *http.Request, roomID string, rsAPI roomserverAPI.ClientRoomserverAPI) ( func extractRequestData(req *http.Request) (body *threepid.MembershipRequest, evTime time.Time, resErr *util.JSONResponse) {
body *threepid.MembershipRequest, evTime time.Time, roomVer gomatrixserverlib.RoomVersion, resErr *util.JSONResponse,
) {
verReq := roomserverAPI.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := roomserverAPI.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(req.Context(), &verReq, &verRes); err != nil {
resErr = &util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.UnsupportedRoomVersion(err.Error()),
}
return
}
roomVer = verRes.RoomVersion
if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil {
resErr = reqErr resErr = reqErr
@ -373,7 +529,7 @@ func extractRequestData(req *http.Request, roomID string, rsAPI roomserverAPI.Cl
if err != nil { if err != nil {
resErr = &util.JSONResponse{ resErr = &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidArgumentValue(err.Error()), JSON: spec.InvalidParam(err.Error()),
} }
return return
} }
@ -395,67 +551,59 @@ func checkAndProcessThreepid(
req.Context(), device, body, cfg, rsAPI, profileAPI, req.Context(), device, body, cfg, rsAPI, profileAPI,
roomID, evTime, roomID, evTime,
) )
if err == threepid.ErrMissingParameter { switch e := err.(type) {
case nil:
case threepid.ErrMissingParameter:
util.GetLogger(req.Context()).WithError(err).Error("threepid.CheckAndProcessInvite failed")
return inviteStored, &util.JSONResponse{ return inviteStored, &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON(err.Error()), JSON: spec.BadJSON(err.Error()),
} }
} else if err == threepid.ErrNotTrusted { case threepid.ErrNotTrusted:
util.GetLogger(req.Context()).WithError(err).Error("threepid.CheckAndProcessInvite failed")
return inviteStored, &util.JSONResponse{ return inviteStored, &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.NotTrusted(body.IDServer), JSON: spec.NotTrusted(body.IDServer),
} }
} else if err == eventutil.ErrRoomNoExists { case eventutil.ErrRoomNoExists:
util.GetLogger(req.Context()).WithError(err).Error("threepid.CheckAndProcessInvite failed")
return inviteStored, &util.JSONResponse{ return inviteStored, &util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
JSON: jsonerror.NotFound(err.Error()), JSON: spec.NotFound(err.Error()),
} }
} else if e, ok := err.(gomatrixserverlib.BadJSONError); ok { case gomatrixserverlib.BadJSONError:
util.GetLogger(req.Context()).WithError(err).Error("threepid.CheckAndProcessInvite failed")
return inviteStored, &util.JSONResponse{ return inviteStored, &util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON(e.Error()), JSON: spec.BadJSON(e.Error()),
} }
} default:
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("threepid.CheckAndProcessInvite failed") util.GetLogger(req.Context()).WithError(err).Error("threepid.CheckAndProcessInvite failed")
er := jsonerror.InternalServerError() return inviteStored, &util.JSONResponse{
return inviteStored, &er Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return return
} }
func checkMemberInRoom(ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, userID, roomID string) *util.JSONResponse { func checkMemberInRoom(ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, userID spec.UserID, roomID string) *util.JSONResponse {
tuple := gomatrixserverlib.StateKeyTuple{ var membershipRes roomserverAPI.QueryMembershipForUserResponse
EventType: gomatrixserverlib.MRoomMember, err := rsAPI.QueryMembershipForUser(ctx, &roomserverAPI.QueryMembershipForUserRequest{
StateKey: userID,
}
var membershipRes roomserverAPI.QueryCurrentStateResponse
err := rsAPI.QueryCurrentState(ctx, &roomserverAPI.QueryCurrentStateRequest{
RoomID: roomID, RoomID: roomID,
StateTuples: []gomatrixserverlib.StateKeyTuple{tuple}, UserID: userID,
}, &membershipRes) }, &membershipRes)
if err != nil { if err != nil {
util.GetLogger(ctx).WithError(err).Error("QueryCurrentState: could not query membership for user") util.GetLogger(ctx).WithError(err).Error("QueryMembershipForUser: could not query membership for user")
e := jsonerror.InternalServerError() return &util.JSONResponse{
return &e Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
} }
ev := membershipRes.StateEvents[tuple] }
if ev == nil { if !membershipRes.IsInRoom {
return &util.JSONResponse{ return &util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("user does not belong to room"), JSON: spec.Forbidden("user does not belong to room"),
}
}
membership, err := ev.Membership()
if err != nil {
util.GetLogger(ctx).WithError(err).Error("Member event isn't valid")
e := jsonerror.InternalServerError()
return &e
}
if membership != gomatrixserverlib.Join {
return &util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("user does not belong to room"),
} }
} }
return nil return nil
@ -467,26 +615,38 @@ func SendForget(
) util.JSONResponse { ) util.JSONResponse {
ctx := req.Context() ctx := req.Context()
logger := util.GetLogger(ctx).WithField("roomID", roomID).WithField("userID", device.UserID) logger := util.GetLogger(ctx).WithField("roomID", roomID).WithField("userID", device.UserID)
deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to kick this user, bad userID"),
}
}
var membershipRes roomserverAPI.QueryMembershipForUserResponse var membershipRes roomserverAPI.QueryMembershipForUserResponse
membershipReq := roomserverAPI.QueryMembershipForUserRequest{ membershipReq := roomserverAPI.QueryMembershipForUserRequest{
RoomID: roomID, RoomID: roomID,
UserID: device.UserID, UserID: *deviceUserID,
} }
err := rsAPI.QueryMembershipForUser(ctx, &membershipReq, &membershipRes) err = rsAPI.QueryMembershipForUser(ctx, &membershipReq, &membershipRes)
if err != nil { if err != nil {
logger.WithError(err).Error("QueryMembershipForUser: could not query membership for user") logger.WithError(err).Error("QueryMembershipForUser: could not query membership for user")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
if !membershipRes.RoomExists { if !membershipRes.RoomExists {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("room does not exist"), JSON: spec.Forbidden("room does not exist"),
} }
} }
if membershipRes.IsInRoom { if membershipRes.IsInRoom {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown(fmt.Sprintf("User %s is in room %s", device.UserID, roomID)), JSON: spec.Unknown(fmt.Sprintf("User %s is in room %s", device.UserID, roomID)),
} }
} }
@ -497,10 +657,34 @@ func SendForget(
response := roomserverAPI.PerformForgetResponse{} response := roomserverAPI.PerformForgetResponse{}
if err := rsAPI.PerformForget(ctx, &request, &response); err != nil { if err := rsAPI.PerformForget(ctx, &request, &response); err != nil {
logger.WithError(err).Error("PerformForget: unable to forget room") logger.WithError(err).Error("PerformForget: unable to forget room")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
JSON: struct{}{}, JSON: struct{}{},
} }
} }
func getPowerlevels(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, roomID string) (*gomatrixserverlib.PowerLevelContent, *util.JSONResponse) {
plEvent := roomserverAPI.GetStateEvent(req.Context(), rsAPI, roomID, gomatrixserverlib.StateKeyTuple{
EventType: spec.MRoomPowerLevels,
StateKey: "",
})
if plEvent == nil {
return nil, &util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to perform this action, no power_levels event in this room."),
}
}
pl, err := plEvent.PowerLevels()
if err != nil {
return nil, &util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("You don't have permission to perform this action, the power_levels event for this room is malformed so auth checks cannot be performed."),
}
}
return pl, nil
}

View file

@ -1,4 +1,4 @@
// Copyright 2017 Vector Creations Ltd // Copyright 2024 The Matrix.org Foundation C.I.C.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -18,22 +18,12 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/config"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
type getMembershipResponse struct {
Chunk []gomatrixserverlib.ClientEvent `json:"chunk"`
}
type getJoinedRoomsResponse struct {
JoinedRooms []string `json:"joined_rooms"`
}
// https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-joined-members // https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-joined-members
type getJoinedMembersResponse struct { type getJoinedMembersResponse struct {
Joined map[string]joinedMember `json:"joined"` Joined map[string]joinedMember `json:"joined"`
@ -51,71 +41,99 @@ type databaseJoinedMember struct {
AvatarURL string `json:"avatar_url"` AvatarURL string `json:"avatar_url"`
} }
// GetMemberships implements GET /rooms/{roomId}/members // GetJoinedMembers implements
func GetMemberships( //
req *http.Request, device *userapi.Device, roomID string, joinedOnly bool, // GET /rooms/{roomId}/joined_members
_ *config.ClientAPI, func GetJoinedMembers(
req *http.Request, device *userapi.Device, roomID string,
rsAPI api.ClientRoomserverAPI, rsAPI api.ClientRoomserverAPI,
) util.JSONResponse { ) util.JSONResponse {
queryReq := api.QueryMembershipsForRoomRequest{ // Validate the userID
JoinedOnly: joinedOnly, userID, err := spec.NewUserID(device.UserID, true)
RoomID: roomID, if err != nil {
Sender: device.UserID, 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{},
} }
var queryRes api.QueryMembershipsForRoomResponse
if err := rsAPI.QueryMembershipsForRoom(req.Context(), &queryReq, &queryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryMembershipsForRoom failed")
return jsonerror.InternalServerError()
} }
if !queryRes.HasBeenInRoom { if !queryRes.HasBeenInRoom {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You aren't a member of the room and weren't previously a member of the room."), 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{},
} }
} }
if joinedOnly {
var res getJoinedMembersResponse var res getJoinedMembersResponse
res.Joined = make(map[string]joinedMember) res.Joined = make(map[string]joinedMember)
for _, ev := range queryRes.JoinEvents { for _, ev := range membershipsForRoomResp.JoinEvents {
var content databaseJoinedMember var content databaseJoinedMember
if err := json.Unmarshal(ev.Content, &content); err != nil { if err := json.Unmarshal(ev.Content, &content); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("failed to unmarshal event content") util.GetLogger(req.Context()).WithError(err).Error("failed to unmarshal event content")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
} }
res.Joined[ev.Sender] = joinedMember(content) }
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{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
JSON: res, JSON: res,
} }
} }
return util.JSONResponse{
Code: http.StatusOK,
JSON: getMembershipResponse{queryRes.JoinEvents},
}
}
func GetJoinedRooms(
req *http.Request,
device *userapi.Device,
rsAPI api.ClientRoomserverAPI,
) util.JSONResponse {
var res api.QueryRoomsForUserResponse
err := rsAPI.QueryRoomsForUser(req.Context(), &api.QueryRoomsForUserRequest{
UserID: device.UserID,
WantMembership: "join",
}, &res)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryRoomsForUser failed")
return jsonerror.InternalServerError()
}
if res.RoomIDs == nil {
res.RoomIDs = []string{}
}
return util.JSONResponse{
Code: http.StatusOK,
JSON: getJoinedRoomsResponse{res.RoomIDs},
}
}

View file

@ -18,9 +18,9 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -35,25 +35,35 @@ func GetNotifications(
limit, err = strconv.ParseInt(limitStr, 10, 64) limit, err = strconv.ParseInt(limitStr, 10, 64)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("ParseInt(limit) failed") util.GetLogger(req.Context()).WithError(err).Error("ParseInt(limit) failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
} }
var queryRes userapi.QueryNotificationsResponse var queryRes userapi.QueryNotificationsResponse
localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) localpart, domain, err := gomatrixserverlib.SplitID('@', device.UserID)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("SplitID failed") util.GetLogger(req.Context()).WithError(err).Error("SplitID failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
err = userAPI.QueryNotifications(req.Context(), &userapi.QueryNotificationsRequest{ err = userAPI.QueryNotifications(req.Context(), &userapi.QueryNotificationsRequest{
Localpart: localpart, Localpart: localpart,
ServerName: domain,
From: req.URL.Query().Get("from"), From: req.URL.Query().Get("from"),
Limit: int(limit), Limit: int(limit),
Only: req.URL.Query().Get("only"), Only: req.URL.Query().Get("only"),
}, &queryRes) }, &queryRes)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryNotifications failed") util.GetLogger(req.Context()).WithError(err).Error("QueryNotifications failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
util.GetLogger(req.Context()).WithField("from", req.URL.Query().Get("from")).WithField("limit", limit).WithField("only", req.URL.Query().Get("only")).WithField("next", queryRes.NextToken).Infof("QueryNotifications: len %d", len(queryRes.Notifications)) util.GetLogger(req.Context()).WithField("from", req.URL.Query().Get("from")).WithField("limit", limit).WithField("only", req.URL.Query().Get("only")).WithField("next", queryRes.NextToken).Infof("QueryNotifications: len %d", len(queryRes.Notifications))
return util.JSONResponse{ return util.JSONResponse{

View file

@ -17,9 +17,9 @@ package routing
import ( import (
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -43,7 +43,7 @@ func CreateOpenIDToken(
if userID != device.UserID { if userID != device.UserID {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Cannot request tokens for other users"), JSON: spec.Forbidden("Cannot request tokens for other users"),
} }
} }
@ -55,7 +55,10 @@ func CreateOpenIDToken(
err := userAPI.PerformOpenIDTokenCreation(req.Context(), &request, &response) err := userAPI.PerformOpenIDTokenCreation(req.Context(), &request, &response)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("userAPI.CreateOpenIDToken failed") util.GetLogger(req.Context()).WithError(err).Error("userAPI.CreateOpenIDToken failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
@ -63,7 +66,7 @@ func CreateOpenIDToken(
JSON: openIDTokenResponse{ JSON: openIDTokenResponse{
AccessToken: response.Token.Token, AccessToken: response.Token.Token,
TokenType: "Bearer", TokenType: "Bearer",
MatrixServerName: string(cfg.Matrix.ServerName), MatrixServerName: string(device.UserDomain()),
ExpiresIn: response.Token.ExpiresAtMS / 1000, // convert ms to s ExpiresIn: response.Token.ExpiresAtMS / 1000, // convert ms to s
}, },
} }

View file

@ -6,10 +6,11 @@ import (
"github.com/matrix-org/dendrite/clientapi/auth" "github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -72,7 +73,7 @@ func Password(
// Check if the existing password is correct. // Check if the existing password is correct.
typePassword := auth.LoginTypePassword{ typePassword := auth.LoginTypePassword{
GetAccountByPassword: userAPI.QueryAccountByPassword, UserAPI: userAPI,
Config: cfg, Config: cfg,
} }
if _, authErr := typePassword.Login(req.Context(), &r.Auth.PasswordRequest); authErr != nil { if _, authErr := typePassword.Login(req.Context(), &r.Auth.PasswordRequest); authErr != nil {
@ -81,30 +82,40 @@ func Password(
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword) sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
// Check the new password strength. // Check the new password strength.
if resErr = validatePassword(r.NewPassword); resErr != nil { if err := internal.ValidatePassword(r.NewPassword); err != nil {
return *resErr return *internal.PasswordResponse(err)
} }
// Get the local part. // Get the local part.
localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) localpart, domain, err := gomatrixserverlib.SplitID('@', device.UserID)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed") util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
// Ask the user API to perform the password change. // Ask the user API to perform the password change.
passwordReq := &api.PerformPasswordUpdateRequest{ passwordReq := &api.PerformPasswordUpdateRequest{
Localpart: localpart, Localpart: localpart,
ServerName: domain,
Password: r.NewPassword, Password: r.NewPassword,
} }
passwordRes := &api.PerformPasswordUpdateResponse{} passwordRes := &api.PerformPasswordUpdateResponse{}
if err := userAPI.PerformPasswordUpdate(req.Context(), passwordReq, passwordRes); err != nil { if err := userAPI.PerformPasswordUpdate(req.Context(), passwordReq, passwordRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("PerformPasswordUpdate failed") util.GetLogger(req.Context()).WithError(err).Error("PerformPasswordUpdate failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
if !passwordRes.PasswordUpdated { if !passwordRes.PasswordUpdated {
util.GetLogger(req.Context()).Error("Expected password to have been updated but wasn't") util.GetLogger(req.Context()).Error("Expected password to have been updated but wasn't")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
// If the request asks us to log out all other devices then // If the request asks us to log out all other devices then
@ -118,16 +129,23 @@ func Password(
logoutRes := &api.PerformDeviceDeletionResponse{} logoutRes := &api.PerformDeviceDeletionResponse{}
if err := userAPI.PerformDeviceDeletion(req.Context(), logoutReq, logoutRes); err != nil { if err := userAPI.PerformDeviceDeletion(req.Context(), logoutReq, logoutRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("PerformDeviceDeletion failed") util.GetLogger(req.Context()).WithError(err).Error("PerformDeviceDeletion failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
pushersReq := &api.PerformPusherDeletionRequest{ pushersReq := &api.PerformPusherDeletionRequest{
Localpart: localpart, Localpart: localpart,
ServerName: domain,
SessionID: device.SessionID, SessionID: device.SessionID,
} }
if err := userAPI.PerformPusherDeletion(req.Context(), pushersReq, &struct{}{}); err != nil { if err := userAPI.PerformPusherDeletion(req.Context(), pushersReq, &struct{}{}); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("PerformPusherDeletion failed") util.GetLogger(req.Context()).WithError(err).Error("PerformPusherDeletion failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
} }

View file

@ -15,13 +15,15 @@
package routing package routing
import ( import (
"encoding/json"
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrix"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/sirupsen/logrus"
) )
func PeekRoomByIDOrAlias( func PeekRoomByIDOrAlias(
@ -41,25 +43,42 @@ func PeekRoomByIDOrAlias(
UserID: device.UserID, UserID: device.UserID,
DeviceID: device.ID, DeviceID: device.ID,
} }
peekRes := roomserverAPI.PerformPeekResponse{}
// Check to see if any ?server_name= query parameters were // Check to see if any ?server_name= query parameters were
// given in the request. // given in the request.
if serverNames, ok := req.URL.Query()["server_name"]; ok { if serverNames, ok := req.URL.Query()["server_name"]; ok {
for _, serverName := range serverNames { for _, serverName := range serverNames {
peekReq.ServerNames = append( peekReq.ServerNames = append(
peekReq.ServerNames, peekReq.ServerNames,
gomatrixserverlib.ServerName(serverName), spec.ServerName(serverName),
) )
} }
} }
// Ask the roomserver to perform the peek. // Ask the roomserver to perform the peek.
if err := rsAPI.PerformPeek(req.Context(), &peekReq, &peekRes); err != nil { roomID, err := rsAPI.PerformPeek(req.Context(), &peekReq)
return util.ErrorResponse(err) switch e := err.(type) {
case roomserverAPI.ErrInvalidID:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown(e.Error()),
}
case roomserverAPI.ErrNotAllowed:
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden(e.Error()),
}
case *gomatrix.HTTPError:
return util.JSONResponse{
Code: e.Code,
JSON: json.RawMessage(e.Message),
}
case nil:
default:
logrus.WithError(err).WithField("roomID", roomIDOrAlias).Errorf("Failed to peek room")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
} }
if peekRes.Error != nil {
return peekRes.Error.JSONResponse()
} }
// if this user is already joined to the room, we let them peek anyway // if this user is already joined to the room, we let them peek anyway
@ -75,7 +94,7 @@ func PeekRoomByIDOrAlias(
// TODO: Put the response struct somewhere internal. // TODO: Put the response struct somewhere internal.
JSON: struct { JSON: struct {
RoomID string `json:"room_id"` RoomID string `json:"room_id"`
}{peekRes.RoomID}, }{roomID},
} }
} }
@ -85,18 +104,20 @@ func UnpeekRoomByID(
rsAPI roomserverAPI.ClientRoomserverAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
roomID string, roomID string,
) util.JSONResponse { ) util.JSONResponse {
unpeekReq := roomserverAPI.PerformUnpeekRequest{ err := rsAPI.PerformUnpeek(req.Context(), roomID, device.UserID, device.ID)
RoomID: roomID, switch e := err.(type) {
UserID: device.UserID, case roomserverAPI.ErrInvalidID:
DeviceID: device.ID, return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown(e.Error()),
} }
unpeekRes := roomserverAPI.PerformUnpeekResponse{} case nil:
default:
if err := rsAPI.PerformUnpeek(req.Context(), &unpeekReq, &unpeekRes); err != nil { logrus.WithError(err).WithField("roomID", roomID).Errorf("Failed to un-peek room")
return jsonerror.InternalAPIError(req.Context(), err) return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
} }
if unpeekRes.Error != nil {
return unpeekRes.Error.JSONResponse()
} }
return util.JSONResponse{ return util.JSONResponse{

View file

@ -21,13 +21,12 @@ import (
"time" "time"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/producers" "github.com/matrix-org/dendrite/clientapi/producers"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/jetstream"
"github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/syncapi/types"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -54,7 +53,7 @@ func SetPresence(
if device.UserID != userID { if device.UserID != userID {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Unable to set presence for other user."), JSON: spec.Forbidden("Unable to set presence for other user."),
} }
} }
var presence presenceReq var presence presenceReq
@ -67,7 +66,7 @@ func SetPresence(
if !ok { if !ok {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.Unknown(fmt.Sprintf("Unknown presence '%s'.", presence.Presence)), JSON: spec.Unknown(fmt.Sprintf("Unknown presence '%s'.", presence.Presence)),
} }
} }
err := producer.SendPresence(req.Context(), userID, presenceStatus, presence.StatusMsg) err := producer.SendPresence(req.Context(), userID, presenceStatus, presence.StatusMsg)
@ -75,7 +74,7 @@ func SetPresence(
log.WithError(err).Errorf("failed to update presence") log.WithError(err).Errorf("failed to update presence")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(), JSON: spec.InternalServerError{},
} }
} }
@ -100,7 +99,7 @@ func GetPresence(
log.WithError(err).Errorf("unable to get presence") log.WithError(err).Errorf("unable to get presence")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(), JSON: spec.InternalServerError{},
} }
} }
@ -119,11 +118,11 @@ func GetPresence(
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(), JSON: spec.InternalServerError{},
} }
} }
p := types.PresenceInternal{LastActiveTS: gomatrixserverlib.Timestamp(lastActive)} p := types.PresenceInternal{LastActiveTS: spec.Timestamp(lastActive)}
currentlyActive := p.CurrentlyActive() currentlyActive := p.CurrentlyActive()
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,

View file

@ -16,46 +16,52 @@ package routing
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
"time" "time"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
appserviceAPI "github.com/matrix-org/dendrite/appservice/api" appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/dendrite/internal/eventutil"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/roomserver/types"
"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"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrix"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
// GetProfile implements GET /profile/{userID} // GetProfile implements GET /profile/{userID}
func GetProfile( func GetProfile(
req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, req *http.Request, profileAPI userapi.ProfileAPI, cfg *config.ClientAPI,
userID string, userID string,
asAPI appserviceAPI.AppServiceInternalAPI, asAPI appserviceAPI.AppServiceInternalAPI,
federation *gomatrixserverlib.FederationClient, federation fclient.FederationClient,
) util.JSONResponse { ) util.JSONResponse {
profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation) profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation)
if err != nil { if err != nil {
if err == eventutil.ErrProfileNoExists { if err == appserviceAPI.ErrProfileNotExists {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
JSON: jsonerror.NotFound("The user does not exist or does not have a profile"), JSON: spec.NotFound("The user does not exist or does not have a profile"),
} }
} }
util.GetLogger(req.Context()).WithError(err).Error("getProfile failed") util.GetLogger(req.Context()).WithError(err).Error("getProfile failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
JSON: eventutil.ProfileResponse{ JSON: eventutil.UserProfile{
AvatarURL: profile.AvatarURL, AvatarURL: profile.AvatarURL,
DisplayName: profile.DisplayName, DisplayName: profile.DisplayName,
}, },
@ -64,125 +70,85 @@ func GetProfile(
// GetAvatarURL implements GET /profile/{userID}/avatar_url // GetAvatarURL implements GET /profile/{userID}/avatar_url
func GetAvatarURL( func GetAvatarURL(
req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, req *http.Request, profileAPI userapi.ProfileAPI, cfg *config.ClientAPI,
userID string, asAPI appserviceAPI.AppServiceInternalAPI, userID string, asAPI appserviceAPI.AppServiceInternalAPI,
federation *gomatrixserverlib.FederationClient, federation fclient.FederationClient,
) util.JSONResponse { ) util.JSONResponse {
profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation) profile := GetProfile(req, profileAPI, cfg, userID, asAPI, federation)
if err != nil { p, ok := profile.JSON.(eventutil.UserProfile)
if err == eventutil.ErrProfileNoExists { // not a profile response, so most likely an error, return that
return util.JSONResponse{ if !ok {
Code: http.StatusNotFound, return profile
JSON: jsonerror.NotFound("The user does not exist or does not have a profile"),
}
}
util.GetLogger(req.Context()).WithError(err).Error("getProfile failed")
return jsonerror.InternalServerError()
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
JSON: eventutil.AvatarURL{ JSON: eventutil.UserProfile{
AvatarURL: profile.AvatarURL, AvatarURL: p.AvatarURL,
}, },
} }
} }
// SetAvatarURL implements PUT /profile/{userID}/avatar_url // SetAvatarURL implements PUT /profile/{userID}/avatar_url
func SetAvatarURL( func SetAvatarURL(
req *http.Request, profileAPI userapi.ClientUserAPI, req *http.Request, profileAPI userapi.ProfileAPI,
device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI, device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI,
) util.JSONResponse { ) util.JSONResponse {
if userID != device.UserID { if userID != device.UserID {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("userID does not match the current user"), JSON: spec.Forbidden("userID does not match the current user"),
} }
} }
var r eventutil.AvatarURL var r eventutil.UserProfile
if resErr := httputil.UnmarshalJSONRequest(req, &r); resErr != nil { if resErr := httputil.UnmarshalJSONRequest(req, &r); resErr != nil {
return *resErr return *resErr
} }
if r.AvatarURL == "" {
localpart, domain, err := gomatrixserverlib.SplitID('@', userID)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusInternalServerError,
JSON: jsonerror.BadJSON("'avatar_url' must be supplied."), JSON: spec.InternalServerError{},
} }
} }
localpart, _, err := gomatrixserverlib.SplitID('@', userID) if !cfg.Matrix.IsLocalServerName(domain) {
if err != nil { return util.JSONResponse{
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed") Code: http.StatusForbidden,
return jsonerror.InternalServerError() JSON: spec.Forbidden("userID does not belong to a locally configured domain"),
}
} }
evTime, err := httputil.ParseTSParam(req) evTime, err := httputil.ParseTSParam(req)
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidArgumentValue(err.Error()), JSON: spec.InvalidParam(err.Error()),
} }
} }
res := &userapi.QueryProfileResponse{} profile, changed, err := profileAPI.SetAvatarURL(req.Context(), localpart, domain, r.AvatarURL)
err = profileAPI.QueryProfile(req.Context(), &userapi.QueryProfileRequest{
UserID: userID,
}, res)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.QueryProfile failed")
return jsonerror.InternalServerError()
}
oldProfile := &authtypes.Profile{
Localpart: localpart,
DisplayName: res.DisplayName,
AvatarURL: res.AvatarURL,
}
setRes := &userapi.PerformSetAvatarURLResponse{}
if err = profileAPI.SetAvatarURL(req.Context(), &userapi.PerformSetAvatarURLRequest{
Localpart: localpart,
AvatarURL: r.AvatarURL,
}, setRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetAvatarURL failed") util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetAvatarURL failed")
return jsonerror.InternalServerError()
}
var roomsRes api.QueryRoomsForUserResponse
err = rsAPI.QueryRoomsForUser(req.Context(), &api.QueryRoomsForUserRequest{
UserID: device.UserID,
WantMembership: "join",
}, &roomsRes)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryRoomsForUser failed")
return jsonerror.InternalServerError()
}
newProfile := authtypes.Profile{
Localpart: localpart,
DisplayName: oldProfile.DisplayName,
AvatarURL: r.AvatarURL,
}
events, err := buildMembershipEvents(
req.Context(), roomsRes.RoomIDs, newProfile, userID, cfg, evTime, rsAPI,
)
switch e := err.(type) {
case nil:
case gomatrixserverlib.BadJSONError:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusInternalServerError,
JSON: jsonerror.BadJSON(e.Error()), JSON: spec.InternalServerError{},
}
}
// No need to build new membership events, since nothing changed
if !changed {
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
} }
default:
util.GetLogger(req.Context()).WithError(err).Error("buildMembershipEvents failed")
return jsonerror.InternalServerError()
} }
if err := api.SendEvents(req.Context(), rsAPI, api.KindNew, events, cfg.Matrix.ServerName, cfg.Matrix.ServerName, nil, true); err != nil { response, err := updateProfile(req.Context(), rsAPI, device, profile, userID, evTime)
util.GetLogger(req.Context()).WithError(err).Error("SendEvents failed") if err != nil {
return jsonerror.InternalServerError() return response
} }
return util.JSONResponse{ return util.JSONResponse{
@ -193,125 +159,85 @@ func SetAvatarURL(
// GetDisplayName implements GET /profile/{userID}/displayname // GetDisplayName implements GET /profile/{userID}/displayname
func GetDisplayName( func GetDisplayName(
req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, req *http.Request, profileAPI userapi.ProfileAPI, cfg *config.ClientAPI,
userID string, asAPI appserviceAPI.AppServiceInternalAPI, userID string, asAPI appserviceAPI.AppServiceInternalAPI,
federation *gomatrixserverlib.FederationClient, federation fclient.FederationClient,
) util.JSONResponse { ) util.JSONResponse {
profile, err := getProfile(req.Context(), profileAPI, cfg, userID, asAPI, federation) profile := GetProfile(req, profileAPI, cfg, userID, asAPI, federation)
if err != nil { p, ok := profile.JSON.(eventutil.UserProfile)
if err == eventutil.ErrProfileNoExists { // not a profile response, so most likely an error, return that
return util.JSONResponse{ if !ok {
Code: http.StatusNotFound, return profile
JSON: jsonerror.NotFound("The user does not exist or does not have a profile"),
}
}
util.GetLogger(req.Context()).WithError(err).Error("getProfile failed")
return jsonerror.InternalServerError()
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
JSON: eventutil.DisplayName{ JSON: eventutil.UserProfile{
DisplayName: profile.DisplayName, DisplayName: p.DisplayName,
}, },
} }
} }
// SetDisplayName implements PUT /profile/{userID}/displayname // SetDisplayName implements PUT /profile/{userID}/displayname
func SetDisplayName( func SetDisplayName(
req *http.Request, profileAPI userapi.ClientUserAPI, req *http.Request, profileAPI userapi.ProfileAPI,
device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI, device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI,
) util.JSONResponse { ) util.JSONResponse {
if userID != device.UserID { if userID != device.UserID {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("userID does not match the current user"), JSON: spec.Forbidden("userID does not match the current user"),
} }
} }
var r eventutil.DisplayName var r eventutil.UserProfile
if resErr := httputil.UnmarshalJSONRequest(req, &r); resErr != nil { if resErr := httputil.UnmarshalJSONRequest(req, &r); resErr != nil {
return *resErr return *resErr
} }
if r.DisplayName == "" {
localpart, domain, err := gomatrixserverlib.SplitID('@', userID)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusInternalServerError,
JSON: jsonerror.BadJSON("'displayname' must be supplied."), JSON: spec.InternalServerError{},
} }
} }
localpart, _, err := gomatrixserverlib.SplitID('@', userID) if !cfg.Matrix.IsLocalServerName(domain) {
if err != nil { return util.JSONResponse{
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed") Code: http.StatusForbidden,
return jsonerror.InternalServerError() JSON: spec.Forbidden("userID does not belong to a locally configured domain"),
}
} }
evTime, err := httputil.ParseTSParam(req) evTime, err := httputil.ParseTSParam(req)
if err != nil { if err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidArgumentValue(err.Error()), JSON: spec.InvalidParam(err.Error()),
} }
} }
pRes := &userapi.QueryProfileResponse{} profile, changed, err := profileAPI.SetDisplayName(req.Context(), localpart, domain, r.DisplayName)
err = profileAPI.QueryProfile(req.Context(), &userapi.QueryProfileRequest{
UserID: userID,
}, pRes)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.QueryProfile failed")
return jsonerror.InternalServerError()
}
oldProfile := &authtypes.Profile{
Localpart: localpart,
DisplayName: pRes.DisplayName,
AvatarURL: pRes.AvatarURL,
}
err = profileAPI.SetDisplayName(req.Context(), &userapi.PerformUpdateDisplayNameRequest{
Localpart: localpart,
DisplayName: r.DisplayName,
}, &struct{}{})
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetDisplayName failed") util.GetLogger(req.Context()).WithError(err).Error("profileAPI.SetDisplayName failed")
return jsonerror.InternalServerError()
}
var res api.QueryRoomsForUserResponse
err = rsAPI.QueryRoomsForUser(req.Context(), &api.QueryRoomsForUserRequest{
UserID: device.UserID,
WantMembership: "join",
}, &res)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryRoomsForUser failed")
return jsonerror.InternalServerError()
}
newProfile := authtypes.Profile{
Localpart: localpart,
DisplayName: r.DisplayName,
AvatarURL: oldProfile.AvatarURL,
}
events, err := buildMembershipEvents(
req.Context(), res.RoomIDs, newProfile, userID, cfg, evTime, rsAPI,
)
switch e := err.(type) {
case nil:
case gomatrixserverlib.BadJSONError:
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusInternalServerError,
JSON: jsonerror.BadJSON(e.Error()), JSON: spec.InternalServerError{},
}
}
// No need to build new membership events, since nothing changed
if !changed {
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
} }
default:
util.GetLogger(req.Context()).WithError(err).Error("buildMembershipEvents failed")
return jsonerror.InternalServerError()
} }
if err := api.SendEvents(req.Context(), rsAPI, api.KindNew, events, cfg.Matrix.ServerName, cfg.Matrix.ServerName, nil, true); err != nil { response, err := updateProfile(req.Context(), rsAPI, device, profile, userID, evTime)
util.GetLogger(req.Context()).WithError(err).Error("SendEvents failed") if err != nil {
return jsonerror.InternalServerError() return response
} }
return util.JSONResponse{ return util.JSONResponse{
@ -320,27 +246,91 @@ func SetDisplayName(
} }
} }
func updateProfile(
ctx context.Context, rsAPI api.ClientRoomserverAPI, device *userapi.Device,
profile *authtypes.Profile,
userID string, evTime time.Time,
) (util.JSONResponse, error) {
deviceUserID, err := spec.NewUserID(device.UserID, true)
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}, err
}
rooms, err := rsAPI.QueryRoomsForUser(ctx, *deviceUserID, "join")
if err != nil {
util.GetLogger(ctx).WithError(err).Error("QueryRoomsForUser failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}, err
}
roomIDStrs := make([]string, len(rooms))
for i, room := range rooms {
roomIDStrs[i] = room.String()
}
_, domain, err := gomatrixserverlib.SplitID('@', userID)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.SplitID failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}, err
}
events, err := buildMembershipEvents(
ctx, roomIDStrs, *profile, userID, evTime, rsAPI,
)
switch e := err.(type) {
case nil:
case gomatrixserverlib.BadJSONError:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON(e.Error()),
}, e
default:
util.GetLogger(ctx).WithError(err).Error("buildMembershipEvents failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}, e
}
if err := api.SendEvents(ctx, rsAPI, api.KindNew, events, device.UserDomain(), domain, domain, nil, false); err != nil {
util.GetLogger(ctx).WithError(err).Error("SendEvents failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}, err
}
return util.JSONResponse{}, nil
}
// getProfile gets the full profile of a user by querying the database or a // getProfile gets the full profile of a user by querying the database or a
// remote homeserver. // remote homeserver.
// Returns an error when something goes wrong or specifically // Returns an error when something goes wrong or specifically
// eventutil.ErrProfileNoExists when the profile doesn't exist. // eventutil.ErrProfileNotExists when the profile doesn't exist.
func getProfile( func getProfile(
ctx context.Context, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, ctx context.Context, profileAPI userapi.ProfileAPI, cfg *config.ClientAPI,
userID string, userID string,
asAPI appserviceAPI.AppServiceInternalAPI, asAPI appserviceAPI.AppServiceInternalAPI,
federation *gomatrixserverlib.FederationClient, federation fclient.FederationClient,
) (*authtypes.Profile, error) { ) (*authtypes.Profile, error) {
localpart, domain, err := gomatrixserverlib.SplitID('@', userID) localpart, domain, err := gomatrixserverlib.SplitID('@', userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if domain != cfg.Matrix.ServerName { if !cfg.Matrix.IsLocalServerName(domain) {
profile, fedErr := federation.LookupProfile(ctx, domain, userID, "") profile, fedErr := federation.LookupProfile(ctx, cfg.Matrix.ServerName, domain, userID, "")
if fedErr != nil { if fedErr != nil {
if x, ok := fedErr.(gomatrix.HTTPError); ok { if x, ok := fedErr.(gomatrix.HTTPError); ok {
if x.Code == http.StatusNotFound { if x.Code == http.StatusNotFound {
return nil, eventutil.ErrProfileNoExists return nil, appserviceAPI.ErrProfileNotExists
} }
} }
@ -365,42 +355,61 @@ func getProfile(
func buildMembershipEvents( func buildMembershipEvents(
ctx context.Context, ctx context.Context,
roomIDs []string, roomIDs []string,
newProfile authtypes.Profile, userID string, cfg *config.ClientAPI, newProfile authtypes.Profile, userID string,
evTime time.Time, rsAPI api.ClientRoomserverAPI, evTime time.Time, rsAPI api.ClientRoomserverAPI,
) ([]*gomatrixserverlib.HeaderedEvent, error) { ) ([]*types.HeaderedEvent, error) {
evs := []*gomatrixserverlib.HeaderedEvent{} evs := []*types.HeaderedEvent{}
for _, roomID := range roomIDs { fullUserID, err := spec.NewUserID(userID, true)
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID} if err != nil {
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(ctx, &verReq, &verRes); err != nil {
return nil, err return nil, err
} }
for _, roomID := range roomIDs {
builder := gomatrixserverlib.EventBuilder{ validRoomID, err := spec.NewRoomID(roomID)
Sender: userID, if err != nil {
return nil, err
}
senderID, err := rsAPI.QuerySenderIDForUser(ctx, *validRoomID, *fullUserID)
if err != nil {
return nil, err
} else if senderID == nil {
return nil, fmt.Errorf("sender ID not found for %s in %s", *fullUserID, *validRoomID)
}
senderIDString := string(*senderID)
proto := gomatrixserverlib.ProtoEvent{
SenderID: senderIDString,
RoomID: roomID, RoomID: roomID,
Type: "m.room.member", Type: "m.room.member",
StateKey: &userID, StateKey: &senderIDString,
} }
content := gomatrixserverlib.MemberContent{ content := gomatrixserverlib.MemberContent{
Membership: gomatrixserverlib.Join, Membership: spec.Join,
} }
content.DisplayName = newProfile.DisplayName content.DisplayName = newProfile.DisplayName
content.AvatarURL = newProfile.AvatarURL content.AvatarURL = newProfile.AvatarURL
if err := builder.SetContent(content); err != nil { if err = proto.SetContent(content); err != nil {
return nil, err return nil, err
} }
event, err := eventutil.QueryAndBuildEvent(ctx, &builder, cfg.Matrix, evTime, rsAPI, nil) user, err := spec.NewUserID(userID, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
evs = append(evs, event.Headered(verRes.RoomVersion)) identity, err := rsAPI.SigningIdentityFor(ctx, *validRoomID, *user)
if err != nil {
return nil, err
}
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, &identity, evTime, rsAPI, nil)
if err != nil {
return nil, err
}
evs = append(evs, event)
} }
return evs, nil return evs, nil

View file

@ -19,9 +19,9 @@ import (
"net/url" "net/url"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -31,17 +31,24 @@ func GetPushers(
userAPI userapi.ClientUserAPI, userAPI userapi.ClientUserAPI,
) util.JSONResponse { ) util.JSONResponse {
var queryRes userapi.QueryPushersResponse var queryRes userapi.QueryPushersResponse
localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) localpart, domain, err := gomatrixserverlib.SplitID('@', device.UserID)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("SplitID failed") util.GetLogger(req.Context()).WithError(err).Error("SplitID failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
err = userAPI.QueryPushers(req.Context(), &userapi.QueryPushersRequest{ err = userAPI.QueryPushers(req.Context(), &userapi.QueryPushersRequest{
Localpart: localpart, Localpart: localpart,
ServerName: domain,
}, &queryRes) }, &queryRes)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("QueryPushers failed") util.GetLogger(req.Context()).WithError(err).Error("QueryPushers failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
for i := range queryRes.Pushers { for i := range queryRes.Pushers {
queryRes.Pushers[i].SessionID = 0 queryRes.Pushers[i].SessionID = 0
@ -59,10 +66,13 @@ func SetPusher(
req *http.Request, device *userapi.Device, req *http.Request, device *userapi.Device,
userAPI userapi.ClientUserAPI, userAPI userapi.ClientUserAPI,
) util.JSONResponse { ) util.JSONResponse {
localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) localpart, domain, err := gomatrixserverlib.SplitID('@', device.UserID)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("SplitID failed") util.GetLogger(req.Context()).WithError(err).Error("SplitID failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
body := userapi.PerformPusherSetRequest{} body := userapi.PerformPusherSetRequest{}
if resErr := httputil.UnmarshalJSONRequest(req, &body); resErr != nil { if resErr := httputil.UnmarshalJSONRequest(req, &body); resErr != nil {
@ -93,11 +103,15 @@ func SetPusher(
} }
body.Localpart = localpart body.Localpart = localpart
body.ServerName = domain
body.SessionID = device.SessionID body.SessionID = device.SessionID
err = userAPI.PerformPusherSet(req.Context(), &body, &struct{}{}) err = userAPI.PerformPusherSet(req.Context(), &body, &struct{}{})
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("PerformPusherSet failed") util.GetLogger(req.Context()).WithError(err).Error("PerformPusherSet failed")
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
return util.JSONResponse{ return util.JSONResponse{
@ -109,6 +123,6 @@ func SetPusher(
func invalidParam(msg string) util.JSONResponse { func invalidParam(msg string) util.JSONResponse {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.InvalidParam(msg), JSON: spec.InvalidParam(msg),
} }
} }

View file

@ -7,31 +7,34 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/internal/pushrules" "github.com/matrix-org/dendrite/internal/pushrules"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
func errorResponse(ctx context.Context, err error, msg string, args ...interface{}) util.JSONResponse { func errorResponse(ctx context.Context, err error, msg string, args ...interface{}) util.JSONResponse {
if eerr, ok := err.(*jsonerror.MatrixError); ok { if eerr, ok := err.(spec.MatrixError); ok {
var status int var status int
switch eerr.ErrCode { switch eerr.ErrCode {
case "M_INVALID_ARGUMENT_VALUE": case spec.ErrorInvalidParam:
status = http.StatusBadRequest status = http.StatusBadRequest
case "M_NOT_FOUND": case spec.ErrorNotFound:
status = http.StatusNotFound status = http.StatusNotFound
default: default:
status = http.StatusInternalServerError status = http.StatusInternalServerError
} }
return util.MatrixErrorResponse(status, eerr.ErrCode, eerr.Err) return util.MatrixErrorResponse(status, string(eerr.ErrCode), eerr.Err)
} }
util.GetLogger(ctx).WithError(err).Errorf(msg, args...) util.GetLogger(ctx).WithError(err).Errorf(msg, args...)
return jsonerror.InternalServerError() return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
} }
func GetAllPushRules(ctx context.Context, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { func GetAllPushRules(ctx context.Context, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "queryPushRulesJSON failed") return errorResponse(ctx, err, "queryPushRulesJSON failed")
} }
@ -42,13 +45,13 @@ func GetAllPushRules(ctx context.Context, device *userapi.Device, userAPI userap
} }
func GetPushRulesByScope(ctx context.Context, scope string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { func GetPushRulesByScope(ctx context.Context, scope string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "queryPushRulesJSON failed") return errorResponse(ctx, err, "queryPushRulesJSON failed")
} }
ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope)) ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope))
if ruleSet == nil { if ruleSet == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rule set"), "pushRuleSetByScope failed") return errorResponse(ctx, spec.InvalidParam("invalid push rule set"), "pushRuleSetByScope failed")
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
@ -57,17 +60,18 @@ func GetPushRulesByScope(ctx context.Context, scope string, device *userapi.Devi
} }
func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "queryPushRules failed") return errorResponse(ctx, err, "queryPushRules failed")
} }
ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope)) ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope))
if ruleSet == nil { if ruleSet == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rule set"), "pushRuleSetByScope failed") return errorResponse(ctx, spec.InvalidParam("invalid push rule set"), "pushRuleSetByScope failed")
} }
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
if rulesPtr == nil { // Even if rulesPtr is not nil, there may not be any rules for this kind
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rules kind"), "pushRuleSetKindPointer failed") if rulesPtr == nil || (rulesPtr != nil && len(*rulesPtr) == 0) {
return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
@ -76,21 +80,21 @@ func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi
} }
func GetPushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { func GetPushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "queryPushRules failed") return errorResponse(ctx, err, "queryPushRules failed")
} }
ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope)) ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope))
if ruleSet == nil { if ruleSet == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rule set"), "pushRuleSetByScope failed") return errorResponse(ctx, spec.InvalidParam("invalid push rule set"), "pushRuleSetByScope failed")
} }
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
if rulesPtr == nil { if rulesPtr == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rules kind"), "pushRuleSetKindPointer failed") return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
} }
i := pushRuleIndexByID(*rulesPtr, ruleID) i := pushRuleIndexByID(*rulesPtr, ruleID)
if i < 0 { if i < 0 {
return errorResponse(ctx, jsonerror.NotFound("push rule ID not found"), "pushRuleIndexByID failed") return errorResponse(ctx, spec.NotFound("push rule ID not found"), "pushRuleIndexByID failed")
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
@ -101,26 +105,30 @@ func GetPushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device
func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, beforeRuleID string, body io.Reader, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, beforeRuleID string, body io.Reader, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
var newRule pushrules.Rule var newRule pushrules.Rule
if err := json.NewDecoder(body).Decode(&newRule); err != nil { if err := json.NewDecoder(body).Decode(&newRule); err != nil {
return errorResponse(ctx, err, "JSON Decode failed") return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.BadJSON(err.Error()),
}
} }
newRule.RuleID = ruleID newRule.RuleID = ruleID
errs := pushrules.ValidateRule(pushrules.Kind(kind), &newRule) errs := pushrules.ValidateRule(pushrules.Kind(kind), &newRule)
if len(errs) > 0 { if len(errs) > 0 {
return errorResponse(ctx, jsonerror.InvalidArgumentValue(errs[0].Error()), "rule sanity check failed: %v", errs) return errorResponse(ctx, spec.InvalidParam(errs[0].Error()), "rule sanity check failed: %v", errs)
} }
ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "queryPushRules failed") return errorResponse(ctx, err, "queryPushRules failed")
} }
ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope)) ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope))
if ruleSet == nil { if ruleSet == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rule set"), "pushRuleSetByScope failed") return errorResponse(ctx, spec.InvalidParam("invalid push rule set"), "pushRuleSetByScope failed")
} }
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
if rulesPtr == nil { if rulesPtr == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rules kind"), "pushRuleSetKindPointer failed") // while this should be impossible (ValidateRule would already return an error), better keep it around
return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
} }
i := pushRuleIndexByID(*rulesPtr, ruleID) i := pushRuleIndexByID(*rulesPtr, ruleID)
if i >= 0 && afterRuleID == "" && beforeRuleID == "" { if i >= 0 && afterRuleID == "" && beforeRuleID == "" {
@ -144,7 +152,7 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID,
} }
// Add new rule. // Add new rule.
i, err := findPushRuleInsertionIndex(*rulesPtr, afterRuleID, beforeRuleID) i, err = findPushRuleInsertionIndex(*rulesPtr, afterRuleID, beforeRuleID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "findPushRuleInsertionIndex failed") return errorResponse(ctx, err, "findPushRuleInsertionIndex failed")
} }
@ -153,7 +161,7 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID,
util.GetLogger(ctx).WithField("after", afterRuleID).WithField("before", beforeRuleID).Infof("Added new push rule at %d", i) util.GetLogger(ctx).WithField("after", afterRuleID).WithField("before", beforeRuleID).Infof("Added new push rule at %d", i)
} }
if err := putPushRules(ctx, device.UserID, ruleSets, userAPI); err != nil { if err = userAPI.PerformPushRulesPut(ctx, device.UserID, ruleSets); err != nil {
return errorResponse(ctx, err, "putPushRules failed") return errorResponse(ctx, err, "putPushRules failed")
} }
@ -161,26 +169,26 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID,
} }
func DeletePushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { func DeletePushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "queryPushRules failed") return errorResponse(ctx, err, "queryPushRules failed")
} }
ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope)) ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope))
if ruleSet == nil { if ruleSet == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rule set"), "pushRuleSetByScope failed") return errorResponse(ctx, spec.InvalidParam("invalid push rule set"), "pushRuleSetByScope failed")
} }
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
if rulesPtr == nil { if rulesPtr == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rules kind"), "pushRuleSetKindPointer failed") return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
} }
i := pushRuleIndexByID(*rulesPtr, ruleID) i := pushRuleIndexByID(*rulesPtr, ruleID)
if i < 0 { if i < 0 {
return errorResponse(ctx, jsonerror.NotFound("push rule ID not found"), "pushRuleIndexByID failed") return errorResponse(ctx, spec.NotFound("push rule ID not found"), "pushRuleIndexByID failed")
} }
*rulesPtr = append((*rulesPtr)[:i], (*rulesPtr)[i+1:]...) *rulesPtr = append((*rulesPtr)[:i], (*rulesPtr)[i+1:]...)
if err := putPushRules(ctx, device.UserID, ruleSets, userAPI); err != nil { if err = userAPI.PerformPushRulesPut(ctx, device.UserID, ruleSets); err != nil {
return errorResponse(ctx, err, "putPushRules failed") return errorResponse(ctx, err, "putPushRules failed")
} }
@ -192,21 +200,21 @@ func GetPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri
if err != nil { if err != nil {
return errorResponse(ctx, err, "pushRuleAttrGetter failed") return errorResponse(ctx, err, "pushRuleAttrGetter failed")
} }
ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "queryPushRules failed") return errorResponse(ctx, err, "queryPushRules failed")
} }
ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope)) ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope))
if ruleSet == nil { if ruleSet == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rule set"), "pushRuleSetByScope failed") return errorResponse(ctx, spec.InvalidParam("invalid push rule set"), "pushRuleSetByScope failed")
} }
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
if rulesPtr == nil { if rulesPtr == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rules kind"), "pushRuleSetKindPointer failed") return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
} }
i := pushRuleIndexByID(*rulesPtr, ruleID) i := pushRuleIndexByID(*rulesPtr, ruleID)
if i < 0 { if i < 0 {
return errorResponse(ctx, jsonerror.NotFound("push rule ID not found"), "pushRuleIndexByID failed") return errorResponse(ctx, spec.NotFound("push rule ID not found"), "pushRuleIndexByID failed")
} }
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusOK, Code: http.StatusOK,
@ -221,7 +229,7 @@ func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri
if err := json.NewDecoder(body).Decode(&newPartialRule); err != nil { if err := json.NewDecoder(body).Decode(&newPartialRule); err != nil {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON(err.Error()), JSON: spec.BadJSON(err.Error()),
} }
} }
if newPartialRule.Actions == nil { if newPartialRule.Actions == nil {
@ -238,27 +246,27 @@ func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri
return errorResponse(ctx, err, "pushRuleAttrSetter failed") return errorResponse(ctx, err, "pushRuleAttrSetter failed")
} }
ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) ruleSets, err := userAPI.QueryPushRules(ctx, device.UserID)
if err != nil { if err != nil {
return errorResponse(ctx, err, "queryPushRules failed") return errorResponse(ctx, err, "queryPushRules failed")
} }
ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope)) ruleSet := pushRuleSetByScope(ruleSets, pushrules.Scope(scope))
if ruleSet == nil { if ruleSet == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rule set"), "pushRuleSetByScope failed") return errorResponse(ctx, spec.InvalidParam("invalid push rule set"), "pushRuleSetByScope failed")
} }
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind)) rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
if rulesPtr == nil { if rulesPtr == nil {
return errorResponse(ctx, jsonerror.InvalidArgumentValue("invalid push rules kind"), "pushRuleSetKindPointer failed") return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
} }
i := pushRuleIndexByID(*rulesPtr, ruleID) i := pushRuleIndexByID(*rulesPtr, ruleID)
if i < 0 { if i < 0 {
return errorResponse(ctx, jsonerror.NotFound("push rule ID not found"), "pushRuleIndexByID failed") return errorResponse(ctx, spec.NotFound("push rule ID not found"), "pushRuleIndexByID failed")
} }
if !reflect.DeepEqual(attrGet((*rulesPtr)[i]), attrGet(&newPartialRule)) { if !reflect.DeepEqual(attrGet((*rulesPtr)[i]), attrGet(&newPartialRule)) {
attrSet((*rulesPtr)[i], &newPartialRule) attrSet((*rulesPtr)[i], &newPartialRule)
if err := putPushRules(ctx, device.UserID, ruleSets, userAPI); err != nil { if err = userAPI.PerformPushRulesPut(ctx, device.UserID, ruleSets); err != nil {
return errorResponse(ctx, err, "putPushRules failed") return errorResponse(ctx, err, "putPushRules failed")
} }
} }
@ -266,28 +274,6 @@ func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri
return util.JSONResponse{Code: http.StatusOK, JSON: struct{}{}} return util.JSONResponse{Code: http.StatusOK, JSON: struct{}{}}
} }
func queryPushRules(ctx context.Context, userID string, userAPI userapi.ClientUserAPI) (*pushrules.AccountRuleSets, error) {
var res userapi.QueryPushRulesResponse
if err := userAPI.QueryPushRules(ctx, &userapi.QueryPushRulesRequest{UserID: userID}, &res); err != nil {
util.GetLogger(ctx).WithError(err).Error("userAPI.QueryPushRules failed")
return nil, err
}
return res.RuleSets, nil
}
func putPushRules(ctx context.Context, userID string, ruleSets *pushrules.AccountRuleSets, userAPI userapi.ClientUserAPI) error {
req := userapi.PerformPushRulesPutRequest{
UserID: userID,
RuleSets: ruleSets,
}
var res struct{}
if err := userAPI.PerformPushRulesPut(ctx, &req, &res); err != nil {
util.GetLogger(ctx).WithError(err).Error("userAPI.PerformPushRulesPut failed")
return err
}
return nil
}
func pushRuleSetByScope(ruleSets *pushrules.AccountRuleSets, scope pushrules.Scope) *pushrules.RuleSet { func pushRuleSetByScope(ruleSets *pushrules.AccountRuleSets, scope pushrules.Scope) *pushrules.RuleSet {
switch scope { switch scope {
case pushrules.GlobalScope: case pushrules.GlobalScope:
@ -330,7 +316,7 @@ func pushRuleAttrGetter(attr string) (func(*pushrules.Rule) interface{}, error)
case "enabled": case "enabled":
return func(rule *pushrules.Rule) interface{} { return rule.Enabled }, nil return func(rule *pushrules.Rule) interface{} { return rule.Enabled }, nil
default: default:
return nil, jsonerror.InvalidArgumentValue("invalid push rule attribute") return nil, spec.InvalidParam("invalid push rule attribute")
} }
} }
@ -341,7 +327,7 @@ func pushRuleAttrSetter(attr string) (func(dest, src *pushrules.Rule), error) {
case "enabled": case "enabled":
return func(dest, src *pushrules.Rule) { dest.Enabled = src.Enabled }, nil return func(dest, src *pushrules.Rule) { dest.Enabled = src.Enabled }, nil
default: default:
return nil, jsonerror.InvalidArgumentValue("invalid push rule attribute") return nil, spec.InvalidParam("invalid push rule attribute")
} }
} }
@ -355,10 +341,10 @@ func findPushRuleInsertionIndex(rules []*pushrules.Rule, afterID, beforeID strin
} }
} }
if i == len(rules) { if i == len(rules) {
return 0, jsonerror.NotFound("after: rule ID not found") return 0, spec.NotFound("after: rule ID not found")
} }
if rules[i].Default { if rules[i].Default {
return 0, jsonerror.NotFound("after: rule ID must not be a default rule") return 0, spec.NotFound("after: rule ID must not be a default rule")
} }
// We stopped on the "after" match to differentiate // We stopped on the "after" match to differentiate
// not-found from is-last-entry. Now we move to the earliest // not-found from is-last-entry. Now we move to the earliest
@ -373,10 +359,10 @@ func findPushRuleInsertionIndex(rules []*pushrules.Rule, afterID, beforeID strin
} }
} }
if i == len(rules) { if i == len(rules) {
return 0, jsonerror.NotFound("before: rule ID not found") return 0, spec.NotFound("before: rule ID not found")
} }
if rules[i].Default { if rules[i].Default {
return 0, jsonerror.NotFound("before: rule ID must not be a default rule") return 0, spec.NotFound("before: rule ID must not be a default rule")
} }
} }

Some files were not shown because too many files have changed in this diff Show more