Merge remote-tracking branch 'origin/main' into quenting/stable-api
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
[build]
|
||||
rustflags = ["--cfg", "tokio_unstable"]
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
comment: false
|
||||
|
||||
flag_management:
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
[profile.default]
|
||||
retries = 1
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
target/
|
||||
crates/*/target
|
||||
crates/*/node_modules
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
*.wasm binary
|
||||
7
.github/actions/build-frontend/action.yml
vendored
7
.github/actions/build-frontend/action.yml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Build the frontend assets
|
||||
description: Installs Node.js and builds the frontend assets from the frontend directory
|
||||
|
||||
@@ -7,7 +12,7 @@ runs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: '22'
|
||||
node-version: "22"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
5
.github/actions/build-policies/action.yml
vendored
5
.github/actions/build-policies/action.yml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Build the Open Policy Agent policies
|
||||
description: Installs OPA and builds the policies
|
||||
|
||||
|
||||
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "cargo"
|
||||
|
||||
5
.github/release.yml
vendored
5
.github/release.yml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
changelog:
|
||||
categories:
|
||||
- title: Bug Fixes
|
||||
|
||||
5
.github/scripts/.gitignore
vendored
5
.github/scripts/.gitignore
vendored
@@ -1,2 +1,7 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
node_modules/
|
||||
package-lock.json
|
||||
|
||||
4
.github/scripts/cleanup-pr.cjs
vendored
4
.github/scripts/cleanup-pr.cjs
vendored
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
// @ts-check
|
||||
|
||||
|
||||
4
.github/scripts/commit-and-tag.cjs
vendored
4
.github/scripts/commit-and-tag.cjs
vendored
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
// @ts-check
|
||||
|
||||
|
||||
4
.github/scripts/create-release-branch.cjs
vendored
4
.github/scripts/create-release-branch.cjs
vendored
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
// @ts-check
|
||||
|
||||
|
||||
4
.github/scripts/create-version-tag.cjs
vendored
4
.github/scripts/create-version-tag.cjs
vendored
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
// @ts-check
|
||||
|
||||
|
||||
4
.github/scripts/merge-back.cjs
vendored
4
.github/scripts/merge-back.cjs
vendored
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
// @ts-check
|
||||
|
||||
|
||||
4
.github/scripts/update-release-branch.cjs
vendored
4
.github/scripts/update-release-branch.cjs
vendored
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
// @ts-check
|
||||
|
||||
|
||||
4
.github/scripts/update-unstable-tag.cjs
vendored
4
.github/scripts/update-unstable-tag.cjs
vendored
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
// @ts-check
|
||||
|
||||
|
||||
13
.github/workflows/build.yaml
vendored
13
.github/workflows/build.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
@@ -221,7 +226,7 @@ jobs:
|
||||
steps:
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5.7.0
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
with:
|
||||
images: "${{ env.IMAGE }}"
|
||||
bake-target: docker-metadata-action
|
||||
@@ -237,7 +242,7 @@ jobs:
|
||||
|
||||
- name: Docker meta (debug variant)
|
||||
id: meta-debug
|
||||
uses: docker/metadata-action@v5.7.0
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
with:
|
||||
images: "${{ env.IMAGE }}"
|
||||
bake-target: docker-metadata-action-debug
|
||||
@@ -253,10 +258,10 @@ jobs:
|
||||
type=sha
|
||||
|
||||
- name: Setup Cosign
|
||||
uses: sigstore/cosign-installer@v3.8.2
|
||||
uses: sigstore/cosign-installer@v3.9.2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
with:
|
||||
buildkitd-config-inline: |
|
||||
[registry."docker.io"]
|
||||
|
||||
9
.github/workflows/ci.yaml
vendored
9
.github/workflows/ci.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
@@ -153,7 +158,7 @@ jobs:
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Run `cargo-deny`
|
||||
uses: EmbarkStudios/cargo-deny-action@v2.0.11
|
||||
uses: EmbarkStudios/cargo-deny-action@v2.0.12
|
||||
with:
|
||||
rust-version: stable
|
||||
|
||||
@@ -210,7 +215,7 @@ jobs:
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@1.86.0
|
||||
uses: dtolnay/rust-toolchain@1.87.0
|
||||
with:
|
||||
components: clippy
|
||||
|
||||
|
||||
5
.github/workflows/coverage.yaml
vendored
5
.github/workflows/coverage.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Coverage
|
||||
|
||||
on:
|
||||
|
||||
5
.github/workflows/docs.yaml
vendored
5
.github/workflows/docs.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Build and deploy the documentation
|
||||
|
||||
on:
|
||||
|
||||
5
.github/workflows/merge-back.yaml
vendored
5
.github/workflows/merge-back.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Merge back a reference to main
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
5
.github/workflows/release-branch.yaml
vendored
5
.github/workflows/release-branch.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Create a new release branch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
5
.github/workflows/release-bump.yaml
vendored
5
.github/workflows/release-bump.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Bump the version on a release branch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
5
.github/workflows/tag.yaml
vendored
5
.github/workflows/tag.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Tag a new version
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
5
.github/workflows/translations-download.yaml
vendored
5
.github/workflows/translations-download.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Download translation files from Localazy
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
5
.github/workflows/translations-upload.yaml
vendored
5
.github/workflows/translations-upload.yaml
vendored
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
name: Upload translation files to Localazy
|
||||
on:
|
||||
push:
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,5 +1,10 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
# Rust
|
||||
target/
|
||||
target
|
||||
|
||||
# Editors
|
||||
.idea
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
max_width = 100
|
||||
comment_width = 80
|
||||
wrap_comments = true
|
||||
|
||||
1484
Cargo.lock
generated
1484
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
150
Cargo.toml
150
Cargo.toml
@@ -1,11 +1,16 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
[workspace]
|
||||
default-members = ["crates/cli"]
|
||||
members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
# Updated in the CI with a `sed` command
|
||||
package.version = "0.17.0-rc.0"
|
||||
package.license = "AGPL-3.0-only"
|
||||
package.version = "0.20.0"
|
||||
package.license = "AGPL-3.0-only OR LicenseRef-Element-Commercial"
|
||||
package.authors = ["Element Backend Team"]
|
||||
package.edition = "2024"
|
||||
package.homepage = "https://element-hq.github.io/matrix-authentication-service/"
|
||||
@@ -21,6 +26,7 @@ all = { level = "deny", priority = -1 }
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
|
||||
str_to_string = "deny"
|
||||
too_many_lines = "allow"
|
||||
|
||||
[workspace.lints.rustdoc]
|
||||
broken_intra_doc_links = "deny"
|
||||
@@ -28,35 +34,35 @@ broken_intra_doc_links = "deny"
|
||||
[workspace.dependencies]
|
||||
|
||||
# Workspace crates
|
||||
mas-axum-utils = { path = "./crates/axum-utils/", version = "=0.17.0-rc.0" }
|
||||
mas-cli = { path = "./crates/cli/", version = "=0.17.0-rc.0" }
|
||||
mas-config = { path = "./crates/config/", version = "=0.17.0-rc.0" }
|
||||
mas-context = { path = "./crates/context/", version = "=0.17.0-rc.0" }
|
||||
mas-data-model = { path = "./crates/data-model/", version = "=0.17.0-rc.0" }
|
||||
mas-email = { path = "./crates/email/", version = "=0.17.0-rc.0" }
|
||||
mas-graphql = { path = "./crates/graphql/", version = "=0.17.0-rc.0" }
|
||||
mas-handlers = { path = "./crates/handlers/", version = "=0.17.0-rc.0" }
|
||||
mas-http = { path = "./crates/http/", version = "=0.17.0-rc.0" }
|
||||
mas-i18n = { path = "./crates/i18n/", version = "=0.17.0-rc.0" }
|
||||
mas-i18n-scan = { path = "./crates/i18n-scan/", version = "=0.17.0-rc.0" }
|
||||
mas-iana = { path = "./crates/iana/", version = "=0.17.0-rc.0" }
|
||||
mas-iana-codegen = { path = "./crates/iana-codegen/", version = "=0.17.0-rc.0" }
|
||||
mas-jose = { path = "./crates/jose/", version = "=0.17.0-rc.0" }
|
||||
mas-keystore = { path = "./crates/keystore/", version = "=0.17.0-rc.0" }
|
||||
mas-listener = { path = "./crates/listener/", version = "=0.17.0-rc.0" }
|
||||
mas-matrix = { path = "./crates/matrix/", version = "=0.17.0-rc.0" }
|
||||
mas-matrix-synapse = { path = "./crates/matrix-synapse/", version = "=0.17.0-rc.0" }
|
||||
mas-oidc-client = { path = "./crates/oidc-client/", version = "=0.17.0-rc.0" }
|
||||
mas-policy = { path = "./crates/policy/", version = "=0.17.0-rc.0" }
|
||||
mas-router = { path = "./crates/router/", version = "=0.17.0-rc.0" }
|
||||
mas-spa = { path = "./crates/spa/", version = "=0.17.0-rc.0" }
|
||||
mas-storage = { path = "./crates/storage/", version = "=0.17.0-rc.0" }
|
||||
mas-storage-pg = { path = "./crates/storage-pg/", version = "=0.17.0-rc.0" }
|
||||
mas-tasks = { path = "./crates/tasks/", version = "=0.17.0-rc.0" }
|
||||
mas-templates = { path = "./crates/templates/", version = "=0.17.0-rc.0" }
|
||||
mas-tower = { path = "./crates/tower/", version = "=0.17.0-rc.0" }
|
||||
oauth2-types = { path = "./crates/oauth2-types/", version = "=0.17.0-rc.0" }
|
||||
syn2mas = { path = "./crates/syn2mas", version = "=0.17.0-rc.0" }
|
||||
mas-axum-utils = { path = "./crates/axum-utils/", version = "=0.20.0" }
|
||||
mas-cli = { path = "./crates/cli/", version = "=0.20.0" }
|
||||
mas-config = { path = "./crates/config/", version = "=0.20.0" }
|
||||
mas-context = { path = "./crates/context/", version = "=0.20.0" }
|
||||
mas-data-model = { path = "./crates/data-model/", version = "=0.20.0" }
|
||||
mas-email = { path = "./crates/email/", version = "=0.20.0" }
|
||||
mas-graphql = { path = "./crates/graphql/", version = "=0.20.0" }
|
||||
mas-handlers = { path = "./crates/handlers/", version = "=0.20.0" }
|
||||
mas-http = { path = "./crates/http/", version = "=0.20.0" }
|
||||
mas-i18n = { path = "./crates/i18n/", version = "=0.20.0" }
|
||||
mas-i18n-scan = { path = "./crates/i18n-scan/", version = "=0.20.0" }
|
||||
mas-iana = { path = "./crates/iana/", version = "=0.20.0" }
|
||||
mas-iana-codegen = { path = "./crates/iana-codegen/", version = "=0.20.0" }
|
||||
mas-jose = { path = "./crates/jose/", version = "=0.20.0" }
|
||||
mas-keystore = { path = "./crates/keystore/", version = "=0.20.0" }
|
||||
mas-listener = { path = "./crates/listener/", version = "=0.20.0" }
|
||||
mas-matrix = { path = "./crates/matrix/", version = "=0.20.0" }
|
||||
mas-matrix-synapse = { path = "./crates/matrix-synapse/", version = "=0.20.0" }
|
||||
mas-oidc-client = { path = "./crates/oidc-client/", version = "=0.20.0" }
|
||||
mas-policy = { path = "./crates/policy/", version = "=0.20.0" }
|
||||
mas-router = { path = "./crates/router/", version = "=0.20.0" }
|
||||
mas-spa = { path = "./crates/spa/", version = "=0.20.0" }
|
||||
mas-storage = { path = "./crates/storage/", version = "=0.20.0" }
|
||||
mas-storage-pg = { path = "./crates/storage-pg/", version = "=0.20.0" }
|
||||
mas-tasks = { path = "./crates/tasks/", version = "=0.20.0" }
|
||||
mas-templates = { path = "./crates/templates/", version = "=0.20.0" }
|
||||
mas-tower = { path = "./crates/tower/", version = "=0.20.0" }
|
||||
oauth2-types = { path = "./crates/oauth2-types/", version = "=0.20.0" }
|
||||
syn2mas = { path = "./crates/syn2mas", version = "=0.20.0" }
|
||||
|
||||
# OpenAPI schema generation and validation
|
||||
[workspace.dependencies.aide]
|
||||
@@ -149,7 +155,7 @@ version = "0.15.11"
|
||||
|
||||
# Cookie store
|
||||
[workspace.dependencies.cookie_store]
|
||||
version = "0.21.1"
|
||||
version = "0.22.0"
|
||||
default-features = false
|
||||
features = ["serde_json"]
|
||||
|
||||
@@ -161,7 +167,7 @@ features = ["serde", "clock"]
|
||||
|
||||
# CLI argument parsing
|
||||
[workspace.dependencies.clap]
|
||||
version = "4.5.40"
|
||||
version = "4.5.42"
|
||||
features = ["derive"]
|
||||
|
||||
# Object Identifiers (OIDs) as constants
|
||||
@@ -268,7 +274,7 @@ features = ["client", "server", "http1", "http2"]
|
||||
|
||||
# Additional Hyper utilties
|
||||
[workspace.dependencies.hyper-util]
|
||||
version = "0.1.14"
|
||||
version = "0.1.16"
|
||||
features = [
|
||||
"client",
|
||||
"server",
|
||||
@@ -315,7 +321,7 @@ features = ["std"]
|
||||
|
||||
# HashMap which preserves insertion order
|
||||
[workspace.dependencies.indexmap]
|
||||
version = "2.9.0"
|
||||
version = "2.10.0"
|
||||
features = ["serde"]
|
||||
|
||||
# Indented string literals
|
||||
@@ -348,10 +354,12 @@ features = ["serde"]
|
||||
|
||||
# Email sending
|
||||
[workspace.dependencies.lettre]
|
||||
version = "0.11.15"
|
||||
version = "0.11.18"
|
||||
default-features = false
|
||||
features = [
|
||||
"tokio1-rustls-tls",
|
||||
"tokio1-rustls",
|
||||
"rustls-platform-verifier",
|
||||
"aws-lc-rs",
|
||||
"hostname",
|
||||
"builder",
|
||||
"tracing",
|
||||
@@ -370,12 +378,12 @@ version = "0.3.17"
|
||||
|
||||
# Templates
|
||||
[workspace.dependencies.minijinja]
|
||||
version = "2.10.2"
|
||||
version = "2.11.0"
|
||||
features = ["loader", "json", "speedups", "unstable_machinery"]
|
||||
|
||||
# Additional filters for minijinja
|
||||
[workspace.dependencies.minijinja-contrib]
|
||||
version = "2.10.2"
|
||||
version = "2.11.0"
|
||||
features = ["pycompat"]
|
||||
|
||||
# Utilities to deal with non-zero values
|
||||
@@ -384,40 +392,42 @@ version = "0.3.0"
|
||||
|
||||
# Open Policy Agent support through WASM
|
||||
[workspace.dependencies.opa-wasm]
|
||||
version = "0.1.5"
|
||||
version = "0.1.7"
|
||||
|
||||
# OpenTelemetry
|
||||
[workspace.dependencies.opentelemetry]
|
||||
version = "0.29.1"
|
||||
version = "0.30.0"
|
||||
features = ["trace", "metrics"]
|
||||
[workspace.dependencies.opentelemetry-http]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
features = ["reqwest"]
|
||||
[workspace.dependencies.opentelemetry-jaeger-propagator]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
[workspace.dependencies.opentelemetry-otlp]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
default-features = false
|
||||
features = ["trace", "metrics", "http-proto"]
|
||||
[workspace.dependencies.opentelemetry-prometheus]
|
||||
version = "0.29.1"
|
||||
# https://github.com/open-telemetry/opentelemetry-rust/pull/3076
|
||||
git = "https://github.com/sandhose/opentelemetry-rust.git"
|
||||
branch = "otel-prometheus-0.30"
|
||||
[workspace.dependencies.opentelemetry-resource-detectors]
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
[workspace.dependencies.opentelemetry-semantic-conventions]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
features = ["semconv_experimental"]
|
||||
[workspace.dependencies.opentelemetry-stdout]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
features = ["trace", "metrics"]
|
||||
[workspace.dependencies.opentelemetry_sdk]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
features = [
|
||||
"experimental_trace_batch_span_processor_with_async_runtime",
|
||||
"experimental_metrics_periodicreader_with_async_runtime",
|
||||
"rt-tokio",
|
||||
]
|
||||
[workspace.dependencies.tracing-opentelemetry]
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
default-features = false
|
||||
|
||||
# P256 elliptic curve
|
||||
@@ -446,11 +456,11 @@ features = ["std"]
|
||||
|
||||
# Parser generator
|
||||
[workspace.dependencies.pest]
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
|
||||
# Pest derive macros
|
||||
[workspace.dependencies.pest_derive]
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
|
||||
# Pin projection
|
||||
[workspace.dependencies.pin-project-lite]
|
||||
@@ -468,7 +478,7 @@ features = ["std", "pkcs5", "encryption"]
|
||||
|
||||
# Public Suffix List
|
||||
[workspace.dependencies.psl]
|
||||
version = "2.1.120"
|
||||
version = "2.1.127"
|
||||
|
||||
# Prometheus metrics
|
||||
[workspace.dependencies.prometheus]
|
||||
@@ -492,9 +502,15 @@ version = "1.11.1"
|
||||
|
||||
# High-level HTTP client
|
||||
[workspace.dependencies.reqwest]
|
||||
version = "0.12.20"
|
||||
version = "0.12.22"
|
||||
default-features = false
|
||||
features = ["http2", "rustls-tls-manual-roots", "charset", "json", "socks"]
|
||||
features = [
|
||||
"http2",
|
||||
"rustls-tls-manual-roots-no-provider",
|
||||
"charset",
|
||||
"json",
|
||||
"socks",
|
||||
]
|
||||
|
||||
# RSA cryptography
|
||||
[workspace.dependencies.rsa]
|
||||
@@ -507,11 +523,11 @@ version = "2.1.1"
|
||||
|
||||
# Matrix-related types
|
||||
[workspace.dependencies.ruma-common]
|
||||
version = "0.15.2"
|
||||
version = "0.15.4"
|
||||
|
||||
# TLS stack
|
||||
[workspace.dependencies.rustls]
|
||||
version = "0.23.27"
|
||||
version = "0.23.31"
|
||||
|
||||
# PEM parsing for rustls
|
||||
[workspace.dependencies.rustls-pemfile]
|
||||
@@ -523,7 +539,7 @@ version = "1.12.0"
|
||||
|
||||
# Use platform-specific verifier for TLS
|
||||
[workspace.dependencies.rustls-platform-verifier]
|
||||
version = "0.5.3"
|
||||
version = "0.6.0"
|
||||
|
||||
# systemd service status notification
|
||||
[workspace.dependencies.sd-notify]
|
||||
@@ -557,18 +573,18 @@ features = [
|
||||
|
||||
# Sentry error tracking
|
||||
[workspace.dependencies.sentry]
|
||||
version = "0.37.0"
|
||||
version = "0.42.0"
|
||||
default-features = false
|
||||
features = ["backtrace", "contexts", "panic", "tower", "reqwest"]
|
||||
|
||||
# Sentry tower layer
|
||||
[workspace.dependencies.sentry-tower]
|
||||
version = "0.37.0"
|
||||
version = "0.42.0"
|
||||
features = ["http", "axum-matched-path"]
|
||||
|
||||
# Sentry tracing integration
|
||||
[workspace.dependencies.sentry-tracing]
|
||||
version = "0.37.0"
|
||||
version = "0.42.0"
|
||||
|
||||
# Serialization and deserialization
|
||||
[workspace.dependencies.serde]
|
||||
@@ -577,7 +593,7 @@ features = ["derive"] # Most of the time, if we need serde, we need derive
|
||||
|
||||
# JSON serialization and deserialization
|
||||
[workspace.dependencies.serde_json]
|
||||
version = "1.0.140"
|
||||
version = "1.0.142"
|
||||
features = ["preserve_order"]
|
||||
|
||||
# URL encoded form serialization
|
||||
@@ -586,7 +602,7 @@ version = "0.7.1"
|
||||
|
||||
# Custom serialization helpers
|
||||
[workspace.dependencies.serde_with]
|
||||
version = "3.12.0"
|
||||
version = "3.14.0"
|
||||
features = ["hex", "chrono"]
|
||||
|
||||
# YAML serialization
|
||||
@@ -604,7 +620,7 @@ version = "2.2.0"
|
||||
|
||||
# Low-level socket manipulation
|
||||
[workspace.dependencies.socket2]
|
||||
version = "0.5.10"
|
||||
version = "0.6.0"
|
||||
|
||||
# Subject Public Key Info
|
||||
[workspace.dependencies.spki]
|
||||
@@ -630,11 +646,11 @@ features = [
|
||||
version = "2.0.12"
|
||||
|
||||
[workspace.dependencies.thiserror-ext]
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
|
||||
# Async runtime
|
||||
[workspace.dependencies.tokio]
|
||||
version = "1.45.1"
|
||||
version = "1.47.1"
|
||||
features = ["full"]
|
||||
|
||||
[workspace.dependencies.tokio-stream]
|
||||
@@ -713,7 +729,7 @@ version = "2.5.0"
|
||||
|
||||
# HTTP mock server
|
||||
[workspace.dependencies.wiremock]
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
|
||||
# User-agent parser
|
||||
[workspace.dependencies.woothee]
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# syntax = docker/dockerfile:1.7.1
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
# Builds a minimal image with the binary only. It is multi-arch capable,
|
||||
# cross-building to aarch64 and x86_64. When cross-compiling, Docker sets two
|
||||
@@ -8,7 +12,7 @@
|
||||
# The Debian version and version name must be in sync
|
||||
ARG DEBIAN_VERSION=12
|
||||
ARG DEBIAN_VERSION_NAME=bookworm
|
||||
ARG RUSTC_VERSION=1.86.0
|
||||
ARG RUSTC_VERSION=1.87.0
|
||||
ARG NODEJS_VERSION=20.15.0
|
||||
ARG OPA_VERSION=1.1.0
|
||||
ARG CARGO_AUDITABLE_VERSION=0.6.6
|
||||
|
||||
6
LICENSE-COMMERCIAL
Normal file
6
LICENSE-COMMERCIAL
Normal file
@@ -0,0 +1,6 @@
|
||||
Licensees holding a valid commercial license with Element may use this
|
||||
software in accordance with the terms contained in a written agreement
|
||||
between you and Element.
|
||||
|
||||
To purchase a commercial license please contact our sales team at
|
||||
licensing@element.io
|
||||
12
README.md
12
README.md
@@ -27,3 +27,15 @@ Anyone can contribute to translations through [Localazy](https://localazy.com/el
|
||||
## 🏗️ Contributing
|
||||
|
||||
See the [contribution guidelines](https://element-hq.github.io/matrix-authentication-service/development/contributing.html) for information on how to contribute to this project.
|
||||
|
||||
## ⚖️ Copyright & License
|
||||
|
||||
Copyright 2024, 2025 New Vector Ltd.
|
||||
Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
|
||||
This software is dual-licensed by New Vector Ltd (Element). It can be used either:
|
||||
|
||||
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
|
||||
|
||||
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
|
||||
|
||||
48
biome.json
48
biome.json
@@ -1,28 +1,27 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"ignore": [
|
||||
".devcontainer/**",
|
||||
"docs/**",
|
||||
"translations/**",
|
||||
"policies/**",
|
||||
"crates/**",
|
||||
"frontend/package.json",
|
||||
"frontend/src/gql/**",
|
||||
"frontend/src/routeTree.gen.ts",
|
||||
"frontend/.storybook/locales.ts",
|
||||
"frontend/.storybook/public/mockServiceWorker.js",
|
||||
"frontend/locales/*.json",
|
||||
"**/coverage/**",
|
||||
"**/dist/**"
|
||||
"includes": [
|
||||
"**",
|
||||
"!**/.devcontainer/**",
|
||||
"!**/docs/**",
|
||||
"!**/translations/**",
|
||||
"!**/policies/**",
|
||||
"!**/crates/**",
|
||||
"!**/frontend/package.json",
|
||||
"!**/frontend/src/gql/**",
|
||||
"!**/frontend/src/routeTree.gen.ts",
|
||||
"!**/frontend/.storybook/locales.ts",
|
||||
"!**/frontend/.storybook/public/mockServiceWorker.js",
|
||||
"!**/frontend/locales/**/*.json",
|
||||
"!**/coverage/**",
|
||||
"!**/dist/**"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
@@ -36,6 +35,19 @@
|
||||
"correctness": {
|
||||
"noUnusedImports": "warn",
|
||||
"noUnusedVariables": "warn"
|
||||
},
|
||||
"style": {
|
||||
"noParameterAssign": "error",
|
||||
"useAsConstAssertion": "error",
|
||||
"useDefaultParameterLast": "error",
|
||||
"useEnumInitializers": "error",
|
||||
"useSelfClosingElements": "error",
|
||||
"useSingleVarDeclarator": "error",
|
||||
"noUnusedTemplateLiteral": "error",
|
||||
"useNumberNamespace": "error",
|
||||
"noInferrableTypes": "error",
|
||||
"noUselessElse": "error",
|
||||
"noDescendingSpecificity": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
# Documentation for possible options in this file is at
|
||||
# https://rust-lang.github.io/mdBook/format/config.html
|
||||
[book]
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
doc-valid-idents = ["OpenID", "OAuth", "..", "PostgreSQL", "SQLite"]
|
||||
|
||||
disallowed-methods = [
|
||||
@@ -10,7 +15,6 @@ disallowed-methods = [
|
||||
]
|
||||
|
||||
disallowed-types = [
|
||||
"rand::OsRng",
|
||||
{ path = "std::path::PathBuf", reason = "use camino::Utf8PathBuf instead" },
|
||||
{ path = "std::path::Path", reason = "use camino::Utf8Path instead" },
|
||||
]
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
[package]
|
||||
name = "mas-axum-utils"
|
||||
version.workspace = true
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{
|
||||
BoxError, Json,
|
||||
extract::{
|
||||
Form, FromRequest, FromRequestParts,
|
||||
Form, FromRequest,
|
||||
rejection::{FailedToDeserializeForm, FormRejection},
|
||||
},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use axum_extra::typed_header::{TypedHeader, TypedHeaderRejectionReason};
|
||||
use headers::{Authorization, authorization::Basic};
|
||||
use headers::authorization::{Basic, Bearer, Credentials as _};
|
||||
use http::{Request, StatusCode};
|
||||
use mas_data_model::{Client, JwksOrJwksUri};
|
||||
use mas_http::RequestBuilderExt;
|
||||
@@ -60,17 +59,30 @@ pub enum Credentials {
|
||||
client_id: String,
|
||||
jwt: Box<Jwt<'static, HashMap<String, serde_json::Value>>>,
|
||||
},
|
||||
BearerToken {
|
||||
token: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Credentials {
|
||||
/// Get the `client_id` of the credentials
|
||||
#[must_use]
|
||||
pub fn client_id(&self) -> &str {
|
||||
pub fn client_id(&self) -> Option<&str> {
|
||||
match self {
|
||||
Credentials::None { client_id }
|
||||
| Credentials::ClientSecretBasic { client_id, .. }
|
||||
| Credentials::ClientSecretPost { client_id, .. }
|
||||
| Credentials::ClientAssertionJwtBearer { client_id, .. } => client_id,
|
||||
| Credentials::ClientAssertionJwtBearer { client_id, .. } => Some(client_id),
|
||||
Credentials::BearerToken { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the bearer token from the credentials.
|
||||
#[must_use]
|
||||
pub fn bearer_token(&self) -> Option<&str> {
|
||||
match self {
|
||||
Credentials::BearerToken { token } => Some(token),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +101,7 @@ impl Credentials {
|
||||
| Credentials::ClientSecretBasic { client_id, .. }
|
||||
| Credentials::ClientSecretPost { client_id, .. }
|
||||
| Credentials::ClientAssertionJwtBearer { client_id, .. } => client_id,
|
||||
Credentials::BearerToken { .. } => return Ok(None),
|
||||
};
|
||||
|
||||
repo.oauth2_client().find_by_client_id(client_id).await
|
||||
@@ -239,7 +252,7 @@ pub struct ClientAuthorization<F = ()> {
|
||||
impl<F> ClientAuthorization<F> {
|
||||
/// Get the `client_id` from the credentials.
|
||||
#[must_use]
|
||||
pub fn client_id(&self) -> &str {
|
||||
pub fn client_id(&self) -> Option<&str> {
|
||||
self.credentials.client_id()
|
||||
}
|
||||
}
|
||||
@@ -355,30 +368,40 @@ where
|
||||
{
|
||||
type Rejection = ClientAuthorizationError;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
async fn from_request(
|
||||
req: Request<axum::body::Body>,
|
||||
state: &S,
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
// Split the request into parts so we can extract some headers
|
||||
let (mut parts, body) = req.into_parts();
|
||||
enum Authorization {
|
||||
Basic(String, String),
|
||||
Bearer(String),
|
||||
}
|
||||
|
||||
let header =
|
||||
TypedHeader::<Authorization<Basic>>::from_request_parts(&mut parts, state).await;
|
||||
|
||||
// Take the Authorization header
|
||||
let credentials_from_header = match header {
|
||||
Ok(header) => Some((header.username().to_owned(), header.password().to_owned())),
|
||||
Err(err) => match err.reason() {
|
||||
// If it's missing it is fine
|
||||
TypedHeaderRejectionReason::Missing => None,
|
||||
// If the header could not be parsed, return the error
|
||||
_ => return Err(ClientAuthorizationError::InvalidHeader),
|
||||
},
|
||||
// Sadly, the typed-header 'Authorization' doesn't let us check for both
|
||||
// Basic and Bearer at the same time, so we need to parse them manually
|
||||
let authorization = if let Some(header) = req.headers().get(http::header::AUTHORIZATION) {
|
||||
let bytes = header.as_bytes();
|
||||
if bytes.len() >= 6 && bytes[..6].eq_ignore_ascii_case(b"Basic ") {
|
||||
let Some(decoded) = Basic::decode(header) else {
|
||||
return Err(ClientAuthorizationError::InvalidHeader);
|
||||
};
|
||||
|
||||
// Reconstruct the request from the parts
|
||||
let req = Request::from_parts(parts, body);
|
||||
Some(Authorization::Basic(
|
||||
decoded.username().to_owned(),
|
||||
decoded.password().to_owned(),
|
||||
))
|
||||
} else if bytes.len() >= 7 && bytes[..7].eq_ignore_ascii_case(b"Bearer ") {
|
||||
let Some(decoded) = Bearer::decode(header) else {
|
||||
return Err(ClientAuthorizationError::InvalidHeader);
|
||||
};
|
||||
|
||||
Some(Authorization::Bearer(decoded.token().to_owned()))
|
||||
} else {
|
||||
return Err(ClientAuthorizationError::InvalidHeader);
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Take the form value
|
||||
let (
|
||||
@@ -407,13 +430,19 @@ where
|
||||
|
||||
// And now, figure out the actual auth method
|
||||
let credentials = match (
|
||||
credentials_from_header,
|
||||
authorization,
|
||||
client_id_from_form,
|
||||
client_secret_from_form,
|
||||
client_assertion_type,
|
||||
client_assertion,
|
||||
) {
|
||||
(Some((client_id, client_secret)), client_id_from_form, None, None, None) => {
|
||||
(
|
||||
Some(Authorization::Basic(client_id, client_secret)),
|
||||
client_id_from_form,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
) => {
|
||||
if let Some(client_id_from_form) = client_id_from_form {
|
||||
// If the client_id was in the body, verify it matches with the header
|
||||
if client_id != client_id_from_form {
|
||||
@@ -483,6 +512,11 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
(Some(Authorization::Bearer(token)), None, None, None, None) => {
|
||||
// Got a bearer token
|
||||
Credentials::BearerToken { token }
|
||||
}
|
||||
|
||||
(None, None, None, None, None) => {
|
||||
// Special case when there are no credentials anywhere
|
||||
return Err(ClientAuthorizationError::MissingCredentials);
|
||||
@@ -677,4 +711,29 @@ mod tests {
|
||||
jwt.verify_with_shared_secret(b"client-secret".to_vec())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn bearer_token_test() {
|
||||
let req = Request::builder()
|
||||
.method(Method::POST)
|
||||
.header(
|
||||
http::header::CONTENT_TYPE,
|
||||
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
|
||||
)
|
||||
.header(http::header::AUTHORIZATION, "Bearer token")
|
||||
.body(Body::new("foo=bar".to_owned()))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
ClientAuthorization::<serde_json::Value>::from_request(req, &())
|
||||
.await
|
||||
.unwrap(),
|
||||
ClientAuthorization {
|
||||
credentials: Credentials::BearerToken {
|
||||
token: "token".to_owned(),
|
||||
},
|
||||
form: Some(serde_json::json!({"foo": "bar"})),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
//! Private (encrypted) cookie jar, based on axum-extra's cookie jar
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use base64ct::{Base64UrlUnpadded, Encoding};
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use axum::response::{IntoResponse, Response};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use axum::{
|
||||
Extension,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum_extra::typed_header::TypedHeader;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::cmp::Reverse;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
#![deny(clippy::future_not_send)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::convert::Infallible;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use mas_data_model::BrowserSession;
|
||||
use mas_storage::RepositoryAccess;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{collections::HashMap, error::Error};
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
[package]
|
||||
name = "mas-cli"
|
||||
version.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use vergen_gitcl::{Emitter, GitclBuilder, RustcBuilder};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{convert::Infallible, net::IpAddr, sync::Arc};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
@@ -72,7 +72,7 @@ impl Options {
|
||||
SC::Dump { output } => {
|
||||
let _span = info_span!("cli.config.dump").entered();
|
||||
|
||||
let config = RootConfig::extract(figment)?;
|
||||
let config = RootConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let config = serde_yaml::to_string(&config)?;
|
||||
|
||||
if let Some(output) = output {
|
||||
@@ -88,7 +88,7 @@ impl Options {
|
||||
SC::Check => {
|
||||
let _span = info_span!("cli.config.check").entered();
|
||||
|
||||
let _config = RootConfig::extract(figment)?;
|
||||
let _config = RootConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
info!("Configuration file looks good");
|
||||
}
|
||||
|
||||
@@ -105,7 +105,8 @@ impl Options {
|
||||
|
||||
if !synapse_config.is_empty() {
|
||||
info!("Adjusting MAS config to match Synapse config from {synapse_config:?}");
|
||||
let synapse_config = syn2mas::synapse_config::Config::load(&synapse_config)?;
|
||||
let synapse_config = syn2mas::synapse_config::Config::load(&synapse_config)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
config = synapse_config.adjust_mas_config(config, &mut rng, clock.now());
|
||||
}
|
||||
|
||||
@@ -121,7 +122,7 @@ impl Options {
|
||||
}
|
||||
|
||||
SC::Sync { prune, dry_run } => {
|
||||
let config = SyncConfig::extract(figment)?;
|
||||
let config = SyncConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let clock = SystemClock::default();
|
||||
let encrypter = config.secrets.encrypter().await?;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
@@ -30,7 +30,8 @@ enum Subcommand {
|
||||
impl Options {
|
||||
pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
|
||||
let _span = info_span!("cli.database.migrate").entered();
|
||||
let config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let config =
|
||||
DatabaseConfig::extract_or_default(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let mut conn = database_connection_from_config(&config).await?;
|
||||
|
||||
// Run pending migrations
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
@@ -41,13 +41,16 @@ impl Options {
|
||||
match self.subcommand {
|
||||
SC::Policy { with_dynamic_data } => {
|
||||
let _span = info_span!("cli.debug.policy").entered();
|
||||
let config = PolicyConfig::extract_or_default(figment)?;
|
||||
let matrix_config = MatrixConfig::extract(figment)?;
|
||||
let config =
|
||||
PolicyConfig::extract_or_default(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let matrix_config =
|
||||
MatrixConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
info!("Loading and compiling the policy module");
|
||||
let policy_factory = policy_factory_from_config(&config, &matrix_config).await?;
|
||||
|
||||
if with_dynamic_data {
|
||||
let database_config = DatabaseConfig::extract(figment)?;
|
||||
let database_config =
|
||||
DatabaseConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let pool = database_pool_from_config(&database_config).await?;
|
||||
let repository_factory = PgRepositoryFactory::new(pool.clone());
|
||||
load_policy_factory_dynamic_data(&policy_factory, &repository_factory).await?;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
//! Diagnostic utility to check the health of the deployment
|
||||
//!
|
||||
@@ -26,14 +26,13 @@ const DOCS_BASE: &str = "https://element-hq.github.io/matrix-authentication-serv
|
||||
pub(super) struct Options {}
|
||||
|
||||
impl Options {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
|
||||
let _span = info_span!("cli.doctor").entered();
|
||||
info!(
|
||||
"💡 Running diagnostics, make sure that both MAS and Synapse are running, and that MAS is using the same configuration files as this tool."
|
||||
);
|
||||
|
||||
let config = RootConfig::extract(figment)?;
|
||||
let config = RootConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
// We'll need an HTTP client
|
||||
let http_client = mas_http::reqwest_client();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{collections::BTreeMap, process::ExitCode};
|
||||
|
||||
@@ -149,6 +149,10 @@ enum Subcommand {
|
||||
UnlockUser {
|
||||
/// User to unlock
|
||||
username: String,
|
||||
|
||||
/// Whether to reactivate the user if it had been deactivated
|
||||
#[arg(long)]
|
||||
reactivate: bool,
|
||||
},
|
||||
|
||||
/// Register a user
|
||||
@@ -203,7 +207,6 @@ enum Subcommand {
|
||||
}
|
||||
|
||||
impl Options {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
|
||||
use Subcommand as SC;
|
||||
let clock = SystemClock::default();
|
||||
@@ -219,8 +222,10 @@ impl Options {
|
||||
let _span =
|
||||
info_span!("cli.manage.set_password", user.username = %username).entered();
|
||||
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let passwords_config = PasswordsConfig::extract_or_default(figment)?;
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let passwords_config = PasswordsConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
let mut conn = database_connection_from_config(&database_config).await?;
|
||||
let password_manager = password_manager_from_config(&passwords_config).await?;
|
||||
@@ -260,7 +265,8 @@ impl Options {
|
||||
)
|
||||
.entered();
|
||||
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let mut conn = database_connection_from_config(&database_config).await?;
|
||||
let txn = conn.begin().await?;
|
||||
let mut repo = PgRepository::from_conn(txn);
|
||||
@@ -314,7 +320,12 @@ impl Options {
|
||||
admin,
|
||||
device_id,
|
||||
} => {
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let matrix_config =
|
||||
MatrixConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let http_client = mas_http::reqwest_client();
|
||||
let homeserver = homeserver_connection_from_config(&matrix_config, http_client);
|
||||
let mut conn = database_connection_from_config(&database_config).await?;
|
||||
let txn = conn.begin().await?;
|
||||
let mut repo = PgRepository::from_conn(txn);
|
||||
@@ -331,6 +342,24 @@ impl Options {
|
||||
Device::generate(&mut rng)
|
||||
};
|
||||
|
||||
if let Err(e) = homeserver
|
||||
.upsert_device(&user.username, device.as_str(), None)
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
error = &*e,
|
||||
"Could not create the device on the homeserver, aborting"
|
||||
);
|
||||
|
||||
// Schedule a device sync job to remove the potential leftover device
|
||||
repo.queue_job()
|
||||
.schedule_job(&mut rng, &clock, SyncDevicesJob::new(&user))
|
||||
.await?;
|
||||
|
||||
repo.into_inner().commit().await?;
|
||||
return Ok(ExitCode::FAILURE);
|
||||
}
|
||||
|
||||
let compat_session = repo
|
||||
.compat_session()
|
||||
.add(&mut rng, &clock, &user, device, None, admin, None)
|
||||
@@ -372,7 +401,8 @@ impl Options {
|
||||
(Some(_), true) => unreachable!(), // This should be handled by the clap group
|
||||
};
|
||||
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let mut conn = database_connection_from_config(&database_config).await?;
|
||||
let txn = conn.begin().await?;
|
||||
let mut repo = PgRepository::from_conn(txn);
|
||||
@@ -399,7 +429,8 @@ impl Options {
|
||||
|
||||
SC::ProvisionAllUsers => {
|
||||
let _span = info_span!("cli.manage.provision_all_users").entered();
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let mut conn = database_connection_from_config(&database_config).await?;
|
||||
let mut txn = conn.begin().await?;
|
||||
|
||||
@@ -425,7 +456,8 @@ impl Options {
|
||||
SC::KillSessions { username, dry_run } => {
|
||||
let _span =
|
||||
info_span!("cli.manage.kill_sessions", user.username = username).entered();
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let mut conn = database_connection_from_config(&database_config).await?;
|
||||
let txn = conn.begin().await?;
|
||||
let mut repo = PgRepository::from_conn(txn);
|
||||
@@ -497,7 +529,8 @@ impl Options {
|
||||
deactivate,
|
||||
} => {
|
||||
let _span = info_span!("cli.manage.lock_user", user.username = username).entered();
|
||||
let config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let mut conn = database_connection_from_config(&config).await?;
|
||||
let txn = conn.begin().await?;
|
||||
let mut repo = PgRepository::from_conn(txn);
|
||||
@@ -527,9 +560,14 @@ impl Options {
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
|
||||
SC::UnlockUser { username } => {
|
||||
let _span = info_span!("cli.manage.lock_user", user.username = username).entered();
|
||||
let config = DatabaseConfig::extract_or_default(figment)?;
|
||||
SC::UnlockUser {
|
||||
username,
|
||||
reactivate,
|
||||
} => {
|
||||
let _span =
|
||||
info_span!("cli.manage.unlock_user", user.username = username).entered();
|
||||
let config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let mut conn = database_connection_from_config(&config).await?;
|
||||
let txn = conn.begin().await?;
|
||||
let mut repo = PgRepository::from_conn(txn);
|
||||
@@ -540,10 +578,14 @@ impl Options {
|
||||
.await?
|
||||
.context("User not found")?;
|
||||
|
||||
warn!(%user.id, "User scheduling user reactivation");
|
||||
if reactivate {
|
||||
warn!(%user.id, "Scheduling user reactivation");
|
||||
repo.queue_job()
|
||||
.schedule_job(&mut rng, &clock, ReactivateUserJob::new(&user))
|
||||
.await?;
|
||||
} else {
|
||||
repo.user().unlock(user).await?;
|
||||
}
|
||||
|
||||
repo.into_inner().commit().await?;
|
||||
|
||||
@@ -562,9 +604,12 @@ impl Options {
|
||||
ignore_password_complexity,
|
||||
} => {
|
||||
let http_client = mas_http::reqwest_client();
|
||||
let password_config = PasswordsConfig::extract_or_default(figment)?;
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let matrix_config = MatrixConfig::extract(figment)?;
|
||||
let password_config = PasswordsConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let database_config = DatabaseConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let matrix_config =
|
||||
MatrixConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
let password_manager = password_manager_from_config(&password_config).await?;
|
||||
let homeserver = homeserver_connection_from_config(&matrix_config, http_client);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{collections::BTreeSet, process::ExitCode, sync::Arc, time::Duration};
|
||||
|
||||
@@ -55,11 +55,10 @@ pub(super) struct Options {
|
||||
}
|
||||
|
||||
impl Options {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
|
||||
let span = info_span!("cli.run.init").entered();
|
||||
let mut shutdown = LifecycleManager::new()?;
|
||||
let config = AppConfig::extract(figment)?;
|
||||
let config = AppConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
info!(version = crate::VERSION, "Starting up");
|
||||
|
||||
@@ -101,8 +100,10 @@ impl Options {
|
||||
} else {
|
||||
// Sync the configuration with the database
|
||||
let mut conn = pool.acquire().await?;
|
||||
let clients_config = ClientsConfig::extract_or_default(figment)?;
|
||||
let upstream_oauth2_config = UpstreamOAuth2Config::extract_or_default(figment)?;
|
||||
let clients_config =
|
||||
ClientsConfig::extract_or_default(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let upstream_oauth2_config = UpstreamOAuth2Config::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
crate::sync::config_sync(
|
||||
upstream_oauth2_config,
|
||||
@@ -173,8 +174,9 @@ impl Options {
|
||||
test_mailer_in_background(&mailer, Duration::from_secs(30));
|
||||
|
||||
info!("Starting task worker");
|
||||
mas_tasks::init(
|
||||
mas_tasks::init_and_run(
|
||||
PgRepositoryFactory::new(pool.clone()),
|
||||
SystemClock::default(),
|
||||
&mailer,
|
||||
homeserver_connection.clone(),
|
||||
url_builder.clone(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{collections::HashMap, process::ExitCode, time::Duration};
|
||||
|
||||
@@ -88,7 +88,6 @@ const NUM_WRITER_CONNECTIONS: usize = 8;
|
||||
|
||||
impl Options {
|
||||
#[tracing::instrument("cli.syn2mas.run", skip_all)]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
|
||||
if self.synapse_configuration_files.is_empty() {
|
||||
error!("Please specify the path to the Synapse configuration file(s).");
|
||||
@@ -96,6 +95,7 @@ impl Options {
|
||||
}
|
||||
|
||||
let synapse_config = synapse_config::Config::load(&self.synapse_configuration_files)
|
||||
.map_err(anyhow::Error::from_boxed)
|
||||
.context("Failed to load Synapse configuration")?;
|
||||
|
||||
// Establish a connection to Synapse's Postgres database
|
||||
@@ -111,7 +111,8 @@ impl Options {
|
||||
.await
|
||||
.context("could not connect to Synapse Postgres database")?;
|
||||
|
||||
let config = DatabaseConfig::extract_or_default(figment)?;
|
||||
let config =
|
||||
DatabaseConfig::extract_or_default(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
let mut mas_connection = database_connection_from_config_with_options(
|
||||
&config,
|
||||
@@ -131,7 +132,7 @@ impl Options {
|
||||
// First perform a config sync
|
||||
// This is crucial to ensure we register upstream OAuth providers
|
||||
// in the MAS database
|
||||
let config = SyncConfig::extract(figment)?;
|
||||
let config = SyncConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let clock = SystemClock::default();
|
||||
let encrypter = config.secrets.encrypter().await?;
|
||||
|
||||
@@ -213,7 +214,8 @@ impl Options {
|
||||
|
||||
Subcommand::Migrate { dry_run } => {
|
||||
let provider_id_mappings: HashMap<String, Uuid> = {
|
||||
let mas_oauth2 = UpstreamOAuth2Config::extract_or_default(figment)?;
|
||||
let mas_oauth2 = UpstreamOAuth2Config::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
mas_oauth2
|
||||
.providers
|
||||
@@ -252,7 +254,8 @@ impl Options {
|
||||
let occasional_progress_logger_task =
|
||||
tokio::spawn(occasional_progress_logger(progress.clone()));
|
||||
|
||||
let mas_matrix = MatrixConfig::extract(figment)?;
|
||||
let mas_matrix =
|
||||
MatrixConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
syn2mas::migrate(
|
||||
reader,
|
||||
writer,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
@@ -37,13 +37,20 @@ impl Options {
|
||||
SC::Check => {
|
||||
let _span = info_span!("cli.templates.check").entered();
|
||||
|
||||
let template_config = TemplatesConfig::extract_or_default(figment)?;
|
||||
let branding_config = BrandingConfig::extract_or_default(figment)?;
|
||||
let matrix_config = MatrixConfig::extract(figment)?;
|
||||
let experimental_config = ExperimentalConfig::extract_or_default(figment)?;
|
||||
let password_config = PasswordsConfig::extract_or_default(figment)?;
|
||||
let account_config = AccountConfig::extract_or_default(figment)?;
|
||||
let captcha_config = CaptchaConfig::extract_or_default(figment)?;
|
||||
let template_config = TemplatesConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let branding_config = BrandingConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let matrix_config =
|
||||
MatrixConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
let experimental_config = ExperimentalConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let password_config = PasswordsConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let account_config = AccountConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
let captcha_config = CaptchaConfig::extract_or_default(figment)
|
||||
.map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
let clock = SystemClock::default();
|
||||
// XXX: we should disallow SeedableRng::from_entropy
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{process::ExitCode, time::Duration};
|
||||
|
||||
@@ -10,6 +10,7 @@ use clap::Parser;
|
||||
use figment::Figment;
|
||||
use mas_config::{AppConfig, ConfigurationSection};
|
||||
use mas_router::UrlBuilder;
|
||||
use mas_storage::SystemClock;
|
||||
use mas_storage_pg::PgRepositoryFactory;
|
||||
use tracing::{info, info_span};
|
||||
|
||||
@@ -28,7 +29,7 @@ impl Options {
|
||||
pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
|
||||
let shutdown = LifecycleManager::new()?;
|
||||
let span = info_span!("cli.worker.init").entered();
|
||||
let config = AppConfig::extract(figment)?;
|
||||
let config = AppConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
|
||||
|
||||
// Connect to the database
|
||||
info!("Connecting to the database");
|
||||
@@ -63,8 +64,9 @@ impl Options {
|
||||
drop(config);
|
||||
|
||||
info!("Starting task scheduler");
|
||||
mas_tasks::init(
|
||||
mas_tasks::init_and_run(
|
||||
PgRepositoryFactory::new(pool.clone()),
|
||||
SystemClock::default(),
|
||||
&mailer,
|
||||
conn,
|
||||
url_builder,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{process::ExitCode, time::Duration};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
@@ -115,8 +115,9 @@ async fn try_main() -> anyhow::Result<ExitCode> {
|
||||
// Load the base configuration files
|
||||
let figment = opts.figment();
|
||||
|
||||
let telemetry_config =
|
||||
TelemetryConfig::extract_or_default(&figment).context("Failed to load telemetry config")?;
|
||||
let telemetry_config = TelemetryConfig::extract_or_default(&figment)
|
||||
.map_err(anyhow::Error::from_boxed)
|
||||
.context("Failed to load telemetry config")?;
|
||||
|
||||
// Setup Sentry
|
||||
let sentry = sentry::init((
|
||||
@@ -127,8 +128,6 @@ async fn try_main() -> anyhow::Result<ExitCode> {
|
||||
release: Some(VERSION.into()),
|
||||
sample_rate: telemetry_config.sentry.sample_rate.unwrap_or(1.0),
|
||||
traces_sample_rate: telemetry_config.sentry.traces_sample_rate.unwrap_or(0.0),
|
||||
auto_session_tracking: true,
|
||||
session_mode: sentry::SessionMode::Request,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, TcpListener, ToSocketAddrs},
|
||||
@@ -205,7 +205,6 @@ async fn log_response_middleware(
|
||||
response
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn build_router(
|
||||
state: AppState,
|
||||
resources: &[HttpResource],
|
||||
@@ -269,7 +268,7 @@ pub fn build_router(
|
||||
}
|
||||
mas_config::HttpResource::OAuth => router.merge(mas_handlers::api_router::<AppState>()),
|
||||
mas_config::HttpResource::Compat => {
|
||||
router.merge(mas_handlers::compat_router::<AppState>())
|
||||
router.merge(mas_handlers::compat_router::<AppState>(templates.clone()))
|
||||
}
|
||||
mas_config::HttpResource::AdminApi => {
|
||||
let (_, api_router) = mas_handlers::admin_api_router::<AppState>();
|
||||
@@ -333,7 +332,7 @@ pub fn build_router(
|
||||
// which is the other way around compared to `tower::ServiceBuilder`.
|
||||
// So even if the Sentry docs has an example that does
|
||||
// 'NewSentryHttpLayer then SentryHttpLayer', we must do the opposite.
|
||||
.layer(SentryHttpLayer::with_transaction())
|
||||
.layer(SentryHttpLayer::new().enable_transaction())
|
||||
.layer(NewSentryLayer::new_from_top())
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
//! Utilities to synchronize the configuration file with the database.
|
||||
|
||||
@@ -37,6 +37,19 @@ fn map_import_action(
|
||||
}
|
||||
}
|
||||
|
||||
fn map_import_on_conflict(
|
||||
config: mas_config::UpstreamOAuth2OnConflict,
|
||||
) -> mas_data_model::UpstreamOAuthProviderOnConflict {
|
||||
match config {
|
||||
mas_config::UpstreamOAuth2OnConflict::Add => {
|
||||
mas_data_model::UpstreamOAuthProviderOnConflict::Add
|
||||
}
|
||||
mas_config::UpstreamOAuth2OnConflict::Fail => {
|
||||
mas_data_model::UpstreamOAuthProviderOnConflict::Fail
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_claims_imports(
|
||||
config: &mas_config::UpstreamOAuth2ClaimsImports,
|
||||
) -> mas_data_model::UpstreamOAuthProviderClaimsImports {
|
||||
@@ -44,9 +57,10 @@ fn map_claims_imports(
|
||||
subject: mas_data_model::UpstreamOAuthProviderSubjectPreference {
|
||||
template: config.subject.template.clone(),
|
||||
},
|
||||
localpart: mas_data_model::UpstreamOAuthProviderImportPreference {
|
||||
localpart: mas_data_model::UpstreamOAuthProviderLocalpartPreference {
|
||||
action: map_import_action(config.localpart.action),
|
||||
template: config.localpart.template.clone(),
|
||||
on_conflict: map_import_on_conflict(config.localpart.on_conflict),
|
||||
},
|
||||
displayname: mas_data_model::UpstreamOAuthProviderImportPreference {
|
||||
action: map_import_action(config.displayname.action),
|
||||
@@ -276,6 +290,18 @@ pub async fn config_sync(
|
||||
}
|
||||
};
|
||||
|
||||
let on_backchannel_logout = match provider.on_backchannel_logout {
|
||||
mas_config::UpstreamOAuth2OnBackchannelLogout::DoNothing => {
|
||||
mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing
|
||||
}
|
||||
mas_config::UpstreamOAuth2OnBackchannelLogout::LogoutBrowserOnly => {
|
||||
mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::LogoutBrowserOnly
|
||||
}
|
||||
mas_config::UpstreamOAuth2OnBackchannelLogout::LogoutAll => {
|
||||
mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::LogoutAll
|
||||
}
|
||||
};
|
||||
|
||||
repo.upstream_oauth_provider()
|
||||
.upsert(
|
||||
clock,
|
||||
@@ -306,6 +332,7 @@ pub async fn config_sync(
|
||||
.collect(),
|
||||
forward_login_hint: provider.forward_login_hint,
|
||||
ui_order,
|
||||
on_backchannel_logout,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
mod tokio;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use opentelemetry::KeyValue;
|
||||
use tokio::runtime::RuntimeMetrics;
|
||||
@@ -9,7 +9,6 @@ use tokio::runtime::RuntimeMetrics;
|
||||
use super::METER;
|
||||
|
||||
/// Install metrics for the tokio runtime.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn observe(metrics: RuntimeMetrics) {
|
||||
{
|
||||
let metrics = metrics.clone();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
@@ -17,7 +17,7 @@ use mas_data_model::{SessionExpirationConfig, SiteConfig};
|
||||
use mas_email::{MailTransport, Mailer};
|
||||
use mas_handlers::passwords::PasswordManager;
|
||||
use mas_matrix::{HomeserverConnection, ReadOnlyHomeserverConnection};
|
||||
use mas_matrix_synapse::SynapseConnection;
|
||||
use mas_matrix_synapse::{LegacySynapseConnection, SynapseConnection};
|
||||
use mas_policy::PolicyFactory;
|
||||
use mas_router::UrlBuilder;
|
||||
use mas_storage::{BoxRepositoryFactory, RepositoryAccess, RepositoryFactory};
|
||||
@@ -469,14 +469,22 @@ pub fn homeserver_connection_from_config(
|
||||
http_client: reqwest::Client,
|
||||
) -> Arc<dyn HomeserverConnection> {
|
||||
match config.kind {
|
||||
HomeserverKind::Synapse => Arc::new(SynapseConnection::new(
|
||||
HomeserverKind::Synapse | HomeserverKind::SynapseLegacy => {
|
||||
Arc::new(LegacySynapseConnection::new(
|
||||
config.homeserver.clone(),
|
||||
config.endpoint.clone(),
|
||||
config.secret.clone(),
|
||||
http_client,
|
||||
))
|
||||
}
|
||||
HomeserverKind::SynapseModern => Arc::new(SynapseConnection::new(
|
||||
config.homeserver.clone(),
|
||||
config.endpoint.clone(),
|
||||
config.secret.clone(),
|
||||
http_client,
|
||||
)),
|
||||
HomeserverKind::SynapseReadOnly => {
|
||||
let connection = SynapseConnection::new(
|
||||
let connection = LegacySynapseConnection::new(
|
||||
config.homeserver.clone(),
|
||||
config.endpoint.clone(),
|
||||
config.secret.clone(),
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
[package]
|
||||
name = "mas-config"
|
||||
version.workspace = true
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use schemars::r#gen::SchemaSettings;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
#![deny(missing_docs, rustdoc::missing_crate_level_docs)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
//! Useful JSON Schema definitions
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize, de::Error};
|
||||
@@ -51,7 +51,10 @@ impl CaptchaConfig {
|
||||
impl ConfigurationSection for CaptchaConfig {
|
||||
const PATH: Option<&'static str> = Some("captcha");
|
||||
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let metadata = figment.find_metadata(Self::PATH.unwrap());
|
||||
|
||||
let error_on_field = |mut error: figment::error::Error, field: &'static str| {
|
||||
@@ -67,11 +70,11 @@ impl ConfigurationSection for CaptchaConfig {
|
||||
|
||||
if let Some(CaptchaServiceKind::RecaptchaV2) = self.service {
|
||||
if self.site_key.is_none() {
|
||||
return Err(missing_field("site_key"));
|
||||
return Err(missing_field("site_key").into());
|
||||
}
|
||||
|
||||
if self.secret_key.is_none() {
|
||||
return Err(missing_field("secret_key"));
|
||||
return Err(missing_field("secret_key").into());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use figment::Figment;
|
||||
use mas_iana::oauth::OAuthClientAuthenticationMethod;
|
||||
use mas_jose::jwk::PublicJsonWebKeySet;
|
||||
use schemars::JsonSchema;
|
||||
@@ -104,7 +103,7 @@ pub struct ClientConfig {
|
||||
}
|
||||
|
||||
impl ClientConfig {
|
||||
fn validate(&self) -> Result<(), figment::error::Error> {
|
||||
fn validate(&self) -> Result<(), Box<figment::error::Error>> {
|
||||
let auth_method = self.client_auth_method;
|
||||
match self.client_auth_method {
|
||||
ClientAuthMethodConfig::PrivateKeyJwt => {
|
||||
@@ -112,20 +111,20 @@ impl ClientConfig {
|
||||
let error = figment::error::Error::custom(
|
||||
"jwks or jwks_uri is required for private_key_jwt",
|
||||
);
|
||||
return Err(error.with_path("client_auth_method"));
|
||||
return Err(Box::new(error.with_path("client_auth_method")));
|
||||
}
|
||||
|
||||
if self.jwks.is_some() && self.jwks_uri.is_some() {
|
||||
let error =
|
||||
figment::error::Error::custom("jwks and jwks_uri are mutually exclusive");
|
||||
return Err(error.with_path("jwks"));
|
||||
return Err(Box::new(error.with_path("jwks")));
|
||||
}
|
||||
|
||||
if self.client_secret.is_some() {
|
||||
let error = figment::error::Error::custom(
|
||||
"client_secret is not allowed with private_key_jwt",
|
||||
);
|
||||
return Err(error.with_path("client_secret"));
|
||||
return Err(Box::new(error.with_path("client_secret")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,21 +135,21 @@ impl ClientConfig {
|
||||
let error = figment::error::Error::custom(format!(
|
||||
"client_secret is required for {auth_method}"
|
||||
));
|
||||
return Err(error.with_path("client_auth_method"));
|
||||
return Err(Box::new(error.with_path("client_auth_method")));
|
||||
}
|
||||
|
||||
if self.jwks.is_some() {
|
||||
let error = figment::error::Error::custom(format!(
|
||||
"jwks is not allowed with {auth_method}"
|
||||
));
|
||||
return Err(error.with_path("jwks"));
|
||||
return Err(Box::new(error.with_path("jwks")));
|
||||
}
|
||||
|
||||
if self.jwks_uri.is_some() {
|
||||
let error = figment::error::Error::custom(format!(
|
||||
"jwks_uri is not allowed with {auth_method}"
|
||||
));
|
||||
return Err(error.with_path("jwks_uri"));
|
||||
return Err(Box::new(error.with_path("jwks_uri")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,21 +158,21 @@ impl ClientConfig {
|
||||
let error = figment::error::Error::custom(
|
||||
"client_secret is not allowed with none authentication method",
|
||||
);
|
||||
return Err(error.with_path("client_secret"));
|
||||
return Err(Box::new(error.with_path("client_secret")));
|
||||
}
|
||||
|
||||
if self.jwks.is_some() {
|
||||
let error = figment::error::Error::custom(
|
||||
"jwks is not allowed with none authentication method",
|
||||
);
|
||||
return Err(error);
|
||||
return Err(Box::new(error));
|
||||
}
|
||||
|
||||
if self.jwks_uri.is_some() {
|
||||
let error = figment::error::Error::custom(
|
||||
"jwks_uri is not allowed with none authentication method",
|
||||
);
|
||||
return Err(error);
|
||||
return Err(Box::new(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +231,10 @@ impl IntoIterator for ClientsConfig {
|
||||
impl ConfigurationSection for ClientsConfig {
|
||||
const PATH: Option<&'static str> = Some("clients");
|
||||
|
||||
fn validate(&self, figment: &Figment) -> Result<(), figment::error::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
for (index, client) in self.0.iter().enumerate() {
|
||||
client.validate().map_err(|mut err| {
|
||||
// Save the error location information in the error
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{num::NonZeroU32, time::Duration};
|
||||
|
||||
@@ -222,13 +222,16 @@ pub struct DatabaseConfig {
|
||||
impl ConfigurationSection for DatabaseConfig {
|
||||
const PATH: Option<&'static str> = Some("database");
|
||||
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::error::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let metadata = figment.find_metadata(Self::PATH.unwrap());
|
||||
let annotate = |mut error: figment::Error| {
|
||||
error.metadata = metadata.cloned();
|
||||
error.profile = Some(figment::Profile::Default);
|
||||
error.path = vec![Self::PATH.unwrap().to_owned()];
|
||||
Err(error)
|
||||
error
|
||||
};
|
||||
|
||||
// Check that the user did not specify both `uri` and the split options at the
|
||||
@@ -241,37 +244,41 @@ impl ConfigurationSection for DatabaseConfig {
|
||||
|| self.database.is_some();
|
||||
|
||||
if self.uri.is_some() && has_split_options {
|
||||
return annotate(figment::error::Error::from(
|
||||
return Err(annotate(figment::error::Error::from(
|
||||
"uri must not be specified if host, port, socket, username, password, or database are specified".to_owned(),
|
||||
));
|
||||
)).into());
|
||||
}
|
||||
|
||||
if self.ssl_ca.is_some() && self.ssl_ca_file.is_some() {
|
||||
return annotate(figment::error::Error::from(
|
||||
return Err(annotate(figment::error::Error::from(
|
||||
"ssl_ca must not be specified if ssl_ca_file is specified".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
if self.ssl_certificate.is_some() && self.ssl_certificate_file.is_some() {
|
||||
return annotate(figment::error::Error::from(
|
||||
return Err(annotate(figment::error::Error::from(
|
||||
"ssl_certificate must not be specified if ssl_certificate_file is specified"
|
||||
.to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
if self.ssl_key.is_some() && self.ssl_key_file.is_some() {
|
||||
return annotate(figment::error::Error::from(
|
||||
return Err(annotate(figment::error::Error::from(
|
||||
"ssl_key must not be specified if ssl_key_file is specified".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
if (self.ssl_key.is_some() || self.ssl_key_file.is_some())
|
||||
^ (self.ssl_certificate.is_some() || self.ssl_certificate_file.is_some())
|
||||
{
|
||||
return annotate(figment::error::Error::from(
|
||||
return Err(annotate(figment::error::Error::from(
|
||||
"both a ssl_certificate and a ssl_key must be set at the same time or none of them"
|
||||
.to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
@@ -175,7 +175,10 @@ impl Default for EmailConfig {
|
||||
impl ConfigurationSection for EmailConfig {
|
||||
const PATH: Option<&'static str> = Some("email");
|
||||
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::error::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let metadata = figment.find_metadata(Self::PATH.unwrap());
|
||||
|
||||
let error_on_field = |mut error: figment::error::Error, field: &'static str| {
|
||||
@@ -201,29 +204,29 @@ impl ConfigurationSection for EmailConfig {
|
||||
|
||||
EmailTransportKind::Smtp => {
|
||||
if let Err(e) = Mailbox::from_str(&self.from) {
|
||||
return Err(error_on_field(figment::error::Error::custom(e), "from"));
|
||||
return Err(error_on_field(figment::error::Error::custom(e), "from").into());
|
||||
}
|
||||
|
||||
if let Err(e) = Mailbox::from_str(&self.reply_to) {
|
||||
return Err(error_on_field(figment::error::Error::custom(e), "reply_to"));
|
||||
return Err(error_on_field(figment::error::Error::custom(e), "reply_to").into());
|
||||
}
|
||||
|
||||
match (self.username.is_some(), self.password.is_some()) {
|
||||
(true, true) | (false, false) => {}
|
||||
(true, false) => {
|
||||
return Err(missing_field("password"));
|
||||
return Err(missing_field("password").into());
|
||||
}
|
||||
(false, true) => {
|
||||
return Err(missing_field("username"));
|
||||
return Err(missing_field("username").into());
|
||||
}
|
||||
}
|
||||
|
||||
if self.mode.is_none() {
|
||||
return Err(missing_field("mode"));
|
||||
return Err(missing_field("mode").into());
|
||||
}
|
||||
|
||||
if self.hostname.is_none() {
|
||||
return Err(missing_field("hostname"));
|
||||
return Err(missing_field("hostname").into());
|
||||
}
|
||||
|
||||
if self.command.is_some() {
|
||||
@@ -239,7 +242,8 @@ impl ConfigurationSection for EmailConfig {
|
||||
"username",
|
||||
"password",
|
||||
],
|
||||
));
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,35 +251,35 @@ impl ConfigurationSection for EmailConfig {
|
||||
let expected_fields = &["from", "reply_to", "transport", "command"];
|
||||
|
||||
if let Err(e) = Mailbox::from_str(&self.from) {
|
||||
return Err(error_on_field(figment::error::Error::custom(e), "from"));
|
||||
return Err(error_on_field(figment::error::Error::custom(e), "from").into());
|
||||
}
|
||||
|
||||
if let Err(e) = Mailbox::from_str(&self.reply_to) {
|
||||
return Err(error_on_field(figment::error::Error::custom(e), "reply_to"));
|
||||
return Err(error_on_field(figment::error::Error::custom(e), "reply_to").into());
|
||||
}
|
||||
|
||||
if self.command.is_none() {
|
||||
return Err(missing_field("command"));
|
||||
return Err(missing_field("command").into());
|
||||
}
|
||||
|
||||
if self.mode.is_some() {
|
||||
return Err(unexpected_field("mode", expected_fields));
|
||||
return Err(unexpected_field("mode", expected_fields).into());
|
||||
}
|
||||
|
||||
if self.hostname.is_some() {
|
||||
return Err(unexpected_field("hostname", expected_fields));
|
||||
return Err(unexpected_field("hostname", expected_fields).into());
|
||||
}
|
||||
|
||||
if self.port.is_some() {
|
||||
return Err(unexpected_field("port", expected_fields));
|
||||
return Err(unexpected_field("port", expected_fields).into());
|
||||
}
|
||||
|
||||
if self.username.is_some() {
|
||||
return Err(unexpected_field("username", expected_fields));
|
||||
return Err(unexpected_field("username", expected_fields).into());
|
||||
}
|
||||
|
||||
if self.password.is_some() {
|
||||
return Err(unexpected_field("password", expected_fields));
|
||||
return Err(unexpected_field("password", expected_fields).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use chrono::Duration;
|
||||
use schemars::JsonSchema;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
@@ -412,7 +412,10 @@ impl Default for HttpConfig {
|
||||
impl ConfigurationSection for HttpConfig {
|
||||
const PATH: Option<&'static str> = Some("http");
|
||||
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
for (index, listener) in self.listeners.iter().enumerate() {
|
||||
let annotate = |mut error: figment::Error| {
|
||||
error.metadata = figment
|
||||
@@ -424,49 +427,57 @@ impl ConfigurationSection for HttpConfig {
|
||||
"listeners".to_owned(),
|
||||
index.to_string(),
|
||||
];
|
||||
Err(error)
|
||||
error
|
||||
};
|
||||
|
||||
if listener.resources.is_empty() {
|
||||
return annotate(figment::Error::from("listener has no resources".to_owned()));
|
||||
return Err(
|
||||
annotate(figment::Error::from("listener has no resources".to_owned())).into(),
|
||||
);
|
||||
}
|
||||
|
||||
if listener.binds.is_empty() {
|
||||
return annotate(figment::Error::from(
|
||||
return Err(annotate(figment::Error::from(
|
||||
"listener does not bind to any address".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
if let Some(tls_config) = &listener.tls {
|
||||
if tls_config.certificate.is_some() && tls_config.certificate_file.is_some() {
|
||||
return annotate(figment::Error::from(
|
||||
return Err(annotate(figment::Error::from(
|
||||
"Only one of `certificate` or `certificate_file` can be set at a time"
|
||||
.to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
if tls_config.certificate.is_none() && tls_config.certificate_file.is_none() {
|
||||
return annotate(figment::Error::from(
|
||||
return Err(annotate(figment::Error::from(
|
||||
"TLS configuration is missing a certificate".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
if tls_config.key.is_some() && tls_config.key_file.is_some() {
|
||||
return annotate(figment::Error::from(
|
||||
return Err(annotate(figment::Error::from(
|
||||
"Only one of `key` or `key_file` can be set at a time".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
if tls_config.key.is_none() && tls_config.key_file.is_none() {
|
||||
return annotate(figment::Error::from(
|
||||
return Err(annotate(figment::Error::from(
|
||||
"TLS configuration is missing a private key".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
if tls_config.password.is_some() && tls_config.password_file.is_some() {
|
||||
return annotate(figment::Error::from(
|
||||
return Err(annotate(figment::Error::from(
|
||||
"Only one of `password` or `password_file` can be set at a time".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use rand::{
|
||||
Rng,
|
||||
@@ -27,15 +27,25 @@ fn default_endpoint() -> Url {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HomeserverKind {
|
||||
/// Homeserver is Synapse
|
||||
/// Homeserver is Synapse, using the legacy API
|
||||
///
|
||||
/// This will switch to using the modern API in a few releases.
|
||||
#[default]
|
||||
Synapse,
|
||||
|
||||
/// Homeserver is Synapse, in read-only mode
|
||||
/// Homeserver is Synapse, using the legacy API, in read-only mode
|
||||
///
|
||||
/// This is meant for testing rolling out Matrix Authentication Service with
|
||||
/// no risk of writing data to the homeserver.
|
||||
///
|
||||
/// This will switch to using the modern API in a few releases.
|
||||
SynapseReadOnly,
|
||||
|
||||
/// Homeserver is Synapse, using the legacy API,
|
||||
SynapseLegacy,
|
||||
|
||||
/// Homeserver is Synapse, with the modern API available
|
||||
SynapseModern,
|
||||
}
|
||||
|
||||
/// Configuration related to the Matrix homeserver
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use rand::Rng;
|
||||
use schemars::JsonSchema;
|
||||
@@ -52,7 +52,9 @@ pub use self::{
|
||||
upstream_oauth2::{
|
||||
ClaimsImports as UpstreamOAuth2ClaimsImports, DiscoveryMode as UpstreamOAuth2DiscoveryMode,
|
||||
EmailImportPreference as UpstreamOAuth2EmailImportPreference,
|
||||
ImportAction as UpstreamOAuth2ImportAction, PkceMethod as UpstreamOAuth2PkceMethod,
|
||||
ImportAction as UpstreamOAuth2ImportAction,
|
||||
OnBackchannelLogout as UpstreamOAuth2OnBackchannelLogout,
|
||||
OnConflict as UpstreamOAuth2OnConflict, PkceMethod as UpstreamOAuth2PkceMethod,
|
||||
Provider as UpstreamOAuth2Provider, ResponseMode as UpstreamOAuth2ResponseMode,
|
||||
TokenAuthMethod as UpstreamOAuth2TokenAuthMethod, UpstreamOAuth2Config,
|
||||
},
|
||||
@@ -128,7 +130,10 @@ pub struct RootConfig {
|
||||
}
|
||||
|
||||
impl ConfigurationSection for RootConfig {
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
self.clients.validate(figment)?;
|
||||
self.http.validate(figment)?;
|
||||
self.database.validate(figment)?;
|
||||
@@ -247,7 +252,10 @@ pub struct AppConfig {
|
||||
}
|
||||
|
||||
impl ConfigurationSection for AppConfig {
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
self.http.validate(figment)?;
|
||||
self.database.validate(figment)?;
|
||||
self.templates.validate(figment)?;
|
||||
@@ -283,7 +291,10 @@ pub struct SyncConfig {
|
||||
}
|
||||
|
||||
impl ConfigurationSection for SyncConfig {
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
self.database.validate(figment)?;
|
||||
self.secrets.validate(figment)?;
|
||||
self.clients.validate(figment)?;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::cmp::Reverse;
|
||||
|
||||
@@ -72,12 +72,15 @@ impl Default for PasswordsConfig {
|
||||
impl ConfigurationSection for PasswordsConfig {
|
||||
const PATH: Option<&'static str> = Some("passwords");
|
||||
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let annotate = |mut error: figment::Error| {
|
||||
error.metadata = figment.find_metadata(Self::PATH.unwrap()).cloned();
|
||||
error.profile = Some(figment::Profile::Default);
|
||||
error.path = vec![Self::PATH.unwrap().to_owned()];
|
||||
Err(error)
|
||||
error
|
||||
};
|
||||
|
||||
if !self.enabled {
|
||||
@@ -86,16 +89,18 @@ impl ConfigurationSection for PasswordsConfig {
|
||||
}
|
||||
|
||||
if self.schemes.is_empty() {
|
||||
return annotate(figment::Error::from(
|
||||
return Err(annotate(figment::Error::from(
|
||||
"Requires at least one password scheme in the config".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
for scheme in &self.schemes {
|
||||
if scheme.secret.is_some() && scheme.secret_file.is_some() {
|
||||
return annotate(figment::Error::from(
|
||||
return Err(annotate(figment::Error::from(
|
||||
"Cannot specify both `secret` and `secret_file`".to_owned(),
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use camino::Utf8PathBuf;
|
||||
use schemars::JsonSchema;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{num::NonZeroU32, time::Duration};
|
||||
|
||||
@@ -117,7 +117,10 @@ pub struct RateLimiterConfiguration {
|
||||
impl ConfigurationSection for RateLimitingConfig {
|
||||
const PATH: Option<&'static str> = Some("rate_limiting");
|
||||
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let metadata = figment.find_metadata(Self::PATH.unwrap());
|
||||
|
||||
let error_on_field = |mut error: figment::error::Error, field: &'static str| {
|
||||
@@ -154,25 +157,21 @@ impl ConfigurationSection for RateLimitingConfig {
|
||||
};
|
||||
|
||||
if let Some(error) = error_on_limiter(&self.account_recovery.per_ip) {
|
||||
return Err(error_on_nested_field(error, "account_recovery", "per_ip"));
|
||||
return Err(error_on_nested_field(error, "account_recovery", "per_ip").into());
|
||||
}
|
||||
if let Some(error) = error_on_limiter(&self.account_recovery.per_address) {
|
||||
return Err(error_on_nested_field(
|
||||
error,
|
||||
"account_recovery",
|
||||
"per_address",
|
||||
));
|
||||
return Err(error_on_nested_field(error, "account_recovery", "per_address").into());
|
||||
}
|
||||
|
||||
if let Some(error) = error_on_limiter(&self.registration) {
|
||||
return Err(error_on_field(error, "registration"));
|
||||
return Err(error_on_field(error, "registration").into());
|
||||
}
|
||||
|
||||
if let Some(error) = error_on_limiter(&self.login.per_ip) {
|
||||
return Err(error_on_nested_field(error, "login", "per_ip"));
|
||||
return Err(error_on_nested_field(error, "login", "per_ip").into());
|
||||
}
|
||||
if let Some(error) = error_on_limiter(&self.login.per_account) {
|
||||
return Err(error_on_nested_field(error, "login", "per_account"));
|
||||
return Err(error_on_nested_field(error, "login", "per_account").into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
@@ -149,10 +149,10 @@ impl KeyConfig {
|
||||
/// Returns the password in case any is provided.
|
||||
///
|
||||
/// If `password_file` was given, the password is read from that file.
|
||||
async fn password(&self) -> anyhow::Result<Option<Cow<String>>> {
|
||||
async fn password(&self) -> anyhow::Result<Option<Cow<[u8]>>> {
|
||||
Ok(match &self.password {
|
||||
Some(Password::File(path)) => Some(Cow::Owned(tokio::fs::read_to_string(path).await?)),
|
||||
Some(Password::Value(password)) => Some(Cow::Borrowed(password)),
|
||||
Some(Password::File(path)) => Some(Cow::Owned(tokio::fs::read(path).await?)),
|
||||
Some(Password::Value(password)) => Some(Cow::Borrowed(password.as_bytes())),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
@@ -160,10 +160,10 @@ impl KeyConfig {
|
||||
/// Returns the key.
|
||||
///
|
||||
/// If `key_file` was given, the key is read from that file.
|
||||
async fn key(&self) -> anyhow::Result<Cow<String>> {
|
||||
async fn key(&self) -> anyhow::Result<Cow<[u8]>> {
|
||||
Ok(match &self.key {
|
||||
Key::File(path) => Cow::Owned(tokio::fs::read_to_string(path).await?),
|
||||
Key::Value(key) => Cow::Borrowed(key),
|
||||
Key::File(path) => Cow::Owned(tokio::fs::read(path).await?),
|
||||
Key::Value(key) => Cow::Borrowed(key.as_bytes()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -174,8 +174,8 @@ impl KeyConfig {
|
||||
let (key, password) = try_join(self.key(), self.password()).await?;
|
||||
|
||||
let private_key = match password {
|
||||
Some(password) => PrivateKey::load_encrypted(key.as_bytes(), password.as_bytes())?,
|
||||
None => PrivateKey::load(key.as_bytes())?,
|
||||
Some(password) => PrivateKey::load_encrypted(&key, password)?,
|
||||
None => PrivateKey::load(&key)?,
|
||||
};
|
||||
|
||||
Ok(JsonWebKey::new(private_key)
|
||||
@@ -303,6 +303,7 @@ impl ConfigurationSection for SecretsConfig {
|
||||
}
|
||||
|
||||
impl SecretsConfig {
|
||||
#[expect(clippy::similar_names, reason = "Key type names are very similar")]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn generate<R>(mut rng: R) -> anyhow::Result<Self>
|
||||
where
|
||||
@@ -347,7 +348,7 @@ impl SecretsConfig {
|
||||
let ec_p384_key = task::spawn_blocking(move || {
|
||||
let _entered = span.enter();
|
||||
let ret = PrivateKey::generate_ec_p384(key_rng);
|
||||
info!("Done generating EC P-256 key");
|
||||
info!("Done generating EC P-384 key");
|
||||
ret
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize, de::Error as _};
|
||||
@@ -194,13 +194,17 @@ impl TelemetryConfig {
|
||||
impl ConfigurationSection for TelemetryConfig {
|
||||
const PATH: Option<&'static str> = Some("telemetry");
|
||||
|
||||
fn validate(&self, _figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
_figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
if let Some(sample_rate) = self.sentry.sample_rate {
|
||||
if !(0.0..=1.0).contains(&sample_rate) {
|
||||
return Err(figment::error::Error::custom(
|
||||
"Sentry sample rate must be between 0.0 and 1.0",
|
||||
)
|
||||
.with_path("sentry.sample_rate"));
|
||||
.with_path("sentry.sample_rate")
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +213,8 @@ impl ConfigurationSection for TelemetryConfig {
|
||||
return Err(figment::error::Error::custom(
|
||||
"Sentry sample rate must be between 0.0 and 1.0",
|
||||
)
|
||||
.with_path("sentry.traces_sample_rate"));
|
||||
.with_path("sentry.traces_sample_rate")
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +223,8 @@ impl ConfigurationSection for TelemetryConfig {
|
||||
return Err(figment::error::Error::custom(
|
||||
"Tracing sample rate must be between 0.0 and 1.0",
|
||||
)
|
||||
.with_path("tracing.sample_rate"));
|
||||
.with_path("tracing.sample_rate")
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use camino::Utf8PathBuf;
|
||||
use schemars::JsonSchema;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -33,7 +33,10 @@ impl UpstreamOAuth2Config {
|
||||
impl ConfigurationSection for UpstreamOAuth2Config {
|
||||
const PATH: Option<&'static str> = Some("upstream_oauth2");
|
||||
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
fn validate(
|
||||
&self,
|
||||
figment: &figment::Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
for (index, provider) in self.providers.iter().enumerate() {
|
||||
let annotate = |mut error: figment::Error| {
|
||||
error.metadata = figment
|
||||
@@ -45,15 +48,16 @@ impl ConfigurationSection for UpstreamOAuth2Config {
|
||||
"providers".to_owned(),
|
||||
index.to_string(),
|
||||
];
|
||||
Err(error)
|
||||
error
|
||||
};
|
||||
|
||||
if !matches!(provider.discovery_mode, DiscoveryMode::Disabled)
|
||||
&& provider.issuer.is_none()
|
||||
{
|
||||
return annotate(figment::Error::custom(
|
||||
return Err(annotate(figment::Error::custom(
|
||||
"The `issuer` field is required when discovery is enabled",
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
match provider.token_endpoint_auth_method {
|
||||
@@ -61,16 +65,16 @@ impl ConfigurationSection for UpstreamOAuth2Config {
|
||||
| TokenAuthMethod::PrivateKeyJwt
|
||||
| TokenAuthMethod::SignInWithApple => {
|
||||
if provider.client_secret.is_some() {
|
||||
return annotate(figment::Error::custom(
|
||||
return Err(annotate(figment::Error::custom(
|
||||
"Unexpected field `client_secret` for the selected authentication method",
|
||||
));
|
||||
)).into());
|
||||
}
|
||||
}
|
||||
TokenAuthMethod::ClientSecretBasic
|
||||
| TokenAuthMethod::ClientSecretPost
|
||||
| TokenAuthMethod::ClientSecretJwt => {
|
||||
if provider.client_secret.is_none() {
|
||||
return annotate(figment::Error::missing_field("client_secret"));
|
||||
return Err(annotate(figment::Error::missing_field("client_secret")).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,16 +85,17 @@ impl ConfigurationSection for UpstreamOAuth2Config {
|
||||
| TokenAuthMethod::ClientSecretPost
|
||||
| TokenAuthMethod::SignInWithApple => {
|
||||
if provider.token_endpoint_auth_signing_alg.is_some() {
|
||||
return annotate(figment::Error::custom(
|
||||
return Err(annotate(figment::Error::custom(
|
||||
"Unexpected field `token_endpoint_auth_signing_alg` for the selected authentication method",
|
||||
));
|
||||
)).into());
|
||||
}
|
||||
}
|
||||
TokenAuthMethod::ClientSecretJwt | TokenAuthMethod::PrivateKeyJwt => {
|
||||
if provider.token_endpoint_auth_signing_alg.is_none() {
|
||||
return annotate(figment::Error::missing_field(
|
||||
return Err(annotate(figment::Error::missing_field(
|
||||
"token_endpoint_auth_signing_alg",
|
||||
));
|
||||
))
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,18 +103,32 @@ impl ConfigurationSection for UpstreamOAuth2Config {
|
||||
match provider.token_endpoint_auth_method {
|
||||
TokenAuthMethod::SignInWithApple => {
|
||||
if provider.sign_in_with_apple.is_none() {
|
||||
return annotate(figment::Error::missing_field("sign_in_with_apple"));
|
||||
return Err(
|
||||
annotate(figment::Error::missing_field("sign_in_with_apple")).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
if provider.sign_in_with_apple.is_some() {
|
||||
return annotate(figment::Error::custom(
|
||||
return Err(annotate(figment::Error::custom(
|
||||
"Unexpected field `sign_in_with_apple` for the selected authentication method",
|
||||
));
|
||||
)).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(
|
||||
provider.claims_imports.localpart.on_conflict,
|
||||
OnConflict::Add
|
||||
) && !matches!(
|
||||
provider.claims_imports.localpart.action,
|
||||
ImportAction::Force | ImportAction::Require
|
||||
) {
|
||||
return Err(annotate(figment::Error::custom(
|
||||
"The field `action` must be either `force` or `require` when `on_conflict` is set to `add`",
|
||||
)).into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -183,6 +202,26 @@ impl ImportAction {
|
||||
}
|
||||
}
|
||||
|
||||
/// How to handle an existing localpart claim
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OnConflict {
|
||||
/// Fails the sso login on conflict
|
||||
#[default]
|
||||
Fail,
|
||||
|
||||
/// Adds the oauth identity link, regardless of whether there is an existing
|
||||
/// link or not
|
||||
Add,
|
||||
}
|
||||
|
||||
impl OnConflict {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
const fn is_default(&self) -> bool {
|
||||
matches!(self, OnConflict::Fail)
|
||||
}
|
||||
}
|
||||
|
||||
/// What should be done for the subject attribute
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct SubjectImportPreference {
|
||||
@@ -211,6 +250,10 @@ pub struct LocalpartImportPreference {
|
||||
/// If not provided, the default template is `{{ user.preferred_username }}`
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
|
||||
/// How to handle conflicts on the claim, default value is `Fail`
|
||||
#[serde(default, skip_serializing_if = "OnConflict::is_default")]
|
||||
pub on_conflict: OnConflict,
|
||||
}
|
||||
|
||||
impl LocalpartImportPreference {
|
||||
@@ -408,6 +451,29 @@ fn is_default_scope(scope: &str) -> bool {
|
||||
scope == default_scope()
|
||||
}
|
||||
|
||||
/// What to do when receiving an OIDC Backchannel logout request.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OnBackchannelLogout {
|
||||
/// Do nothing
|
||||
#[default]
|
||||
DoNothing,
|
||||
|
||||
/// Only log out the MAS 'browser session' started by this OIDC session
|
||||
LogoutBrowserOnly,
|
||||
|
||||
/// Log out all sessions started by this OIDC session, including MAS
|
||||
/// 'browser sessions' and client sessions
|
||||
LogoutAll,
|
||||
}
|
||||
|
||||
impl OnBackchannelLogout {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
const fn is_default(&self) -> bool {
|
||||
matches!(self, OnBackchannelLogout::DoNothing)
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for one upstream OAuth 2 provider.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
@@ -583,4 +649,10 @@ pub struct Provider {
|
||||
/// Defaults to `false`.
|
||||
#[serde(default)]
|
||||
pub forward_login_hint: bool,
|
||||
|
||||
/// What to do when receiving an OIDC Backchannel logout request.
|
||||
///
|
||||
/// Defaults to "do_nothing".
|
||||
#[serde(default, skip_serializing_if = "OnBackchannelLogout::is_default")]
|
||||
pub on_backchannel_logout: OnBackchannelLogout,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use figment::{Figment, error::Error as FigmentError};
|
||||
use figment::Figment;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
/// Trait implemented by all configuration section to help loading specific part
|
||||
@@ -18,7 +18,10 @@ pub trait ConfigurationSection: Sized + DeserializeOwned {
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the configuration is invalid
|
||||
fn validate(&self, _figment: &Figment) -> Result<(), FigmentError> {
|
||||
fn validate(
|
||||
&self,
|
||||
_figment: &Figment,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -27,7 +30,9 @@ pub trait ConfigurationSection: Sized + DeserializeOwned {
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the configuration could not be loaded
|
||||
fn extract(figment: &Figment) -> Result<Self, FigmentError> {
|
||||
fn extract(
|
||||
figment: &Figment,
|
||||
) -> Result<Self, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let this: Self = if let Some(path) = Self::PATH {
|
||||
figment.extract_inner(path)?
|
||||
} else {
|
||||
@@ -49,7 +54,9 @@ pub trait ConfigurationSectionExt: ConfigurationSection + Default {
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the configuration section is invalid.
|
||||
fn extract_or_default(figment: &Figment) -> Result<Self, figment::Error> {
|
||||
fn extract_or_default(
|
||||
figment: &Figment,
|
||||
) -> Result<Self, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let this: Self = if let Some(path) = Self::PATH {
|
||||
// If the configuration section is not present, we return the default value
|
||||
if !figment.contains(path) {
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
[package]
|
||||
name = "mas-context"
|
||||
version.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use console::{Color, Style};
|
||||
use opentelemetry::{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{
|
||||
pin::Pin,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
mod fmt;
|
||||
mod future;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright 2025 New Vector Ltd.
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
# Please see LICENSE files in the repository root for full details.
|
||||
|
||||
[package]
|
||||
name = "mas-data-model"
|
||||
version.workspace = true
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use mas_data_model::UserAgent;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user