diff --git a/.editorconfig b/.editorconfig index 3e7fd1b8..95843e73 100644 --- a/.editorconfig +++ b/.editorconfig @@ -26,3 +26,7 @@ max_line_length = 98 [*.yml] indent_size = 2 indent_style = space + +[*.json] +indent_size = 4 +indent_style = space diff --git a/.forgejo/workflows/release-image.yml b/.forgejo/workflows/release-image.yml index 04fc9de9..58d6cab2 100644 --- a/.forgejo/workflows/release-image.yml +++ b/.forgejo/workflows/release-image.yml @@ -161,7 +161,7 @@ jobs: var-lib-apt-${{ matrix.slug }} key: var-lib-apt-${{ matrix.slug }} - name: inject cache into docker - uses: https://github.com/reproducible-containers/buildkit-cache-dance@v3.1.0 + uses: https://github.com/reproducible-containers/buildkit-cache-dance@v3.3.0 with: cache-map: | { diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index e8522bec..0152b387 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -39,24 +39,73 @@ jobs: renovate: name: Renovate runs-on: ubuntu-latest + container: + image: ghcr.io/renovatebot/renovate:41 + options: --tmpfs /tmp:exec steps: - name: Checkout uses: actions/checkout@v4 + with: + show-progress: false + + - name: print node heap + run: /usr/local/renovate/node -e 'console.log(`node heap limit = ${require("v8").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)' + + - name: Restore renovate repo cache + uses: https://github.com/actions/cache@v4 + with: + path: | + /tmp/renovate/cache/renovate/repository + key: repo-cache-${{ github.run_id }} + restore-keys: | + repo-cache- + + - name: Restore renovate package cache + uses: https://github.com/actions/cache@v4 + with: + path: | + /tmp/renovate/cache/renovate/renovate-cache-sqlite + key: package-cache-${{ github.run_id }} + restore-keys: | + package-cache- - name: Self-hosted Renovate - uses: https://github.com/renovatebot/github-action@v40.1.0 + uses: https://github.com/renovatebot/github-action@v43.0.9 env: LOG_LEVEL: ${{ inputs.logLevel || 'info' }} - RENOVATE_AUTODISCOVER: 'false' - RENOVATE_BINARY_SOURCE: 'install' RENOVATE_DRY_RUN: ${{ inputs.dryRun || 'false' }} - RENOVATE_ENDPOINT: ${{ github.server_url }}/api/v1 - RENOVATE_GIT_TIMEOUT: 60000 - RENOVATE_GIT_URL: 'endpoint' - RENOVATE_GITHUB_TOKEN_WARN: 'false' - RENOVATE_ONBOARDING: 'false' - RENOVATE_PLATFORM: 'forgejo' - RENOVATE_PR_COMMITS_PER_RUN_LIMIT: 3 + + RENOVATE_PLATFORM: forgejo + RENOVATE_ENDPOINT: ${{ github.server_url }} + RENOVATE_AUTODISCOVER: 'false' RENOVATE_REPOSITORIES: '["${{ github.repository }}"]' + + RENOVATE_GIT_TIMEOUT: 60000 + RENOVATE_REQUIRE_CONFIG: 'required' + RENOVATE_ONBOARDING: 'false' + + RENOVATE_PR_COMMITS_PER_RUN_LIMIT: 3 + + RENOVATE_GITHUB_TOKEN_WARN: 'false' RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }} + GITHUB_COM_TOKEN: ${{ secrets.GH_PUBLIC_RO }} + + RENOVATE_REPOSITORY_CACHE: 'enabled' + RENOVATE_X_SQLITE_PACKAGE_CACHE: true + + - name: Save renovate repo cache + if: always() && env.RENOVATE_DRY_RUN != 'full' + uses: https://github.com/actions/cache@v4 + with: + path: | + /tmp/renovate/cache/renovate/repository + key: repo-cache-${{ github.run_id }} + + - name: Save renovate package cache + if: always() && env.RENOVATE_DRY_RUN != 'full' + uses: https://github.com/actions/cache@v4 + with: + path: | + /tmp/renovate/cache/renovate/renovate-cache-sqlite + key: package-cache-${{ github.run_id }} diff --git a/Cargo.lock b/Cargo.lock index 2b044a1f..a3962c31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4058,7 +4058,7 @@ checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" [[package]] name = "ruma" version = "0.10.1" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "assign", "js_int", @@ -4078,7 +4078,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.10.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "js_int", "ruma-common", @@ -4090,7 +4090,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "as_variant", "assign", @@ -4113,7 +4113,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "as_variant", "base64 0.22.1", @@ -4145,7 +4145,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "as_variant", "indexmap 2.10.0", @@ -4170,7 +4170,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "bytes", "headers", @@ -4192,7 +4192,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "js_int", "thiserror 2.0.12", @@ -4201,7 +4201,7 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "js_int", "ruma-common", @@ -4211,7 +4211,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "cfg-if", "proc-macro-crate", @@ -4226,7 +4226,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "js_int", "ruma-common", @@ -4238,7 +4238,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.15.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd#8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" dependencies = [ "base64 0.22.1", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 9452066c..e2af2d94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -352,7 +352,7 @@ version = "0.1.2" [workspace.dependencies.ruma] git = "https://forgejo.ellis.link/continuwuation/ruwuma" #branch = "conduwuit-changes" -rev = "b753738047d1f443aca870896ef27ecaacf027da" +rev = "8fb268fa2771dfc3a1c8075ef1246e7c9a0a53fd" features = [ "compat", "rand", diff --git a/renovate.json b/renovate.json index deb428af..68d21b9d 100644 --- a/renovate.json +++ b/renovate.json @@ -1,62 +1,59 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ], - "lockFileMaintenance": { - "enabled": true, - "schedule": [ - "at any time" + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "lockFileMaintenance": { + "enabled": true, + "schedule": ["at any time"] + }, + "nix": { + "enabled": true + }, + "labels": ["Dependencies", "Dependencies/Renovate"], + "ignoreDeps": [ + "tikv-jemallocator", + "tikv-jemalloc-sys", + "tikv-jemalloc-ctl", + "opentelemetry", + "opentelemetry_sdk", + "opentelemetry-jaeger", + "tracing-opentelemetry" + ], + "github-actions": { + "enabled": true, + "managerFilePatterns": [ + "/(^|/)\\.forgejo/workflows/[^/]+\\.ya?ml$/", + "/(^|/)\\.forgejo/actions/[^/]+/action\\.ya?ml$/", + "/(^|/)\\.github/workflows/[^/]+\\.ya?ml$/", + "/(^|/)\\.github/actions/[^/]+/action\\.ya?ml$/" + ] + }, + "packageRules": [ + { + "description": "Batch minor and patch GitHub Actions updates", + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["minor", "patch"], + "groupName": "github-actions-non-major" + }, + { + "description": "Group Rust toolchain updates into a single PR", + "matchManagers": ["custom.regex"], + "matchPackageNames": ["rust", "rustc", "cargo"], + "groupName": "rust-toolchain" + }, + { + "description": "Group lockfile updates into a single PR", + "matchUpdateTypes": ["lockFileMaintenance"], + "groupName": "lockfile-maintenance" + }, + { + "description": "Batch patch-level Rust dependency updates", + "matchManagers": ["cargo"], + "matchUpdateTypes": ["patch"], + "groupName": "rust-patch-updates" + }, + { + "matchManagers": ["cargo"], + "prConcurrentLimit": 5 + } ] - }, - "nix": { - "enabled": true - }, - "labels": [ - "Dependencies", - "Dependencies/Renovate" - ], - "ignoreDeps": [ - "tikv-jemallocator", - "tikv-jemalloc-sys", - "tikv-jemalloc-ctl", - "opentelemetry", - "opentelemetry_sdk", - "opentelemetry-jaeger", - "tracing-opentelemetry" - ], - "github-actions": { - "enabled": true, - "fileMatch": [ - "(^|/)\\.forgejo/workflows/[^/]+\\.ya?ml$", - "(^|/)\\.forgejo/actions/[^/]+/action\\.ya?ml$", - "(^|/)\\.github/workflows/[^/]+\\.ya?ml$", - "(^|/)\\.github/actions/[^/]+/action\\.ya?ml$" - ] - }, - "packageRules": [ - { - "description": "Batch minor and patch GitHub Actions updates", - "matchManagers": ["github-actions"], - "matchUpdateTypes": ["minor", "patch"], - "groupName": "github-actions-non-major" - }, - { - "description": "Group Rust toolchain updates into a single PR", - "matchManagers": ["regex"], - "matchPackageNames": ["rust", "rustc", "cargo"], - "groupName": "rust-toolchain" - }, - { - "description": "Group lockfile updates into a single PR", - "matchUpdateTypes": ["lockFileMaintenance"], - "groupName": "lockfile-maintenance" - }, - { - "description": "Batch patch-level Rust dependency updates", - "matchManagers": ["cargo"], - "matchUpdateTypes": ["patch"], - "groupName": "rust-patch-updates" - } - ] } diff --git a/src/api/client/admin/mod.rs b/src/api/client/admin/mod.rs new file mode 100644 index 00000000..8355a600 --- /dev/null +++ b/src/api/client/admin/mod.rs @@ -0,0 +1,3 @@ +mod suspend; + +pub(crate) use self::suspend::*; diff --git a/src/api/client/admin/suspend.rs b/src/api/client/admin/suspend.rs new file mode 100644 index 00000000..bab1cb5a --- /dev/null +++ b/src/api/client/admin/suspend.rs @@ -0,0 +1,89 @@ +use axum::extract::State; +use conduwuit::{Err, Result}; +use futures::future::{join, join3}; +use ruma::api::client::admin::{get_suspended, set_suspended}; + +use crate::Ruma; + +/// # `GET /_matrix/client/v1/admin/suspend/{userId}` +/// +/// Check the suspension status of a target user +pub(crate) async fn get_suspended_status( + State(services): State, + body: Ruma, +) -> Result { + let sender_user = body.sender_user(); + + let (admin, active) = + join(services.users.is_admin(sender_user), services.users.is_active(&body.user_id)).await; + if !admin { + return Err!(Request(Forbidden("Only server administrators can use this endpoint"))); + } + if !services.globals.user_is_local(&body.user_id) { + return Err!(Request(InvalidParam("Can only check the suspended status of local users"))); + } + if !active { + return Err!(Request(NotFound("Unknown user"))); + } + Ok(get_suspended::v1::Response::new( + services.users.is_suspended(&body.user_id).await?, + )) +} + +/// # `PUT /_matrix/client/v1/admin/suspend/{userId}` +/// +/// Set the suspension status of a target user +pub(crate) async fn put_suspended_status( + State(services): State, + body: Ruma, +) -> Result { + let sender_user = body.sender_user(); + + let (sender_admin, active, target_admin) = join3( + services.users.is_admin(sender_user), + services.users.is_active(&body.user_id), + services.users.is_admin(&body.user_id), + ) + .await; + + if !sender_admin { + return Err!(Request(Forbidden("Only server administrators can use this endpoint"))); + } + if !services.globals.user_is_local(&body.user_id) { + return Err!(Request(InvalidParam("Can only set the suspended status of local users"))); + } + if !active { + return Err!(Request(NotFound("Unknown user"))); + } + if body.user_id == *sender_user { + return Err!(Request(Forbidden("You cannot suspend yourself"))); + } + if target_admin { + return Err!(Request(Forbidden("You cannot suspend another server administrator"))); + } + if services.users.is_suspended(&body.user_id).await? == body.suspended { + // No change + return Ok(set_suspended::v1::Response::new(body.suspended)); + } + + let action = if body.suspended { + services + .users + .suspend_account(&body.user_id, sender_user) + .await; + "suspended" + } else { + services.users.unsuspend_account(&body.user_id).await; + "unsuspended" + }; + + if services.config.admin_room_notices { + // Notify the admin room that an account has been un/suspended + services + .admin + .send_text(&format!("{} has been {} by {}.", body.user_id, action, sender_user)) + .await; + } + + Ok(set_suspended::v1::Response::new(body.suspended)) +} diff --git a/src/api/client/capabilities.rs b/src/api/client/capabilities.rs index c42c6dfd..afa61498 100644 --- a/src/api/client/capabilities.rs +++ b/src/api/client/capabilities.rs @@ -19,7 +19,7 @@ use crate::Ruma; /// of this server. pub(crate) async fn get_capabilities_route( State(services): State, - _body: Ruma, + body: Ruma, ) -> Result { let available: BTreeMap = Server::available_room_versions().collect(); @@ -45,5 +45,14 @@ pub(crate) async fn get_capabilities_route( json!({"enabled": services.config.forget_forced_upon_leave}), )?; + if services + .users + .is_admin(body.sender_user.as_ref().unwrap()) + .await + { + // Advertise suspension API + capabilities.set("uk.timedout.msc4323", json!({"suspend":true, "lock": false}))?; + } + Ok(get_capabilities::v3::Response { capabilities }) } diff --git a/src/api/client/mod.rs b/src/api/client/mod.rs index be335cba..c8ca7757 100644 --- a/src/api/client/mod.rs +++ b/src/api/client/mod.rs @@ -1,5 +1,6 @@ pub(super) mod account; pub(super) mod account_data; +pub(super) mod admin; pub(super) mod alias; pub(super) mod appservice; pub(super) mod backup; @@ -43,6 +44,7 @@ pub(super) mod well_known; pub use account::full_user_deactivate; pub(super) use account::*; pub(super) use account_data::*; +pub(super) use admin::*; pub(super) use alias::*; pub(super) use appservice::*; pub(super) use backup::*; diff --git a/src/api/client/sync/v3.rs b/src/api/client/sync/v3.rs index 01428c08..298a6e4b 100644 --- a/src/api/client/sync/v3.rs +++ b/src/api/client/sync/v3.rs @@ -430,7 +430,7 @@ async fn handle_left_room( .ok(); // Left before last sync - if Some(since) >= left_count { + if (Some(since) >= left_count && !include_leave) || Some(next_batch) < left_count { return Ok(None); } diff --git a/src/api/client/unversioned.rs b/src/api/client/unversioned.rs index a4136d1a..7f19bc94 100644 --- a/src/api/client/unversioned.rs +++ b/src/api/client/unversioned.rs @@ -58,6 +58,7 @@ pub(crate) async fn get_supported_versions_route( ("uk.tcpip.msc4133".to_owned(), true), /* Extending User Profile API with Key:Value Pairs (https://github.com/matrix-org/matrix-spec-proposals/pull/4133) */ ("us.cloke.msc4175".to_owned(), true), /* Profile field for user time zone (https://github.com/matrix-org/matrix-spec-proposals/pull/4175) */ ("org.matrix.simplified_msc3575".to_owned(), true), /* Simplified Sliding sync (https://github.com/matrix-org/matrix-spec-proposals/pull/4186) */ + ("uk.timedout.msc4323".to_owned(), true), /* agnostic suspend (https://github.com/matrix-org/matrix-spec-proposals/pull/4323) */ ]), }; diff --git a/src/api/router.rs b/src/api/router.rs index 8072fa5b..42934f70 100644 --- a/src/api/router.rs +++ b/src/api/router.rs @@ -184,6 +184,8 @@ pub fn build(router: Router, server: &Server) -> Router { "/_matrix/client/unstable/im.nheko.summary/rooms/:room_id_or_alias/summary", get(client::get_room_summary_legacy) ) + .ruma_route(&client::get_suspended_status) + .ruma_route(&client::put_suspended_status) .ruma_route(&client::well_known_support) .ruma_route(&client::well_known_client) .route("/_conduwuit/server_version", get(client::conduwuit_server_version))