mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-07-18 17:46:33 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba0ec6ba70 |
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- name: Setup Node.js
|
||||
if: steps.runner-env.outputs.node_major == '' || steps.runner-env.outputs.node_major < '20'
|
||||
uses: https://github.com/actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
uses: https://github.com/actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 📦 Setup Node.js
|
||||
uses: https://github.com/actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
uses: https://github.com/actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: "22"
|
||||
|
||||
|
||||
Generated
+9
-9
@@ -637,7 +637,7 @@ checksum = "aa61aec073ec94791433ddf3df2323ff9d1711557c2a0eefb0f99cb4f8dca520"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"toml 1.1.3+spec-1.1.0",
|
||||
"toml 1.1.2+spec-1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -996,7 +996,7 @@ dependencies = [
|
||||
"tikv-jemallocator",
|
||||
"tokio",
|
||||
"tokio-metrics",
|
||||
"toml 1.1.3+spec-1.1.0",
|
||||
"toml 1.1.2+spec-1.1.0",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
@@ -4728,7 +4728,7 @@ dependencies = [
|
||||
"ruma-identifiers-validation",
|
||||
"serde",
|
||||
"syn",
|
||||
"toml 1.1.3+spec-1.1.0",
|
||||
"toml 1.1.2+spec-1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5631,9 +5631,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.119"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5944,9 +5944,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c"
|
||||
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
|
||||
dependencies = [
|
||||
"indexmap 2.14.0",
|
||||
"serde_core",
|
||||
@@ -6018,9 +6018,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Fixed requests returning `500 Internal Server Error` when the header selected by `request_ip_source` is absent, duplicated, or malformed (for example Envoy omitting `X-Envoy-External-Address` on internal requests). The client IP now falls back to the connection peer address instead of failing the request. Contributed by @eleboucher
|
||||
@@ -1 +0,0 @@
|
||||
Resolve alias service by correct name for auto-join. Contributed by @eleboucher
|
||||
@@ -1 +0,0 @@
|
||||
Fixed newly joined rooms failing to sync their full state (including the room name) to clients using legacy sync.
|
||||
@@ -1 +0,0 @@
|
||||
Fixed `create` being returned as a supported prompt value regardless of if registration is enabled or not. Contributed by @ginger
|
||||
@@ -1 +0,0 @@
|
||||
Updated an out-of-date statement about Oracle Linux release cadences.
|
||||
@@ -1 +0,0 @@
|
||||
Fixed high CPU usage when multiple clients from the same account were connected at once. Each sync woke the account's other sync loops, causing them to wake each other in a loop.
|
||||
@@ -1 +0,0 @@
|
||||
Fix joining restricted rooms over federation failing with signature verification error.
|
||||
@@ -15,8 +15,8 @@ ## Overview
|
||||
|
||||
:::warning Oracle Linux support
|
||||
Due to upstream limitations, Terra is only usable on Oracle Linux if you use [upstream EPEL](https://docs.fedoraproject.org/en-US/epel/getting-started/)
|
||||
rather than Oracle's rebuilds of EPEL. Oracle tends to lag behind on new EL versions, both major and minor—for example, Oracle's release of 10.2 lagged
|
||||
approximately 1.5 months behind other ELs—so you may encounter further compatibility issues with EPEL.
|
||||
rather than Oracle's rebuilds of EPEL. Oracle tends to lag behind on new EL versions, both major and minor—for example, as of the time of writing, 10.2 has been
|
||||
available for over a month through other ELs, but Oracle has not yet released corresponding updates—so you may encounter further compatibility issues with EPEL.
|
||||
**For this reason, it is recommended that you use another EL distribution if at all possible.**
|
||||
:::
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
target:
|
||||
target.fromToolchainName {
|
||||
name = (lib.importTOML "${inputs.self}/rust-toolchain.toml").toolchain.channel;
|
||||
sha256 = "sha256-A1abGIbOtcBSdrUMhDGrER3pRM1hQP4fp9gh3Y4PKc8=";
|
||||
sha256 = "sha256-OATSZm98Es5kIFuqaba+UvkQtFsVgJEBMmS+t6od5/U=";
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
Generated
+13
-13
@@ -419,9 +419,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspress/core": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/core/-/core-2.0.18.tgz",
|
||||
"integrity": "sha512-DBpsr/6XAItQkZPL5FSjthLGuzdG9ks/7EOxkqaaXp93uAT54LO0PD5Dkoy6ydup1PnvkpHyiHpUyDcnHHTVhg==",
|
||||
"version": "2.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/core/-/core-2.0.17.tgz",
|
||||
"integrity": "sha512-oynYHE2aBj7BFzD/UgqKnrj/PyYxfWGrmRPlTmjLL22UPcocdUdFYLb/6nijyseAST59VMr5Gj4wOfFUeJT5vw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -429,7 +429,7 @@
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@rsbuild/core": "^2.1.5",
|
||||
"@rsbuild/plugin-react": "~2.1.0",
|
||||
"@rspress/shared": "2.0.18",
|
||||
"@rspress/shared": "2.0.17",
|
||||
"@shikijs/rehype": "^4.2.0",
|
||||
"@types/unist": "^3.0.3",
|
||||
"@unhead/react": "^2.1.15",
|
||||
@@ -472,9 +472,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspress/plugin-client-redirects": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/plugin-client-redirects/-/plugin-client-redirects-2.0.18.tgz",
|
||||
"integrity": "sha512-q70ufN0S125kl0QlucO7JuiZ0WJaxFOtPPoGu7eWwam9fb+i4YsWnOWtAKSKGH4JP5+897mgpDPP+dmfl9DPCw==",
|
||||
"version": "2.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/plugin-client-redirects/-/plugin-client-redirects-2.0.17.tgz",
|
||||
"integrity": "sha512-HJMlW5xsQe2cAFYNHw3LhMy8WcuqRb4l0ey3E3OIu121ymbSLqIQJVPS/IF/NuoyPoxCAmV58LYIaCxGEriUww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -485,9 +485,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspress/plugin-sitemap": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/plugin-sitemap/-/plugin-sitemap-2.0.18.tgz",
|
||||
"integrity": "sha512-XfaXzZx+ASN9OMTc0fOvW8QmbmyCyDWkO8xJj4XF4E0YJtsu7j/65+v8/KXXdwpywOfUIQ6M94/meRTy2urNmQ==",
|
||||
"version": "2.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/plugin-sitemap/-/plugin-sitemap-2.0.17.tgz",
|
||||
"integrity": "sha512-MpNxF+hFK7zsk8t+Ta7KYI/Py6Csjyw0AMGrWf3XG/kr0+fisVxbT/jUanXRLSoj2m9VX9CVsi180rPnezUpbQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -498,9 +498,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspress/shared": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/shared/-/shared-2.0.18.tgz",
|
||||
"integrity": "sha512-GJswqJQCPSxvBt5r+gJzz8Em8EEK/sOmCQjTpemTvldvxr1Lva85BGVnSQZOgofnM0nrjt18kn4mmnuuSstbpA==",
|
||||
"version": "2.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/shared/-/shared-2.0.17.tgz",
|
||||
"integrity": "sha512-ZzpZ4hm5svgwU0w5CpTLZy4vFD3uPo8+gXtWMOREYMzwfzJeHqwOyXwxHf6byGpx85mVK5DQqMDi61meAS7ZUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -60,13 +60,6 @@
|
||||
"matchPackageNames": ["rust", "rustc", "cargo"],
|
||||
"groupName": "rust-toolchain"
|
||||
},
|
||||
{
|
||||
"description": "Delay major rust toolchain updates",
|
||||
"matchManagers": ["custom.regex"],
|
||||
"matchPackageNames": ["rust", "rustc", "cargo"],
|
||||
"matchUpdateTypes": ["major"],
|
||||
"minimumReleaseAge": "30 days"
|
||||
},
|
||||
{
|
||||
"description": "Batch minor and patch GitHub Actions updates",
|
||||
"matchManagers": ["github-actions"],
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
|
||||
[toolchain]
|
||||
profile = "minimal"
|
||||
channel = "1.97.1"
|
||||
channel = "1.97.0"
|
||||
components = [
|
||||
# For rust-analyzer
|
||||
"rust-src",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, err, info,
|
||||
pdu::PartialPdu,
|
||||
@@ -26,7 +27,7 @@
|
||||
use service::{mailer::messages, uiaa::UiaaInitiator, users::HashedPassword};
|
||||
|
||||
use super::DEVICE_ID_LENGTH;
|
||||
use crate::{Ruma, client_ip::ClientIp, router::ClientIdentity};
|
||||
use crate::{Ruma, router::ClientIdentity};
|
||||
|
||||
pub(crate) mod register;
|
||||
pub(crate) mod threepid;
|
||||
@@ -42,9 +43,10 @@
|
||||
///
|
||||
/// Note: This will not reserve the username, so the username might become
|
||||
/// invalid when trying to register
|
||||
#[tracing::instrument(skip_all, name = "register_available", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "register_available", level = "info")]
|
||||
pub(crate) async fn get_register_available_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_username_availability::v3::Request>,
|
||||
) -> Result<get_username_availability::v3::Response> {
|
||||
let _ = services
|
||||
@@ -78,9 +80,10 @@ pub(crate) async fn get_register_available_route(
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
#[tracing::instrument(skip_all, name = "change_password", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "change_password", level = "info")]
|
||||
pub(crate) async fn change_password_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<change_password::v3::Request>,
|
||||
) -> Result<change_password::v3::Response> {
|
||||
let identity = if let Some(identity) = body.identity.as_ref() {
|
||||
@@ -245,9 +248,10 @@ pub(crate) async fn whoami_route(
|
||||
/// - Forgets all to-device events
|
||||
/// - Triggers device list updates
|
||||
/// - Removes ability to log in again
|
||||
#[tracing::instrument(skip_all, name = "deactivate", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "deactivate", level = "info")]
|
||||
pub(crate) async fn deactivate_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<deactivate::v3::Request>,
|
||||
) -> Result<deactivate::v3::Response> {
|
||||
// Authentication for this endpoint is technically optional,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug_info, info,
|
||||
utils::{self},
|
||||
@@ -25,7 +26,7 @@
|
||||
};
|
||||
|
||||
use super::DEVICE_ID_LENGTH;
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/v3/register`
|
||||
///
|
||||
@@ -35,10 +36,10 @@
|
||||
/// /_matrix/client/v3/register/available`](fn.get_register_available_route.
|
||||
/// html) to check if the user id is valid and available.
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[tracing::instrument(skip_all, name = "register", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "register", level = "info")]
|
||||
pub(crate) async fn register_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp, // NOTE: Required for metadata.
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<register::v3::Request>,
|
||||
) -> Result<register::v3::Response> {
|
||||
if body.kind != RegistrationKind::User {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Result, at};
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
@@ -11,16 +12,17 @@
|
||||
assign,
|
||||
};
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
const MAX_BATCH_EVENTS: usize = 50;
|
||||
|
||||
/// # `PUT /_matrix/client/../dehydrated_device`
|
||||
///
|
||||
/// Creates or overwrites the user's dehydrated device.
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument(skip_all, fields(%client))]
|
||||
pub(crate) async fn put_dehydrated_device_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<put_dehydrated_device::Request>,
|
||||
) -> Result<put_dehydrated_device::Response> {
|
||||
let device_id = body.device_id.clone();
|
||||
@@ -36,9 +38,10 @@ pub(crate) async fn put_dehydrated_device_route(
|
||||
/// # `DELETE /_matrix/client/../dehydrated_device`
|
||||
///
|
||||
/// Deletes the user's dehydrated device without replacement.
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument(skip_all, fields(%client))]
|
||||
pub(crate) async fn delete_dehydrated_device_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<delete_dehydrated_device::Request>,
|
||||
) -> Result<delete_dehydrated_device::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -53,9 +56,10 @@ pub(crate) async fn delete_dehydrated_device_route(
|
||||
/// # `GET /_matrix/client/../dehydrated_device`
|
||||
///
|
||||
/// Gets the user's dehydrated device
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument(skip_all, fields(%client))]
|
||||
pub(crate) async fn get_dehydrated_device_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_dehydrated_device::Request>,
|
||||
) -> Result<get_dehydrated_device::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -68,9 +72,10 @@ pub(crate) async fn get_dehydrated_device_route(
|
||||
/// # `GET /_matrix/client/../dehydrated_device/{device_id}/events`
|
||||
///
|
||||
/// Paginates the events of the dehydrated device.
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument(skip_all, fields(%client))]
|
||||
pub(crate) async fn get_dehydrated_events_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_events::Request>,
|
||||
) -> Result<get_events::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Result, debug, err, utils};
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
@@ -8,7 +9,7 @@
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{Ruma, client::DEVICE_ID_LENGTH, client_ip::ClientIp};
|
||||
use crate::{Ruma, client::DEVICE_ID_LENGTH};
|
||||
|
||||
/// # `GET /_matrix/client/r0/devices`
|
||||
///
|
||||
@@ -45,10 +46,10 @@ pub(crate) async fn get_device_route(
|
||||
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
||||
///
|
||||
/// Updates the metadata on a given device of the sender user.
|
||||
#[tracing::instrument(skip_all, name = "update_device", level = "debug")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "update_device", level = "debug")]
|
||||
pub(crate) async fn update_device_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp, // NOTE: Required for updating device metadata
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<update_device::v3::Request>,
|
||||
) -> Result<update_device::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, err, info,
|
||||
utils::{
|
||||
@@ -27,16 +28,17 @@
|
||||
};
|
||||
use tokio::join;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/v3/publicRooms`
|
||||
///
|
||||
/// Lists the public rooms on this server.
|
||||
///
|
||||
/// - Rooms are ordered by the number of joined members
|
||||
#[tracing::instrument(skip_all, name = "publicrooms", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms", level = "info")]
|
||||
pub(crate) async fn get_public_rooms_filtered_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
@@ -69,9 +71,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
/// Lists the public rooms on this server.
|
||||
///
|
||||
/// - Rooms are ordered by the number of joined members
|
||||
#[tracing::instrument(skip_all, name = "publicrooms", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms", level = "info")]
|
||||
pub(crate) async fn get_public_rooms_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_public_rooms::v3::Request>,
|
||||
) -> Result<get_public_rooms::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
@@ -103,9 +106,10 @@ pub(crate) async fn get_public_rooms_route(
|
||||
/// # `PUT /_matrix/client/r0/directory/list/room/{roomId}`
|
||||
///
|
||||
/// Sets the visibility of a given room in the room directory.
|
||||
#[tracing::instrument(skip_all, name = "room_directory", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "room_directory", level = "info")]
|
||||
pub(crate) async fn set_room_visibility_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<set_room_visibility::v3::Request>,
|
||||
) -> Result<set_room_visibility::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
+37
-1
@@ -1,6 +1,7 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, err,
|
||||
utils::{self, content_disposition::make_content_disposition, math::ruma_from_usize},
|
||||
@@ -23,7 +24,7 @@
|
||||
};
|
||||
use service::media::mxc::Mxc;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/client/v1/media/config`
|
||||
pub(crate) async fn get_media_config_route(
|
||||
@@ -41,8 +42,15 @@ pub(crate) async fn get_media_config_route(
|
||||
///
|
||||
/// - Some metadata will be saved in the database
|
||||
/// - Media will be saved in the media/ directory
|
||||
#[tracing::instrument(
|
||||
name = "media_upload",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client),
|
||||
)]
|
||||
pub(crate) async fn create_content_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<create_content::v3::Response> {
|
||||
let user = body.identity.expect_sender_user()?;
|
||||
@@ -73,8 +81,15 @@ pub(crate) async fn create_content_route(
|
||||
/// # `GET /_matrix/client/v1/media/thumbnail/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server or over federation.
|
||||
#[tracing::instrument(
|
||||
name = "media_thumbnail_get",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client),
|
||||
)]
|
||||
pub(crate) async fn get_content_thumbnail_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_thumbnail::v1::Request>,
|
||||
) -> Result<get_content_thumbnail::v1::Response> {
|
||||
let user = body.identity.expect_sender_user()?;
|
||||
@@ -116,8 +131,15 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
/// # `GET /_matrix/client/v1/media/download/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media from our server or over federation.
|
||||
#[tracing::instrument(
|
||||
name = "media_get",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client),
|
||||
)]
|
||||
pub(crate) async fn get_content_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content::v1::Request>,
|
||||
) -> Result<get_content::v1::Response> {
|
||||
let user = body.identity.expect_sender_user()?;
|
||||
@@ -156,8 +178,15 @@ pub(crate) async fn get_content_route(
|
||||
/// # `GET /_matrix/client/v1/media/download/{serverName}/{mediaId}/{fileName}`
|
||||
///
|
||||
/// Load media from our server or over federation as fileName.
|
||||
#[tracing::instrument(
|
||||
name = "media_get_af",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client),
|
||||
)]
|
||||
pub(crate) async fn get_content_as_filename_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_as_filename::v1::Request>,
|
||||
) -> Result<get_content_as_filename::v1::Response> {
|
||||
let user = body.identity.expect_sender_user()?;
|
||||
@@ -200,8 +229,15 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
/// # `GET /_matrix/client/v1/media/preview_url`
|
||||
///
|
||||
/// Returns URL preview.
|
||||
#[tracing::instrument(
|
||||
name = "url_preview",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client),
|
||||
)]
|
||||
pub(crate) async fn get_media_preview_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_media_preview::v1::Request>,
|
||||
) -> Result<get_media_preview::v1::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, err,
|
||||
utils::{content_disposition::make_content_disposition, math::ruma_from_usize},
|
||||
@@ -16,7 +17,7 @@
|
||||
};
|
||||
use service::media::mxc::Mxc;
|
||||
|
||||
use crate::{Ruma, RumaResponse, client::create_content_route, client_ip::ClientIp};
|
||||
use crate::{Ruma, RumaResponse, client::create_content_route};
|
||||
|
||||
/// # `GET /_matrix/media/v3/config`
|
||||
///
|
||||
@@ -49,8 +50,10 @@ pub(crate) async fn get_media_config_legacy_legacy_route(
|
||||
/// # `GET /_matrix/media/v3/preview_url`
|
||||
///
|
||||
/// Returns URL preview.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "url_preview_legacy", level = "debug")]
|
||||
pub(crate) async fn get_media_preview_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<get_media_preview::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -92,9 +95,10 @@ pub(crate) async fn get_media_preview_legacy_route(
|
||||
/// Returns URL preview.
|
||||
pub(crate) async fn get_media_preview_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
||||
get_media_preview_legacy_route(State(services), body)
|
||||
get_media_preview_legacy_route(State(services), ClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -111,9 +115,10 @@ pub(crate) async fn get_media_preview_legacy_legacy_route(
|
||||
/// - Media will be saved in the media/ directory
|
||||
pub(crate) async fn create_content_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<RumaResponse<create_content::v3::Response>> {
|
||||
create_content_route(State(services), body)
|
||||
create_content_route(State(services), ClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -126,8 +131,10 @@ pub(crate) async fn create_content_legacy_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
|
||||
pub(crate) async fn get_content_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<get_content::v3::Response> {
|
||||
let mxc = Mxc {
|
||||
@@ -202,11 +209,13 @@ pub(crate) async fn get_content_legacy_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
|
||||
pub(crate) async fn get_content_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||
get_content_legacy_route(State(services), body)
|
||||
get_content_legacy_route(State(services), ClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -219,8 +228,10 @@ pub(crate) async fn get_content_legacy_legacy_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
|
||||
pub(crate) async fn get_content_as_filename_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<get_content_as_filename::v3::Response> {
|
||||
let mxc = Mxc {
|
||||
@@ -296,9 +307,10 @@ pub(crate) async fn get_content_as_filename_legacy_route(
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_as_filename_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
||||
get_content_as_filename_legacy_route(State(services), body)
|
||||
get_content_as_filename_legacy_route(State(services), ClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -311,8 +323,10 @@ pub(crate) async fn get_content_as_filename_legacy_legacy_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get_legacy", level = "debug")]
|
||||
pub(crate) async fn get_content_thumbnail_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<get_content_thumbnail::v3::Response> {
|
||||
let mxc = Mxc {
|
||||
@@ -390,9 +404,10 @@ pub(crate) async fn get_content_thumbnail_legacy_route(
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_thumbnail_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
||||
get_content_thumbnail_legacy_route(State(services), body)
|
||||
get_content_thumbnail_legacy_route(State(services), ClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug_error, err, info,
|
||||
matrix::{event::gen_event_id_canonical_json, pdu::PartialPdu},
|
||||
@@ -17,14 +18,15 @@
|
||||
use service::Services;
|
||||
|
||||
use super::banned_room_check;
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
|
||||
///
|
||||
/// Tries to send an invite event into the room.
|
||||
#[tracing::instrument(skip_all, name = "invite", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "invite", level = "info")]
|
||||
pub(crate) async fn invite_user_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<invite_user::v3::Request>,
|
||||
) -> Result<invite_user::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -40,8 +42,14 @@ pub(crate) async fn invite_user_route(
|
||||
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
||||
}
|
||||
|
||||
banned_room_check(&services, sender_user, Some(&body.room_id), body.room_id.server_name())
|
||||
.await?;
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
body.room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match &body.recipient {
|
||||
| invite_user::v3::InvitationRecipient::UserId(InviteUserId {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug,
|
||||
result::FlatOk,
|
||||
@@ -11,7 +12,7 @@
|
||||
};
|
||||
|
||||
use super::banned_room_check;
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
|
||||
///
|
||||
@@ -21,9 +22,10 @@
|
||||
/// rules locally
|
||||
/// - If the server does not know about the room: asks other servers over
|
||||
/// federation
|
||||
#[tracing::instrument(skip_all, name = "join", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "join", level = "info")]
|
||||
pub(crate) async fn join_room_by_id_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<join_room_by_id::v3::Request>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -31,8 +33,14 @@ pub(crate) async fn join_room_by_id_route(
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
|
||||
banned_room_check(&services, sender_user, Some(&body.room_id), body.room_id.server_name())
|
||||
.await?;
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
body.room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// There is no body.server_name for /roomId/join
|
||||
let mut servers: Vec<_> = services
|
||||
@@ -83,9 +91,10 @@ pub(crate) async fn join_room_by_id_route(
|
||||
/// - If the server does not know about the room: use the server name query
|
||||
/// param if specified. if not specified, asks other servers over federation
|
||||
/// via room alias server name and room ID server name
|
||||
#[tracing::instrument(skip_all, name = "join", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "join", level = "info")]
|
||||
pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
||||
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -96,9 +105,15 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
|
||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias.clone()) {
|
||||
| Ok(room_id) => {
|
||||
banned_room_check(&services, sender_user, Some(&room_id), room_id.server_name())
|
||||
.boxed()
|
||||
.await?;
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
let mut servers = body.via.clone();
|
||||
if servers.is_empty() {
|
||||
@@ -144,6 +159,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
Some(room_alias.server_name()),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::{borrow::Borrow, collections::HashMap, iter::once, sync::Arc};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug, debug_info, debug_warn, err, info,
|
||||
matrix::{
|
||||
@@ -39,14 +40,15 @@
|
||||
};
|
||||
|
||||
use super::banned_room_check;
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/*/knock/{roomIdOrAlias}`
|
||||
///
|
||||
/// Tries to knock the room to ask permission to join for the sender user.
|
||||
#[tracing::instrument(skip_all, name = "knock", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "knock", level = "info")]
|
||||
pub(crate) async fn knock_room_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<knock_room::v3::Request>,
|
||||
) -> Result<knock_room::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -57,8 +59,14 @@ pub(crate) async fn knock_room_route(
|
||||
|
||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias.clone()) {
|
||||
| Ok(room_id) => {
|
||||
banned_room_check(&services, sender_user, Some(&room_id), room_id.server_name())
|
||||
.await?;
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut servers = body.via.clone();
|
||||
servers.extend(
|
||||
@@ -101,6 +109,7 @@ pub(crate) async fn knock_room_route(
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
Some(room_alias.server_name()),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
mod members;
|
||||
mod unban;
|
||||
|
||||
use std::net::IpAddr;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, warn};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
@@ -56,6 +58,7 @@ pub(crate) async fn banned_room_check(
|
||||
user_id: &UserId,
|
||||
room_id: Option<&RoomId>,
|
||||
server_name: Option<&ServerName>,
|
||||
client_ip: IpAddr,
|
||||
) -> Result {
|
||||
if services.users.is_admin(user_id).await {
|
||||
return Ok(());
|
||||
@@ -82,7 +85,7 @@ pub(crate) async fn banned_room_check(
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned \
|
||||
room join"
|
||||
room join from IP {client_ip}"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
@@ -118,7 +121,7 @@ pub(crate) async fn banned_room_check(
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned \
|
||||
room join"
|
||||
room join from IP {client_ip}"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Error, Result, at, debug_warn,
|
||||
matrix::{
|
||||
@@ -37,7 +38,7 @@
|
||||
};
|
||||
use ruminuwuity::invite_permission_config::FilterLevel;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// list of safe and common non-state events to ignore if the user is ignored
|
||||
const IGNORED_MESSAGE_TYPES: &[TimelineEventType] = &[
|
||||
@@ -71,7 +72,7 @@
|
||||
/// where the user was joined, depending on `history_visibility`)
|
||||
pub(crate) async fn get_message_events_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client_ip): ClientIp, // NOTE: Required for updating device metadata
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<get_message_events::v3::Request>,
|
||||
) -> Result<get_message_events::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -37,17 +37,6 @@ pub(crate) async fn authorization_server_metadata(services: &Services) -> Value
|
||||
.join(super::BASE_PATH)
|
||||
.unwrap();
|
||||
|
||||
let prompt_values_supported = if services
|
||||
.uiaa
|
||||
.registration_flow_status()
|
||||
.await
|
||||
.any_available()
|
||||
{
|
||||
json!(["create"])
|
||||
} else {
|
||||
json!([])
|
||||
};
|
||||
|
||||
json!({
|
||||
"account_management_uri": endpoint_base.join(ACCOUNT_MANAGEMENT_PATH).unwrap(),
|
||||
"account_management_actions_supported": [
|
||||
@@ -63,7 +52,7 @@ pub(crate) async fn authorization_server_metadata(services: &Services) -> Value
|
||||
"grant_types_supported": ["authorization_code", "refresh_token"],
|
||||
"issuer": services.config.get_client_domain(),
|
||||
"jwks_uri": endpoint_base.join(JWKS_URI_PATH).unwrap(),
|
||||
"prompt_values_supported": prompt_values_supported,
|
||||
"prompt_values_supported": ["create"],
|
||||
"registration_endpoint": endpoint_base.join(CLIENT_REGISTER_PATH).unwrap(),
|
||||
"response_modes_supported": ["query", "fragment"],
|
||||
"response_types_supported": ["code"],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, PduCount, Result, err};
|
||||
use ruma::{
|
||||
MilliSecondsSinceUnixEpoch,
|
||||
@@ -12,7 +13,7 @@
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
|
||||
///
|
||||
@@ -116,7 +117,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
/// Sets private read marker and public read receipt EDU.
|
||||
pub(crate) async fn create_receipt_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client_ip): ClientIp, // NOTE: Required for updating device metadata
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<create_receipt::v3::Request>,
|
||||
) -> Result<create_receipt::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Result, matrix::pdu::PartialPdu};
|
||||
use ruma::{
|
||||
api::client::redact::redact_event, assign, events::room::redaction::RoomRedactionEventContent,
|
||||
};
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}`
|
||||
///
|
||||
@@ -13,7 +14,7 @@
|
||||
/// - TODO: Handle txn id
|
||||
pub(crate) async fn redact_event_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client_ip): ClientIp, // NOTE: required for updating device metadata
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<redact_event::v3::Request>,
|
||||
) -> Result<redact_event::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::{fmt::Write as _, time::Duration};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Event, Result, debug_info, info, matrix::pdu::PduEvent, utils::ReadyExt};
|
||||
use conduwuit_service::Services;
|
||||
use ruma::{
|
||||
@@ -13,7 +14,7 @@
|
||||
};
|
||||
use tokio::time::sleep;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
struct Report {
|
||||
sender: OwnedUserId,
|
||||
@@ -29,9 +30,10 @@ struct Report {
|
||||
/// # `POST /_matrix/client/v3/rooms/{roomId}/report`
|
||||
///
|
||||
/// Reports an abusive room to homeserver admins
|
||||
#[tracing::instrument(skip_all, name = "report_room", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "report_room", level = "info")]
|
||||
pub(crate) async fn report_room_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<report_room::v3::Request>,
|
||||
) -> Result<report_room::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -83,9 +85,10 @@ pub(crate) async fn report_room_route(
|
||||
/// # `POST /_matrix/client/v3/rooms/{roomId}/report/{eventId}`
|
||||
///
|
||||
/// Reports an inappropriate event to homeserver admins
|
||||
#[tracing::instrument(skip_all, name = "report_event", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "report_event", level = "info")]
|
||||
pub(crate) async fn report_event_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<report_content::v3::Request>,
|
||||
) -> Result<report_content::v3::Response> {
|
||||
// user authentication
|
||||
@@ -126,9 +129,10 @@ pub(crate) async fn report_event_route(
|
||||
Ok(report_content::v3::Response::new())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, name = "report_user", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "report_user", level = "info")]
|
||||
pub(crate) async fn report_user_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<report_user::v3::Request>,
|
||||
) -> Result<report_user::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Result};
|
||||
use ruma::api::client::room::get_summary;
|
||||
use service::rooms::summary::Accessibility;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp, router::ClientIdentity};
|
||||
use crate::{Ruma, router::ClientIdentity};
|
||||
|
||||
/// # `GET /_matrix/client/v1/room_summary/{roomIdOrAlias}`
|
||||
///
|
||||
/// Returns a short description of the state of a room.
|
||||
#[tracing::instrument(skip_all, name = "room_summary", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "room_summary", level = "info")]
|
||||
pub(crate) async fn get_room_summary(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_summary::v1::Request>,
|
||||
) -> Result<get_summary::v1::Response> {
|
||||
let (room_id, servers) = services
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Result, err, matrix::pdu::PartialPdu, utils};
|
||||
use ruma::{api::client::message::send_message_event, events::MessageLikeEventType};
|
||||
use serde_json::from_str;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`
|
||||
///
|
||||
@@ -18,7 +19,7 @@
|
||||
/// allowed
|
||||
pub(crate) async fn send_message_event_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client_ip): ClientIp, // NOTE: required for updating device metadata
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<send_message_event::v3::Request>,
|
||||
) -> Result<send_message_event::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug, err, info,
|
||||
utils::{self, ReadyExt, stream::BroadbandExt},
|
||||
@@ -31,15 +32,16 @@
|
||||
use service::users::DeviceToken;
|
||||
|
||||
use super::DEVICE_ID_LENGTH;
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/client/v3/login`
|
||||
///
|
||||
/// Get the supported login types of this server. One of these should be used as
|
||||
/// the `type` field when logging in.
|
||||
#[tracing::instrument(skip_all, name = "login", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "login", level = "info")]
|
||||
pub(crate) async fn get_login_types_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
_body: Ruma<get_login_types::v3::Request>,
|
||||
) -> Result<get_login_types::v3::Response> {
|
||||
if !services.config.oauth.compatibility_mode().uiaa_available() {
|
||||
@@ -113,10 +115,10 @@ pub async fn handle_login(
|
||||
/// Note: You can use [`GET
|
||||
/// /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
|
||||
/// supported login types.
|
||||
#[tracing::instrument(skip_all, name = "login", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "login", level = "info")]
|
||||
pub(crate) async fn login_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp, // NOTE: Required for device metadata
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<login::v3::Request>,
|
||||
) -> Result<login::v3::Response> {
|
||||
if !services.config.oauth.compatibility_mode().uiaa_available() {
|
||||
@@ -253,9 +255,10 @@ pub(crate) async fn login_route(
|
||||
/// to log in with the m.login.token flow.
|
||||
///
|
||||
/// <https://spec.matrix.org/v1.13/client-server-api/#post_matrixclientv1loginget_token>
|
||||
#[tracing::instrument(skip_all, name = "login_token", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "login_token", level = "info")]
|
||||
pub(crate) async fn login_token_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_login_token::v1::Request>,
|
||||
) -> Result<get_login_token::v1::Response> {
|
||||
if !services.config.login_via_existing_session {
|
||||
@@ -288,9 +291,10 @@ pub(crate) async fn login_token_route(
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
#[tracing::instrument(skip_all, name = "logout", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "logout", level = "info")]
|
||||
pub(crate) async fn logout_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<logout::v3::Request>,
|
||||
) -> Result<logout::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -335,9 +339,10 @@ pub(crate) async fn logout_route(
|
||||
/// Note: This is equivalent to calling [`GET
|
||||
/// /_matrix/client/r0/logout`](fn.logout_route.html) from each device of this
|
||||
/// user.
|
||||
#[tracing::instrument(skip_all, name = "logout", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "logout", level = "info")]
|
||||
pub(crate) async fn logout_all_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<logout_all::v3::Request>,
|
||||
) -> Result<logout_all::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, err,
|
||||
matrix::{Event, pdu::PartialPdu},
|
||||
@@ -27,14 +28,14 @@
|
||||
};
|
||||
use serde_json::{json, value::to_raw_value};
|
||||
|
||||
use crate::{Ruma, RumaResponse, client_ip::ClientIp};
|
||||
use crate::{Ruma, RumaResponse};
|
||||
|
||||
/// # `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}`
|
||||
///
|
||||
/// Sends a state event into the room.
|
||||
pub(crate) async fn send_state_event_for_key_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(ip): ClientIp, // NOTE: Required for updating device metadata
|
||||
ClientIp(ip): ClientIp,
|
||||
body: Ruma<send_state_event::v3::Request>,
|
||||
) -> Result<send_state_event::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
@@ -71,7 +72,7 @@ pub(crate) async fn send_state_event_for_key_route(
|
||||
/// Sends a state event into the room.
|
||||
pub(crate) async fn send_state_event_for_empty_key_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(ip): ClientIp, // NOTE: Required for updating device metadata
|
||||
ClientIp(ip): ClientIp,
|
||||
body: Ruma<send_state_event::v3::Request>,
|
||||
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
||||
send_state_event_for_key_route(State(services), ClientIp(ip), body)
|
||||
|
||||
@@ -269,22 +269,13 @@ async fn build_state_and_timeline(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let (notification_counts, joined_since_last_sync) = try_join(
|
||||
let (state_events, notification_counts, joined_since_last_sync) = try_join3(
|
||||
build_state_events(services, sync_context, room_id, shortstatehashes, &timeline),
|
||||
build_notification_counts(services, sync_context, room_id, &timeline),
|
||||
check_joined_since_last_sync(services, shortstatehashes, sync_context),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let state_events = build_state_events(
|
||||
services,
|
||||
sync_context,
|
||||
room_id,
|
||||
shortstatehashes,
|
||||
&timeline,
|
||||
joined_since_last_sync,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// the timeline should always include at least one PDU if the syncing user
|
||||
// joined since the last sync, that being the syncing user's join event. if
|
||||
// it's empty something is wrong.
|
||||
@@ -468,7 +459,6 @@ async fn build_state_events(
|
||||
room_id: &RoomId,
|
||||
shortstatehashes: ShortStateHashes,
|
||||
timeline: &TimelinePdus,
|
||||
joined_since_last_sync: bool,
|
||||
) -> Result<Vec<PduEvent>> {
|
||||
let SyncContext {
|
||||
syncing_user,
|
||||
@@ -498,10 +488,9 @@ async fn build_state_events(
|
||||
/*
|
||||
if `last_sync_end_count` is Some (meaning this is an incremental sync), and `last_sync_end_shortstatehash`
|
||||
is Some (meaning the syncing user didn't just join this room for the first time ever), and `full_state` is false,
|
||||
and the user didn't just join the room since the last sync, then use `build_state_incremental`.
|
||||
then use `build_state_incremental`.
|
||||
*/
|
||||
| (Some(_), Some(last_sync_end_shortstatehash))
|
||||
if !full_state && !joined_since_last_sync =>
|
||||
| (Some(_), Some(last_sync_end_shortstatehash)) if !full_state =>
|
||||
build_state_incremental(
|
||||
services,
|
||||
syncing_user,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, at, error, extract_variant,
|
||||
utils::{
|
||||
@@ -47,7 +48,6 @@
|
||||
is_ignored_invite,
|
||||
sync::v3::{joined::load_joined_room, left::load_left_room},
|
||||
},
|
||||
client_ip::ClientIp,
|
||||
};
|
||||
|
||||
/// The default maximum number of events to return in the `timeline` key of
|
||||
@@ -181,7 +181,7 @@ fn lazy_loading_enabled(&self) -> bool {
|
||||
)]
|
||||
pub(crate) async fn sync_events_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client_ip): ClientIp, // NOTE: Required for updating device metadata
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<sync_events::v3::Request>,
|
||||
) -> Result<sync_events::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Error, Result, at, error, extract_variant, is_equal_to,
|
||||
matrix::{Event, TypeStateKey, pdu::PduCount},
|
||||
@@ -48,7 +49,6 @@
|
||||
client::{
|
||||
DEFAULT_BUMP_TYPES, TimelinePdus, ignored_filter, is_ignored_invite, sync::load_timeline,
|
||||
},
|
||||
client_ip::ClientIp,
|
||||
};
|
||||
|
||||
type SyncInfo<'a> = (&'a UserId, &'a DeviceId, u64, &'a sync_events::v5::Request);
|
||||
@@ -67,7 +67,7 @@
|
||||
/// [MSC4186]: https://github.com/matrix-org/matrix-spec-proposals/pull/4186
|
||||
pub(crate) async fn sync_events_v5_route(
|
||||
State(ref services): State<crate::State>,
|
||||
ClientIp(client_ip): ClientIp, // NOTE: Required for updating device metadata
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<sync_events::v5::Request>,
|
||||
) -> Result<sync_events::v5::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Result, utils, utils::math::Tried};
|
||||
use ruma::api::client::typing::create_typing_event::{self, v3::TypingInfo};
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}`
|
||||
///
|
||||
/// Sets the typing state of the sender user.
|
||||
pub(crate) async fn create_typing_event_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(ip): ClientIp, // NOTE: Required for updating device metadata
|
||||
ClientIp(ip): ClientIp,
|
||||
body: Ruma<create_typing_event::v3::Request>,
|
||||
) -> Result<create_typing_event::v3::Response> {
|
||||
use create_typing_event::v3::Typing;
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
};
|
||||
|
||||
use axum::extract::{ConnectInfo, FromRequestParts};
|
||||
use axum_client_ip::ClientIp as SourcedClientIp;
|
||||
use conduwuit::debug_warn;
|
||||
use http::request::Parts;
|
||||
|
||||
const UNKNOWN_IP: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
|
||||
|
||||
/// [`ClientIp`] extractor that falls back to the connection peer address
|
||||
/// instead of rejecting the request when `request_ip_source` can't be resolved.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct ClientIp(pub IpAddr);
|
||||
|
||||
impl<S> FromRequestParts<S> for ClientIp
|
||||
where
|
||||
S: Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let ip = match SourcedClientIp::from_request_parts(parts, state).await {
|
||||
| Ok(SourcedClientIp(ip)) => ip,
|
||||
| Err(rejection) => {
|
||||
debug_warn!(
|
||||
%rejection,
|
||||
"Could not resolve client IP from request_ip_source; using peer address"
|
||||
);
|
||||
parts
|
||||
.extensions
|
||||
.get::<ConnectInfo<SocketAddr>>()
|
||||
.map_or(UNKNOWN_IP, |ConnectInfo(addr)| addr.ip())
|
||||
},
|
||||
};
|
||||
|
||||
Ok(Self(ip))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
use axum_client_ip::ClientIpSource;
|
||||
|
||||
use super::*;
|
||||
|
||||
const PEER: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(198, 51, 100, 7)), 8448);
|
||||
const PEER_V6: SocketAddr =
|
||||
SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0x2001, 0xDB8, 0, 0, 0, 0, 0, 1)), 8448);
|
||||
|
||||
fn parts(
|
||||
source: Option<ClientIpSource>,
|
||||
peer: Option<SocketAddr>,
|
||||
headers: &[(&str, &str)],
|
||||
) -> Parts {
|
||||
let mut builder = http::Request::builder();
|
||||
for (name, value) in headers {
|
||||
builder = builder.header(*name, *value);
|
||||
}
|
||||
|
||||
let (mut parts, ()) = builder.body(()).unwrap().into_parts();
|
||||
if let Some(source) = source {
|
||||
parts.extensions.insert(source);
|
||||
}
|
||||
if let Some(peer) = peer {
|
||||
parts.extensions.insert(ConnectInfo(peer));
|
||||
}
|
||||
|
||||
parts
|
||||
}
|
||||
|
||||
async fn extract(mut parts: Parts) -> IpAddr {
|
||||
let ClientIp(ip) = ClientIp::from_request_parts(&mut parts, &()).await.unwrap();
|
||||
ip
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolves_from_configured_source() {
|
||||
let parts =
|
||||
parts(Some(ClientIpSource::XRealIp), Some(PEER), &[("x-real-ip", "203.0.113.5")]);
|
||||
|
||||
assert_eq!(extract(parts).await, IpAddr::V4(Ipv4Addr::new(203, 0, 113, 5)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolves_ipv6_from_configured_source() {
|
||||
let parts =
|
||||
parts(Some(ClientIpSource::XRealIp), Some(PEER), &[("x-real-ip", "2001:db8::2")]);
|
||||
|
||||
assert_eq!(
|
||||
extract(parts).await,
|
||||
IpAddr::V6(Ipv6Addr::new(0x2001, 0xDB8, 0, 0, 0, 0, 0, 2))
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn falls_back_to_peer_when_header_missing() {
|
||||
let parts = parts(Some(ClientIpSource::XRealIp), Some(PEER), &[]);
|
||||
|
||||
assert_eq!(extract(parts).await, PEER.ip());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn falls_back_to_ipv6_peer_when_header_missing() {
|
||||
let parts = parts(Some(ClientIpSource::XRealIp), Some(PEER_V6), &[]);
|
||||
|
||||
assert_eq!(extract(parts).await, PEER_V6.ip());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn falls_back_to_peer_when_header_unparsable() {
|
||||
let parts =
|
||||
parts(Some(ClientIpSource::XRealIp), Some(PEER), &[("x-real-ip", "not-an-ip")]);
|
||||
|
||||
assert_eq!(extract(parts).await, PEER.ip());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn falls_back_to_peer_when_source_unset() {
|
||||
let parts = parts(None, Some(PEER), &[("x-real-ip", "203.0.113.5")]);
|
||||
|
||||
assert_eq!(extract(parts).await, PEER.ip());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn falls_back_to_unspecified_without_peer() {
|
||||
let parts = parts(Some(ClientIpSource::XRealIp), None, &[]);
|
||||
|
||||
assert_eq!(extract(parts).await, UNKNOWN_IP);
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,6 @@
|
||||
pub mod router;
|
||||
pub mod server;
|
||||
|
||||
pub(crate) mod client_ip;
|
||||
|
||||
pub mod admin;
|
||||
|
||||
pub(crate) use self::router::{Ruma, RumaResponse, State};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::{HashMap, hash_map::Entry};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use conduwuit::{
|
||||
Err, Error, EventTypeExt, PduEvent, Result, debug, err, error,
|
||||
@@ -22,14 +23,15 @@
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp, server::utils::validate_any_membership_event};
|
||||
use crate::{Ruma, server::utils::validate_any_membership_event};
|
||||
|
||||
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
|
||||
///
|
||||
/// Invites a remote user to a room.
|
||||
#[tracing::instrument(skip_all, name = "invite", level = "info")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "invite", level = "info")]
|
||||
pub(crate) async fn create_invite_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<create_invite::v2::Request>,
|
||||
) -> Result<create_invite::v2::Response> {
|
||||
if !services.server.supported_room_version(&body.room_version) {
|
||||
|
||||
+16
-3
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Result, utils::content_disposition::make_content_disposition};
|
||||
use conduwuit_service::media::{Dim, FileMeta};
|
||||
use ruma::api::federation::authenticated_media::{
|
||||
@@ -6,14 +7,20 @@
|
||||
};
|
||||
use service::media::mxc::Mxc;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/federation/v1/media/download/{mediaId}`
|
||||
///
|
||||
/// Load media from our server.
|
||||
#[tracing::instrument(name = "media_get", level = "debug", skip_all)]
|
||||
#[tracing::instrument(
|
||||
name = "media_get",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client)
|
||||
)]
|
||||
pub(crate) async fn get_content_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content::v1::Request>,
|
||||
) -> Result<get_content::v1::Response> {
|
||||
let mxc = Mxc {
|
||||
@@ -47,9 +54,15 @@ pub(crate) async fn get_content_route(
|
||||
/// # `GET /_matrix/federation/v1/media/thumbnail/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server.
|
||||
#[tracing::instrument(name = "media_thumbnail_get", level = "debug", skip_all)]
|
||||
#[tracing::instrument(
|
||||
name = "media_thumbnail_get",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client)
|
||||
)]
|
||||
pub(crate) async fn get_content_thumbnail_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_thumbnail::v1::Request>,
|
||||
) -> Result<get_content_thumbnail::v1::Response> {
|
||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{Err, Result, err};
|
||||
use ruma::{
|
||||
api::federation::directory::{get_public_rooms, get_public_rooms_filtered},
|
||||
@@ -6,14 +7,15 @@
|
||||
directory::Filter,
|
||||
};
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/federation/v1/publicRooms`
|
||||
///
|
||||
/// Lists the public rooms on this server.
|
||||
#[tracing::instrument(name = "publicrooms", level = "debug", skip_all)]
|
||||
#[tracing::instrument(name = "publicrooms", level = "debug", skip_all, fields(%client))]
|
||||
pub(crate) async fn get_public_rooms_filtered_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_public_rooms_filtered::v1::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v1::Response> {
|
||||
if !services
|
||||
@@ -46,9 +48,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
/// # `GET /_matrix/federation/v1/publicRooms`
|
||||
///
|
||||
/// Lists the public rooms on this server.
|
||||
#[tracing::instrument(name = "publicrooms", level = "debug", skip_all)]
|
||||
#[tracing::instrument(name = "publicrooms", level = "debug", skip_all, fields(%client))]
|
||||
pub(crate) async fn get_public_rooms_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_public_rooms::v1::Request>,
|
||||
) -> Result<get_public_rooms::v1::Response> {
|
||||
if !services
|
||||
|
||||
+34
-14
@@ -1,9 +1,11 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
net::IpAddr,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug, debug_error, debug_warn, err, error,
|
||||
result::LogErr,
|
||||
@@ -45,7 +47,7 @@
|
||||
use tokio::sync::watch::{Receiver, Sender};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
use crate::Ruma;
|
||||
|
||||
type ResolvedMap = BTreeMap<OwnedEventId, Result>;
|
||||
type Pdu = (OwnedRoomId, OwnedEventId, CanonicalJsonObject);
|
||||
@@ -55,6 +57,7 @@
|
||||
/// Push EDUs and PDUs to this server.
|
||||
pub(crate) async fn send_transaction_message_route(
|
||||
State(services): State<crate::State>,
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<send_transaction_message::v1::Request>,
|
||||
) -> Result<send_transaction_message::v1::Response> {
|
||||
if body.identity != body.body.origin {
|
||||
@@ -95,7 +98,7 @@ pub(crate) async fn send_transaction_message_route(
|
||||
services
|
||||
.server
|
||||
.runtime()
|
||||
.spawn(process_inbound_transaction(services, body, txn_key, sender));
|
||||
.spawn(process_inbound_transaction(services, body, client, txn_key, sender));
|
||||
// and wait for it
|
||||
wait_for_result(receiver).await
|
||||
},
|
||||
@@ -138,6 +141,7 @@ async fn wait_for_result(
|
||||
async fn process_inbound_transaction(
|
||||
services: crate::State,
|
||||
body: Ruma<send_transaction_message::v1::Request>,
|
||||
client: IpAddr,
|
||||
txn_key: TxnKey,
|
||||
sender: Sender<WrappedTransactionResponse>,
|
||||
) {
|
||||
@@ -159,7 +163,7 @@ async fn process_inbound_transaction(
|
||||
.stream();
|
||||
|
||||
debug!(pdus = body.pdus.len(), edus = body.edus.len(), "Processing transaction",);
|
||||
let results = match handle(&services, &body.identity, pdus, edus).await {
|
||||
let results = match handle(&services, &client, &body.identity, pdus, edus).await {
|
||||
| Ok(results) => results,
|
||||
| Err(err) => {
|
||||
fail_federation_txn(services, &txn_key, &sender, err);
|
||||
@@ -232,6 +236,7 @@ fn transaction_error_to_response(err: &TransactionError) -> Error {
|
||||
}
|
||||
async fn handle(
|
||||
services: &Services,
|
||||
client: &IpAddr,
|
||||
origin: &ServerName,
|
||||
pdus: impl Stream<Item = Pdu> + Send,
|
||||
edus: impl Stream<Item = Edu> + Send,
|
||||
@@ -252,7 +257,7 @@ async fn handle(
|
||||
.into_iter()
|
||||
.try_stream()
|
||||
.broad_and_then(|(room_id, pdus): (_, Vec<_>)| {
|
||||
handle_room(services, origin, room_id, pdus.into_iter())
|
||||
handle_room(services, client, origin, room_id, pdus.into_iter())
|
||||
.map_ok(Vec::into_iter)
|
||||
.map_ok(IterStream::try_stream)
|
||||
})
|
||||
@@ -262,7 +267,7 @@ async fn handle(
|
||||
.await?;
|
||||
|
||||
// Evaluate EDUs after PDUs in case some of the PDUs then forbid some EDUs.
|
||||
edus.for_each_concurrent(automatic_width(), |edu| handle_edu(services, origin, edu))
|
||||
edus.for_each_concurrent(automatic_width(), |edu| handle_edu(services, client, origin, edu))
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
@@ -271,6 +276,7 @@ async fn handle(
|
||||
|
||||
async fn handle_room(
|
||||
services: &Services,
|
||||
_client: &IpAddr,
|
||||
origin: &ServerName,
|
||||
room_id: OwnedRoomId,
|
||||
pdus: impl Iterator<Item = Pdu> + Send,
|
||||
@@ -317,25 +323,25 @@ async fn handle_room(
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
async fn handle_edu(services: &Services, origin: &ServerName, edu: Edu) {
|
||||
async fn handle_edu(services: &Services, client: &IpAddr, origin: &ServerName, edu: Edu) {
|
||||
match edu {
|
||||
| Edu::Presence(presence) if services.server.config.allow_incoming_presence =>
|
||||
handle_edu_presence(services, origin, presence).await,
|
||||
handle_edu_presence(services, client, origin, presence).await,
|
||||
|
||||
| Edu::Receipt(receipt) if services.server.config.allow_incoming_read_receipts =>
|
||||
handle_edu_receipt(services, origin, receipt).await,
|
||||
handle_edu_receipt(services, client, origin, receipt).await,
|
||||
|
||||
| Edu::Typing(typing) if services.server.config.allow_incoming_typing =>
|
||||
handle_edu_typing(services, origin, typing).await,
|
||||
handle_edu_typing(services, client, origin, typing).await,
|
||||
|
||||
| Edu::DeviceListUpdate(content) =>
|
||||
handle_edu_device_list_update(services, origin, content).await,
|
||||
handle_edu_device_list_update(services, client, origin, content).await,
|
||||
|
||||
| Edu::DirectToDevice(content) =>
|
||||
handle_edu_direct_to_device(services, origin, content).await,
|
||||
handle_edu_direct_to_device(services, client, origin, content).await,
|
||||
|
||||
| Edu::SigningKeyUpdate(content) =>
|
||||
handle_edu_signing_key_update(services, origin, content).await,
|
||||
handle_edu_signing_key_update(services, client, origin, content).await,
|
||||
|
||||
| Edu::_Custom(ref _custom) => debug_warn!(?edu, "received custom/unknown EDU"),
|
||||
|
||||
@@ -345,6 +351,7 @@ async fn handle_edu(services: &Services, origin: &ServerName, edu: Edu) {
|
||||
|
||||
async fn handle_edu_presence(
|
||||
services: &Services,
|
||||
_client: &IpAddr,
|
||||
origin: &ServerName,
|
||||
presence: PresenceContent,
|
||||
) {
|
||||
@@ -385,7 +392,12 @@ async fn handle_edu_presence_update(
|
||||
.ok();
|
||||
}
|
||||
|
||||
async fn handle_edu_receipt(services: &Services, origin: &ServerName, receipt: ReceiptContent) {
|
||||
async fn handle_edu_receipt(
|
||||
services: &Services,
|
||||
_client: &IpAddr,
|
||||
origin: &ServerName,
|
||||
receipt: ReceiptContent,
|
||||
) {
|
||||
receipt
|
||||
.receipts
|
||||
.into_iter()
|
||||
@@ -480,7 +492,12 @@ async fn handle_edu_receipt_room_user(
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn handle_edu_typing(services: &Services, origin: &ServerName, typing: TypingContent) {
|
||||
async fn handle_edu_typing(
|
||||
services: &Services,
|
||||
_client: &IpAddr,
|
||||
origin: &ServerName,
|
||||
typing: TypingContent,
|
||||
) {
|
||||
if typing.user_id.server_name() != origin {
|
||||
debug_warn!(
|
||||
%typing.user_id, %origin,
|
||||
@@ -540,6 +557,7 @@ async fn handle_edu_typing(services: &Services, origin: &ServerName, typing: Typ
|
||||
|
||||
async fn handle_edu_device_list_update(
|
||||
services: &Services,
|
||||
_client: &IpAddr,
|
||||
origin: &ServerName,
|
||||
content: DeviceListUpdateContent,
|
||||
) {
|
||||
@@ -558,6 +576,7 @@ async fn handle_edu_device_list_update(
|
||||
|
||||
async fn handle_edu_direct_to_device(
|
||||
services: &Services,
|
||||
_client: &IpAddr,
|
||||
origin: &ServerName,
|
||||
content: DirectDeviceContent,
|
||||
) {
|
||||
@@ -677,6 +696,7 @@ async fn handle_edu_direct_to_device_event(
|
||||
|
||||
async fn handle_edu_signing_key_update(
|
||||
services: &Services,
|
||||
_client: &IpAddr,
|
||||
origin: &ServerName,
|
||||
content: SigningKeyUpdateContent,
|
||||
) {
|
||||
|
||||
+1
-32
@@ -91,7 +91,7 @@ pub(crate) async fn validate_any_membership_event(
|
||||
expected_room_id: OwnedRoomId,
|
||||
expected_event_id: OwnedEventId,
|
||||
) -> Result<(CanonicalJsonObject, MembershipState, OwnedUserId, OwnedUserId)> {
|
||||
let (template_room_id, template_event_id, mut pdu) = services
|
||||
let (template_room_id, template_event_id, pdu) = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.parse_incoming_pdu(body, Some(room_version_rules))
|
||||
@@ -109,37 +109,6 @@ pub(crate) async fn validate_any_membership_event(
|
||||
))));
|
||||
}
|
||||
|
||||
// Only `join` events carry `join_authorised_via_users_server`; co-sign
|
||||
// restricted joins so verification passes. Authorisation is enforced in
|
||||
// create_join_event.
|
||||
let membership_is_join = pdu
|
||||
.get("content")
|
||||
.and_then(|v| v.as_object())
|
||||
.and_then(|c| c.get("membership"))
|
||||
.and_then(|v| v.as_str())
|
||||
.is_some_and(|m| m == "join");
|
||||
|
||||
let authorising_user = pdu
|
||||
.get("content")
|
||||
.and_then(|v| v.as_object())
|
||||
.and_then(|c| c.get("join_authorised_via_users_server"))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(UserId::parse)
|
||||
.and_then(Result::ok);
|
||||
|
||||
if room_version_rules.authorization.restricted_join_rule
|
||||
&& membership_is_join
|
||||
&& let Some(authorising_user) = authorising_user
|
||||
&& services.globals.user_is_local(&authorising_user)
|
||||
{
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut pdu, room_version_rules)
|
||||
.map_err(|e| {
|
||||
err!(Request(InvalidParam("Failed to sign restricted join event: {e}")))
|
||||
})?;
|
||||
}
|
||||
|
||||
services
|
||||
.server_keys
|
||||
.verify_event(&pdu, room_version_rules)
|
||||
|
||||
+8
-22
@@ -1,10 +1,10 @@
|
||||
use std::{any::Any, sync::Arc, time::Duration};
|
||||
|
||||
use axum::{
|
||||
Router, extract,
|
||||
extract::{DefaultBodyLimit, FromRequestParts, MatchedPath},
|
||||
Router,
|
||||
extract::{DefaultBodyLimit, MatchedPath},
|
||||
};
|
||||
use axum_client_ip::{ClientIp, ClientIpSource};
|
||||
use axum_client_ip::ClientIpSource;
|
||||
use conduwuit::{Result, Server, debug, error};
|
||||
use conduwuit_service::{Services, state::Guard};
|
||||
use http::{
|
||||
@@ -20,6 +20,7 @@
|
||||
timeout::{RequestBodyTimeoutLayer, ResponseBodyTimeoutLayer, TimeoutLayer},
|
||||
trace::{DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer},
|
||||
};
|
||||
use tracing::Level;
|
||||
|
||||
use crate::{request, router};
|
||||
|
||||
@@ -66,16 +67,15 @@ pub(crate) fn build(services: &Arc<Services>) -> Result<(Router, Guard)> {
|
||||
let services_ = services.clone();
|
||||
let layers = layers
|
||||
.layer(SetSensitiveHeadersLayer::new([header::AUTHORIZATION]))
|
||||
.layer(client_ip_layer.into_extension())
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
.make_span_with(tracing_span::<_>)
|
||||
.on_failure(DefaultOnFailure::new().level(tracing::Level::ERROR))
|
||||
.on_request(DefaultOnRequest::new().level(tracing::Level::TRACE))
|
||||
.on_response(DefaultOnResponse::new().level(tracing::Level::DEBUG)),
|
||||
.on_failure(DefaultOnFailure::new().level(Level::ERROR))
|
||||
.on_request(DefaultOnRequest::new().level(Level::TRACE))
|
||||
.on_response(DefaultOnResponse::new().level(Level::DEBUG)),
|
||||
)
|
||||
.layer(axum::middleware::from_fn(request_ip))
|
||||
.layer(axum::middleware::from_fn_with_state(Arc::clone(services), request::handle))
|
||||
.layer(client_ip_layer.into_extension())
|
||||
.layer(ResponseBodyTimeoutLayer::new(Duration::from_secs(
|
||||
server.config.client_response_timeout,
|
||||
)))
|
||||
@@ -230,25 +230,11 @@ fn tracing_span<T>(request: &http::Request<T>) -> tracing::Span {
|
||||
parent: None,
|
||||
debug::INFO_SPAN_LEVEL,
|
||||
"router",
|
||||
ip=tracing::field::Empty,
|
||||
method = %request.method(),
|
||||
%path,
|
||||
}
|
||||
}
|
||||
|
||||
/// Annotates the tracing span with the client IP
|
||||
async fn request_ip(
|
||||
request: extract::Request,
|
||||
next: axum::middleware::Next,
|
||||
) -> axum::response::Response {
|
||||
let (mut parts, body) = request.into_parts();
|
||||
if let Ok(ip) = ClientIp::from_request_parts(&mut parts, &()).await {
|
||||
let span = tracing::Span::current();
|
||||
span.record("ip", ip.0.to_string());
|
||||
}
|
||||
next.run(extract::Request::from_parts(parts, body)).await
|
||||
}
|
||||
|
||||
fn request_path_str<T>(request: &http::Request<T>) -> &str {
|
||||
request
|
||||
.uri()
|
||||
|
||||
+7
-89
@@ -5,7 +5,6 @@
|
||||
};
|
||||
|
||||
use conduwuit::{Err, Error, Result, error, utils};
|
||||
use futures::StreamExt;
|
||||
use lettre::Address;
|
||||
use ruma::{
|
||||
DeviceId, UserId,
|
||||
@@ -26,7 +25,7 @@
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
Dep, config, firstrun, globals,
|
||||
Dep, config, globals,
|
||||
oauth::{self, OAuthTicket},
|
||||
registration_tokens, threepid, users,
|
||||
};
|
||||
@@ -37,54 +36,25 @@ pub struct Service {
|
||||
}
|
||||
|
||||
struct Services {
|
||||
config: Dep<config::Service>,
|
||||
firstrun: Dep<firstrun::Service>,
|
||||
globals: Dep<globals::Service>,
|
||||
oauth: Dep<oauth::Service>,
|
||||
users: Dep<users::Service>,
|
||||
config: Dep<config::Service>,
|
||||
registration_tokens: Dep<registration_tokens::Service>,
|
||||
threepid: Dep<threepid::Service>,
|
||||
users: Dep<users::Service>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TrustedFlowStatus {
|
||||
Unavailable,
|
||||
Available,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UntrustedFlowStatus {
|
||||
Unavailable,
|
||||
Available {
|
||||
require_email: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct FlowStatus {
|
||||
pub trusted: TrustedFlowStatus,
|
||||
pub untrusted: UntrustedFlowStatus,
|
||||
}
|
||||
|
||||
impl FlowStatus {
|
||||
#[must_use]
|
||||
pub fn any_available(&self) -> bool {
|
||||
matches!(self.trusted, TrustedFlowStatus::Available)
|
||||
|| matches!(self.untrusted, UntrustedFlowStatus::Available { .. })
|
||||
}
|
||||
oauth: Dep<oauth::Service>,
|
||||
}
|
||||
|
||||
impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
Ok(Arc::new(Self {
|
||||
services: Services {
|
||||
config: args.depend::<config::Service>("config"),
|
||||
firstrun: args.depend::<firstrun::Service>("firstrun"),
|
||||
globals: args.depend::<globals::Service>("globals"),
|
||||
oauth: args.depend::<oauth::Service>("oauth"),
|
||||
users: args.depend::<users::Service>("users"),
|
||||
config: args.depend::<config::Service>("config"),
|
||||
registration_tokens: args
|
||||
.depend::<registration_tokens::Service>("registration_tokens"),
|
||||
threepid: args.depend::<threepid::Service>("threepid"),
|
||||
users: args.depend::<users::Service>("users"),
|
||||
oauth: args.depend::<oauth::Service>("oauth"),
|
||||
},
|
||||
uiaa_sessions: Mutex::new(HashMap::new()),
|
||||
}))
|
||||
@@ -625,56 +595,4 @@ async fn check_stage(
|
||||
|
||||
Ok((completed_auth_type, session_metadata))
|
||||
}
|
||||
|
||||
pub async fn registration_flow_status(&self) -> FlowStatus {
|
||||
// Allow registration if it's enabled in the config file or if this is the first
|
||||
// run (so the first user account can be created)
|
||||
let allow_registration =
|
||||
self.services.config.allow_registration || self.services.firstrun.is_first_run();
|
||||
|
||||
// Trusted flow is only available if any registration tokens exist
|
||||
let trusted = {
|
||||
if !allow_registration {
|
||||
TrustedFlowStatus::Unavailable
|
||||
} else if self
|
||||
.services
|
||||
.registration_tokens
|
||||
.iterate_tokens()
|
||||
.next()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
TrustedFlowStatus::Available
|
||||
} else {
|
||||
TrustedFlowStatus::Unavailable
|
||||
}
|
||||
};
|
||||
|
||||
// Untrusted flow is available if email is required for registration,
|
||||
// or reCAPTCHA is configured, or open registration is enabled
|
||||
let untrusted = {
|
||||
let require_email = self
|
||||
.services
|
||||
.config
|
||||
.smtp
|
||||
.as_ref()
|
||||
.is_some_and(|smtp| smtp.require_email_for_registration);
|
||||
|
||||
if !allow_registration || self.services.firstrun.is_first_run() {
|
||||
UntrustedFlowStatus::Unavailable
|
||||
} else if self.services.config.recaptcha_private_site_key.is_some() || require_email {
|
||||
UntrustedFlowStatus::Available { require_email }
|
||||
} else if self
|
||||
.services
|
||||
.config
|
||||
.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
{
|
||||
UntrustedFlowStatus::Available { require_email: false }
|
||||
} else {
|
||||
UntrustedFlowStatus::Unavailable
|
||||
}
|
||||
};
|
||||
|
||||
FlowStatus { trusted, untrusted }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +253,8 @@ pub async fn remove_to_device_events<Until>(
|
||||
self.db.todeviceid_events.del(key);
|
||||
})
|
||||
.await;
|
||||
|
||||
self.services.sync.wake(user_id).await;
|
||||
}
|
||||
|
||||
/// Updates device metadata and increments the device list version.
|
||||
|
||||
@@ -109,7 +109,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
services: Services {
|
||||
account_data: args.depend::<account_data::Service>("account_data"),
|
||||
admin: args.depend::<admin::Service>("admin"),
|
||||
alias: args.depend::<alias::Service>("rooms::alias"),
|
||||
alias: args.depend::<alias::Service>("alias"),
|
||||
appservice: args.depend::<appservice::Service>("appservice"),
|
||||
config: args.depend::<config::Service>("config"),
|
||||
firstrun: args.depend::<firstrun::Service>("firstrun"),
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
extract::{Expect, PostForm},
|
||||
pages::{
|
||||
GET_POST, Result, TemplateContext,
|
||||
account::register::{TrustedFlowStatus, UntrustedFlowStatus, registration_flow_status},
|
||||
components::UserCard,
|
||||
oidc::{OIDC_SESSION_ID_KEY, OidcSession, OidcSessionState},
|
||||
},
|
||||
@@ -100,13 +101,13 @@ async fn route_login(
|
||||
|
||||
LoginType::Oidc { redirect_url }
|
||||
} else {
|
||||
LoginType::Interactive {
|
||||
registration_available: services
|
||||
.uiaa
|
||||
.registration_flow_status()
|
||||
.await
|
||||
.any_available(),
|
||||
}
|
||||
let (trusted_flow_status, untrusted_flow_status) =
|
||||
registration_flow_status(&services).await;
|
||||
|
||||
let registration_available = matches!(trusted_flow_status, TrustedFlowStatus::Available)
|
||||
|| matches!(untrusted_flow_status, UntrustedFlowStatus::Available { .. });
|
||||
|
||||
LoginType::Interactive { registration_available }
|
||||
};
|
||||
|
||||
let body = match &user_id {
|
||||
|
||||
@@ -8,12 +8,9 @@
|
||||
};
|
||||
use conduwuit_core::{config::TermsDocument, warn};
|
||||
use conduwuit_service::{
|
||||
mailer::messages,
|
||||
registration_tokens::ValidToken,
|
||||
uiaa::{FlowStatus, TrustedFlowStatus, UntrustedFlowStatus},
|
||||
users::HashedPassword,
|
||||
mailer::messages, registration_tokens::ValidToken, users::HashedPassword,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use lettre::{Address, message::Mailbox};
|
||||
use ruma::{ClientSecret, OwnedClientSecret, OwnedServerName, OwnedSessionId, OwnedUserId};
|
||||
use serde::{Deserialize, Serialize, de::IgnoredAny};
|
||||
@@ -64,6 +61,20 @@ enum RegisterBody {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum TrustedFlowStatus {
|
||||
Unavailable,
|
||||
Available,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum UntrustedFlowStatus {
|
||||
Unavailable,
|
||||
Available {
|
||||
require_email: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub(crate) struct RegisterQuery {
|
||||
pub username: Option<String>,
|
||||
@@ -158,10 +169,7 @@ async fn route_register(
|
||||
ValidationErrors::new()
|
||||
};
|
||||
|
||||
let FlowStatus {
|
||||
trusted: trusted_flow_status,
|
||||
untrusted: untrusted_flow_status,
|
||||
} = services.uiaa.registration_flow_status().await;
|
||||
let (trusted_flow_status, untrusted_flow_status) = registration_flow_status(&services).await;
|
||||
|
||||
if matches!(trusted_flow_status, TrustedFlowStatus::Unavailable)
|
||||
&& matches!(untrusted_flow_status, UntrustedFlowStatus::Unavailable)
|
||||
@@ -532,3 +540,54 @@ async fn complete_registration(
|
||||
|
||||
Ok(Redirect::to(&next.unwrap_or_default().target_path()))
|
||||
}
|
||||
|
||||
pub(super) async fn registration_flow_status(
|
||||
services: &crate::State,
|
||||
) -> (TrustedFlowStatus, UntrustedFlowStatus) {
|
||||
// Allow registration if it's enabled in the config file or if this is the first
|
||||
// run (so the first user account can be created)
|
||||
let allow_registration =
|
||||
services.config.allow_registration || services.firstrun.is_first_run();
|
||||
|
||||
// Trusted flow is only available if any registration tokens exist
|
||||
let trusted_flow_status = {
|
||||
if !allow_registration {
|
||||
TrustedFlowStatus::Unavailable
|
||||
} else if services
|
||||
.registration_tokens
|
||||
.iterate_tokens()
|
||||
.next()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
TrustedFlowStatus::Available
|
||||
} else {
|
||||
TrustedFlowStatus::Unavailable
|
||||
}
|
||||
};
|
||||
|
||||
// Untrusted flow is available if email is required for registration,
|
||||
// or reCAPTCHA is configured, or open registration is enabled
|
||||
let untrusted_flow_status = {
|
||||
let require_email = services
|
||||
.config
|
||||
.smtp
|
||||
.as_ref()
|
||||
.is_some_and(|smtp| smtp.require_email_for_registration);
|
||||
|
||||
if !allow_registration || services.firstrun.is_first_run() {
|
||||
UntrustedFlowStatus::Unavailable
|
||||
} else if services.config.recaptcha_private_site_key.is_some() || require_email {
|
||||
UntrustedFlowStatus::Available { require_email }
|
||||
} else if services
|
||||
.config
|
||||
.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
{
|
||||
UntrustedFlowStatus::Available { require_email: false }
|
||||
} else {
|
||||
UntrustedFlowStatus::Unavailable
|
||||
}
|
||||
};
|
||||
|
||||
(trusted_flow_status, untrusted_flow_status)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user